scp.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. # greaseweazle/scp.py
  2. #
  3. # Written & released by Keir Fraser <keir.xen@gmail.com>
  4. #
  5. # This is free and unencumbered software released into the public domain.
  6. # See the file COPYING for more details, or visit <http://unlicense.org>.
  7. import struct
  8. class SCP:
  9. # 40MHz
  10. sample_freq = 40000000
  11. def __init__(self, start_cyl, nr_sides):
  12. self.start_cyl = start_cyl
  13. self.nr_sides = nr_sides
  14. self.nr_revs = None
  15. self.track_list = []
  16. # append_track:
  17. # Converts a Flux object into a Supercard Pro Track and appends it to
  18. # the current image-in-progress.
  19. def append_track(self, flux):
  20. nr_revs = len(flux.index_list) - 1
  21. if not self.nr_revs:
  22. self.nr_revs = nr_revs
  23. else:
  24. assert self.nr_revs == nr_revs
  25. factor = SCP.sample_freq / flux.sample_freq
  26. trknr = self.start_cyl * self.nr_sides + len(self.track_list)
  27. tdh = struct.pack("<3sB", b"TRK", trknr)
  28. dat = bytearray()
  29. len_at_index = rev = 0
  30. to_index = flux.index_list[0]
  31. rem = 0.0
  32. for x in flux.list:
  33. # Are we processing initial samples before the first revolution?
  34. if rev == 0:
  35. if to_index >= x:
  36. # Discard initial samples
  37. to_index -= x
  38. continue
  39. # Now starting the first full revolution
  40. rev = 1
  41. to_index += flux.index_list[rev]
  42. # Does the next flux interval cross the index mark?
  43. while to_index < x:
  44. # Append to the TDH for the previous full revolution
  45. tdh += struct.pack("<III",
  46. int(round(flux.index_list[rev]*factor)),
  47. (len(dat) - len_at_index) // 2,
  48. 4 + nr_revs*12 + len_at_index)
  49. # Set up for the next revolution
  50. len_at_index = len(dat)
  51. rev += 1
  52. if rev > nr_revs:
  53. # We're done: We simply discard any surplus flux samples
  54. self.track_list.append((tdh, dat))
  55. return
  56. to_index += flux.index_list[rev]
  57. # Process the current flux sample into SCP "bitcell" format
  58. to_index -= x
  59. y = x * factor + rem
  60. val = int(round(y))
  61. if (val & 65535) == 0:
  62. val += 1
  63. rem = y - val
  64. while val >= 65536:
  65. dat.append(0)
  66. dat.append(0)
  67. val -= 65536
  68. dat.append(val>>8)
  69. dat.append(val&255)
  70. # Header for last track(s) in case we ran out of flux timings.
  71. while rev <= nr_revs:
  72. tdh += struct.pack("<III",
  73. int(round(flux.index_list[rev]*factor)),
  74. (len(dat) - len_at_index) // 2,
  75. 4 + nr_revs*12 + len_at_index)
  76. len_at_index = len(dat)
  77. rev += 1
  78. self.track_list.append((tdh, dat))
  79. def get_image(self):
  80. s_trk = self.start_cyl * self.nr_sides
  81. e_trk = s_trk + len(self.track_list) - 1
  82. # Generate the TLUT and concatenate all the tracks together.
  83. trk_offs = bytearray(s_trk * 4)
  84. trk_dat = bytearray()
  85. for tdh, dat in self.track_list:
  86. trk_offs += struct.pack("<I", 0x2b0 + len(trk_dat))
  87. trk_dat += tdh + dat
  88. trk_offs += bytes(0x2a0 - len(trk_offs))
  89. # Calculate checksum over all data (except 16-byte image header).
  90. csum = 0
  91. for x in trk_offs:
  92. csum += x
  93. for x in trk_dat:
  94. csum += x
  95. # Generate the image header.
  96. header = struct.pack("<3s9BI",
  97. b"SCP", # Signature
  98. 0, # Version
  99. 0x80, # DiskType = Other
  100. self.nr_revs, s_trk, e_trk,
  101. 0x01, # Flags = Index
  102. 0, # 16-bit cell width
  103. 1 if self.nr_sides == 1 else 0,
  104. 0, # 25ns capture
  105. csum & 0xffffffff)
  106. # Concatenate it all together and send it back.
  107. return header + trk_offs + trk_dat
  108. # Local variables:
  109. # python-indent: 4
  110. # End: