amigados.py 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. # greaseweazle/codec/amiga/amigados.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. import itertools as it
  9. from bitarray import bitarray
  10. from greaseweazle.bitcell import Bitcell
  11. sync_bytes = b'\x44\x89\x44\x89'
  12. sync = bitarray(endian='big')
  13. sync.frombytes(sync_bytes)
  14. class AmigaDOS:
  15. DDSEC = 11
  16. def __init__(self, tracknr, nsec=DDSEC):
  17. self.tracknr = tracknr
  18. self.nsec = nsec
  19. self.sector = [None] * nsec
  20. self.map = [None] * nsec
  21. def exists(self, sec_id, togo):
  22. return ((self.sector[sec_id] is not None)
  23. or (self.map[self.nsec-togo] is not None))
  24. def nr_missing(self):
  25. return len([sec for sec in self.sector if sec is None])
  26. def add(self, sec_id, togo, label, data):
  27. assert not self.exists(sec_id, togo)
  28. self.sector[sec_id] = label, data
  29. self.map[self.nsec-togo] = sec_id
  30. def get_adf_track(self):
  31. tdat = bytearray()
  32. for sec in self.sector:
  33. tdat += sec[1] if sec is not None else bytes([0] * 512)
  34. return tdat
  35. def set_adf_track(self, tdat):
  36. self.map = list(range(self.nsec))
  37. for sec in self.map:
  38. self.sector[sec] = bytes([0x00] * 16), tdat[sec*512:(sec+1)*512]
  39. def flux_for_writeout(self):
  40. return self.flux()
  41. def flux(self):
  42. return self
  43. def bits(self):
  44. next_bad_sec_id = 0
  45. t = bytearray(64)
  46. for nr in range(self.nsec):
  47. sec_id = self.map[nr]
  48. if sec_id is None:
  49. while self.sector[next_bad_sec_id] is not None:
  50. next_bad_sec_id += 1
  51. sec_id = next_bad_sec_id
  52. label, data = bytes([0x00] * 16), bytes([0x00] * 512)
  53. else:
  54. label, data = self.sector[sec_id]
  55. t += sync_bytes
  56. header = bytes([0xff, self.tracknr, sec_id, self.nsec-nr])
  57. t += encode(header)
  58. t += encode(label)
  59. t += encode(struct.pack('>I', checksum(header + label)))
  60. t += encode(struct.pack('>I', checksum(data)))
  61. t += encode(data)
  62. t += encode([0x00] * 2)
  63. tlen = 101376 if self.nsec == 11 else 202752
  64. t += bytes(tlen//8-len(t))
  65. return mfm_encode(t)
  66. def verify_track(self, flux):
  67. cyl = self.tracknr // 2
  68. head = self.tracknr & 1
  69. readback_track = decode_track(cyl, head, flux)
  70. return readback_track.nr_missing() == 0
  71. def mfm_encode(dat):
  72. y = 0
  73. out = bytearray()
  74. for x in dat:
  75. y = (y<<8) | x
  76. if (x & 0xaa) == 0:
  77. y |= ~((y>>1)|(y<<1)) & 0xaaaa
  78. y &= 255
  79. out.append(y)
  80. return bytes(out)
  81. def encode(dat):
  82. return bytes(it.chain(map(lambda x: (x >> 1) & 0x55, dat),
  83. map(lambda x: x & 0x55, dat)))
  84. def decode(dat):
  85. length = len(dat)//2
  86. return bytes(map(lambda x, y: (x << 1 & 0xaa) | (y & 0x55),
  87. it.islice(dat, 0, length),
  88. it.islice(dat, length, None)))
  89. def checksum(dat):
  90. csum = 0
  91. for i in range(0, len(dat), 4):
  92. csum ^= struct.unpack('>I', dat[i:i+4])[0]
  93. return (csum ^ (csum>>1)) & 0x55555555
  94. def decode_track(cyl, head, flux):
  95. bc = Bitcell(clock = 2e-6, flux = flux)
  96. bits, times = bc.bitarray, bc.timearray
  97. tracknr = cyl*2 + head
  98. ados = AmigaDOS(tracknr)
  99. sectors = bits.search(sync)
  100. for offs in bits.itersearch(sync):
  101. sec = bits[offs:offs+544*16].tobytes()
  102. header = decode(sec[4:12])
  103. format, track, sec_id, togo = tuple(header)
  104. if format != 0xff or track != tracknr \
  105. or not(sec_id < ados.nsec and 0 < togo <= ados.nsec) \
  106. or ados.exists(sec_id, togo):
  107. continue
  108. label = decode(sec[12:44])
  109. hsum, = struct.unpack('>I', decode(sec[44:52]))
  110. if hsum != checksum(header + label):
  111. continue
  112. dsum, = struct.unpack('>I', decode(sec[52:60]))
  113. data = decode(sec[60:1084])
  114. gap = decode(sec[1084:1088])
  115. if dsum != checksum(data):
  116. continue;
  117. ados.add(sec_id, togo, label, data)
  118. if ados.nr_missing() == 0:
  119. break
  120. return ados
  121. # Local variables:
  122. # python-indent: 4
  123. # End: