Эх сурвалжийг харах

Initial support for writing .dsk (EDSK) image files.
Refs #55

Keir Fraser 4 жил өмнө
parent
commit
b65305f9f7

+ 54 - 30
scripts/greaseweazle/codec/ibm/mfm.py

@@ -5,7 +5,7 @@
 # This is free and unencumbered software released into the public domain.
 # See the file COPYING for more details, or visit <http://unlicense.org>.
 
-import heapq, struct
+import copy, heapq, struct
 import itertools as it
 from bitarray import bitarray
 import crcmod.predefined
@@ -54,6 +54,9 @@ class IDAM(TrackArea):
         return (super().__eq__(x)
                 and self.c == x.c and self.h == x.h
                 and self.r == x.r and self.n == x.n)
+    def __copy__(self):
+        return IDAM(self.start, self.end, self.crc,
+                    self.c, self.h, self.r, self.n)
 
 class DAM(TrackArea):
     def __init__(self, start, end, crc, mark, data=None):
@@ -66,6 +69,8 @@ class DAM(TrackArea):
         return (super().__eq__(x)
                 and self.mark == x.mark
                 and self.data == x.data)
+    def __copy__(self):
+        return DAM(self.start, self.end, self.crc, self.mark, self.data)
 
 class Sector(TrackArea):
     def __init__(self, idam, dam):
@@ -89,6 +94,8 @@ class Sector(TrackArea):
 class IAM(TrackArea):
     def __str__(self):
         return "IAM: %6d-%6d" % (self.start, self.end)
+    def __copy__(self):
+        return IAM(self.start, self.end)
     
 class IBM_MFM:
 
@@ -98,10 +105,12 @@ class IBM_MFM:
     DDAM = 0xf8
 
     gap_presync = 12
-    filler = 0x4e
     
     def __init__(self, cyl, head):
         self.cyl, self.head = cyl, head
+        self.filler = 0x4e
+        self.time_per_rev = 0.2
+        self.clock = 1e-6
         self.sectors = []
         self.iams = []
 
@@ -127,7 +136,7 @@ class IBM_MFM:
 
     def decode_raw(self, track):
         track.cue_at_index()
-        raw = RawTrack(clock = 1e-6, data = track)
+        raw = RawTrack(clock = self.clock, data = track)
         bits, _ = raw.get_all_data()
 
         areas = []
@@ -232,25 +241,18 @@ class IBM_MFM:
                 t += encode(dam[3:])
 
         # Add the pre-index gap.
-        tlen = 200000//16
+        tlen = int((self.time_per_rev / self.clock) // 16)
         gap = max(tlen - len(t)//2, 0)
         t += encode(bytes([self.filler] * gap))
 
         track = MasterTrack(
             bits = mfm_encode(t),
-            time_per_rev = 0.2)
+            time_per_rev = self.time_per_rev)
         track.verify = self
         track.verify_revs = default_revs
         return track
 
 
-    def verify_track(self, flux):
-        readback_track = decode_track(self.cyl, self.head, flux)
-        if readback_track.nr_missing() != 0:
-            return False
-        return self.sectors == readback_track.sectors
-
-
 class IBM_MFM_Formatted(IBM_MFM):
 
     gap_4a = 80 # Post-Index
@@ -262,22 +264,6 @@ class IBM_MFM_Formatted(IBM_MFM):
         super().__init__(cyl, head)
         self.raw_iams, self.raw_sectors = [], []
 
-        pos = self.gap_4a
-        if self.gap_1 is not None:
-            self.iams = [IAM(pos*16,(pos+4)*16)]
-            pos += self.gap_1
-
-        for i in range(self.nsec):
-            pos += self.gap_presync
-            idam = IDAM(pos*16, (pos+10)*16, 0xffff,
-                        c=cyl, h=head, r=self.id0+i, n = self.sz)
-            pos += 10 + self.gap_2 + self.gap_presync
-            size = 128 << self.sz
-            dam = DAM(pos*16, (pos+4+size+2)*16, 0xffff,
-                      mark=self.DAM, data=bytes(size))
-            self.sectors.append(Sector(idam, dam))
-            pos += 4 + size + 2 + self.gap_3
-
     def decode_raw(self, track):
         iams, sectors = self.iams, self.sectors
         self.iams, self.sectors = self.raw_iams, self.raw_sectors
@@ -314,8 +300,46 @@ class IBM_MFM_Formatted(IBM_MFM):
             tdat += s.dam.data
         return tdat
         
-    
-class IBM_MFM_1M44(IBM_MFM_Formatted):
+    def verify_track(self, flux):
+        readback_track = IBM_MFM_Formatted(self.cyl, self.head)
+        readback_track.clock = self.clock
+        readback_track.time_per_rev = self.time_per_rev
+        for x in self.iams:
+            readback_track.iams.append(copy.copy(x))
+        for x in self.sectors:
+            idam, dam = copy.copy(x.idam), copy.copy(x.dam)
+            idam.crc, dam.crc = 0xffff, 0xffff
+            readback_track.sectors.append(Sector(idam, dam))
+        readback_track.decode_raw(flux)
+        if readback_track.nr_missing() != 0:
+            return False
+        return self.sectors == readback_track.sectors
+
+
+class IBM_MFM_Predefined(IBM_MFM_Formatted):
+
+    def __init__(self, cyl, head):
+
+        super().__init__(cyl, head)
+
+        pos = self.gap_4a
+        if self.gap_1 is not None:
+            self.iams = [IAM(pos*16,(pos+4)*16)]
+            pos += 4 + self.gap_1
+
+        for i in range(self.nsec):
+            pos += self.gap_presync
+            idam = IDAM(pos*16, (pos+10)*16, 0xffff,
+                        c=cyl, h=head, r=self.id0+i, n = self.sz)
+            pos += 10 + self.gap_2 + self.gap_presync
+            size = 128 << self.sz
+            dam = DAM(pos*16, (pos+4+size+2)*16, 0xffff,
+                      mark=self.DAM, data=bytes(size))
+            self.sectors.append(Sector(idam, dam))
+            pos += 4 + size + 2 + self.gap_3
+
+
+class IBM_MFM_1M44(IBM_MFM_Predefined):
 
     gap_3  = 84 # Post-DAM
     nsec   = 18

+ 115 - 0
scripts/greaseweazle/image/edsk.py

@@ -0,0 +1,115 @@
+# greaseweazle/image/edsk.py
+#
+# Written & released by Keir Fraser <keir.xen@gmail.com>
+# 
+# This is free and unencumbered software released into the public domain.
+# See the file COPYING for more details, or visit <http://unlicense.org>.
+
+import math, struct
+import itertools as it
+
+from greaseweazle import error
+from greaseweazle.codec.ibm import mfm
+from .image import Image
+
+class EDSK(Image):
+
+    read_only = True
+    default_format = 'ibm.mfm'
+
+    clock = 2e-6
+    time_per_rev = 0.2
+
+    def __init__(self):
+        self.to_track = dict()
+
+    @classmethod
+    def from_file(cls, name):
+
+        with open(name, "rb") as f:
+            dat = f.read()
+
+        edsk = cls()
+
+        sig, creator, ncyls, nsides, track_sz = struct.unpack(
+            '<34s14s2BH', dat[:52])
+        if sig[:8] == b'MV - CPC':
+            extended = False
+        elif sig[:16] == b'EXTENDED CPC DSK':
+            extended = True
+        else:
+            raise error.Fatal('Unrecognised CPC DSK file: bad signature')
+
+        if extended:
+            tsizes = list(dat[52:52+ncyls*nsides])
+            tsizes = list(map(lambda x: x*256, tsizes))
+        else:
+            raise error.Fatal('Standard CPC DSK file not yet supported')
+
+        o = 256 # skip disk header and track-size table
+        for tsize in tsizes:
+            if tsize == 0:
+                continue
+            sig, cyl, head, sec_sz, nsecs, gap_3, filler = struct.unpack(
+                '<12s4x2B2x4B', dat[o:o+24])
+            error.check(sig == b'Track-Info\r\n',
+                        'EDSK: Missing track header')
+            error.check((cyl, head) not in edsk.to_track,
+                        'EDSK: Track specified twice')
+            while True:
+                track = mfm.IBM_MFM_Formatted(cyl, head)
+                track.filler = filler
+                track.clock = cls().clock
+                track.time_per_rev = cls().time_per_rev
+                pos = track.gap_4a
+                track.iams = [mfm.IAM(pos*16,(pos+4)*16)]
+                pos += 4 + track.gap_1
+                secs = dat[o+24:o+24+8*nsecs]
+                data_pos = o + 256 # skip track header and sector-info table
+                while secs:
+                    c, h, r, n, stat1, stat2, actual_length = struct.unpack(
+                        '<6BH', secs[:8])
+                    secs = secs[8:]
+                    pos += track.gap_presync
+                    idam = mfm.IDAM(pos*16, (pos+10)*16, 0,
+                                    c=c, h=h, r=r, n=n)
+                    pos += 10 + track.gap_2 + track.gap_presync
+                    size = 128 << n
+                    error.check(size == actual_length,
+                                'EDSK: Weird sector size (copy protection?)')
+                    error.check(stat1 == 0 and stat2 == 0,
+                                'EDSK: Mangled sector (copy protection?)')
+                    sec_data = dat[data_pos:data_pos+size]
+                    dam = mfm.DAM(pos*16, (pos+4+size+2)*16, 0,
+                                  mark=track.DAM, data=sec_data)
+                    track.sectors.append(mfm.Sector(idam, dam))
+                    pos += 4 + size + 2 + gap_3
+                    data_pos += size
+                # Some EDSK images have bogus GAP3 values. If the track is too
+                # long to comfortably fit in 300rpm at double density, shrink
+                # GAP3 as far as necessary.
+                tracklen = int((track.time_per_rev / track.clock) / 16)
+                overhang = int(pos - tracklen*0.99)
+                if overhang <= 0:
+                    break
+                new_gap_3 = gap_3 - math.ceil(overhang / nsecs)
+                error.check(new_gap_3 >= 0,
+                            'EDSK: Track %d.%d is too long '
+                            '(%d bits @ GAP3=%d; %d bits @ GAP3=0)'
+                            % (cyl, head, pos*16, gap_3, (pos-gap_3*nsecs)*16))
+                gap_3 = new_gap_3
+            edsk.to_track[cyl,head] = track
+            o += tsize
+
+        return edsk
+
+
+    def get_track(self, cyl, side):
+        if (cyl,side) not in self.to_track:
+            return None
+        return self.to_track[cyl,side].raw_track()
+
+
+# Local variables:
+# python-indent: 4
+# End:

+ 1 - 0
scripts/greaseweazle/tools/util.py

@@ -164,6 +164,7 @@ def get_image_class(name):
                     '.ima': 'IMG',
                     '.img': 'IMG',
                     '.ipf': 'IPF',
+                    '.dsk': 'EDSK',
                     '.raw': 'KryoFlux' }
     if os.path.isdir(name):
         typename = 'KryoFlux'