hfe.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. # greaseweazle/image/hfe.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. from greaseweazle import error
  9. from greaseweazle.track import MasterTrack
  10. from greaseweazle.bitcell import Bitcell
  11. from bitarray import bitarray
  12. class HFE:
  13. def __init__(self, start_cyl, nr_sides):
  14. self.start_cyl = start_cyl
  15. self.nr_sides = nr_sides
  16. self.bitrate = 250 # XXX real bitrate?
  17. # Each track is (bitlen, rawbytes).
  18. # rawbytes is a bytes() object in little-endian bit order.
  19. self.track_list = []
  20. @classmethod
  21. def to_file(cls, start_cyl, nr_sides):
  22. hfe = cls(start_cyl, nr_sides)
  23. return hfe
  24. @classmethod
  25. def from_file(cls, dat):
  26. (sig, f_rev, nr_cyls, nr_sides, t_enc, bitrate,
  27. _, _, _, tlut_base) = struct.unpack("<8s4B2H2BH", dat[:20])
  28. error.check(sig != b"HXCHFEV3", "HFEv3 is not supported")
  29. error.check(sig == b"HXCPICFE" and f_rev <= 1, "Not a valid HFE file")
  30. error.check(0 < nr_cyls, "HFE: Invalid #cyls")
  31. error.check(0 < nr_sides < 3, "HFE: Invalid #sides")
  32. error.check(bitrate != 0, "HFE: Invalid bitrate")
  33. hfe = cls(0, nr_sides)
  34. hfe.bitrate = bitrate
  35. tlut = dat[tlut_base*512:tlut_base*512+nr_cyls*4]
  36. for cyl in range(nr_cyls):
  37. for side in range(nr_sides):
  38. offset, length = struct.unpack("<2H", tlut[cyl*4:(cyl+1)*4])
  39. todo = length // 2
  40. tdat = bytes()
  41. while todo:
  42. d_off = offset*512 + side*256
  43. d_nr = 256 if todo > 256 else todo
  44. tdat += dat[d_off:d_off+d_nr]
  45. todo -= d_nr
  46. offset += 1
  47. hfe.track_list.append((len(tdat)*8, tdat))
  48. return hfe
  49. def get_track(self, cyl, side, writeout=False):
  50. if side >= self.nr_sides or cyl < self.start_cyl:
  51. return None
  52. off = cyl * self.nr_sides + side
  53. if off >= len(self.track_list):
  54. return None
  55. bitlen, rawbytes = self.track_list[off]
  56. tdat = bitarray(endian='little')
  57. tdat.frombytes(rawbytes)
  58. track = MasterTrack(
  59. bits = tdat[:bitlen],
  60. time_per_rev = bitlen / (2000*self.bitrate))
  61. return track
  62. def append_track(self, flux):
  63. bc = Bitcell(clock = 5e-4 / self.bitrate, flux = flux)
  64. bits, _ = bc.get_revolution(0)
  65. bits.bytereverse()
  66. self.track_list.append((len(bits), bits.tobytes()))
  67. def get_image(self):
  68. # Construct the image header.
  69. n_cyl = self.start_cyl + len(self.track_list) // self.nr_sides
  70. header = struct.pack("<8s4B2H2BH",
  71. b"HXCPICFE",
  72. 0,
  73. n_cyl,
  74. self.nr_sides,
  75. 0xff, # unknown encoding
  76. self.bitrate,
  77. 0, # rpm (unused)
  78. 0xff, # unknown interface
  79. 1, # rsvd
  80. 1) # track list offset
  81. # We dynamically build the Track-LUT and -Data arrays.
  82. tlut = bytearray()
  83. tdat = bytearray()
  84. # Dummy data for unused initial cylinders. Assumes 300RPM.
  85. for i in range(self.start_cyl):
  86. nr_bytes = 100 * self.bitrate
  87. tlut += struct.pack("<2H", len(tdat)//512 + 2, nr_bytes)
  88. tdat += bytes([0x88] * (nr_bytes+0x1ff & ~0x1ff))
  89. # Stuff real data into the image.
  90. for i in range(0, len(self.track_list), self.nr_sides):
  91. bc = [self.track_list[i],
  92. self.track_list[i+1] if self.nr_sides > 1 else (0,bytes())]
  93. nr_bytes = max(len(t[1]) for t in bc)
  94. nr_blocks = (nr_bytes + 0xff) // 0x100
  95. tlut += struct.pack("<2H", len(tdat)//512 + 2, 2 * nr_bytes)
  96. for b in range(nr_blocks):
  97. for t in bc:
  98. slice = t[1][b*256:(b+1)*256]
  99. tdat += slice + bytes([0x88] * (256 - len(slice)))
  100. # Pad the header and TLUT to 512-byte blocks.
  101. header += bytes([0xff] * (0x200 - len(header)))
  102. tlut += bytes([0xff] * (0x200 - len(tlut)))
  103. return header + tlut + tdat
  104. # Local variables:
  105. # python-indent: 4
  106. # End: