write.py 6.8 KB

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