lib/DSK.rb in dsktool-0.4.2 vs lib/DSK.rb in dsktool-0.5.1
- old
+ new
@@ -8,25 +8,26 @@
# For manipulating DSK files, as created by ADT (http://adt.berlios.de) and ADTPRo (http://adtpro.sourceforge.net)
# used by many Apple 2 emulators.
#
class DSK
- FILE_SYSTEMS=[:prodos,:dos33,:nadol,:pascal,:unknown,:none]
+ FILE_SYSTEMS=[:prodos,:dos33,:nadol,:cpm,:pascal,:modified_dos,:unknown,:none]
SECTOR_ORDERS=[:physical,:dos]
- DSK_IMAGE_EXTENSIONS=[".dsk",".po",".do",".hdv"]
+ DSK_IMAGE_EXTENSIONS=[".dsk",".po",".do",".hdv",".nib"]
INTERLEAVES={
:physical=> [0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F],
- :dos=>[0x00,0x0E,0x0D,0x0C,0x0B,0x0A,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x0F]
+ :dos=>[0x00,0x0E,0x0D,0x0C,0x0B,0x0A,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x0F],
}
#does this filename have a suitable extension?
def DSK.is_dsk_file?(filename)
extension=File.extname(File.basename(filename,".gz")).downcase
DSK_IMAGE_EXTENSIONS.include?(extension)
end
DSK_FILE_LENGTH=143360
- attr_accessor :file_bytes,:sector_order,:track_count
+ NIB_FILE_LENGTH=232960
+ attr_accessor :file_bytes,:sector_order,:track_count,:source_filename
def file_system
:unknown
end
@@ -61,16 +62,42 @@
# see if these are reasonable values
vtoc_sector=get_sector(0x11,0)
(vtoc_sector[01]<=34) && (vtoc_sector[02]<=15) && (vtoc_sector[0x27]==0x7A) && (vtoc_sector[0x35]==0x10)
end
+ def is_modified_dos?(vtoc_track_no,vtoc_sector_no)
+ vtoc_sector=get_sector(vtoc_track_no,vtoc_sector_no)
+ (vtoc_sector[01]<=34) && (vtoc_sector[02]<=15) && (vtoc_sector[0x27]==0x7A) && (vtoc_sector[0x35]==0x10)
+ end
+
def is_nadol?(sector_order)
#currently ignores sector order
# track $00, sector $02 , bytes $11 - "NADOL"
(@file_bytes[0x00211..0x00215]=="NADOL")
end
+ def is_cpm?(sector_order)
+ #currently ignores sector order
+ #look for a valid looking CPM directory on track 3.
+ #go through each sector in order, for each sector, look at every 32nd byte, and see if it is a valid 'user number' (i.e. a number from 00..0F). Stop looking when you see an 'E5'.
+ #if an invalid user number is found before an E5, then the disk is NOT a CPM disk
+ found_0xE5_byte=false
+ [0x0,0x6,0xC,0x3,0x9,0xF,0xE,0x5].each do |sector_no|
+ sector=get_sector(3,INTERLEAVES[sector_order][sector_no])
+ [0x00,0x20,0x40,0x60,0x80,0xA0,0xC0,0xE0].each do |byte_number|
+ if (sector[byte_number]>0x0F && sector[byte_number]!=0xe5 && sector[byte_number]!=0x1F) then
+ # puts "found #{sprintf '%02x',sector[byte_number]} at #{sprintf '%02x', byte_number} sector #{sprintf '%02x', sector_no}"
+ return false
+ end
+ if (sector[byte_number]==0xe5) then
+ found_0xE5_byte=true
+ end
+ end
+ end
+ return found_0xE5_byte #if we've only seen 00 bytes, then it's not really a CPM
+ end
+
def is_prodos?(sector_order)
#block $02 - bytes $00/$01 are both $00, byte $04 is $F?,
#bytes $29-$2A = sectors ( 0x118 on a 35 track 5.25" disk)
first_sector_in_block_2=INTERLEAVES[sector_order][4]
first_sector_in_block_2=get_sector(0,4,sector_order)
@@ -93,13 +120,14 @@
raise "DSK files must be #{DSK_FILE_LENGTH} bytes long (was #{file_bytes.length} bytes)"
end
@file_bytes=file_bytes
@files={}
@sector_order=sector_order
- @track_count=file_bytes.length/4096
+ @track_count=file_bytes.length/4096
+ @source_filename="(unknown)"
end
-
+
#write out DSK to file
def save_as(filename)
if !(filename=~/\.gz$/).nil? then
require 'zlib'
f=Zlib::GzipWriter.new(open(filename,"wb"))
@@ -117,78 +145,100 @@
filesystem=:dos33 if filesystem==:dos
case filesystem
when :none
return DSK.new()
+ when :cpm
+ require 'CPMDisk'
+ return CPMDisk.new("\xe5"*DSK_FILE_LENGTH,:physical)
when :nadol
return DSK.read(File.dirname(__FILE__)+"/nadol_blank.po.gz")
when :dos33
return DSK.read(File.dirname(__FILE__)+"/dos33_blank.dsk.gz")
else
raise "initialisation of #{filesystem} file system not currently supported"
end
end
-
-
- #read in an existing DSK file (must exist)
- def DSK.read(filename)
- #is the file extension .gz?
- if !(filename=~/\.gz$/).nil? then
- require 'zlib'
- file_bytes=Zlib::GzipReader.new(open(filename,"rb")).read
- else
- file_bytes=open(filename,"rb").read
- end
-
-
- dsk=DSK.new(file_bytes)
- SECTOR_ORDERS.each do |sector_order|
- begin
+
+ #for a generic DSK,return an instance of subclass representing the the best match file system
+ def best_subclass
+ SECTOR_ORDERS.each do |sector_order|
+ begin
candidate_filesystem="unknown"
- if (dsk.is_dos33?(sector_order))
- require 'DOSDisk'
+ if (self.is_dos33?(sector_order))
+ require 'DOSDisk'
candidate_filesystem="DOS 3.3"
- dsk=DOSDisk.new(file_bytes,sector_order)
- break
+ return DOSDisk.new(self.file_bytes,sector_order)
end
- if (dsk.is_nadol?(sector_order))
- require 'NADOLDisk'
+ if (self.is_nadol?(sector_order))
+ require 'NADOLDisk'
candidate_filesystem="NADOL"
- dsk=NADOLDisk.new(file_bytes,sector_order)
- break
+ return NADOLDisk.new(self.file_bytes,sector_order)
end
-
- if (dsk.is_prodos?(sector_order))
- require 'ProDOSDisk'
- candidate_filesystem="ProDOS"
- dsk=ProDOSDisk.new(file_bytes,sector_order)
- break
+
+ if (self.is_prodos?(sector_order))
+ require 'ProDOSDisk'
+ candidate_filesystem="ProDOS"
+ return ProDOSDisk.new(self.file_bytes,sector_order)
end
- if (dsk.is_pascal?(sector_order))
- require 'PascalDisk'
+ if (self.is_pascal?(sector_order))
+ require 'PascalDisk'
candidate_filesystem="Pascal"
- dsk=PascalDisk.new(file_bytes,sector_order)
- break
+ return PascalDisk.new(self.file_bytes,sector_order)
end
-
- rescue Exception=>e
- STDERR<<"error while parsing #{filename} as #{candidate_filesystem} (sector order #{sector_order}\n"
- STDERR<<"#{e}\n"
- STDERR<<e.backtrace.join("\n")
- end
+
+ if (self.is_cpm?(sector_order))
+ require 'CPMDisk'
+ candidate_filesystem="CP/M"
+ return CPMDisk.new(self.file_bytes,sector_order)
+ end
+ rescue Exception=>e
+ STDERR<<"error while parsing #{self.source_filename} as #{candidate_filesystem} (sector order #{sector_order}\n"
+ STDERR<<"#{e}\n"
+ STDERR<<e.backtrace.join("\n")
+ end
end
- dsk
+
+ #if none of the above matched, look for a DOS image with a VTOC in the wrong spot
+ 0.upto(0x22) do |track|
+ if (self.is_modified_dos?(track,0))
+ require 'DOSDisk'
+ candidate_filesystem="MODIFIED DOS"
+ return DOSDisk.new(self.file_bytes,:physical,track,0)
+ end
+ end
+ #if we didn't find a better match, return self
+ self
+ end
+ #read in an existing DSK file (must exist)
+ def DSK.read(filename)
+ #is the file extension .gz?
+ if !(filename=~/\.gz$/).nil? then
+ require 'zlib'
+ file_bytes=Zlib::GzipReader.new(open(filename,"rb")).read
+ else
+ file_bytes=open(filename,"rb").read
+ end
+
+ if (file_bytes.length-NIB_FILE_LENGTH).abs<=1 then
+ require 'Nibbles'
+ dsk=Nibbles.make_dsk_from_nibbles(file_bytes)
+ else
+ dsk=DSK.new(file_bytes)
+ end
+ dsk.source_filename=filename
+ dsk.best_subclass
end
def get_sector(track,requested_sector,sector_order=@sector_order)
raise "bad sector #{requested_sector}" unless requested_sector.between?(0,0x0F)
raise "bad sector_order #{sector_order}" if INTERLEAVES[sector_order].nil?
physical_sector=INTERLEAVES[sector_order][requested_sector]
start_byte=track*16*256+physical_sector*256
- @file_bytes[start_byte..start_byte+255]
+ @file_bytes[start_byte,256]
end
def set_sector(track,sector,contents)
physical_sector=INTERLEAVES[@sector_order][sector]
start_byte=track*16*256+physical_sector*256
@@ -203,10 +253,10 @@
def set_boot_track(contents)
sectors_needed=(contents.length / 256)+1
raise "boot code can't exceed 16 sectors" if sectors_needed>16
s=sectors_needed.chr+contents
for sector in 0..sectors_needed-1
- sector_data=s[sector*256..255+sector*256]
+ sector_data=s[sector*256,256]
set_sector(0,sector,sector_data)
end
end
def get_block(block_no)