def bitrate(self, bitrate): try: self._bitrate = int(bitrate) if self._bitrate <= 0: raise ValueError except ValueError: raise error.Fatal("HFE: Invalid bitrate: '%s'" % bitrate)
def main(argv): epilog = (util.drive_desc) parser = util.ArgumentParser(usage='%(prog)s [options] cylinder', epilog=epilog) parser.add_argument("--device", help="device name (COM/serial port)") parser.add_argument("--drive", type=util.drive_letter, default='A', help="drive to read") parser.add_argument("--force", action="store_true", help="allow extreme cylinders with no prompt") parser.add_argument("--motor-on", action="store_true", help="seek with drive motor activated") parser.add_argument("cylinder", type=int, help="cylinder to seek") parser.description = description parser.prog += ' ' + argv[1] args = parser.parse_args(argv[2:]) try: struct.pack('b', args.cylinder) except struct.error: raise error.Fatal("Cylinder %d out of range" % args.cylinder) if not 0 <= args.cylinder <= 83 and not args.force: answer = input("Seek to extreme cylinder %d, Yes/No? " % args.cylinder) if answer != "Yes": return try: usb = util.usb_open(args.device) util.with_drive_selected(seek, usb, args, motor=args.motor_on) except USB.CmdError as err: print("Command Failed: %s" % err)
def main(argv): parser = util.ArgumentParser(usage='%(prog)s [options] file') parser.add_argument("--device", help="greaseweazle device name") 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, help="number of revolutions to read per track") parser.add_argument("--scyl", type=int, help="first cylinder to read") parser.add_argument("--ecyl", type=int, help="last cylinder to read") parser.add_argument("--single-sided", action="store_true", help="single-sided read") parser.add_argument("--double-step", action="store_true", help="double-step drive heads") parser.add_argument("--rate", type=int, help="data rate (kbit/s)") parser.add_argument("--rpm", type=int, help="convert drive speed to RPM") parser.add_argument("file", help="output filename") parser.description = description parser.prog += ' ' + argv[1] args = parser.parse_args(argv[2:]) args.nr_sides = 1 if args.single_sided else 2 args.file, args.file_opts = util.split_opts(args.file) try: usb = util.usb_open(args.device) 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: 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)
def main(argv): parser = util.ArgumentParser(usage='%(prog)s [options] file') parser.add_argument("--device", help="greaseweazle device name") 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, help="number of revolutions to read per track") parser.add_argument("--tracks", type=util.TrackSet, help="which tracks to read") parser.add_argument("--rate", type=int, help="data rate (kbit/s)") parser.add_argument("--rpm", type=int, help="convert drive speed to RPM") parser.add_argument("--retries", type=int, default=3, help="number of retries on decode failure") parser.add_argument("file", help="output filename") parser.description = description parser.prog += ' ' + argv[1] args = parser.parse_args(argv[2:]) args.file, args.file_opts = util.split_opts(args.file) try: usb = util.usb_open(args.device) 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 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 if def_tracks is None: def_tracks = util.TrackSet('c=0-81:h=0-1') if args.revs is None: args.revs = 3 if args.tracks is not None: def_tracks.update_from_trackspec(args.tracks.trackspec) args.tracks = def_tracks print(("Reading %s revs=" % args.tracks) + str(args.revs)) with open_image(args, image_class) as image: util.with_drive_selected(read_to_image, usb, args, image, decoder=decoder) except USB.CmdError as err: print("Command Failed: %s" % err)
def disktype(self, disktype): try: self._disktype = DiskType[disktype.lower()] except KeyError: try: self._disktype = int(disktype, 0) except ValueError: raise error.Fatal("Bad SCP disktype: '%s'" % disktype)
def download_by_tag(tag_name): '''Download the latest Update File from GitHub.''' rsp = requests.get( 'https://api.github.com/repos/keirf/' 'greaseweazle-firmware/releases', timeout=5) for release in rsp.json(): if release['tag_name'] == tag_name: return download(release) raise error.Fatal("Unknown tag name '%s'" % tag_name)
def main(argv): parser = util.ArgumentParser(allow_abbrev=False, usage='%(prog)s [options] [file]') parser.add_argument("file", nargs="?", help="update filename") parser.add_argument("--device", help="device name (COM/serial port)") parser.add_argument("--force", action="store_true", help="force update even if firmware is older") parser.add_argument("--bootloader", action="store_true", help="update the bootloader (use with caution!)") parser.description = description parser.prog += ' ' + argv[1] args = parser.parse_args(argv[2:]) if args.file is None: args.file, dat = download_latest() else: with open(args.file, "rb") as f: dat = f.read() try: usb = util.usb_open(args.device, mode_check=False) dat_version, dat = extract_update(usb, dat, args) print("Updating %s to v%u.%u..." % ("Bootloader" if args.bootloader else "Main Firmware", *dat_version)) if not args.force and (usb.can_mode_switch or args.bootloader == usb.update_mode): if args.bootloader != usb.update_mode: usb = util.usb_reopen(usb, is_update=args.bootloader) error.check(args.bootloader == usb.update_mode, 'Device did not mode switch as requested') if usb.version >= dat_version: if usb.update_mode and usb.can_mode_switch: usb = util.usb_reopen(usb, is_update=False) raise error.Fatal('Device is running v%d.%d (>= v%d.%d). ' 'Use --force to update anyway.' % (usb.version + dat_version)) usb = util.usb_mode_check(usb, is_update=not args.bootloader) update_firmware(usb, dat, args) if usb.update_mode and usb.can_mode_switch: util.usb_reopen(usb, is_update=False) except USB.CmdError as err: if err.code == USB.Ack.OutOfSRAM and args.bootloader: # Special warning for Low-Density F1 devices. The new bootloader # cannot be fully buffered in the limited RAM available. print("ERROR: Bootloader update unsupported on this device " "(insufficient SRAM)") elif err.code == USB.Ack.OutOfFlash and not args.bootloader: print("ERROR: New firmware is too large for this device " "(insufficient Flash memory)") else: print("Command Failed: %s" % err)
def main(argv): epilog = "FORMAT options:\n" + formats.print_formats() parser = util.ArgumentParser(usage='%(prog)s [options] in_file out_file', epilog=epilog) parser.add_argument("--format", help="disk format") parser.add_argument("--tracks", type=util.TrackSet, help="which tracks to convert") parser.add_argument("--rpm", type=int, help="convert drive speed to RPM") parser.add_argument("-n", "--no-clobber", action="store_true", help="do not overwrite an existing file") parser.add_argument("in_file", help="input filename") parser.add_argument("out_file", help="output filename") parser.description = description parser.prog += ' ' + argv[1] args = parser.parse_args(argv[2:]) args.out_file, args.out_file_opts = util.split_opts(args.out_file) in_image_class = util.get_image_class(args.in_file) if not args.format and hasattr(in_image_class, 'default_format'): args.format = in_image_class.default_format out_image_class = util.get_image_class(args.out_file) if not args.format and hasattr(out_image_class, 'default_format'): args.format = out_image_class.default_format decoder, def_tracks, args.fmt_cls = None, 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:\n%s""" % (args.format, formats.print_formats())) decoder = args.fmt_cls.decode_track def_tracks = args.fmt_cls.default_tracks 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 print("Converting %s" % args.tracks) if args.format: print("Format " + args.format) in_image = open_input_image(args, in_image_class) with open_output_image(args, out_image_class) as out_image: convert(args, in_image, out_image, decoder=decoder)
def main(argv): epilog = "FORMAT options:\n" + formats.print_formats() parser = util.ArgumentParser(usage='%(prog)s [options] file', epilog=epilog) parser.add_argument("--device", help="device name (COM/serial port)") 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", help="erase empty tracks (default: skip)") parser.add_argument("--no-verify", action="store_true", help="disable verify") parser.add_argument("--retries", type=int, default=3, help="number of retries on verify failure") parser.add_argument("--precomp", type=PrecompSpec, help="write precompensation") parser.add_argument("file", help="input filename") parser.description = description parser.prog += ' ' + argv[1] args = parser.parse_args(argv[2:]) try: image_class = util.get_image_class(args.file) if not args.format and hasattr(image_class, 'default_format'): args.format = image_class.default_format 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:\n%s""" % (args.format, formats.print_formats())) def_tracks = args.fmt_cls.default_tracks 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, image_class) print("Writing " + str(args.tracks)) if args.precomp is not None: print(args.precomp) if args.format: print("Format " + args.format) util.with_drive_selected(write_from_image, usb, args, image) except USB.CmdError as err: print("Command Failed: %s" % err)
def with_drive_selected(fn, usb, args, *_args, **_kwargs): try: usb.set_bus_type(args.drive[0].value) except USB.CmdError as err: if err.code == USB.Ack.BadCommand: raise error.Fatal("Device does not support " + str(args.drive[0])) raise try: usb.drive_select(args.drive[1]) usb.drive_motor(args.drive[1], _kwargs.pop('motor', True)) fn(usb, args, *_args, **_kwargs) except KeyboardInterrupt: print() usb.reset() raise finally: usb.drive_motor(args.drive[1], False) usb.drive_deselect()
def _decode_flux(self, dat): flux, index = [], [] assert dat[-1] == 0 dat_i = it.islice(dat, 0, len(dat) - 1) ticks, ticks_since_index = 0, 0 def _read_28bit(): val = (next(dat_i) & 254) >> 1 val += (next(dat_i) & 254) << 6 val += (next(dat_i) & 254) << 13 val += (next(dat_i) & 254) << 20 return val try: while True: i = next(dat_i) if i == 255: opcode = next(dat_i) if opcode == FluxOp.Index: val = _read_28bit() index.append(ticks_since_index + ticks + val) ticks_since_index = -(ticks + val) elif opcode == FluxOp.Space: ticks += _read_28bit() else: raise error.Fatal("Bad opcode in flux stream (%d)" % opcode) else: if i < 250: val = i else: val = 250 + (i - 250) * 255 val += next(dat_i) - 1 ticks += val flux.append(ticks) ticks_since_index += ticks ticks = 0 except StopIteration: pass return flux, index
def from_file(cls, name): with open(name, "rb") as f: dat = f.read() edsk = cls() sig, creator, ncyls, nsides, track_sz = struct.unpack( '<34s14s2BH', dat[:52]) if sig[:8] == b'MV - CPC': extended = False elif sig[:16] == b'EXTENDED CPC DSK': extended = True else: raise error.Fatal('Unrecognised CPC DSK file: bad signature') if extended: track_sizes = list(dat[52:52+ncyls*nsides]) track_sizes = list(map(lambda x: x*256, track_sizes)) else: track_sizes = [track_sz] * (ncyls * nsides) o = 256 # skip disk header and track-size table for track_size in track_sizes: if track_size == 0: continue sig, cyl, head, sec_sz, nsecs, gap_3, filler = struct.unpack( '<12s4x2B2x4B', dat[o:o+24]) error.check(sig == b'Track-Info\r\n', 'EDSK: Missing track header') error.check((cyl, head) not in edsk.to_track, 'EDSK: Track specified twice') bad_crc_clip_data = False while True: track = EDSKTrack() t = track.bytes # Post-index gap t += mfm.encode(bytes([track.gapbyte] * track.gap_4a)) # IAM t += mfm.encode(bytes(track.gap_presync)) t += mfm.iam_sync_bytes t += mfm.encode(bytes([mfm.IBM_MFM.IAM])) t += mfm.encode(bytes([track.gapbyte] * track.gap_1)) sh = dat[o+24:o+24+8*nsecs] data_pos = o + 256 # skip track header and sector-info table clippable, ngap3, sectors, idam_included = 0, 0, [], False while sh: c, h, r, n, stat1, stat2, data_size = struct.unpack( '<6BH', sh[:8]) sh = sh[8:] native_size = mfm.sec_sz(n) weak = [] errs = SectorErrors(stat1, stat2) num_copies = 0 if errs.data_not_found else 1 if not extended: data_size = mfm.sec_sz(sec_sz) sec_data = dat[data_pos:data_pos+data_size] data_pos += data_size if (extended and data_size > native_size and errs.data_crc_error and (data_size % native_size == 0 or data_size == 49152)): num_copies = (3 if data_size == 49152 else data_size // native_size) data_size //= num_copies weak = cls().find_weak_ranges(sec_data, data_size) sec_data = sec_data[:data_size] sectors.append((c,h,r,n,errs,sec_data)) # IDAM if not idam_included: t += mfm.encode(bytes(track.gap_presync)) t += mfm.sync_bytes am = bytes([0xa1, 0xa1, 0xa1, mfm.IBM_MFM.IDAM, c, h, r, n]) crc = mfm.crc16.new(am).crcValue if errs.id_crc_error: crc ^= 0x5555 am += struct.pack('>H', crc) t += mfm.encode(am[3:]) t += mfm.encode(bytes([track.gapbyte] * track.gap_2)) # DAM gap_included, idam_included = False, False if errs.id_crc_error or errs.data_not_found: continue t += mfm.encode(bytes(track.gap_presync)) t += mfm.sync_bytes track.weak += [((s+len(t)//2+1)*16, n*16) for s,n in weak] dmark = (mfm.IBM_MFM.DDAM if errs.deleted_dam else mfm.IBM_MFM.DAM) if errs.data_crc_error: if sh: # Look for next IDAM idam = bytes([0]*12 + [0xa1]*3 + [mfm.IBM_MFM.IDAM]) idx = sec_data.find(idam) else: # Last sector: Look for GAP3 idx = sec_data.find(bytes([track.gapbyte]*8)) if idx > 0: # 2 + gap_3 = CRC + GAP3 (because gap_included) clippable += data_size - idx + 2 + gap_3 if bad_crc_clip_data: data_size = idx sec_data = sec_data[:data_size] gap_included = True elif data_size < native_size: # Pad short data sec_data += bytes(native_size - data_size) elif data_size > native_size: # Clip long data if it includes pre-sync 00 bytes if (sec_data[-13] != 0 and all([v==0 for v in sec_data[-12:]])): # Includes next pre-sync: Clip it. sec_data = sec_data[:-12] if sh: # Look for next IDAM idam = bytes([0]*12 + [0xa1]*3 + [mfm.IBM_MFM.IDAM] + list(sh[:4])) idx = sec_data.find(idam) if idx > native_size: # Sector data includes next IDAM. Output it # here and skip it on next iteration. t += mfm.encode(bytes([dmark])) t += mfm.encode(sec_data[:idx+12]) t += mfm.sync_bytes t += mfm.encode(sec_data[idx+12+3:]) idam_included = True continue # Long data includes CRC and GAP gap_included = True if gap_included: t += mfm.encode(bytes([dmark])) t += mfm.encode(sec_data) continue am = bytes([0xa1, 0xa1, 0xa1, dmark]) + sec_data crc = mfm.crc16.new(am).crcValue if errs.data_crc_error: crc ^= 0x5555 am += struct.pack('>H', crc) t += mfm.encode(am[3:]) if sh: # GAP3 for all but last sector t += mfm.encode(bytes([track.gapbyte] * gap_3)) ngap3 += 1 # Special track handlers special_track = cls()._build_8k_track(sectors) if special_track is None: special_track = cls()._build_kbi19_track(sectors) if special_track is not None: track = special_track break # The track may be too long to fit: Check for overhang. tracklen = int((track.time_per_rev / track.clock) / 16) overhang = int(len(t)//2 - tracklen*0.99) if overhang <= 0: break # Some EDSK tracks with Bad CRC contain a raw dump following # the DAM. This can usually be clipped. if clippable and not bad_crc_clip_data: bad_crc_clip_data = True continue # Some EDSK images have bogus GAP3 values. Shrink it if # necessary. new_gap_3 = -1 if ngap3 != 0: new_gap_3 = gap_3 - math.ceil(overhang / ngap3) error.check(new_gap_3 >= 0, 'EDSK: Track %d.%d is too long ' '(%d bits @ GAP3=%d; %d bits @ GAP3=0)' % (cyl, head, len(t)*8, gap_3, (len(t)//2-gap_3*ngap3)*16)) #print('EDSK: GAP3 reduced (%d -> %d)' % (gap_3, new_gap_3)) gap_3 = new_gap_3 # Pre-index gap track.verify_len = len(track.bytes)*8 tracklen = int((track.time_per_rev / track.clock) / 16) gap = max(40, tracklen - len(t)//2) track.bytes += mfm.encode(bytes([track.gapbyte] * gap)) # Add the clock buts track.bits = bitarray(endian='big') track.bits.frombytes(mfm.mfm_encode(track.bytes)) # Register the track edsk.to_track[cyl,head] = track o += track_size return edsk
def main(argv): epilog = (util.drive_desc + "\n" + util.speed_desc + "\n" + util.tspec_desc + "\nFORMAT options:\n" + formats.print_formats()) parser = util.ArgumentParser(usage='%(prog)s [options] file', epilog=epilog) parser.add_argument("--device", help="device name (COM/serial port)") parser.add_argument("--drive", type=util.drive_letter, default='A', help="drive to read") parser.add_argument("--format", help="disk format (output is converted unless --raw)") parser.add_argument("--revs", type=int, metavar="N", help="number of revolutions to read per track") parser.add_argument("--tracks", type=util.TrackSet, metavar="TSPEC", help="which tracks to read") parser.add_argument("--raw", action="store_true", help="output raw stream (--format verifies only)") parser.add_argument("--fake-index", type=util.period, metavar="SPEED", help="fake index pulses at SPEED") parser.add_argument("--adjust-speed", type=util.period, metavar="SPEED", help="scale track data to effective drive SPEED") parser.add_argument("--retries", type=int, default=3, metavar="N", help="number of retries per seek-retry") parser.add_argument("--seek-retries", type=int, default=3, metavar="N", help="number of seek retries") parser.add_argument("-n", "--no-clobber", action="store_true", help="do not overwrite an existing file") parser.add_argument("file", help="output filename") parser.description = description parser.prog += ' ' + argv[1] args = parser.parse_args(argv[2:]) args.file, args.file_opts = util.split_opts(args.file) try: usb = util.usb_open(args.device) 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, args.fmt_cls = None, 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:\n%s""" % (args.format, formats.print_formats())) decoder = args.fmt_cls.decode_track def_tracks = args.fmt_cls.default_tracks 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 if args.tracks is not None: def_tracks.update_from_trackspec(args.tracks.trackspec) args.tracks = def_tracks print(("Reading %s revs=" % args.tracks) + str(args.revs)) if args.format: print("Format " + args.format) with open_image(args, image_class) as image: util.with_drive_selected(read_to_image, usb, args, image, decoder=decoder) except USB.CmdError as err: print("Command Failed: %s" % err)
def flux_for_writeout(self, cue_at_index=True): raise error.Fatal("WriteoutFlux: flux_for_writeout is unsupported")
def main(argv): epilog = (util.speed_desc + "\n" + util.tspec_desc + "\nFORMAT options:\n" + formats.print_formats()) parser = util.ArgumentParser(usage='%(prog)s [options] in_file out_file', epilog=epilog) parser.add_argument("--format", help="disk format") parser.add_argument("--tracks", type=util.TrackSet, help="which tracks to read & convert from input", metavar="TSPEC") parser.add_argument("--out-tracks", type=util.TrackSet, help="which tracks to output (default: --tracks)", metavar="TSPEC") parser.add_argument("--adjust-speed", type=util.period, metavar="SPEED", help="scale track data to effective drive SPEED") parser.add_argument("-n", "--no-clobber", action="store_true", help="do not overwrite an existing file") parser.add_argument("in_file", help="input filename") parser.add_argument("out_file", help="output filename") parser.description = description parser.prog += ' ' + argv[1] args = parser.parse_args(argv[2:]) args.out_file, args.out_file_opts = util.split_opts(args.out_file) in_image_class = util.get_image_class(args.in_file) if not args.format and hasattr(in_image_class, 'default_format'): args.format = in_image_class.default_format out_image_class = util.get_image_class(args.out_file) if not args.format and hasattr(out_image_class, 'default_format'): args.format = out_image_class.default_format decoder, def_tracks, args.fmt_cls = None, 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:\n%s""" % (args.format, formats.print_formats())) decoder = args.fmt_cls.decode_track def_tracks = copy.copy(args.fmt_cls.default_tracks) if def_tracks is None: def_tracks = util.TrackSet('c=0-81:h=0-1') out_def_tracks = copy.copy(def_tracks) if args.tracks is not None: def_tracks.update_from_trackspec(args.tracks.trackspec) out_def_tracks.cyls = copy.copy(def_tracks.cyls) out_def_tracks.heads = copy.copy(def_tracks.heads) args.tracks = def_tracks if args.out_tracks is not None: out_def_tracks.update_from_trackspec(args.out_tracks.trackspec) args.out_tracks = out_def_tracks print("Converting %s -> %s" % (args.tracks, args.out_tracks)) if args.format: print("Format " + args.format) in_image = open_input_image(args, in_image_class) with open_output_image(args, out_image_class) as out_image: convert(args, in_image, out_image, decoder=decoder)