edsk.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. # greaseweazle/image/edsk.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 math, struct
  8. import itertools as it
  9. from greaseweazle import error
  10. from greaseweazle.codec.ibm import mfm
  11. from .image import Image
  12. class EDSK(Image):
  13. read_only = True
  14. default_format = 'ibm.mfm'
  15. clock = 2e-6
  16. time_per_rev = 0.2
  17. def __init__(self):
  18. self.to_track = dict()
  19. @classmethod
  20. def from_file(cls, name):
  21. with open(name, "rb") as f:
  22. dat = f.read()
  23. edsk = cls()
  24. sig, creator, ncyls, nsides, track_sz = struct.unpack(
  25. '<34s14s2BH', dat[:52])
  26. if sig[:8] == b'MV - CPC':
  27. extended = False
  28. elif sig[:16] == b'EXTENDED CPC DSK':
  29. extended = True
  30. else:
  31. raise error.Fatal('Unrecognised CPC DSK file: bad signature')
  32. if extended:
  33. tsizes = list(dat[52:52+ncyls*nsides])
  34. tsizes = list(map(lambda x: x*256, tsizes))
  35. else:
  36. raise error.Fatal('Standard CPC DSK file not yet supported')
  37. o = 256 # skip disk header and track-size table
  38. for tsize in tsizes:
  39. if tsize == 0:
  40. continue
  41. sig, cyl, head, sec_sz, nsecs, gap_3, filler = struct.unpack(
  42. '<12s4x2B2x4B', dat[o:o+24])
  43. error.check(sig == b'Track-Info\r\n',
  44. 'EDSK: Missing track header')
  45. error.check((cyl, head) not in edsk.to_track,
  46. 'EDSK: Track specified twice')
  47. while True:
  48. track = mfm.IBM_MFM_Formatted(cyl, head)
  49. track.clock = cls().clock
  50. track.time_per_rev = cls().time_per_rev
  51. pos = track.gap_4a
  52. track.iams = [mfm.IAM(pos*16,(pos+4)*16)]
  53. pos += 4 + track.gap_1
  54. secs = dat[o+24:o+24+8*nsecs]
  55. data_pos = o + 256 # skip track header and sector-info table
  56. while secs:
  57. c, h, r, n, stat1, stat2, actual_length = struct.unpack(
  58. '<6BH', secs[:8])
  59. secs = secs[8:]
  60. pos += track.gap_presync
  61. idam = mfm.IDAM(pos*16, (pos+10)*16, 0,
  62. c=c, h=h, r=r, n=n)
  63. pos += 10 + track.gap_2 + track.gap_presync
  64. size = 128 << n
  65. error.check(size == actual_length,
  66. 'EDSK: Weird sector size (copy protection?)')
  67. error.check(stat1 == 0 and stat2 == 0,
  68. 'EDSK: Mangled sector (copy protection?)')
  69. sec_data = dat[data_pos:data_pos+size]
  70. dam = mfm.DAM(pos*16, (pos+4+size+2)*16, 0,
  71. mark=track.DAM, data=sec_data)
  72. track.sectors.append(mfm.Sector(idam, dam))
  73. pos += 4 + size + 2 + gap_3
  74. data_pos += size
  75. # Some EDSK images have bogus GAP3 values. If the track is too
  76. # long to comfortably fit in 300rpm at double density, shrink
  77. # GAP3 as far as necessary.
  78. tracklen = int((track.time_per_rev / track.clock) / 16)
  79. overhang = int(pos - tracklen*0.99)
  80. if overhang <= 0:
  81. break
  82. new_gap_3 = gap_3 - math.ceil(overhang / nsecs)
  83. error.check(new_gap_3 >= 0,
  84. 'EDSK: Track %d.%d is too long '
  85. '(%d bits @ GAP3=%d; %d bits @ GAP3=0)'
  86. % (cyl, head, pos*16, gap_3, (pos-gap_3*nsecs)*16))
  87. gap_3 = new_gap_3
  88. edsk.to_track[cyl,head] = track
  89. o += tsize
  90. return edsk
  91. def get_track(self, cyl, side):
  92. if (cyl,side) not in self.to_track:
  93. return None
  94. return self.to_track[cyl,side].raw_track()
  95. # Local variables:
  96. # python-indent: 4
  97. # End: