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

This commit is contained in:
Keir Fraser
2021-12-12 14:42:25 +00:00
parent a938da99ac
commit 7b8bf48702
9 changed files with 136 additions and 46 deletions

View File

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

View File

@@ -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

View File

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

View File

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

View File

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

View File

@@ -9,37 +9,44 @@ from greaseweazle import error
from greaseweazle.codec.ibm import mfm
from .image import Image
import greaseweazle.codec.formats
class IMG(Image):
default_format = 'ibm.mfm'
def __init__(self):
def __init__(self, name, fmt):
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
def from_file(cls, name):
def from_file(cls, name, fmt):
with open(name, "rb") as f:
dat = f.read()
img = cls()
nsec = 18
tsz = nsec * 512
ncyl = len(dat) // (tsz*2)
img = cls(name, fmt)
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
@classmethod
def to_file(cls, name, fmt=None):
return cls(name, fmt)
def get_track(self, cyl, side):
if (cyl,side) not in self.to_track:
return None

View File

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

View File

@@ -16,10 +16,11 @@ from greaseweazle.tools import util
from greaseweazle import error
from greaseweazle import usb as USB
from greaseweazle.flux import Flux
from greaseweazle.codec import formats
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:
image.bitrate = args.rate
for opt, val in args.file_opts.items():
@@ -137,16 +138,18 @@ def main(argv):
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, def_tracks = None, None
decoder, def_tracks, args.fmt_cls = None, None, None
if args.format:
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:
def_tracks = util.TrackSet('c=0-81:h=0-1')
if args.revs is None: args.revs = 3

View File

@@ -14,11 +14,16 @@ import sys
from greaseweazle.tools import util
from greaseweazle import error, track
from greaseweazle import usb as USB
from greaseweazle.codec import formats
# Read and parse the image file.
def open_image(args):
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:
# 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("--drive", type=util.drive_letter, default='A',
help="drive to write (A,B,0,1,2)")
parser.add_argument("--format", help="disk format")
parser.add_argument("--tracks", type=util.TrackSet,
help="which tracks to write")
parser.add_argument("--erase-empty", action="store_true",
@@ -163,19 +169,30 @@ def main(argv):
args = parser.parse_args(argv[2:])
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)
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)
if args.precomp is not None:
s += "; %s" % args.precomp
print("Writing %s" % s)
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__":