|  | @@ -0,0 +1,369 @@
 | 
	
		
			
				|  |  | +# greaseweazle/codec/ibm/mfm.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 heapq, struct
 | 
	
		
			
				|  |  | +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
 | 
	
		
			
				|  |  | +iam_sync = bitarray(endian='big')
 | 
	
		
			
				|  |  | +iam_sync.frombytes(iam_sync_bytes)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +sync_bytes = b'\x44\x89' * 3
 | 
	
		
			
				|  |  | +sync = bitarray(endian='big')
 | 
	
		
			
				|  |  | +sync.frombytes(sync_bytes)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +crc16 = crcmod.predefined.Crc('crc-ccitt-false')
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class TrackArea:
 | 
	
		
			
				|  |  | +    def __init__(self, start, end, crc=None):
 | 
	
		
			
				|  |  | +        self.start = start
 | 
	
		
			
				|  |  | +        self.end = end
 | 
	
		
			
				|  |  | +        self.crc = crc
 | 
	
		
			
				|  |  | +    def delta(self, delta):
 | 
	
		
			
				|  |  | +        self.start -= delta
 | 
	
		
			
				|  |  | +        self.end -= delta
 | 
	
		
			
				|  |  | +    def __eq__(self, x):
 | 
	
		
			
				|  |  | +        return (isinstance(x, type(self))
 | 
	
		
			
				|  |  | +                and self.start == x.start
 | 
	
		
			
				|  |  | +                and self.end == x.end
 | 
	
		
			
				|  |  | +                and self.crc == x.crc)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class IDAM(TrackArea):
 | 
	
		
			
				|  |  | +    def __init__(self, start, end, crc, c, h, r, n):
 | 
	
		
			
				|  |  | +        super().__init__(start, end, crc)
 | 
	
		
			
				|  |  | +        self.c = c
 | 
	
		
			
				|  |  | +        self.h = h
 | 
	
		
			
				|  |  | +        self.r = r
 | 
	
		
			
				|  |  | +        self.n = n
 | 
	
		
			
				|  |  | +    def __str__(self):
 | 
	
		
			
				|  |  | +        return ("IDAM:%6d-%6d c=%02x h=%02x r=%02x n=%02x CRC:%04x"
 | 
	
		
			
				|  |  | +                % (self.start, self.end, self.c, self.h, self.r, self.n,
 | 
	
		
			
				|  |  | +                   self.crc))
 | 
	
		
			
				|  |  | +    def __eq__(self, x):
 | 
	
		
			
				|  |  | +        return (super().__eq__(x)
 | 
	
		
			
				|  |  | +                and self.c == x.c and self.h == x.h
 | 
	
		
			
				|  |  | +                and self.r == x.r and self.n == x.n)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class DAM(TrackArea):
 | 
	
		
			
				|  |  | +    def __init__(self, start, end, crc, mark, data=None):
 | 
	
		
			
				|  |  | +        super().__init__(start, end, crc)
 | 
	
		
			
				|  |  | +        self.mark = mark
 | 
	
		
			
				|  |  | +        self.data = data
 | 
	
		
			
				|  |  | +    def __str__(self):
 | 
	
		
			
				|  |  | +        return "DAM: %6d-%6d mark=%02x" % (self.start, self.end, self.mark)
 | 
	
		
			
				|  |  | +    def __eq__(self, x):
 | 
	
		
			
				|  |  | +        return (super().__eq__(x)
 | 
	
		
			
				|  |  | +                and self.mark == x.mark
 | 
	
		
			
				|  |  | +                and self.data == x.data)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +class Sector(TrackArea):
 | 
	
		
			
				|  |  | +    def __init__(self, idam, dam):
 | 
	
		
			
				|  |  | +        super().__init__(idam.start, dam.end, idam.crc | dam.crc)
 | 
	
		
			
				|  |  | +        self.idam = idam
 | 
	
		
			
				|  |  | +        self.dam = dam
 | 
	
		
			
				|  |  | +    def __str__(self):
 | 
	
		
			
				|  |  | +        s = "Sec: %6d-%6d CRC:%04x\n" % (self.start, self.end, self.crc)
 | 
	
		
			
				|  |  | +        s += " " + str(self.idam) + "\n"
 | 
	
		
			
				|  |  | +        s += " " + str(self.dam)
 | 
	
		
			
				|  |  | +        return s
 | 
	
		
			
				|  |  | +    def delta(self, delta):
 | 
	
		
			
				|  |  | +        super().delta(delta)
 | 
	
		
			
				|  |  | +        self.idam.delta(delta)
 | 
	
		
			
				|  |  | +        self.dam.delta(delta)
 | 
	
		
			
				|  |  | +    def __eq__(self, x):
 | 
	
		
			
				|  |  | +        return (super().__eq__(x)
 | 
	
		
			
				|  |  | +                and self.idam == x.idam
 | 
	
		
			
				|  |  | +                and self.dam == x.dam)
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +class IAM(TrackArea):
 | 
	
		
			
				|  |  | +    def __str__(self):
 | 
	
		
			
				|  |  | +        return "IAM: %6d-%6d" % (self.start, self.end)
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +class IBM_MFM:
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    IAM  = 0xfc
 | 
	
		
			
				|  |  | +    IDAM = 0xfe
 | 
	
		
			
				|  |  | +    DAM  = 0xfb
 | 
	
		
			
				|  |  | +    DDAM = 0xf8
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    gap_presync = 12
 | 
	
		
			
				|  |  | +    filler = 0x4e
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +    def __init__(self, cyl, head):
 | 
	
		
			
				|  |  | +        self.cyl, self.head = cyl, head
 | 
	
		
			
				|  |  | +        self.sectors = []
 | 
	
		
			
				|  |  | +        self.iams = []
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def summary_string(self):
 | 
	
		
			
				|  |  | +        return "IBM MFM"
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def has_sec(self, sec_id):
 | 
	
		
			
				|  |  | +        return self.sectors[sec_id].crc == 0
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def nr_missing(self):
 | 
	
		
			
				|  |  | +        return len(list(filter(lambda x: x.crc != 0, self.sectors)))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def flux_for_writeout(self, *args, **kwargs):
 | 
	
		
			
				|  |  | +        return self.raw_track().flux_for_writeout(args, kwargs)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def flux(self, *args, **kwargs):
 | 
	
		
			
				|  |  | +        return self.raw_track().flux(args, kwargs)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def decode_raw(self, track):
 | 
	
		
			
				|  |  | +        track.cue_at_index()
 | 
	
		
			
				|  |  | +        raw = RawTrack(clock = 1e-6, data = track)
 | 
	
		
			
				|  |  | +        bits, _ = raw.get_all_data()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        areas = []
 | 
	
		
			
				|  |  | +        idam = None
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        ## 1. Calculate offsets within dump
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +        for offs in bits.itersearch(iam_sync):
 | 
	
		
			
				|  |  | +            mark = decode(bits[offs+3*16:offs+4*16].tobytes())[0]
 | 
	
		
			
				|  |  | +            if mark == IBM_MFM.IAM:
 | 
	
		
			
				|  |  | +                areas.append(IAM(offs, offs+4*16))
 | 
	
		
			
				|  |  | +                self.has_iam = True
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        for offs in bits.itersearch(sync):
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            mark = decode(bits[offs+3*16:offs+4*16].tobytes())[0]
 | 
	
		
			
				|  |  | +            if mark == IBM_MFM.IDAM:
 | 
	
		
			
				|  |  | +                s, e = offs, offs+10*16
 | 
	
		
			
				|  |  | +                b = decode(bits[s:e].tobytes())
 | 
	
		
			
				|  |  | +                c,h,r,n = struct.unpack(">4x4B2x", b)
 | 
	
		
			
				|  |  | +                crc = crc16.new(b).crcValue
 | 
	
		
			
				|  |  | +                if idam is not None:
 | 
	
		
			
				|  |  | +                    areas.append(idam)
 | 
	
		
			
				|  |  | +                idam = IDAM(s, e, crc, c=c, h=h, r=r, n=n)
 | 
	
		
			
				|  |  | +            elif mark == IBM_MFM.DAM or mark == IBM_MFM.DDAM:
 | 
	
		
			
				|  |  | +                if idam is None or idam.end - offs > 1000:
 | 
	
		
			
				|  |  | +                    areas.append(DAM(offs, offs+4*16, 0xffff, mark=mark))
 | 
	
		
			
				|  |  | +                else:
 | 
	
		
			
				|  |  | +                    sz = 128 << idam.n
 | 
	
		
			
				|  |  | +                    s, e = offs, offs+(4+sz+2)*16
 | 
	
		
			
				|  |  | +                    b = decode(bits[s:e].tobytes())
 | 
	
		
			
				|  |  | +                    crc = crc16.new(b).crcValue
 | 
	
		
			
				|  |  | +                    dam = DAM(s, e, crc, mark=mark, data=b[4:-2])
 | 
	
		
			
				|  |  | +                    areas.append(Sector(idam, dam))
 | 
	
		
			
				|  |  | +                idam = None
 | 
	
		
			
				|  |  | +            else:
 | 
	
		
			
				|  |  | +                print("Unknown mark %02x" % mark)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if idam is not None:
 | 
	
		
			
				|  |  | +            areas.append(idam)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        # Convert to offsets within track
 | 
	
		
			
				|  |  | +        areas.sort(key=lambda x:x.start)
 | 
	
		
			
				|  |  | +        index = iter(raw.revolutions)
 | 
	
		
			
				|  |  | +        p, n = 0, next(index)
 | 
	
		
			
				|  |  | +        for a in areas:
 | 
	
		
			
				|  |  | +            if a.start >= n:
 | 
	
		
			
				|  |  | +                p = n
 | 
	
		
			
				|  |  | +                try:
 | 
	
		
			
				|  |  | +                    n = next(index)
 | 
	
		
			
				|  |  | +                except StopIteration:
 | 
	
		
			
				|  |  | +                    n = float('inf')
 | 
	
		
			
				|  |  | +            a.delta(p)
 | 
	
		
			
				|  |  | +        areas.sort(key=lambda x:x.start)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        # Add to the deduped lists
 | 
	
		
			
				|  |  | +        for a in areas:
 | 
	
		
			
				|  |  | +            match = False
 | 
	
		
			
				|  |  | +            if isinstance(a, IAM):
 | 
	
		
			
				|  |  | +                list = self.iams
 | 
	
		
			
				|  |  | +            elif isinstance(a, Sector):
 | 
	
		
			
				|  |  | +                list = self.sectors
 | 
	
		
			
				|  |  | +            else:
 | 
	
		
			
				|  |  | +                continue
 | 
	
		
			
				|  |  | +            for s in list:
 | 
	
		
			
				|  |  | +                if abs(s.start - a.start) < 1000:
 | 
	
		
			
				|  |  | +                    match = True
 | 
	
		
			
				|  |  | +                    break
 | 
	
		
			
				|  |  | +            if match and list == self.sectors and s.crc != 0 and a.crc == 0:
 | 
	
		
			
				|  |  | +                self.sectors = [x for x in self.sectors if x != a]
 | 
	
		
			
				|  |  | +                match = False
 | 
	
		
			
				|  |  | +            if not match:
 | 
	
		
			
				|  |  | +                list.append(a)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def raw_track(self):
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        areas = heapq.merge(self.iams, self.sectors, key=lambda x:x.start)
 | 
	
		
			
				|  |  | +        t = bytes()
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        for a in areas:
 | 
	
		
			
				|  |  | +            start = a.start//16 - self.gap_presync
 | 
	
		
			
				|  |  | +            gap = max(start - len(t)//2, 0)
 | 
	
		
			
				|  |  | +            t += encode(bytes([self.filler] * gap))
 | 
	
		
			
				|  |  | +            t += encode(bytes(self.gap_presync))
 | 
	
		
			
				|  |  | +            if isinstance(a, IAM):
 | 
	
		
			
				|  |  | +                t += iam_sync_bytes
 | 
	
		
			
				|  |  | +                t += encode(bytes([self.IAM]))
 | 
	
		
			
				|  |  | +            elif isinstance(a, Sector):
 | 
	
		
			
				|  |  | +                t += sync_bytes
 | 
	
		
			
				|  |  | +                idam = bytes([0xa1, 0xa1, 0xa1, self.IDAM,
 | 
	
		
			
				|  |  | +                              a.idam.c, a.idam.h, a.idam.r, a.idam.n])
 | 
	
		
			
				|  |  | +                idam += struct.pack('>H', crc16.new(idam).crcValue)
 | 
	
		
			
				|  |  | +                t += encode(idam[3:])
 | 
	
		
			
				|  |  | +                start = a.dam.start//16 - self.gap_presync
 | 
	
		
			
				|  |  | +                gap = max(start - len(t)//2, 0)
 | 
	
		
			
				|  |  | +                t += encode(bytes([self.filler] * gap))
 | 
	
		
			
				|  |  | +                t += encode(bytes(self.gap_presync))
 | 
	
		
			
				|  |  | +                t += sync_bytes
 | 
	
		
			
				|  |  | +                dam = bytes([0xa1, 0xa1, 0xa1, a.dam.mark]) + a.dam.data
 | 
	
		
			
				|  |  | +                dam += struct.pack('>H', crc16.new(dam).crcValue)
 | 
	
		
			
				|  |  | +                t += encode(dam[3:])
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        # Add the pre-index gap.
 | 
	
		
			
				|  |  | +        tlen = 200000//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)
 | 
	
		
			
				|  |  | +        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
 | 
	
		
			
				|  |  | +    gap_1  = 50 # Post-IAM
 | 
	
		
			
				|  |  | +    gap_2  = 22 # Post-IDAM
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def __init__(self, cyl, head):
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        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
 | 
	
		
			
				|  |  | +            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))
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def decode_raw(self, track):
 | 
	
		
			
				|  |  | +        iams, sectors = self.iams, self.sectors
 | 
	
		
			
				|  |  | +        self.iams, self.sectors = self.raw_iams, self.raw_sectors
 | 
	
		
			
				|  |  | +        super().decode_raw(track)
 | 
	
		
			
				|  |  | +        self.iams, self.sectors = iams, sectors
 | 
	
		
			
				|  |  | +        for r in self.raw_sectors:
 | 
	
		
			
				|  |  | +            if r.idam.crc != 0:
 | 
	
		
			
				|  |  | +                continue
 | 
	
		
			
				|  |  | +            for s in self.sectors:
 | 
	
		
			
				|  |  | +                if (s.idam.c == r.idam.c and
 | 
	
		
			
				|  |  | +                    s.idam.h == r.idam.h and
 | 
	
		
			
				|  |  | +                    s.idam.r == r.idam.r and
 | 
	
		
			
				|  |  | +                    s.idam.n == r.idam.n):
 | 
	
		
			
				|  |  | +                    s.idam.crc = 0
 | 
	
		
			
				|  |  | +                    if r.dam.crc == 0 and s.dam.crc != 0:
 | 
	
		
			
				|  |  | +                        s.dam.crc = s.crc = 0
 | 
	
		
			
				|  |  | +                        s.dam.data = r.dam.data
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def set_img_track(self, tdat):
 | 
	
		
			
				|  |  | +        pos = 0
 | 
	
		
			
				|  |  | +        self.sectors.sort(key = lambda x: x.idam.r)
 | 
	
		
			
				|  |  | +        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)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    def get_img_track(self):
 | 
	
		
			
				|  |  | +        tdat = bytearray()
 | 
	
		
			
				|  |  | +        sectors = self.sectors.copy()
 | 
	
		
			
				|  |  | +        sectors.sort(key = lambda x: x.idam.r)
 | 
	
		
			
				|  |  | +        for s in sectors:
 | 
	
		
			
				|  |  | +            tdat += s.dam.data
 | 
	
		
			
				|  |  | +        return tdat
 | 
	
		
			
				|  |  | +        
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +class IBM_MFM_1M44(IBM_MFM_Formatted):
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    gap_3  = 84 # Post-DAM
 | 
	
		
			
				|  |  | +    nsec   = 18
 | 
	
		
			
				|  |  | +    id0    = 1
 | 
	
		
			
				|  |  | +    sz     = 2
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def mfm_encode(dat):
 | 
	
		
			
				|  |  | +    y = 0
 | 
	
		
			
				|  |  | +    out = bytearray()
 | 
	
		
			
				|  |  | +    for x in dat:
 | 
	
		
			
				|  |  | +        y = (y<<8) | x
 | 
	
		
			
				|  |  | +        if (x & 0xaa) == 0:
 | 
	
		
			
				|  |  | +            y |= ~((y>>1)|(y<<1)) & 0xaaaa
 | 
	
		
			
				|  |  | +        y &= 255
 | 
	
		
			
				|  |  | +        out.append(y)
 | 
	
		
			
				|  |  | +    return bytes(out)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +encode_list = []
 | 
	
		
			
				|  |  | +for x in range(256):
 | 
	
		
			
				|  |  | +    y = 0
 | 
	
		
			
				|  |  | +    for i in range(8):
 | 
	
		
			
				|  |  | +        y <<= 2
 | 
	
		
			
				|  |  | +        y |= (x >> (7-i)) & 1
 | 
	
		
			
				|  |  | +    encode_list.append(y)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def encode(dat):
 | 
	
		
			
				|  |  | +    out = bytearray()
 | 
	
		
			
				|  |  | +    for x in dat:
 | 
	
		
			
				|  |  | +        out += struct.pack('>H', encode_list[x])
 | 
	
		
			
				|  |  | +    return bytes(out)
 | 
	
		
			
				|  |  | +    
 | 
	
		
			
				|  |  | +decode_list = bytearray()
 | 
	
		
			
				|  |  | +for x in range(0x5555+1):
 | 
	
		
			
				|  |  | +    y = 0
 | 
	
		
			
				|  |  | +    for i in range(16):
 | 
	
		
			
				|  |  | +        if x&(1<<(i*2)):
 | 
	
		
			
				|  |  | +            y |= 1<<i
 | 
	
		
			
				|  |  | +    decode_list.append(y)
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def decode(dat):
 | 
	
		
			
				|  |  | +    out = bytearray()
 | 
	
		
			
				|  |  | +    for x,y in zip(dat[::2], dat[1::2]):
 | 
	
		
			
				|  |  | +        out.append(decode_list[((x<<8)|y)&0x5555])
 | 
	
		
			
				|  |  | +    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:
 |