# greaseweazle/image/scp.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 struct from greaseweazle import error from greaseweazle.flux import Flux class SCP: # 40MHz sample_freq = 40000000 def __init__(self, start_cyl, nr_sides): self.start_cyl = start_cyl self.nr_sides = nr_sides self.nr_revs = None self.track_list = [] @classmethod def to_file(cls, start_cyl, nr_sides): hfe = cls(start_cyl, nr_sides) return hfe @classmethod def from_file(cls, dat): header = struct.unpack("<3s9BI", dat[0:16]) (sig, _, _, nr_revs, s_trk, e_trk, flags, _, ss, _, _) = header error.check(sig == b"SCP", "SCP: Bad signature") nr_sides = 1 if ss else 2 trk_offs = struct.unpack("<168I", dat[16:0x2b0]) scp = cls(s_trk // nr_sides, nr_sides) scp.nr_revs = nr_revs for trknr in range(s_trk, e_trk+1): trk_off = trk_offs[trknr] if trk_off == 0: scp.track_list.append((None, None)) # Parse the SCP track header and extract the flux data. thdr = dat[trk_off:trk_off+4+12*nr_revs] sig, tnr, _, _, s_off = struct.unpack("<3sB3I", thdr[:16]) error.check(sig == b"TRK", "SCP: Missing track signature") error.check(tnr == trknr, "SCP: Wrong track number in header") _, e_nr, e_off = struct.unpack("<3I", thdr[-12:]) tdat = dat[trk_off+s_off:trk_off+e_off+e_nr*2] scp.track_list.append((thdr, tdat)) return scp def get_track(self, cyl, side, writeout=False): if side >= self.nr_sides or cyl < self.start_cyl: return None off = (cyl - self.start_cyl) * self.nr_sides + side if off >= len(self.track_list): return None tdh, dat = self.track_list[off] if not dat: return None tdh = tdh[4:] # Writeout requires only a single revolution if writeout: tdh = tdh[:12] _, nr, _ = struct.unpack("<3I", tdh) dat = dat[:nr*2] index_list = [] while tdh: ticks, _, _ = struct.unpack("<3I", tdh[:12]) index_list.append(ticks) tdh = tdh[12:] # Decode the SCP flux data into a simple list of flux times. flux_list = [] val = 0 for i in range(0, len(dat), 2): x = dat[i]*256 + dat[i+1] if x == 0: val += 65536 continue flux_list.append(val + x) val = 0 return Flux(index_list, flux_list, SCP.sample_freq) # append_track: # Converts a Flux object into a Supercard Pro Track and appends it to # the current image-in-progress. def append_track(self, flux): nr_revs = len(flux.index_list) if not self.nr_revs: self.nr_revs = nr_revs else: assert self.nr_revs == nr_revs factor = SCP.sample_freq / flux.sample_freq trknr = self.start_cyl * self.nr_sides + len(self.track_list) tdh = struct.pack("<3sB", b"TRK", trknr) dat = bytearray() len_at_index = rev = 0 to_index = flux.index_list[0] rem = 0.0 for x in flux.list: # Does the next flux interval cross the index mark? while to_index < x: # Append to the TDH for the previous full revolution tdh += struct.pack("<III", int(round(flux.index_list[rev]*factor)), (len(dat) - len_at_index) // 2, 4 + nr_revs*12 + len_at_index) # Set up for the next revolution len_at_index = len(dat) rev += 1 if rev >= nr_revs: # We're done: We simply discard any surplus flux samples self.track_list.append((tdh, dat)) return to_index += flux.index_list[rev] # Process the current flux sample into SCP "bitcell" format to_index -= x y = x * factor + rem val = int(round(y)) if (val & 65535) == 0: val += 1 rem = y - val while val >= 65536: dat.append(0) dat.append(0) val -= 65536 dat.append(val>>8) dat.append(val&255) # Header for last track(s) in case we ran out of flux timings. while rev < nr_revs: tdh += struct.pack("<III", int(round(flux.index_list[rev]*factor)), (len(dat) - len_at_index) // 2, 4 + nr_revs*12 + len_at_index) len_at_index = len(dat) rev += 1 self.track_list.append((tdh, dat)) def get_image(self): s_trk = self.start_cyl * self.nr_sides e_trk = s_trk + len(self.track_list) - 1 # Generate the TLUT and concatenate all the tracks together. trk_offs = bytearray(s_trk * 4) trk_dat = bytearray() for tdh, dat in self.track_list: trk_offs += struct.pack("<I", 0x2b0 + len(trk_dat)) trk_dat += tdh + dat trk_offs += bytes(0x2a0 - len(trk_offs)) # Calculate checksum over all data (except 16-byte image header). csum = 0 for x in trk_offs: csum += x for x in trk_dat: csum += x # Generate the image header. header = struct.pack("<3s9BI", b"SCP", # Signature 0, # Version 0x80, # DiskType = Other self.nr_revs, s_trk, e_trk, 0x01, # Flags = Index 0, # 16-bit cell width 1 if self.nr_sides == 1 else 0, 0, # 25ns capture csum & 0xffffffff) # Concatenate it all together and send it back. return header + trk_offs + trk_dat # Local variables: # python-indent: 4 # End: