Class: CPMDisk
Constant Summary
collapse
- RECORD_SIZE =
128
- BLOCK_SIZE =
1024
- BLOCKS_PER_EXTENT =
16
- RECORDS_PER_BLOCK =
BLOCK_SIZE/RECORD_SIZE
- RECORDS_PER_EXTENT =
RECORDS_PER_BLOCK*BLOCKS_PER_EXTENT
- SECTORS_IN_BLOCK =
[
[0x00,0x06,0x0C,0x03],
[0x09,0x0F,0x0E,0x05],
[0x0B,0x02,0x08,0x07],
[0x0D,0x04,0x0A,0x01]
]
Constants inherited
from DSK
DSK::DSK_FILE_LENGTH, DSK::DSK_IMAGE_EXTENSIONS, DSK::FILE_SYSTEMS, DSK::INTERLEAVES, DSK::NIB_FILE_LENGTH, DSK::SECTOR_ORDERS
Instance Attribute Summary collapse
Attributes inherited from DSK
#file_bytes, #sector_order, #source_filename, #track_count
Instance Method Summary
collapse
Methods inherited from DSK
#best_subclass, create_new, #disassemble_sector, #dump_sector, #files, #free_sector_list, #get_sector, #hex_dump, #is_cpm?, #is_dos33?, is_dsk_file?, #is_modified_dos?, #is_nadol?, #is_pascal?, #is_prodos?, read, #save_as, #set_boot_track, #set_sector
Constructor Details
#initialize(file_bytes, sector_order) ⇒ CPMDisk
55
56
57
58
|
# File 'lib/CPMDisk.rb', line 55
def initialize(file_bytes,sector_order)
super(file_bytes,sector_order)
self.read_catalog
end
|
Instance Attribute Details
#free_blocks ⇒ Object
Returns the value of attribute free_blocks.
15
16
17
|
# File 'lib/CPMDisk.rb', line 15
def free_blocks
@free_blocks
end
|
#free_directory_entries ⇒ Object
Returns the value of attribute free_directory_entries.
15
16
17
|
# File 'lib/CPMDisk.rb', line 15
def free_directory_entries
@free_directory_entries
end
|
Instance Method Details
#add_file(file) ⇒ Object
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
# File 'lib/CPMDisk.rb', line 86
def add_file(file)
raise "only CPMFiles may be added to CPM format disks!" unless file.kind_of?(CPMFile)
delete_file(file.full_filename)
total_record_count=(file.contents.length/RECORD_SIZE.to_f).ceil
total_blocks_needed=(total_record_count/RECORDS_PER_BLOCK.to_f).ceil
total_extents_needed=(total_blocks_needed/BLOCKS_PER_EXTENT.to_f).ceil
raise "#{total_blocks_needed} free blocks required, only #{free_blocks.length} available" unless total_blocks_needed<=free_blocks.length
raise "#{total_extents_needed} free directory entries required, only #{free_directory_entries.length} available" unless total_extents_needed<=free_directory_entries.length
catalog=get_block(0)+get_block(1)
padded_file_contents=file.contents+(0x1A.chr*BLOCK_SIZE)
0.upto(total_extents_needed-1) do |extent_no|
records_this_extent=(extent_no==(total_extents_needed-1) ? (total_record_count % RECORDS_PER_EXTENT):RECORDS_PER_EXTENT)
blocks_this_extent=(records_this_extent/RECORDS_PER_BLOCK.to_f).ceil
blocks_used=[0]*BLOCKS_PER_EXTENT
first_record_this_extent=extent_no*RECORDS_PER_EXTENT
contents_this_extent=padded_file_contents[(first_record_this_extent*RECORD_SIZE),blocks_this_extent*BLOCK_SIZE]
0.upto(blocks_this_extent-1) do |block_no|
this_block=free_blocks[block_no+(extent_no*BLOCKS_PER_EXTENT)]
set_block(this_block,contents_this_extent[block_no*BLOCK_SIZE,BLOCK_SIZE])
blocks_used[block_no]=this_block
end
dir_entry_no=free_directory_entries[extent_no]
dir_entry_start=dir_entry_no*0x20
dir_entry=[0,file.filename,file.file_type,extent_no,0,0,records_this_extent,blocks_used].flatten.pack("CA8A3C4C16")
catalog[dir_entry_start..dir_entry_start+0x1F]=dir_entry
end
set_block(0,catalog[0,BLOCK_SIZE])
set_block(1,catalog[BLOCK_SIZE,BLOCK_SIZE])
self.read_catalog
end
|
#delete_file(full_filename) ⇒ Object
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
# File 'lib/CPMDisk.rb', line 69
def delete_file(full_filename)
catalog=get_block(0)+get_block(1)
(partial_filename,file_ext)=CPMFile.split_filename(full_filename)
0.upto(63) do |dir_entry_no|
dir_entry_start=dir_entry_no*0x20
dir_entry=catalog[dir_entry_start..dir_entry_start+0x1F]
if (partial_filename==dir_entry[0x01..0x08].gsub(' ','')) && (file_ext==dir_entry[0x09..0x0B].gsub(' ',''))
catalog[dir_entry_start]=0xE5
end
end
set_block(0,catalog[0,BLOCK_SIZE])
set_block(1,catalog[BLOCK_SIZE,BLOCK_SIZE])
self.read_catalog
end
|
#dump_catalog ⇒ Object
45
46
47
48
49
50
51
52
|
# File 'lib/CPMDisk.rb', line 45
def dump_catalog
s=""
files.keys.sort.each { |file_name|
file=files[file_name]
s<< sprintf("% 12s % 6d \n",file.full_filename,file.contents.length)
}
s
end
|
#file_system ⇒ Object
60
61
62
|
# File 'lib/CPMDisk.rb', line 60
def file_system
:cpm
end
|
#get_block(block_no) ⇒ Object
29
30
31
32
33
34
|
# File 'lib/CPMDisk.rb', line 29
def get_block(block_no)
track_no= ((block_no/4)+3)%track_count
s=""
SECTORS_IN_BLOCK[block_no%4].each {|sector_no|s+=get_sector(track_no,sector_no)}
s
end
|
#make_file(filename, contents, file_options = {}) ⇒ Object
64
65
66
|
# File 'lib/CPMDisk.rb', line 64
def make_file(filename,contents,file_options={})
return CPMFile.new(filename,contents)
end
|
#read_catalog ⇒ Object
CPM DIR looks like this:
$00 User number, or E5h if it's a free entry
$01..0B Filename + extension: 8+3 characters
$0C..0D Extent number of this entry
$0E ???
$0F Number of 128-byte records allocated in this extant
$10..1F Allocation map for this directory entry
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
|
# File 'lib/CPMDisk.rb', line 132
def read_catalog
@free_blocks=[]
@free_directory_entries=[]
@files={}
(2..((track_count-3)*4)-1).each {|block| @free_blocks<<block}
catalog=get_block(0)+get_block(1)
0.upto(63) do |dir_entry_no|
dir_entry_start=dir_entry_no*0x20
dir_entry=catalog[dir_entry_start..dir_entry_start+0x1F]
if (dir_entry[0]<0x10) then
file_name=dir_entry[0x01..0x08].gsub(' ','')
file_ext=dir_entry[0x09..0x0B].gsub(' ','')
if (file_ext=="BAS") then
file=MBASICFile.new("#{file_name}.#{file_ext}",'')
else
file=CPMFile.new("#{file_name}.#{file_ext}",'')
end
if @files[file.full_filename].nil? then
@files[file.full_filename]=file
end
s=""
0x10.upto(0x1f) do |i|
block=dir_entry[i]
@free_blocks.delete(block)
s+=get_block(block) unless block==0
end
records_allocated=dir_entry[0x0F]
@files[file.full_filename].contents+=s[0,(records_allocated*128)]
else
@free_directory_entries<<dir_entry_no
end
end
end
|
#set_block(block_no, contents) ⇒ Object
36
37
38
39
40
41
42
43
|
# File 'lib/CPMDisk.rb', line 36
def set_block(block_no,contents)
raise "invalid block #{block_no} - length was #{contents.length}" unless contents.length==BLOCK_SIZE
track_no=(block_no/4)+3
0.upto(3) do |i|
sector_no=SECTORS_IN_BLOCK[block_no%4][i]
set_sector(track_no,sector_no,contents[(i*256),256])
end
end
|