Browse Source

New --format specifier for IBM tracks ibm.720 and ibm.1440

Keir Fraser 3 years ago
parent
commit
7b8bf48702

+ 0 - 1
scripts/greaseweazle/codec/amiga/amigados.py

@@ -11,7 +11,6 @@ from bitarray import bitarray
 
 
 from greaseweazle.track import MasterTrack, RawTrack
 from greaseweazle.track import MasterTrack, RawTrack
 
 
-default_trackset = 'c=0-79:h=0-1'
 default_revs = 1.1
 default_revs = 1.1
 
 
 sync_bytes = b'\x44\x89\x44\x89'
 sync_bytes = b'\x44\x89\x44\x89'

+ 48 - 0
scripts/greaseweazle/codec/formats.py

@@ -0,0 +1,48 @@
+
+from greaseweazle.tools import util
+
+class Format:
+    img_compatible = False
+    default_trackset = 'c=0-79:h=0-1'
+    def __init__(self):
+        self.tracks = util.TrackSet(self.default_trackset)
+
+class Format_Amiga_AmigaDOS(Format):
+    def __init__(self):
+        import greaseweazle.codec.amiga.amigados as m
+        self.fmt = m.AmigaDOS
+        self.default_revs = m.default_revs
+        self.decode_track = m.decode_track
+        super().__init__()
+    
+class Format_IBM_720(Format):
+    img_compatible = True
+    def __init__(self):
+        import greaseweazle.codec.ibm.mfm as m
+        self.fmt = m.IBM_MFM_720
+        self.default_revs = m.default_revs
+        self.decode_track = self.fmt.decode_track
+        super().__init__()
+    
+class Format_IBM_1440(Format):
+    img_compatible = True
+    def __init__(self):
+        import greaseweazle.codec.ibm.mfm as m
+        self.fmt = m.IBM_MFM_1M44
+        self.default_revs = m.default_revs
+        self.decode_track = self.fmt.decode_track
+        super().__init__()
+
+    
+formats = {
+    'amiga.amigados': Format_Amiga_AmigaDOS,
+    'ibm.720': Format_IBM_720,
+    'ibm.1440': Format_IBM_1440
+}
+
+def print_formats(f = None):
+    s = ''
+    for k, v in sorted(formats.items()):
+        if not f or f(k, v):
+            s += k if not s else ', ' + k
+    return s

+ 27 - 11
scripts/greaseweazle/codec/ibm/mfm.py

@@ -5,14 +5,13 @@
 # This is free and unencumbered software released into the public domain.
 # This is free and unencumbered software released into the public domain.
 # See the file COPYING for more details, or visit <http://unlicense.org>.
 # See the file COPYING for more details, or visit <http://unlicense.org>.
 
 
-import copy, heapq, struct
+import copy, heapq, struct, functools
 import itertools as it
 import itertools as it
 from bitarray import bitarray
 from bitarray import bitarray
 import crcmod.predefined
 import crcmod.predefined
 
 
 from greaseweazle.track import MasterTrack, RawTrack
 from greaseweazle.track import MasterTrack, RawTrack
 
 
-default_trackset = 'c=0-79:h=0-1'
 default_revs = 2
 default_revs = 2
 
 
 iam_sync_bytes = b'\x52\x24' * 3
 iam_sync_bytes = b'\x52\x24' * 3
@@ -113,8 +112,6 @@ class IBM_MFM:
 
 
     def __init__(self, cyl, head):
     def __init__(self, cyl, head):
         self.cyl, self.head = cyl, head
         self.cyl, self.head = cyl, head
-        self.time_per_rev = 0.2
-        self.clock = 1e-6
         self.sectors = []
         self.sectors = []
         self.iams = []
         self.iams = []
 
 
@@ -177,7 +174,7 @@ class IBM_MFM:
                     areas.append(Sector(idam, dam))
                     areas.append(Sector(idam, dam))
                 idam = None
                 idam = None
             else:
             else:
-                print("Unknown mark %02x" % mark)
+                pass #print("Unknown mark %02x" % mark)
 
 
         if idam is not None:
         if idam is not None:
             areas.append(idam)
             areas.append(idam)
@@ -289,12 +286,17 @@ class IBM_MFM_Formatted(IBM_MFM):
     def set_img_track(self, tdat):
     def set_img_track(self, tdat):
         pos = 0
         pos = 0
         self.sectors.sort(key = lambda x: x.idam.r)
         self.sectors.sort(key = lambda x: x.idam.r)
+        totsize = functools.reduce(lambda x, y: x + (128<<y.idam.n),
+                                   self.sectors, 0)
+        if len(tdat) < totsize:
+            tdat += bytes(totsize - len(tdat))
         for s in self.sectors:
         for s in self.sectors:
             s.crc = s.idam.crc = s.dam.crc = 0
             s.crc = s.idam.crc = s.dam.crc = 0
             size = 128 << s.idam.n
             size = 128 << s.idam.n
             s.dam.data = tdat[pos:pos+size]
             s.dam.data = tdat[pos:pos+size]
             pos += size
             pos += size
         self.sectors.sort(key = lambda x: x.start)
         self.sectors.sort(key = lambda x: x.start)
+        return totsize
 
 
     def get_img_track(self):
     def get_img_track(self):
         tdat = bytearray()
         tdat = bytearray()
@@ -342,15 +344,35 @@ class IBM_MFM_Predefined(IBM_MFM_Formatted):
             self.sectors.append(Sector(idam, dam))
             self.sectors.append(Sector(idam, dam))
             pos += 4 + size + 2 + self.gap_3
             pos += 4 + size + 2 + self.gap_3
 
 
+    @classmethod
+    def decode_track(cls, cyl, head, track):
+        mfm = cls(cyl, head)
+        mfm.decode_raw(track)
+        return mfm
+
 
 
 class IBM_MFM_1M44(IBM_MFM_Predefined):
 class IBM_MFM_1M44(IBM_MFM_Predefined):
 
 
+    time_per_rev = 0.2
+    clock = 1e-6
+    
     gap_3  = 84 # Post-DAM
     gap_3  = 84 # Post-DAM
     nsec   = 18
     nsec   = 18
     id0    = 1
     id0    = 1
     sz     = 2
     sz     = 2
 
 
 
 
+class IBM_MFM_720(IBM_MFM_Predefined):
+
+    time_per_rev = 0.2
+    clock = 2e-6
+    
+    gap_3  = 84 # Post-DAM
+    nsec   = 9
+    id0    = 1
+    sz     = 2
+
+
 def mfm_encode(dat):
 def mfm_encode(dat):
     y = 0
     y = 0
     out = bytearray()
     out = bytearray()
@@ -391,12 +413,6 @@ def decode(dat):
     return bytes(out)
     return bytes(out)
 
 
 
 
-def decode_track(cyl, head, track):
-    mfm = IBM_MFM_1M44(cyl, head)
-    mfm.decode_raw(track)
-    return mfm
-
-
 # Local variables:
 # Local variables:
 # python-indent: 4
 # python-indent: 4
 # End:
 # End:

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

@@ -156,7 +156,6 @@ class EDSKTrack:
 class EDSK(Image):
 class EDSK(Image):
 
 
     read_only = True
     read_only = True
-    default_format = 'ibm.mfm'
 
 
     def __init__(self):
     def __init__(self):
         self.to_track = dict()
         self.to_track = dict()

+ 2 - 1
scripts/greaseweazle/image/image.py

@@ -33,11 +33,12 @@ class Image:
 
 
     ## Default .to_file() constructor
     ## Default .to_file() constructor
     @classmethod
     @classmethod
-    def to_file(cls, name):
+    def to_file(cls, name, fmt=None):
         error.check(not cls.read_only,
         error.check(not cls.read_only,
                     "%s: Cannot create %s image files" % (name, cls.__name__))
                     "%s: Cannot create %s image files" % (name, cls.__name__))
         obj = cls()
         obj = cls()
         obj.filename = name
         obj.filename = name
+        obj.fmt = fmt
         return obj
         return obj
 
 
     ## Above methods and class variables can be overridden by subclasses.
     ## Above methods and class variables can be overridden by subclasses.

+ 22 - 15
scripts/greaseweazle/image/img.py

@@ -9,37 +9,44 @@ from greaseweazle import error
 from greaseweazle.codec.ibm import mfm
 from greaseweazle.codec.ibm import mfm
 from .image import Image
 from .image import Image
 
 
-class IMG(Image):
+import greaseweazle.codec.formats
 
 
-    default_format = 'ibm.mfm'
+class IMG(Image):
 
 
-    def __init__(self):
+    def __init__(self, name, fmt):
         self.to_track = dict()
         self.to_track = dict()
+        error.check(fmt is not None and fmt.img_compatible, """\
+IMG requires compatible format specifier, eg: --format=ibm.1440
+Compatible formats: %s"""
+                    % greaseweazle.codec.formats.print_formats(
+                        lambda k, v: v.img_compatible))
+        self.filename = name
+        self.fmt = fmt
 
 
 
 
     @classmethod
     @classmethod
-    def from_file(cls, name):
+    def from_file(cls, name, fmt):
 
 
         with open(name, "rb") as f:
         with open(name, "rb") as f:
             dat = f.read()
             dat = f.read()
 
 
-        img = cls()
-
-        nsec = 18
-        tsz = nsec * 512
-        ncyl = len(dat) // (tsz*2)
+        img = cls(name, fmt)
 
 
         pos = 0
         pos = 0
-        for cyl in range(ncyl):
-            for head in range(2):
-                track = mfm.IBM_MFM_1M44(cyl, head)
-                track.set_img_track(dat[pos:pos+tsz])
-                pos += tsz
-                img.to_track[cyl,head] = track
+        for t in fmt.tracks:
+            cyl, head = t.cyl, t.head
+            track = fmt.fmt(cyl, head)
+            pos += track.set_img_track(dat[pos:])
+            img.to_track[cyl,head] = track
 
 
         return img
         return img
 
 
 
 
+    @classmethod
+    def to_file(cls, name, fmt=None):
+        return cls(name, fmt)
+
+
     def get_track(self, cyl, side):
     def get_track(self, cyl, side):
         if (cyl,side) not in self.to_track:
         if (cyl,side) not in self.to_track:
             return None
             return None

+ 1 - 1
scripts/greaseweazle/image/kryoflux.py

@@ -41,7 +41,7 @@ class KryoFlux(Image):
 
 
 
 
     @classmethod
     @classmethod
-    def to_file(cls, name):
+    def to_file(cls, name, fmt=None):
         return cls(name)
         return cls(name)
 
 
     @classmethod
     @classmethod

+ 12 - 9
scripts/greaseweazle/tools/read.py

@@ -16,10 +16,11 @@ from greaseweazle.tools import util
 from greaseweazle import error
 from greaseweazle import error
 from greaseweazle import usb as USB
 from greaseweazle import usb as USB
 from greaseweazle.flux import Flux
 from greaseweazle.flux import Flux
+from greaseweazle.codec import formats
 
 
 
 
 def open_image(args, image_class):
 def open_image(args, image_class):
-    image = image_class.to_file(args.file)
+    image = image_class.to_file(args.file, args.fmt_cls)
     if args.rate is not None:
     if args.rate is not None:
         image.bitrate = args.rate
         image.bitrate = args.rate
     for opt, val in args.file_opts.items():
     for opt, val in args.file_opts.items():
@@ -137,16 +138,18 @@ def main(argv):
         image_class = util.get_image_class(args.file)
         image_class = util.get_image_class(args.file)
         if not args.format and hasattr(image_class, 'default_format'):
         if not args.format and hasattr(image_class, 'default_format'):
             args.format = image_class.default_format
             args.format = image_class.default_format
-        decoder, def_tracks = None, None
+        decoder, def_tracks, args.fmt_cls = None, None, None
         if args.format:
         if args.format:
             try:
             try:
-                mod = importlib.import_module('greaseweazle.codec.'
-                                              + args.format)
-                decoder = mod.decode_track
-            except (ModuleNotFoundError, AttributeError) as ex:
-                raise error.Fatal("Unknown format '%s'" % args.format) from ex
-            def_tracks = util.TrackSet(mod.default_trackset)
-            if args.revs is None: args.revs = mod.default_revs
+                args.fmt_cls = formats.formats[args.format]()
+            except KeyError as ex:
+                raise error.Fatal("""\
+Unknown format '%s'
+Known formats: %s"""
+                                  % (args.format, formats.print_formats()))
+            decoder = args.fmt_cls.decode_track
+            def_tracks = util.TrackSet(args.fmt_cls.default_trackset)
+            if args.revs is None: args.revs = args.fmt_cls.default_revs
         if def_tracks is None:
         if def_tracks is None:
             def_tracks = util.TrackSet('c=0-81:h=0-1')
             def_tracks = util.TrackSet('c=0-81:h=0-1')
         if args.revs is None: args.revs = 3
         if args.revs is None: args.revs = 3

+ 24 - 7
scripts/greaseweazle/tools/write.py

@@ -14,11 +14,16 @@ import sys
 from greaseweazle.tools import util
 from greaseweazle.tools import util
 from greaseweazle import error, track
 from greaseweazle import error, track
 from greaseweazle import usb as USB
 from greaseweazle import usb as USB
+from greaseweazle.codec import formats
 
 
 # Read and parse the image file.
 # Read and parse the image file.
 def open_image(args):
 def open_image(args):
     cls = util.get_image_class(args.file)
     cls = util.get_image_class(args.file)
-    return cls.from_file(args.file)
+    try:
+        image = cls.from_file(args.file)
+    except TypeError:
+        image = cls.from_file(args.file, args.fmt_cls)
+    return image
 
 
 # write_from_image:
 # write_from_image:
 # Writes the specified image file to floppy disk.
 # Writes the specified image file to floppy disk.
@@ -147,6 +152,7 @@ def main(argv):
     parser.add_argument("--device", help="greaseweazle device name")
     parser.add_argument("--device", help="greaseweazle device name")
     parser.add_argument("--drive", type=util.drive_letter, default='A',
     parser.add_argument("--drive", type=util.drive_letter, default='A',
                         help="drive to write (A,B,0,1,2)")
                         help="drive to write (A,B,0,1,2)")
+    parser.add_argument("--format", help="disk format")
     parser.add_argument("--tracks", type=util.TrackSet,
     parser.add_argument("--tracks", type=util.TrackSet,
                         help="which tracks to write")
                         help="which tracks to write")
     parser.add_argument("--erase-empty", action="store_true",
     parser.add_argument("--erase-empty", action="store_true",
@@ -163,19 +169,30 @@ def main(argv):
     args = parser.parse_args(argv[2:])
     args = parser.parse_args(argv[2:])
 
 
     try:
     try:
+        def_tracks, args.fmt_cls = None, None
+        if args.format:
+            try:
+                args.fmt_cls = formats.formats[args.format]()
+            except KeyError as ex:
+                raise error.Fatal("""\
+Unknown format '%s'
+Known formats: %s"""
+                                  % (args.format, formats.print_formats()))
+            def_tracks = util.TrackSet(args.fmt_cls.default_trackset)
+        if def_tracks is None:
+            def_tracks = util.TrackSet('c=0-81:h=0-1')
+        if args.tracks is not None:
+            def_tracks.update_from_trackspec(args.tracks.trackspec)
+        args.tracks = def_tracks
         usb = util.usb_open(args.device)
         usb = util.usb_open(args.device)
         image = open_image(args)
         image = open_image(args)
-        tracks = util.TrackSet('c=0-81:h=0-1')
-        if args.tracks is not None:
-            tracks.update_from_trackspec(args.tracks.trackspec)
-        args.tracks = tracks
         s = str(args.tracks)
         s = str(args.tracks)
         if args.precomp is not None:
         if args.precomp is not None:
             s += "; %s" % args.precomp
             s += "; %s" % args.precomp
         print("Writing %s" % s)
         print("Writing %s" % s)
         util.with_drive_selected(write_from_image, usb, args, image)
         util.with_drive_selected(write_from_image, usb, args, image)
-    except USB.CmdError as error:
-        print("Command Failed: %s" % error)
+    except USB.CmdError as err:
+        print("Command Failed: %s" % err)
 
 
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":