diskstack 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. diskstack/__init__.py +5 -0
  2. diskstack/_vendor/__init__.py +0 -0
  3. diskstack/_vendor/greaseweazle/COPYING +24 -0
  4. diskstack/_vendor/greaseweazle/__init__.py +2 -0
  5. diskstack/_vendor/greaseweazle/codec/__init__.py +0 -0
  6. diskstack/_vendor/greaseweazle/codec/amiga/__init__.py +0 -0
  7. diskstack/_vendor/greaseweazle/codec/amiga/amigados.py +221 -0
  8. diskstack/_vendor/greaseweazle/codec/apple2/__init__.py +0 -0
  9. diskstack/_vendor/greaseweazle/codec/apple2/apple2_gcr.py +223 -0
  10. diskstack/_vendor/greaseweazle/codec/bitcell.py +117 -0
  11. diskstack/_vendor/greaseweazle/codec/codec.py +362 -0
  12. diskstack/_vendor/greaseweazle/codec/commodore/__init__.py +0 -0
  13. diskstack/_vendor/greaseweazle/codec/commodore/c64_gcr.py +229 -0
  14. diskstack/_vendor/greaseweazle/codec/datageneral/__init__.py +0 -0
  15. diskstack/_vendor/greaseweazle/codec/datageneral/datageneral.py +211 -0
  16. diskstack/_vendor/greaseweazle/codec/hp/__init__.py +0 -0
  17. diskstack/_vendor/greaseweazle/codec/hp/hp_mmfm.py +258 -0
  18. diskstack/_vendor/greaseweazle/codec/ibm/__init__.py +0 -0
  19. diskstack/_vendor/greaseweazle/codec/ibm/ibm.py +1005 -0
  20. diskstack/_vendor/greaseweazle/codec/macintosh/__init__.py +0 -0
  21. diskstack/_vendor/greaseweazle/codec/macintosh/mac_gcr.py +238 -0
  22. diskstack/_vendor/greaseweazle/codec/micropolis/__init__.py +0 -0
  23. diskstack/_vendor/greaseweazle/codec/micropolis/micropolis.py +233 -0
  24. diskstack/_vendor/greaseweazle/codec/northstar/__init__.py +0 -0
  25. diskstack/_vendor/greaseweazle/codec/northstar/northstar.py +210 -0
  26. diskstack/_vendor/greaseweazle/data/__init__.py +0 -0
  27. diskstack/_vendor/greaseweazle/data/diskdefs.cfg +81 -0
  28. diskstack/_vendor/greaseweazle/data/diskdefs_acorn.cfg +119 -0
  29. diskstack/_vendor/greaseweazle/data/diskdefs_akai.cfg +27 -0
  30. diskstack/_vendor/greaseweazle/data/diskdefs_amiga.cfg +17 -0
  31. diskstack/_vendor/greaseweazle/data/diskdefs_apple2.cfg +31 -0
  32. diskstack/_vendor/greaseweazle/data/diskdefs_apricot.cfg +25 -0
  33. diskstack/_vendor/greaseweazle/data/diskdefs_atari.cfg +33 -0
  34. diskstack/_vendor/greaseweazle/data/diskdefs_atarist.cfg +76 -0
  35. diskstack/_vendor/greaseweazle/data/diskdefs_coco.cfg +98 -0
  36. diskstack/_vendor/greaseweazle/data/diskdefs_commodore.cfg +127 -0
  37. diskstack/_vendor/greaseweazle/data/diskdefs_datageneral.cfg +8 -0
  38. diskstack/_vendor/greaseweazle/data/diskdefs_dec.cfg +37 -0
  39. diskstack/_vendor/greaseweazle/data/diskdefs_dragon.cfg +61 -0
  40. diskstack/_vendor/greaseweazle/data/diskdefs_eagle.cfg +25 -0
  41. diskstack/_vendor/greaseweazle/data/diskdefs_ensoniq.cfg +39 -0
  42. diskstack/_vendor/greaseweazle/data/diskdefs_epson.cfg +95 -0
  43. diskstack/_vendor/greaseweazle/data/diskdefs_gem.cfg +15 -0
  44. diskstack/_vendor/greaseweazle/data/diskdefs_hp.cfg +25 -0
  45. diskstack/_vendor/greaseweazle/data/diskdefs_ibm.cfg +140 -0
  46. diskstack/_vendor/greaseweazle/data/diskdefs_kaypro.cfg +58 -0
  47. diskstack/_vendor/greaseweazle/data/diskdefs_luxor.cfg +96 -0
  48. diskstack/_vendor/greaseweazle/data/diskdefs_mac.cfg +71 -0
  49. diskstack/_vendor/greaseweazle/data/diskdefs_micropolis.cfg +73 -0
  50. diskstack/_vendor/greaseweazle/data/diskdefs_mm1.cfg +69 -0
  51. diskstack/_vendor/greaseweazle/data/diskdefs_msx.cfg +45 -0
  52. diskstack/_vendor/greaseweazle/data/diskdefs_northstar.cfg +37 -0
  53. diskstack/_vendor/greaseweazle/data/diskdefs_occ1.cfg +23 -0
  54. diskstack/_vendor/greaseweazle/data/diskdefs_olivetti.cfg +18 -0
  55. diskstack/_vendor/greaseweazle/data/diskdefs_pc98.cfg +69 -0
  56. diskstack/_vendor/greaseweazle/data/diskdefs_raw.cfg +25 -0
  57. diskstack/_vendor/greaseweazle/data/diskdefs_rm.cfg +98 -0
  58. diskstack/_vendor/greaseweazle/data/diskdefs_sci.cfg +15 -0
  59. diskstack/_vendor/greaseweazle/data/diskdefs_sega.cfg +13 -0
  60. diskstack/_vendor/greaseweazle/data/diskdefs_sharp.cfg +13 -0
  61. diskstack/_vendor/greaseweazle/data/diskdefs_thomson.cfg +78 -0
  62. diskstack/_vendor/greaseweazle/data/diskdefs_tsc.cfg +81 -0
  63. diskstack/_vendor/greaseweazle/data/diskdefs_xerox.cfg +59 -0
  64. diskstack/_vendor/greaseweazle/data/diskdefs_zx.cfg +245 -0
  65. diskstack/_vendor/greaseweazle/error.py +19 -0
  66. diskstack/_vendor/greaseweazle/flux.py +315 -0
  67. diskstack/_vendor/greaseweazle/image/__init__.py +0 -0
  68. diskstack/_vendor/greaseweazle/image/a2r.py +133 -0
  69. diskstack/_vendor/greaseweazle/image/acorn.py +28 -0
  70. diskstack/_vendor/greaseweazle/image/adf.py +15 -0
  71. diskstack/_vendor/greaseweazle/image/apple2.py +18 -0
  72. diskstack/_vendor/greaseweazle/image/apridisk.py +107 -0
  73. diskstack/_vendor/greaseweazle/image/caps.py +464 -0
  74. diskstack/_vendor/greaseweazle/image/d64.py +51 -0
  75. diskstack/_vendor/greaseweazle/image/d81.py +28 -0
  76. diskstack/_vendor/greaseweazle/image/d88.py +211 -0
  77. diskstack/_vendor/greaseweazle/image/dcp.py +45 -0
  78. diskstack/_vendor/greaseweazle/image/dim.py +53 -0
  79. diskstack/_vendor/greaseweazle/image/dmk.py +166 -0
  80. diskstack/_vendor/greaseweazle/image/dsk.py +31 -0
  81. diskstack/_vendor/greaseweazle/image/edsk.py +591 -0
  82. diskstack/_vendor/greaseweazle/image/fd.py +23 -0
  83. diskstack/_vendor/greaseweazle/image/fdi.py +41 -0
  84. diskstack/_vendor/greaseweazle/image/hdm.py +15 -0
  85. diskstack/_vendor/greaseweazle/image/hfe.py +742 -0
  86. diskstack/_vendor/greaseweazle/image/image.py +130 -0
  87. diskstack/_vendor/greaseweazle/image/imd.py +201 -0
  88. diskstack/_vendor/greaseweazle/image/img.py +111 -0
  89. diskstack/_vendor/greaseweazle/image/kryoflux.py +304 -0
  90. diskstack/_vendor/greaseweazle/image/mgt.py +15 -0
  91. diskstack/_vendor/greaseweazle/image/msa.py +153 -0
  92. diskstack/_vendor/greaseweazle/image/nfd.py +113 -0
  93. diskstack/_vendor/greaseweazle/image/nsi.py +47 -0
  94. diskstack/_vendor/greaseweazle/image/scp.py +471 -0
  95. diskstack/_vendor/greaseweazle/image/sf7.py +15 -0
  96. diskstack/_vendor/greaseweazle/image/sharp2d.py +15 -0
  97. diskstack/_vendor/greaseweazle/image/td0.py +141 -0
  98. diskstack/_vendor/greaseweazle/image/xdf.py +15 -0
  99. diskstack/_vendor/greaseweazle/optimised/__init__.py +22 -0
  100. diskstack/_vendor/greaseweazle/tools/__init__.py +0 -0
  101. diskstack/_vendor/greaseweazle/tools/util.py +133 -0
  102. diskstack/_vendor/greaseweazle/track.py +494 -0
  103. diskstack/cache.py +210 -0
  104. diskstack/candidates.py +396 -0
  105. diskstack/cli.py +348 -0
  106. diskstack/errors.py +5 -0
  107. diskstack/filler.py +29 -0
  108. diskstack/formats.py +381 -0
  109. diskstack/parallel.py +73 -0
  110. diskstack/report.py +353 -0
  111. diskstack/stack.py +317 -0
  112. diskstack-1.0.0.dist-info/METADATA +329 -0
  113. diskstack-1.0.0.dist-info/RECORD +119 -0
  114. diskstack-1.0.0.dist-info/WHEEL +5 -0
  115. diskstack-1.0.0.dist-info/entry_points.txt +2 -0
  116. diskstack-1.0.0.dist-info/licenses/LICENSE +21 -0
  117. diskstack-1.0.0.dist-info/licenses/NOTICE +55 -0
  118. diskstack-1.0.0.dist-info/licenses/diskstack/_vendor/greaseweazle/COPYING +24 -0
  119. diskstack-1.0.0.dist-info/top_level.txt +1 -0
diskstack/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Merge several dumps of one floppy disk into the best possible image."""
2
+
3
+ __version__ = '1.0.0'
4
+
5
+ __all__ = ['__version__']
File without changes
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <http://unlicense.org/>
@@ -0,0 +1,2 @@
1
+ __version__ = 'vendored-26690f89967d'
2
+ from diskstack._vendor.greaseweazle.codec import codec # noqa: F401
File without changes
File without changes
@@ -0,0 +1,221 @@
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
+
8
+ from typing import List, Optional, Tuple
9
+
10
+ import struct
11
+ import itertools as it
12
+ from bitarray import bitarray
13
+
14
+ from diskstack._vendor.greaseweazle import error
15
+ from diskstack._vendor.greaseweazle.codec import codec
16
+ from diskstack._vendor.greaseweazle.codec.ibm import ibm
17
+ from diskstack._vendor.greaseweazle.track import MasterTrack, PLL, PLLTrack
18
+ from diskstack._vendor.greaseweazle.flux import Flux, HasFlux
19
+
20
+ default_revs = 1.1
21
+
22
+ sync_bytes = b'\x44\x89\x44\x89'
23
+ sync = bitarray(endian='big')
24
+ sync.frombytes(sync_bytes)
25
+
26
+ bad_sector = b'-=[BAD SECTOR]=-' * 32
27
+
28
+ class AmigaDOS(codec.Codec):
29
+
30
+ # Subclasses must define these
31
+ nsec: int
32
+ clock: float
33
+
34
+ time_per_rev = 0.2
35
+
36
+ verify_revs = default_revs
37
+
38
+ def __init__(self, cyl: int, head: int):
39
+ self.tracknr = cyl*2 + head
40
+ self.sector: List[Optional[Tuple[bytes,bytes]]]
41
+ self.sector = [None] * self.nsec
42
+ self.map: List[Optional[int]]
43
+ self.map = [None] * self.nsec
44
+
45
+ def summary_string(self) -> str:
46
+ nsec, nbad = self.nsec, self.nr_missing()
47
+ s = "AmigaDOS (%d/%d sectors)" % (nsec - nbad, nsec)
48
+ return s
49
+
50
+ # private
51
+ def exists(self, sec_id, togo) -> bool:
52
+ return ((self.sector[sec_id] is not None)
53
+ or (self.map[self.nsec-togo] is not None))
54
+
55
+ # private
56
+ def add(self, sec_id, togo, label, data) -> None:
57
+ assert not self.exists(sec_id, togo)
58
+ self.sector[sec_id] = label, data
59
+ self.map[self.nsec-togo] = sec_id
60
+
61
+ def has_sec(self, sec_id: int) -> bool:
62
+ return self.sector[sec_id] is not None
63
+
64
+ def nr_missing(self) -> int:
65
+ return len([sec for sec in self.sector if sec is None])
66
+
67
+ def get_img_track(self) -> bytearray:
68
+ tdat = bytearray()
69
+ for sec in self.sector:
70
+ tdat += sec[1] if sec is not None else bad_sector
71
+ return tdat
72
+
73
+ def set_img_track(self, tdat: bytes) -> int:
74
+ totsize = self.nsec * 512
75
+ if len(tdat) < totsize:
76
+ tdat += bytes(totsize - len(tdat))
77
+ self.map = list(range(self.nsec))
78
+ for sec in range(self.nsec):
79
+ self.sector[sec] = bytes(16), tdat[sec*512:(sec+1)*512]
80
+ return totsize
81
+
82
+ def decode_flux(self, track: HasFlux, pll: Optional[PLL]=None) -> None:
83
+ raw = PLLTrack(time_per_rev = self.time_per_rev,
84
+ clock = self.clock, data = track, pll = pll)
85
+ bits, _ = raw.get_all_data()
86
+
87
+ for offs in bits.search(sync):
88
+
89
+ if self.nr_missing() == 0:
90
+ break
91
+
92
+ sec = bits[offs:offs+544*16].tobytes()
93
+ if len(sec) != 1088:
94
+ continue
95
+
96
+ header = decode(sec[4:12])
97
+ format, track, sec_id, togo = tuple(header)
98
+ if format != 0xff or track != self.tracknr \
99
+ or not(sec_id < self.nsec and 0 < togo <= self.nsec) \
100
+ or self.exists(sec_id, togo):
101
+ continue
102
+
103
+ label = decode(sec[12:44])
104
+ hsum, = struct.unpack('>I', decode(sec[44:52]))
105
+ if hsum != checksum(header + label):
106
+ continue
107
+
108
+ dsum, = struct.unpack('>I', decode(sec[52:60]))
109
+ data = decode(sec[60:1084])
110
+ gap = decode(sec[1084:1088])
111
+ if dsum != checksum(data):
112
+ continue;
113
+
114
+ self.add(sec_id, togo, label, data)
115
+
116
+
117
+ def master_track(self) -> MasterTrack:
118
+
119
+ # List of sector IDs missing from the sector map:
120
+ missing = iter([x for x in range(self.nsec) if not x in self.map])
121
+ # Sector map with the missing entries filled in:
122
+ full_map = [next(missing) if x is None else x for x in self.map]
123
+
124
+ # Post-index track gap.
125
+ t = encode(bytes(128 * (self.nsec//11)))
126
+
127
+ for nr, sec_id in zip(range(self.nsec), full_map):
128
+ sector = self.sector[sec_id]
129
+ label, data = (bytes(16), bad_sector) if sector is None else sector
130
+ header = bytes([0xff, self.tracknr, sec_id, self.nsec-nr])
131
+ t += sync_bytes
132
+ t += encode(header)
133
+ t += encode(label)
134
+ t += encode(struct.pack('>I', checksum(header + label)))
135
+ t += encode(struct.pack('>I', checksum(data)))
136
+ t += encode(data)
137
+ t += encode(bytes(2))
138
+
139
+ # Add the pre-index gap.
140
+ tlen = (int((self.time_per_rev / self.clock)) + 31) & ~31
141
+ t += bytes(tlen//8-len(t))
142
+
143
+ track = MasterTrack(
144
+ bits = ibm.mfm_encode(t),
145
+ time_per_rev = self.time_per_rev)
146
+ track.verify = self
147
+ return track
148
+
149
+
150
+ def verify_track(self, flux):
151
+ cyl = self.tracknr // 2
152
+ head = self.tracknr & 1
153
+ readback_track = self.__class__(cyl, head)
154
+ readback_track.decode_flux(flux)
155
+ return (readback_track.nr_missing() == 0
156
+ and self.sector == readback_track.sector)
157
+
158
+
159
+ class AmigaDOS_DD(AmigaDOS):
160
+ nsec = 11
161
+ clock = 14/7093790
162
+
163
+ class AmigaDOS_HD(AmigaDOS):
164
+ nsec = 22
165
+ clock = AmigaDOS_DD.clock / 2
166
+
167
+
168
+ class AmigaDOSDef(codec.TrackDef):
169
+
170
+ default_revs = default_revs
171
+
172
+ def __init__(self, format_name: str):
173
+ self.secs = None
174
+ self.finalised = False
175
+
176
+ def add_param(self, key: str, val) -> None:
177
+ if key == 'secs':
178
+ val = int(val)
179
+ error.check(val in [11, 22], '%s out of range' % key)
180
+ self.secs = val
181
+ else:
182
+ raise error.Fatal('unrecognised track option %s' % key)
183
+
184
+ def finalise(self) -> None:
185
+ if self.finalised:
186
+ return
187
+ error.check(self.secs is not None,
188
+ 'number of sectors not specified')
189
+ self.finalised = True
190
+
191
+ def mk_track(self, cyl: int, head: int) -> AmigaDOS:
192
+ t: AmigaDOS
193
+ if self.secs == 11:
194
+ t = AmigaDOS_DD(cyl, head)
195
+ else:
196
+ t = AmigaDOS_HD(cyl, head)
197
+ return t
198
+
199
+
200
+ def encode(dat):
201
+ return bytes(it.chain(map(lambda x: (x >> 1) & 0x55, dat),
202
+ map(lambda x: x & 0x55, dat)))
203
+
204
+
205
+ def decode(dat):
206
+ length = len(dat)//2
207
+ return bytes(map(lambda x, y: (x << 1 & 0xaa) | (y & 0x55),
208
+ it.islice(dat, 0, length),
209
+ it.islice(dat, length, None)))
210
+
211
+
212
+ def checksum(dat):
213
+ csum = 0
214
+ for i in range(0, len(dat), 4):
215
+ csum ^= struct.unpack('>I', dat[i:i+4])[0]
216
+ return (csum ^ (csum>>1)) & 0x55555555
217
+
218
+
219
+ # Local variables:
220
+ # python-indent: 4
221
+ # End:
@@ -0,0 +1,223 @@
1
+ # greaseweazle/codec/apple2/apple2_gcr.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
+
8
+ from typing import List, Optional, Tuple
9
+
10
+ import struct
11
+ from bitarray import bitarray
12
+
13
+ from diskstack._vendor.greaseweazle import error
14
+ from diskstack._vendor.greaseweazle import optimised
15
+ from diskstack._vendor.greaseweazle.codec import codec
16
+ from diskstack._vendor.greaseweazle.track import MasterTrack, PLL, PLLTrack
17
+ from diskstack._vendor.greaseweazle.flux import Flux, HasFlux
18
+
19
+ default_revs = 1.1
20
+
21
+ gap4 = bitarray([1], endian='big')
22
+ ff40 = bitarray([1]*8 + [0]*2, endian='big')
23
+ ff32 = ff40[:8]
24
+
25
+ addr_sync = bitarray(endian='big')
26
+ addr_sync.frombytes(b'\xd5\xaa\x96')
27
+
28
+ data_sync = bitarray(endian='big')
29
+ data_sync.frombytes(b'\xd5\xaa\xad')
30
+
31
+ trailer = bitarray(endian='big')
32
+ trailer.frombytes(b'\xde\xaa\xeb')
33
+
34
+ bad_sector = b'-=[BAD SECTOR]=-' * 16
35
+
36
+ class Apple2GCR(codec.Codec):
37
+
38
+ time_per_rev = 0.2
39
+
40
+ verify_revs = default_revs
41
+
42
+ def __init__(self, cyl: int, head: int, config):
43
+ self.cyl, self.head = cyl, head
44
+ self.config = config
45
+ self.clock = config.clock
46
+ self.sector: List[Optional[bytes]]
47
+ self.sector = [None] * self.nsec
48
+ self.vol_id: Optional[int] = None
49
+ error.check(optimised.enabled,
50
+ 'Apple2 GCR requires optimised C extension')
51
+
52
+ @property
53
+ def nsec(self) -> int:
54
+ return len(self.config.secs)
55
+
56
+ def summary_string(self) -> str:
57
+ nsec, nbad = self.nsec, self.nr_missing()
58
+ s = "Apple2 GCR (%d/%d sectors)" % (nsec - nbad, nsec)
59
+ return s
60
+
61
+ def set_vol_id(self, vol_id: int):
62
+ assert self.vol_id is None
63
+ self.vol_id = vol_id
64
+
65
+ # private
66
+ def add(self, sec_id, data) -> None:
67
+ assert not self.has_sec(sec_id)
68
+ self.sector[sec_id] = data
69
+
70
+ # private
71
+ def tracknr(self) -> int:
72
+ return self.cyl
73
+
74
+ def has_sec(self, sec_id: int) -> bool:
75
+ return self.sector[sec_id] is not None
76
+
77
+ def nr_missing(self) -> int:
78
+ return len([sec for sec in self.sector if sec is None])
79
+
80
+ def get_img_track(self) -> bytearray:
81
+ tdat = bytearray()
82
+ for i in self.config.secs:
83
+ sec = self.sector[i]
84
+ tdat += sec if sec is not None else bad_sector
85
+ return tdat
86
+
87
+ def set_img_track(self, tdat: bytes) -> int:
88
+ totsize = self.nsec * 256
89
+ if len(tdat) < totsize:
90
+ tdat += bytes(totsize - len(tdat))
91
+ for i,sec in enumerate(self.config.secs):
92
+ self.sector[sec] = tdat[i*256:(i+1)*256]
93
+ return totsize
94
+
95
+ def decode_flux(self, track: HasFlux, pll: Optional[PLL]=None) -> None:
96
+ raw = PLLTrack(time_per_rev = self.time_per_rev,
97
+ clock = self.clock, data = track, pll = pll,
98
+ lowpass_thresh = 2.5e-6)
99
+ bits, _ = raw.get_all_data()
100
+
101
+ for offs in bits.search(addr_sync):
102
+
103
+ if self.nr_missing() == 0:
104
+ break
105
+
106
+ # Decode header
107
+ offs += 3*8
108
+ sec = bits[offs:offs+8*8].tobytes()
109
+ if len(sec) != 8:
110
+ continue
111
+ hdr = map(lambda x: (x & x>>7) & 255,
112
+ list(struct.unpack('>4H', sec)))
113
+ vol_id, trk_id, sec_id, csum = tuple(hdr)
114
+
115
+ # Validate header
116
+ if csum != vol_id ^ trk_id ^ sec_id:
117
+ continue
118
+ if (trk_id != self.tracknr() or sec_id >= self.nsec):
119
+ print('T%d.%d: Ignoring unexpected sector C:%d S:%d ID:%04x'
120
+ % (self.cyl, self.head, trk_id, sec_id, vol_id))
121
+ continue
122
+ if self.vol_id is None:
123
+ self.vol_id = vol_id
124
+ elif self.vol_id != vol_id:
125
+ print('T%d.%d: Expected ID %04x in sector C:%d S:%d ID:%04x'
126
+ % (self.cyl, self.head, self.vol_id,
127
+ trk_id, sec_id, vol_id))
128
+ continue
129
+ if self.has_sec(sec_id):
130
+ continue
131
+
132
+ # Find data
133
+ offs += 8*8
134
+ dat_offs = list(bits[offs:offs+100*8].search(data_sync))
135
+ if len(dat_offs) != 1:
136
+ continue
137
+ offs += dat_offs[0]
138
+
139
+ # Decode data
140
+ sec, csum = optimised.decode_apple2_sector(
141
+ bits[offs+3*8:offs+400*8].tobytes())
142
+ if csum != 0:
143
+ continue
144
+
145
+ self.add(sec_id, sec)
146
+
147
+
148
+ def master_track(self) -> MasterTrack:
149
+
150
+ vol_id = self.vol_id if self.vol_id is not None else 254
151
+ trk_id = self.tracknr()
152
+
153
+ # Post-index track gap.
154
+ t = gap4 * 300
155
+
156
+ for sec_id in range(self.nsec):
157
+ sector = self.sector[sec_id]
158
+ data = bad_sector if sector is None else sector
159
+ t += ff40*18 + ff32 + addr_sync
160
+ t.frombytes(b''.join(map(
161
+ lambda x: bytes([x>>1 | 0xaa, x | 0xaa]),
162
+ [vol_id, trk_id, sec_id, vol_id ^ trk_id ^ sec_id])))
163
+ t += trailer + ff40*6 + data_sync
164
+ t.frombytes(optimised.encode_apple2_sector(data))
165
+ t += trailer
166
+
167
+ # Add the pre-index gap.
168
+ tlen = int((self.time_per_rev / self.clock))
169
+ t += gap4 * (tlen-len(t))
170
+
171
+ track = MasterTrack(bits = t, time_per_rev = self.time_per_rev)
172
+ track.verify = self
173
+ return track
174
+
175
+
176
+ def verify_track(self, flux):
177
+ readback_track = self.__class__(self.cyl, self.head, self.config)
178
+ readback_track.decode_flux(flux)
179
+ return (readback_track.nr_missing() == 0
180
+ and self.sector == readback_track.sector)
181
+
182
+
183
+ class Apple2GCRDef(codec.TrackDef):
184
+
185
+ default_revs = default_revs
186
+
187
+ def __init__(self, format_name: str):
188
+ self.secs: List[int] = []
189
+ self.clock: Optional[float] = None
190
+ self.finalised = False
191
+
192
+ def add_param(self, key: str, val) -> None:
193
+ if key == 'secs':
194
+ self.secs = []
195
+ for x in val.split(','):
196
+ self.secs.append(int(x))
197
+ rsecs = [-1] * len(self.secs)
198
+ for i,x in enumerate(self.secs):
199
+ error.check(x < len(rsecs), f'sector {x} is out of range')
200
+ error.check(rsecs[x] == -1, f'sector {x} is repeated')
201
+ rsecs[x] = i
202
+ elif key == 'clock':
203
+ val = float(val)
204
+ self.clock = val * 1e-6
205
+ else:
206
+ raise error.Fatal('unrecognised track option %s' % key)
207
+
208
+ def finalise(self) -> None:
209
+ if self.finalised:
210
+ return
211
+ error.check(self.secs,
212
+ 'sector list not specified')
213
+ error.check(self.clock is not None,
214
+ 'clock period not specified')
215
+ self.finalised = True
216
+
217
+ def mk_track(self, cyl: int, head: int) -> Apple2GCR:
218
+ return Apple2GCR(cyl, head, self)
219
+
220
+
221
+ # Local variables:
222
+ # python-indent: 4
223
+ # End:
@@ -0,0 +1,117 @@
1
+ # greaseweazle/codec/bitcell.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
+
8
+ from typing import List, Optional, Tuple
9
+
10
+ import struct
11
+ from bitarray import bitarray
12
+
13
+ from diskstack._vendor.greaseweazle import error
14
+ from diskstack._vendor.greaseweazle import optimised
15
+ from diskstack._vendor.greaseweazle.codec import codec
16
+ from diskstack._vendor.greaseweazle.track import MasterTrack, PLL, PLLTrack
17
+ from diskstack._vendor.greaseweazle.flux import Flux, HasFlux
18
+
19
+ default_revs = 1
20
+
21
+ class BitcellTrack(codec.Codec):
22
+
23
+ nsec = 0
24
+
25
+ def __init__(self, cyl: int, head: int, config):
26
+ self.cyl, self.head = cyl, head
27
+ self.config = config
28
+ self.clock = config.clock
29
+ self.raw: Optional[PLLTrack] = None
30
+
31
+ @property
32
+ def time_per_rev(self) -> float:
33
+ if self.raw is not None:
34
+ return self.raw.time_per_rev
35
+ if self.config.time_per_rev is not None:
36
+ return self.config.time_per_rev
37
+ return 0.2
38
+
39
+ def summary_string(self) -> str:
40
+ if self.raw is None:
41
+ s = "Raw Bitcell (empty)"
42
+ else:
43
+ bits, _ = self.raw.get_revolution(0)
44
+ s = ("Raw Bitcell (%d bits, %.2fms)"
45
+ % (len(bits), self.raw.time_per_rev*1000))
46
+ return s
47
+
48
+ def has_sec(self, sec_id: int) -> bool:
49
+ return False
50
+
51
+ def nr_missing(self) -> int:
52
+ return 0
53
+
54
+ def get_img_track(self) -> bytearray:
55
+ return bytearray()
56
+
57
+ def set_img_track(self, tdat: bytes) -> int:
58
+ return 0
59
+
60
+ def decode_flux(self, track: HasFlux, pll: Optional[PLL]=None) -> None:
61
+ flux = track.flux()
62
+ flux.cue_at_index()
63
+ time_per_rev = self.config.time_per_rev
64
+ if time_per_rev is None:
65
+ time_per_rev = flux.time_per_rev
66
+ self.raw = PLLTrack(time_per_rev = time_per_rev,
67
+ clock = self.clock, data = flux, pll = pll)
68
+
69
+ def master_track(self) -> MasterTrack:
70
+ if self.raw is None:
71
+ nbytes = int(self.time_per_rev / self.clock) // 8
72
+ track = MasterTrack(bits = bytes(nbytes),
73
+ time_per_rev = self.time_per_rev,
74
+ weak = [(0,nbytes*8)])
75
+ track.force_random_weak = True
76
+ return track
77
+ bits, _ = self.raw.get_revolution(0)
78
+ track = MasterTrack(bits = bits, time_per_rev = self.time_per_rev)
79
+ return track
80
+
81
+
82
+ class BitcellTrackDef(codec.TrackDef):
83
+
84
+ default_revs = default_revs
85
+
86
+ def __init__(self, format_name: str):
87
+ self.clock: Optional[float] = None
88
+ self.time_per_rev: Optional[float] = None
89
+ self.finalised = False
90
+
91
+ def add_param(self, key: str, val) -> None:
92
+ if key == 'secs':
93
+ val = int(val)
94
+ self.secs = val
95
+ elif key == 'clock':
96
+ val = float(val)
97
+ self.clock = val * 1e-6
98
+ elif key == 'time_per_rev':
99
+ val = float(val)
100
+ self.time_per_rev = val
101
+ else:
102
+ raise error.Fatal('unrecognised track option %s' % key)
103
+
104
+ def finalise(self) -> None:
105
+ if self.finalised:
106
+ return
107
+ error.check(self.clock is not None,
108
+ 'clock period not specified')
109
+ self.finalised = True
110
+
111
+ def mk_track(self, cyl: int, head: int) -> BitcellTrack:
112
+ return BitcellTrack(cyl, head, self)
113
+
114
+
115
+ # Local variables:
116
+ # python-indent: 4
117
+ # End: