edsk.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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.filler = filler
  50. track.clock = cls().clock
  51. track.time_per_rev = cls().time_per_rev
  52. pos = track.gap_4a
  53. track.iams = [mfm.IAM(pos*16,(pos+4)*16)]
  54. pos += 4 + track.gap_1
  55. secs = dat[o+24:o+24+8*nsecs]
  56. data_pos = o + 256 # skip track header and sector-info table
  57. while secs:
  58. c, h, r, n, stat1, stat2, actual_length = struct.unpack(
  59. '<6BH', secs[:8])
  60. secs = secs[8:]
  61. pos += track.gap_presync
  62. idam = mfm.IDAM(pos*16, (pos+10)*16, 0,
  63. c=c, h=h, r=r, n=n)
  64. pos += 10 + track.gap_2 + track.gap_presync
  65. size = 128 << n
  66. error.check(size == actual_length,
  67. 'EDSK: Weird sector size (copy protection?)')
  68. error.check(stat1 == 0 and stat2 == 0,
  69. 'EDSK: Mangled sector (copy protection?)')
  70. sec_data = dat[data_pos:data_pos+size]
  71. dam = mfm.DAM(pos*16, (pos+4+size+2)*16, 0,
  72. mark=track.DAM, data=sec_data)
  73. track.sectors.append(mfm.Sector(idam, dam))
  74. pos += 4 + size + 2 + gap_3
  75. data_pos += size
  76. # Some EDSK images have bogus GAP3 values. If the track is too
  77. # long to comfortably fit in 300rpm at double density, shrink
  78. # GAP3 as far as necessary.
  79. tracklen = int((track.time_per_rev / track.clock) / 16)
  80. overhang = int(pos - tracklen*0.99)
  81. if overhang <= 0:
  82. break
  83. new_gap_3 = gap_3 - math.ceil(overhang / nsecs)
  84. error.check(new_gap_3 >= 0,
  85. 'EDSK: Track %d.%d is too long '
  86. '(%d bits @ GAP3=%d; %d bits @ GAP3=0)'
  87. % (cyl, head, pos*16, gap_3, (pos-gap_3*nsecs)*16))
  88. gap_3 = new_gap_3
  89. edsk.to_track[cyl,head] = track
  90. o += tsize
  91. return edsk
  92. def get_track(self, cyl, side):
  93. if (cyl,side) not in self.to_track:
  94. return None
  95. return self.to_track[cyl,side].raw_track()
  96. # Local variables:
  97. # python-indent: 4
  98. # End: