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], #block 0 [0x09,0x0F,0x0E,0x05], #block 1 [0x0B,0x02,0x08,0x07], #block 2 [0x0D,0x04,0x0A,0x01] #block 3 ]
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
-
#free_blocks ⇒ Object
Returns the value of attribute free_blocks.
-
#free_directory_entries ⇒ Object
Returns the value of attribute free_directory_entries.
Attributes inherited from DSK
#file_bytes, #sector_order, #source_filename, #track_count
Instance Method Summary collapse
- #add_file(file) ⇒ Object
- #delete_file(full_filename) ⇒ Object
- #dump_catalog ⇒ Object
- #file_system ⇒ Object
- #get_block(block_no) ⇒ Object
-
#initialize(file_bytes, sector_order) ⇒ CPMDisk
constructor
A new instance of CPMDisk.
- #make_file(filename, contents, file_options = {}) ⇒ Object
-
#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.
- #set_block(block_no, contents) ⇒ Object
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
Returns a new instance of 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) #so we can overwrite the file if it already exists 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(' ','')) #we found a matching filename, so set the 'user number' field to a 'blank' entry 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,={}) 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={} #first two blocks are where the catalog lives (2..((track_count-3)*4)-1).each {|block| @free_blocks<<block} catalog=get_block(0)+get_block(1) #require 'DumpUtilities' #puts DumpUtilities.hex_dump(catalog) 0.upto(63) do |dir_entry_no| #puts 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 |