gw.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. # gw.py
  2. #
  3. # Greaseweazle control script.
  4. #
  5. # Written & released by Keir Fraser <keir.xen@gmail.com>
  6. #
  7. # This is free and unencumbered software released into the public domain.
  8. # See the file COPYING for more details, or visit <http://unlicense.org>.
  9. #from timeit import default_timer as timer
  10. import sys, struct, argparse, serial
  11. import crcmod.predefined
  12. from greaseweazle import version
  13. from greaseweazle import USB
  14. from greaseweazle.bitcell import Bitcell
  15. from greaseweazle.flux import Flux
  16. from greaseweazle.scp import SCP
  17. # read_to_scp:
  18. # Reads a floppy disk and dumps it into a new Supercard Pro image file.
  19. def read_to_scp(usb, args):
  20. scp = SCP(args.scyl, args.nr_sides)
  21. for cyl in range(args.scyl, args.ecyl+1):
  22. for side in range(0, args.nr_sides):
  23. print("\rReading Track %u.%u..." % (cyl, side), end="")
  24. usb.seek(cyl, side)
  25. # Physically read the track.
  26. for retry in range(1, 5):
  27. ack, index_list, enc_flux = usb.read_track(args.revs+1)
  28. if ack == USB.Ack.Okay:
  29. break
  30. elif ack == USB.Ack.FluxOverflow and retry < 5:
  31. print("Retry #%u..." % (retry))
  32. else:
  33. raise CmdError(ack)
  34. # Decode the flux and clip the initial partial revolution.
  35. flux_list = usb.decode_flux(enc_flux)
  36. del enc_flux
  37. to_index = index_list[0]
  38. for i in range(len(flux_list)):
  39. to_index -= flux_list[i]
  40. if to_index < 0:
  41. flux_list[i] = -to_index
  42. flux_list = flux_list[i:]
  43. index_list = index_list[1:]
  44. break
  45. # Stash the data for later writeout to the image file.
  46. scp.append_track(Flux(index_list, flux_list, usb.sample_freq))
  47. print()
  48. # Write the image file.
  49. with open(args.file, "wb") as f:
  50. f.write(scp.get_image())
  51. # write_from_scp:
  52. # Writes the specified Supercard Pro image file to floppy disk.
  53. def write_from_scp(usb, args):
  54. if args.adjust_speed:
  55. # @drive_ticks is the time in Gresaeweazle ticks between index pulses.
  56. # We will adjust the flux intervals per track to allow for this.
  57. for retry in range(1, 5):
  58. ack, index_list, _ = usb.read_track(3)
  59. if ack == USB.Ack.Okay:
  60. break
  61. elif ack != USB.Ack.FluxOverflow or retry >= 5:
  62. raise CmdError(ack)
  63. drive_ticks = (index_list[1] + index_list[2]) / 2
  64. # Parse the SCP image header.
  65. with open(args.file, "rb") as f:
  66. scp = SCP.from_file(f.read())
  67. for cyl in range(args.scyl, args.ecyl+1):
  68. for side in range(0, args.nr_sides):
  69. flux = scp.get_track(cyl, side, writeout=True)
  70. if not flux:
  71. continue
  72. print("\rWriting Track %u.%u..." % (cyl, side), end="")
  73. usb.seek(cyl, side)
  74. if args.adjust_speed:
  75. # @factor adjusts flux times for speed variations between the
  76. # read-in and write-out drives.
  77. factor = drive_ticks / flux.index_list[0]
  78. else:
  79. # Simple ratio between the GW and SCP sample frequencies.
  80. factor = usb.sample_freq / flux.sample_freq
  81. # Convert the flux samples to Greaseweazle sample frequency.
  82. rem = 0.0
  83. flux_list = []
  84. for x in flux.list:
  85. y = x * factor + rem
  86. val = int(round(y))
  87. rem = y - val
  88. flux_list.append(val)
  89. # Encode the flux times for Greaseweazle, and write them out.
  90. enc_flux = usb.encode_flux(flux_list)
  91. for retry in range(1, 5):
  92. ack = usb.write_track(enc_flux)
  93. if ack == USB.Ack.Okay:
  94. break
  95. elif ack == USB.Ack.FluxUnderflow and retry < 5:
  96. print("Retry #%u..." % (retry))
  97. else:
  98. raise CmdError(ack)
  99. print()
  100. # update_firmware:
  101. # Updates the Greaseweazle firmware using the specified Update File.
  102. def update_firmware(usb, args):
  103. # Check that an update operation was actually requested.
  104. if args.action != "update":
  105. print("Greaseweazle is in Firmware Update Mode:")
  106. print(" The only available action is \"update <update_file>\"")
  107. if usb.update_jumpered:
  108. print(" Remove the Update Jumper for normal operation")
  109. else:
  110. print(" Main firmware is erased: You *must* perform an update!")
  111. return
  112. # Check that the firmware is actually in update mode.
  113. if not usb.update_mode:
  114. print("Greaseweazle is in Normal Mode:")
  115. print(" To \"update\" you must install the Update Jumper")
  116. return
  117. # Read and check the update file.
  118. with open(args.file, "rb") as f:
  119. dat = f.read()
  120. sig, maj, min, pad1, pad2, crc = struct.unpack(">2s4BH", dat[-8:])
  121. if len(dat) & 3 != 0 or sig != b'GW' or pad1 != 0 or pad2 != 0:
  122. print("%s: Bad update file" % (args.file))
  123. return
  124. crc16 = crcmod.predefined.Crc('crc-ccitt-false')
  125. crc16.update(dat)
  126. if crc16.crcValue != 0:
  127. print("%s: Bad CRC" % (args.file))
  128. # Perform the update.
  129. print("Updating to v%u.%u..." % (maj, min))
  130. ack = usb.update_firmware(dat)
  131. if ack != 0:
  132. print("** UPDATE FAILED: Please retry!")
  133. return
  134. print("Done.")
  135. print("** Disconnect Greaseweazle and remove the Programming Jumper.")
  136. # _main:
  137. # Argument processing and dispatch.
  138. def _main(argv):
  139. actions = {
  140. "read" : read_to_scp,
  141. "write" : write_from_scp,
  142. "update" : update_firmware
  143. }
  144. parser = argparse.ArgumentParser(
  145. formatter_class=argparse.ArgumentDefaultsHelpFormatter)
  146. parser.add_argument("action")
  147. parser.add_argument("--revs", type=int, default=3,
  148. help="number of revolutions to read per track")
  149. parser.add_argument("--scyl", type=int, default=0,
  150. help="first cylinder to read/write")
  151. parser.add_argument("--ecyl", type=int, default=81,
  152. help="last cylinder to read/write")
  153. parser.add_argument("--single-sided", action="store_true",
  154. help="read/write a single-sided image")
  155. parser.add_argument("--adjust-speed", action="store_true",
  156. help="adjust write-flux times for drive speed")
  157. parser.add_argument("file", help="in/out filename")
  158. parser.add_argument("device", help="serial device")
  159. args = parser.parse_args(argv[1:])
  160. args.nr_sides = 1 if args.single_sided else 2
  161. if not args.action in actions:
  162. print("** Action \"%s\" is not recognised" % args.action)
  163. print("Valid actions: ", end="")
  164. print(", ".join(str(key) for key in actions.keys()))
  165. return
  166. usb = USB.Unit(serial.Serial(args.device))
  167. print("** %s v%u.%u, Host Tools v%u.%u"
  168. % (("Greaseweazle", "Bootloader")[usb.update_mode],
  169. usb.major, usb.minor,
  170. version.major, version.minor))
  171. if args.action == "update" or usb.update_mode:
  172. actions[args.action](usb, args)
  173. return
  174. elif usb.update_needed:
  175. print("Firmware is out of date: Require v%u.%u"
  176. % (version.major, version.minor))
  177. print("Install the Update Jumper and \"update <update_file>\"")
  178. return
  179. #usb.step_delay = 5000
  180. #print("Select Delay: %uus" % usb.select_delay)
  181. #print("Step Delay: %uus" % usb.step_delay)
  182. #print("Settle Time: %ums" % usb.seek_settle_delay)
  183. #print("Motor Delay: %ums" % usb.motor_delay)
  184. #print("Auto Off: %ums" % usb.auto_off_delay)
  185. try:
  186. usb.drive_select(True)
  187. usb.drive_motor(True)
  188. actions[args.action](usb, args)
  189. except KeyboardInterrupt:
  190. print()
  191. usb.reset()
  192. usb.ser.close()
  193. usb.ser.open()
  194. finally:
  195. usb.drive_motor(False)
  196. usb.drive_select(False)
  197. def main(argv):
  198. try:
  199. _main(argv)
  200. except USB.CmdError as error:
  201. print("Command Failed: %s" % error)
  202. if __name__ == "__main__":
  203. main(sys.argv)
  204. # Local variables:
  205. # python-indent: 4
  206. # End: