gw.py 7.1 KB

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