write.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. # greaseweazle/tools/write.py
  2. #
  3. # Greaseweazle control script: Write Image to Disk.
  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. description = "Write a disk from the specified image file."
  10. import sys
  11. from greaseweazle.tools import util
  12. from greaseweazle import error, track
  13. from greaseweazle import usb as USB
  14. from greaseweazle.codec import formats
  15. # Read and parse the image file.
  16. def open_image(args, image_class):
  17. try:
  18. image = image_class.from_file(args.file)
  19. except TypeError:
  20. image = image_class.from_file(args.file, args.fmt_cls)
  21. return image
  22. # write_from_image:
  23. # Writes the specified image file to floppy disk.
  24. def write_from_image(usb, args, image):
  25. # Measure drive RPM.
  26. # We will adjust the flux intervals per track to allow for this.
  27. drive = usb.read_track(2)
  28. del drive.list
  29. verified_count, not_verified_count = 0, 0
  30. for t in args.tracks:
  31. cyl, head = t.cyl, t.head
  32. track = image.get_track(cyl, head)
  33. if track is None and not args.erase_empty:
  34. continue
  35. print("\r%sing Track %u.%u..." %
  36. ("Writ" if track is not None else "Eras", cyl, head),
  37. end="", flush=True)
  38. usb.seek(t.physical_cyl, head)
  39. if track is None:
  40. usb.erase_track(drive.ticks_per_rev * 1.1)
  41. continue
  42. if args.precomp is not None:
  43. track.precomp = args.precomp.track_precomp(cyl)
  44. flux = track.flux_for_writeout()
  45. # @factor adjusts flux times for speed variations between the
  46. # read-in and write-out drives.
  47. factor = drive.ticks_per_rev / flux.index_list[0]
  48. # Convert the flux samples to Greaseweazle sample frequency.
  49. rem = 0.0
  50. flux_list = []
  51. for x in flux.list:
  52. y = x * factor + rem
  53. val = round(y)
  54. rem = y - val
  55. flux_list.append(val)
  56. # Encode the flux times for Greaseweazle, and write them out.
  57. verified = False
  58. for retry in range(args.retries+1):
  59. if retry != 0:
  60. print("T%u.%u: Verify Failure - Retry (%d)"
  61. % (cyl, head, retry))
  62. usb.write_track(flux_list = flux_list,
  63. cue_at_index = flux.index_cued,
  64. terminate_at_index = flux.terminate_at_index)
  65. try:
  66. no_verify = args.no_verify or track.verify is None
  67. except AttributeError: # track.verify undefined
  68. no_verify = True
  69. if no_verify:
  70. not_verified_count += 1
  71. verified = True
  72. break
  73. v_revs, v_ticks = track.verify_revs, 0
  74. if isinstance(v_revs, float):
  75. v_ticks = int(drive.ticks_per_rev * v_revs)
  76. v_revs = 2
  77. v_flux = usb.read_track(revs = v_revs, ticks = v_ticks)
  78. v_flux.scale(flux.time_per_rev / drive.time_per_rev)
  79. verified = track.verify.verify_track(v_flux)
  80. if verified:
  81. verified_count += 1
  82. break
  83. if retry == 0:
  84. print()
  85. error.check(verified, "Failed to verify Track %u.%u" % (cyl, head))
  86. print()
  87. if not_verified_count == 0:
  88. print("All tracks verified")
  89. else:
  90. if verified_count == 0:
  91. s = "No tracks verified "
  92. else:
  93. s = ("%d tracks verified; %d tracks *not* verified "
  94. % (verified_count, not_verified_count))
  95. s += ("(Reason: Verify %s)"
  96. % ("unavailable", "disabled")[args.no_verify])
  97. print(s)
  98. class PrecompSpec:
  99. def __str__(self):
  100. s = "Precomp %s" % track.Precomp.TYPESTRING[self.type]
  101. for e in self.list:
  102. s += ", %d-:%dns" % e
  103. return s
  104. def track_precomp(self, cyl):
  105. for c,s in reversed(self.list):
  106. if cyl >= c:
  107. return track.Precomp(self.type, s)
  108. return None
  109. def importspec(self, spec):
  110. self.list = []
  111. self.type = track.Precomp.MFM
  112. for x in spec.split(':'):
  113. k,v = x.split('=')
  114. if k == 'type':
  115. self.type = track.Precomp.TYPESTRING.index(v.upper())
  116. else:
  117. self.list.append((int(k), int(v)))
  118. self.list.sort()
  119. def __init__(self, spec):
  120. try:
  121. self.importspec(spec)
  122. except:
  123. raise ValueError
  124. def main(argv):
  125. epilog = "FORMAT options:\n" + formats.print_formats()
  126. parser = util.ArgumentParser(usage='%(prog)s [options] file',
  127. epilog=epilog)
  128. parser.add_argument("--device", help="greaseweazle device name")
  129. parser.add_argument("--drive", type=util.drive_letter, default='A',
  130. help="drive to write (A,B,0,1,2)")
  131. parser.add_argument("--format", help="disk format")
  132. parser.add_argument("--tracks", type=util.TrackSet,
  133. help="which tracks to write")
  134. parser.add_argument("--erase-empty", action="store_true",
  135. help="erase empty tracks (default: skip)")
  136. parser.add_argument("--no-verify", action="store_true",
  137. help="disable verify")
  138. parser.add_argument("--retries", type=int, default=3,
  139. help="number of retries on verify failure")
  140. parser.add_argument("--precomp", type=PrecompSpec,
  141. help="write precompensation")
  142. parser.add_argument("file", help="input filename")
  143. parser.description = description
  144. parser.prog += ' ' + argv[1]
  145. args = parser.parse_args(argv[2:])
  146. try:
  147. image_class = util.get_image_class(args.file)
  148. if not args.format and hasattr(image_class, 'default_format'):
  149. args.format = image_class.default_format
  150. def_tracks, args.fmt_cls = None, None
  151. if args.format:
  152. try:
  153. args.fmt_cls = formats.formats[args.format]()
  154. except KeyError as ex:
  155. raise error.Fatal("""\
  156. Unknown format '%s'
  157. Known formats:\n%s"""
  158. % (args.format, formats.print_formats()))
  159. def_tracks = args.fmt_cls.default_tracks
  160. if def_tracks is None:
  161. def_tracks = util.TrackSet('c=0-81:h=0-1')
  162. if args.tracks is not None:
  163. def_tracks.update_from_trackspec(args.tracks.trackspec)
  164. args.tracks = def_tracks
  165. usb = util.usb_open(args.device)
  166. image = open_image(args, image_class)
  167. s = str(args.tracks)
  168. if args.precomp is not None:
  169. s += "; %s" % args.precomp
  170. print("Writing %s" % s)
  171. util.with_drive_selected(write_from_image, usb, args, image)
  172. except USB.CmdError as err:
  173. print("Command Failed: %s" % err)
  174. if __name__ == "__main__":
  175. main(sys.argv)
  176. # Local variables:
  177. # python-indent: 4
  178. # End: