Browse Source

gw read: Sensible cylinder and revs defaults for AmigaDOS.
Print nice summary output of disk goodness.

Keir Fraser 4 years ago
parent
commit
068d2571e2

+ 13 - 8
scripts/greaseweazle/codec/amiga/amigados.py

@@ -11,6 +11,9 @@ from bitarray import bitarray
 
 from greaseweazle.track import MasterTrack, RawTrack
 
+default_cyls = (0,79)
+default_revs = 2
+
 sync_bytes = b'\x44\x89\x44\x89'
 sync = bitarray(endian='big')
 sync.frombytes(sync_bytes)
@@ -25,21 +28,26 @@ class AmigaDOS:
         self.sector = [None] * nsec
         self.map = [None] * nsec
 
+    def summary_string(self):
+        return ("AmigaDOS (%d/%d sectors)"
+                % (self.nsec - self.nr_missing(), self.nsec))
 
+    # private
     def exists(self, sec_id, togo):
         return ((self.sector[sec_id] is not None)
                 or (self.map[self.nsec-togo] is not None))
 
-
-    def nr_missing(self):
-        return len([sec for sec in self.sector if sec is None])
-
-
+    # private
     def add(self, sec_id, togo, label, data):
         assert not self.exists(sec_id, togo)
         self.sector[sec_id] = label, data
         self.map[self.nsec-togo] = sec_id
 
+    def has_sec(self, sec_id):
+        return self.sector[sec_id] is not None
+
+    def nr_missing(self):
+        return len([sec for sec in self.sector if sec is None])
 
     def get_adf_track(self):
         tdat = bytearray()
@@ -47,17 +55,14 @@ class AmigaDOS:
             tdat += sec[1] if sec is not None else bytes(512)
         return tdat
 
-
     def set_adf_track(self, tdat):
         self.map = list(range(self.nsec))
         for sec in self.map:
             self.sector[sec] = bytes(16), tdat[sec*512:(sec+1)*512]
 
-
     def flux_for_writeout(self):
         return self.raw_track().flux_for_writeout()
 
-
     def flux(self):
         return self.raw_track().flux()
 

+ 4 - 0
scripts/greaseweazle/flux.py

@@ -25,6 +25,10 @@ class Flux:
             rev += 1
         return s[:-1]
 
+    def summary_string(self):
+        return ("Raw Flux (%u flux in %.2fms)"
+                % (len(self.list), sum(self.list)*1000/self.sample_freq))
+
     def flux_for_writeout(self):
         error.check(self.splice == 0,
                     "Cannot write non-index-aligned raw flux")

+ 1 - 5
scripts/greaseweazle/tools/erase.py

@@ -50,11 +50,7 @@ def main(argv):
 
     try:
         usb = util.usb_open(args.device)
-        try:
-            util.with_drive_selected(erase, usb, args)
-        except:
-            print()
-            raise
+        util.with_drive_selected(erase, usb, args)
     except USB.CmdError as error:
         print("Command Failed: %s" % error)
 

+ 65 - 40
scripts/greaseweazle/tools/read.py

@@ -18,8 +18,7 @@ from greaseweazle import usb as USB
 from greaseweazle.flux import Flux
 
 
-def open_image(args):
-    image_class = util.get_image_class(args.file)
+def open_image(args, image_class):
     error.check(hasattr(image_class, 'to_file'),
                 "%s: Cannot create %s image files"
                 % (args.file, image_class.__name__))
@@ -60,60 +59,75 @@ def normalise_rpm(flux, rpm):
     return Flux([norm_to_index]*len(flux.index_list), norm_flux, freq)
 
 
-def read_and_normalise(usb, args):
-    flux = usb.read_track(args.revs)
+def read_and_normalise(usb, args, revs):
+    flux = usb.read_track(revs)
     if args.rpm is not None:
         flux = normalise_rpm(flux, args.rpm)
     return flux
 
 
-class Formatter:
-    def __init__(self):
-        self.length = 0
-    def print(self, s):
-        self.erase()
-        self.length = len(s)
-        print(s, end="", flush=True)
-    def erase(self):
-        l = self.length
-        print("\b"*l + " "*l + "\b"*l, end="", flush=True)
-        self.length = 0
-
-
 def read_with_retry(usb, args, cyl, side, decoder):
-    flux = read_and_normalise(usb, args)
+    flux = read_and_normalise(usb, args, args.revs)
     if decoder is None:
         return flux
     dat = decoder(cyl, side, flux)
     if dat.nr_missing() != 0:
-        formatter = Formatter()
         for retry in range(3):
-            formatter.print(" Retry %d" % (retry+1))
-            flux = read_and_normalise(usb, args)
+            print("T%u.%u: %s - %d sectors missing - Retrying (%d)"
+                  % (cyl, side, dat.summary_string(),
+                     dat.nr_missing(), retry+1))
+            flux = read_and_normalise(usb, args, max(args.revs, 3))
             dat.decode_raw(flux)
             if dat.nr_missing() == 0:
                 break
-        formatter.erase()
     return dat
 
 
+def print_summary(args, summary):
+    print("H. S: Cyls %d-%d -->" % (args.scyl, args.ecyl))
+    tot_sec = good_sec = 0
+    for side in range(0, args.nr_sides):
+        nsec = max(x.nsec for x in summary[side])
+        for sec in range(nsec):
+            print("%d.%2d: " % (side, sec), end="")
+            for cyl in range(args.scyl, args.ecyl+1):
+                s = summary[side][cyl-args.scyl]
+                if sec > s.nsec:
+                    print(" ", end="")
+                else:
+                    tot_sec += 1
+                    if s.has_sec(sec): good_sec += 1
+                    print("." if s.has_sec(sec) else "X", end="")
+            print()
+    print("Found %d sectors of %d (%d%%)" %
+          (good_sec, tot_sec, good_sec*100/tot_sec))
+
+
 def read_to_image(usb, args, image, decoder=None):
     """Reads a floppy disk and dumps it into a new image file.
     """
 
+    summary = [[],[]]
+
     for cyl in range(args.scyl, args.ecyl+1):
         for side in range(0, args.nr_sides):
-            print("\rReading Track %u.%u..." % (cyl, side), end="")
             usb.seek((cyl, cyl*2)[args.double_step], side)
             dat = read_with_retry(usb, args, cyl, side, decoder)
+            print("T%u.%u: %s" % (cyl, side, dat.summary_string()))
+            summary[side].append(dat)
             image.append_track(dat)
 
-    print()
+    if decoder is not None:
+        print_summary(args, summary)
 
     # Write the image file.
     with open(args.file, "wb") as f:
         f.write(image.get_image())
 
+def range_str(s, e):
+    str = "%d" % s
+    if s != e: str += "-%d" % e
+    return str
 
 def main(argv):
 
@@ -122,11 +136,11 @@ def main(argv):
     parser.add_argument("--drive", type=util.drive_letter, default='A',
                         help="drive to read (A,B,0,1,2)")
     parser.add_argument("--format", help="disk format")
-    parser.add_argument("--revs", type=int, default=3,
+    parser.add_argument("--revs", type=int,
                         help="number of revolutions to read per track")
-    parser.add_argument("--scyl", type=int, default=0,
+    parser.add_argument("--scyl", type=int,
                         help="first cylinder to read")
-    parser.add_argument("--ecyl", type=int, default=81,
+    parser.add_argument("--ecyl", type=int,
                         help="last cylinder to read")
     parser.add_argument("--single-sided", action="store_true",
                         help="single-sided read")
@@ -144,21 +158,32 @@ def main(argv):
 
     try:
         usb = util.usb_open(args.device)
-        image = open_image(args)
-        if not args.format and hasattr(image, 'default_format'):
-            args.format = image.default_format
+        image_class = util.get_image_class(args.file)
+        if not args.format and hasattr(image_class, 'default_format'):
+            args.format = image_class.default_format
         decoder = None
         if args.format:
-            mod = importlib.import_module('greaseweazle.codec.' + args.format)
-            decoder = mod.__dict__['decode_track']
-        try:
-            util.with_drive_selected(read_to_image, usb, args, image,
-                                     decoder=decoder)
-        except:
-            print()
-            raise
-    except USB.CmdError as error:
-        print("Command Failed: %s" % error)
+            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
+            if args.scyl is None: args.scyl = mod.default_cyls[0]
+            if args.ecyl is None: args.ecyl = mod.default_cyls[1]
+            if args.revs is None: args.revs = mod.default_revs
+        if args.scyl is None: args.scyl = 0
+        if args.ecyl is None: args.ecyl = 81
+        if args.revs is None: args.revs = 3
+        print("Reading c=%s s=%s revs=%d" %
+              (range_str(args.scyl, args.ecyl),
+               range_str(0, args.nr_sides-1),
+               args.revs))
+        image = open_image(args, image_class)
+        util.with_drive_selected(read_to_image, usb, args, image,
+                                 decoder=decoder)
+    except USB.CmdError as err:
+        print("Command Failed: %s" % err)
 
 
 if __name__ == "__main__":

+ 1 - 5
scripts/greaseweazle/tools/write.py

@@ -146,11 +146,7 @@ def main(argv):
     try:
         usb = util.usb_open(args.device)
         image = open_image(args)
-        try:
-            util.with_drive_selected(write_from_image, usb, args, image)
-        except:
-            print()
-            raise
+        util.with_drive_selected(write_from_image, usb, args, image)
     except USB.CmdError as error:
         print("Command Failed: %s" % error)
 

+ 2 - 2
scripts/greaseweazle/track.py

@@ -170,13 +170,13 @@ class RawTrack:
             s += str(binascii.hexlify(b.tobytes())) + "\n"
         return s[:-1]
 
-    
+
     def get_revolution(self, nr):
         start = sum(self.revolutions[:nr])
         end = start + self.revolutions[nr]
         return self.bitarray[start:end], self.timearray[start:end]
 
-    
+
     def append_revolutions(self, data):
 
         flux = data.flux()