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
@@ -0,0 +1,245 @@
1
+ # prefix: zx.
2
+
3
+ # Betadisk TR-DOS
4
+ disk trdos.ds80
5
+ cyls = 80
6
+ heads = 2
7
+ tracks * ibm.mfm
8
+ secs = 16
9
+ bps = 256
10
+ h = 0
11
+ end
12
+ end
13
+
14
+ # Quorum (ZX Spectrum clone) running CP/M-80
15
+ disk quorum.ds80
16
+ cyls = 80
17
+ heads = 2
18
+ tracks * ibm.mfm
19
+ secs = 5
20
+ bps = 1024
21
+ end
22
+ end
23
+
24
+ # Didaktik D80
25
+ disk d80.ds80
26
+ cyls = 80
27
+ heads = 2
28
+ tracks * ibm.mfm
29
+ secs = 9
30
+ bps = 512
31
+ end
32
+ end
33
+
34
+ #Timex FDD 3/3000
35
+ disk fdd3000.ss40
36
+ cyls = 40
37
+ heads = 1
38
+ tracks 0-39 ibm.mfm
39
+ secs = 16
40
+ bps = 256
41
+ id = 0
42
+ rate = 250
43
+ h = 0
44
+ end
45
+ end
46
+
47
+ disk fdd3000.ds80
48
+ cyls = 80
49
+ heads = 2
50
+ tracks 0-79 ibm.mfm
51
+ secs = 16
52
+ bps = 256
53
+ id = 0
54
+ rate = 250
55
+ h = 1
56
+ end
57
+ tracks 0-79 ibm.mfm
58
+ secs = 16
59
+ bps = 256
60
+ id = 0
61
+ rate = 250
62
+ h = 0
63
+ end
64
+ end
65
+
66
+ # Kempston Disk Interface
67
+ disk kempston.ss40
68
+ cyls = 40
69
+ heads = 1
70
+ tracks * ibm.mfm
71
+ secs = 10
72
+ bps = 512
73
+ rate = 250
74
+ id=0
75
+ end
76
+ end
77
+
78
+ disk kempston.ds80
79
+ cyls = 80
80
+ heads = 2
81
+ tracks * ibm.mfm
82
+ secs = 10
83
+ bps = 512
84
+ rate = 250
85
+ id=0
86
+ end
87
+ end
88
+
89
+ # MGT +D
90
+ disk plusd.ds80
91
+ cyls = 80
92
+ heads = 2
93
+ tracks * ibm.mfm
94
+ secs = 10
95
+ bps = 512
96
+ end
97
+ end
98
+
99
+ #ZX Spectrum Opus Discovery
100
+ disk opus.ss40
101
+ cyls = 40
102
+ heads = 1
103
+ tracks * ibm.mfm
104
+ secs = 18
105
+ bps = 256
106
+ id = 0
107
+ rate = 250
108
+ cskew = 13
109
+ interleave=13
110
+ iam = no
111
+ end
112
+ end
113
+
114
+ disk opus.ds80
115
+ cyls = 80
116
+ heads = 2
117
+ tracks * ibm.mfm
118
+ secs = 18
119
+ bps = 256
120
+ id = 0
121
+ rate = 250
122
+ cskew = 13
123
+ interleave=13
124
+ iam = no
125
+ end
126
+ end
127
+
128
+ # ZX Spectrum +3DOS
129
+ disk 3dos.ss40
130
+ cyls = 40
131
+ heads = 1
132
+ tracks * ibm.mfm
133
+ secs = 9
134
+ bps = 512
135
+ rate = 250
136
+ end
137
+ end
138
+
139
+ disk 3dos.ds80
140
+ cyls = 80
141
+ heads = 2
142
+ tracks * ibm.mfm
143
+ secs = 9
144
+ bps = 512
145
+ rate = 250
146
+ end
147
+ end
148
+
149
+ # Rocky Gush
150
+ disk rocky.ss40
151
+ cyls = 40
152
+ heads = 1
153
+ tracks 0-79 ibm.mfm
154
+ secs = 5
155
+ bps = 1024
156
+ id = 0
157
+ rate = 250
158
+ h = 0
159
+ end
160
+ end
161
+
162
+ disk rocky.ds80
163
+ cyls = 80
164
+ heads = 2
165
+ tracks 0-79 ibm.mfm
166
+ secs = 5
167
+ bps = 1024
168
+ id = 0
169
+ rate = 250
170
+ h = 1
171
+ end
172
+ tracks 0-79 ibm.mfm
173
+ secs = 5
174
+ bps = 1024
175
+ id = 0
176
+ rate = 250
177
+ h = 0
178
+ end
179
+ end
180
+
181
+ # Turbodrive
182
+ disk turbodrive.ds40
183
+ cyls = 40
184
+ heads = 2
185
+ tracks 0-39 ibm.mfm
186
+ secs = 10
187
+ bps = 512
188
+ rate = 250
189
+ interleave = 5
190
+ cskew = 5
191
+ h = 1
192
+ end
193
+ tracks 0-39 ibm.mfm
194
+ secs = 10
195
+ bps = 512
196
+ rate = 250
197
+ interleave = 5
198
+ cskew = 5
199
+ h = 0
200
+ end
201
+ end
202
+
203
+ disk turbodrive.ds80
204
+ cyls = 80
205
+ heads = 2
206
+ tracks 0-79 ibm.mfm
207
+ secs = 10
208
+ bps = 512
209
+ rate = 250
210
+ interleave = 5
211
+ cskew = 5
212
+ h = 1
213
+ end
214
+ tracks 0-79 ibm.mfm
215
+ secs = 10
216
+ bps = 512
217
+ rate = 250
218
+ interleave = 5
219
+ cskew = 5
220
+ h = 0
221
+ end
222
+ end
223
+
224
+ # Watford SPDOS
225
+ disk watford.ss40
226
+ cyls = 40
227
+ heads = 1
228
+ tracks * ibm.mfm
229
+ secs = 10
230
+ bps = 512
231
+ rate = 250
232
+ id=0
233
+ end
234
+ end
235
+
236
+ disk watford.ds80
237
+ cyls = 80
238
+ heads = 2
239
+ tracks * ibm.mfm
240
+ secs = 10
241
+ bps = 512
242
+ rate = 250
243
+ id=0
244
+ end
245
+ end
@@ -0,0 +1,19 @@
1
+ # greaseweazle/error.py
2
+ #
3
+ # Error management and reporting.
4
+ #
5
+ # Written & released by Keir Fraser <keir.xen@gmail.com>
6
+ #
7
+ # This is free and unencumbered software released into the public domain.
8
+ # See the file COPYING for more details, or visit <http://unlicense.org>.
9
+
10
+ class Fatal(Exception):
11
+ pass
12
+
13
+ def check(pred, desc):
14
+ if not pred:
15
+ raise Fatal(desc)
16
+
17
+ # Local variables:
18
+ # python-indent: 4
19
+ # End:
@@ -0,0 +1,315 @@
1
+ # greaseweazle/flux.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 __future__ import annotations
9
+ from typing import List, Optional, Protocol
10
+
11
+ from diskstack._vendor.greaseweazle import error
12
+
13
+ class HasFlux(Protocol):
14
+ def summary_string(self) -> str:
15
+ ...
16
+ def flux(self) -> Flux:
17
+ ...
18
+ def flux_for_writeout(self, cue_at_index) -> WriteoutFlux:
19
+ ...
20
+
21
+ class Flux:
22
+
23
+ _ticks_per_rev: float
24
+
25
+ def __init__(self,
26
+ index_list: List[float],
27
+ flux_list: List[float],
28
+ sample_freq: float,
29
+ index_cued = True) -> None:
30
+ self.index_list = index_list
31
+ self.sector_list: Optional[List[List[float]]] = None
32
+ self.list = flux_list
33
+ self.sample_freq = sample_freq
34
+ self.splice: Optional[float] = None
35
+ self.index_cued = index_cued
36
+
37
+
38
+ def __str__(self) -> str:
39
+ s = "\nFlux: %.2f MHz" % (self.sample_freq*1e-6)
40
+ if self.index_cued: s += ", Index-Cued"
41
+ s += ("\n Total: %u samples, %.2fms\n"
42
+ % (len(self.list), sum(self.list)*1000/self.sample_freq))
43
+ for rev, t in enumerate(self.index_list):
44
+ s += " Revolution %u: %.2fms\n" % (rev, t*1000/self.sample_freq)
45
+ if self.sector_list:
46
+ for sec, t in enumerate(self.sector_list[rev]):
47
+ s += (" Sector %u: %.2fms\n"
48
+ % (sec, t*1000/self.sample_freq))
49
+ return s[:-1]
50
+
51
+
52
+ def summary_string(self) -> str:
53
+ return ("Raw Flux (%u flux in %.2fms)"
54
+ % (len(self.list), sum(self.list)*1000/self.sample_freq))
55
+
56
+
57
+ def identify_hard_sectors(self) -> None:
58
+ if self.sector_list is not None:
59
+ return
60
+ error.check(len(self.index_list) > 3,
61
+ "Not enough index marks for a hard-sectored track")
62
+ self.cue_at_index()
63
+ x = [self.index_list[0], self.index_list[2]]
64
+ x.sort()
65
+ thresh = x[1] * 3 / 4
66
+ ticks_to_index: float = 0
67
+ short_ticks: float = 0
68
+ sectors: List[float] = []
69
+ index_list = self.index_list
70
+ self.index_list = []
71
+ self.sector_list = []
72
+ short_count = 0
73
+ for t in index_list:
74
+ is_short = (t < thresh)
75
+ if is_short:
76
+ short_ticks += t
77
+ short_count += 1
78
+ # Latch an index when we see two short pulses, or a long pulse
79
+ # following at least one short pulse.
80
+ if short_count != 0 and (short_count > 1 or not is_short):
81
+ ticks_to_index += short_ticks
82
+ sectors.append(short_ticks)
83
+ self.index_list.append(ticks_to_index)
84
+ self.sector_list.append(sectors)
85
+ sectors = []
86
+ short_ticks = ticks_to_index = short_count = 0
87
+ if not is_short:
88
+ ticks_to_index += t
89
+ sectors.append(t)
90
+ error.check(len(self.index_list) > 0,
91
+ "No hard-sector index mark found")
92
+ self.index_cued = (
93
+ (len(self.index_list) >= 2) and
94
+ (len(self.sector_list[0]) == len(self.sector_list[1])))
95
+
96
+
97
+ def append(self, flux: Flux) -> None:
98
+ # Scale the new flux if required, to match existing sample frequency.
99
+ # This will result in floating-point flux values.
100
+ if self.sample_freq == flux.sample_freq:
101
+ f_list, i_list = flux.list, flux.index_list
102
+ else:
103
+ factor = self.sample_freq / flux.sample_freq
104
+ f_list = [x*factor for x in flux.list]
105
+ i_list = [x*factor for x in flux.index_list]
106
+ # Any trailing flux is incorporated into the first revolution of
107
+ # the appended flux.
108
+ rev0 = i_list[0] + sum(self.list) - sum(self.index_list)
109
+ self.index_list += [rev0] + i_list[1:]
110
+ self.list += f_list
111
+ # TODO: Work with hard-sectored disks
112
+ self.sector_list = None
113
+
114
+
115
+ def cue_at_index(self) -> None:
116
+
117
+ if self.index_cued:
118
+ return
119
+
120
+ error.check(len(self.index_list) >= 2,
121
+ '''\
122
+ Not enough revolutions of flux data to cue at index.
123
+ Try dumping more revolutions (larger --revs value).''')
124
+
125
+ # Clip the initial partial revolution.
126
+ to_index = self.index_list[0]
127
+ for i in range(len(self.list)):
128
+ to_index -= self.list[i]
129
+ if to_index < 0:
130
+ break
131
+ if to_index < 0:
132
+ self.list = [-to_index] + self.list[i+1:]
133
+ else: # we ran out of flux
134
+ self.list = []
135
+ self.index_list = self.index_list[1:]
136
+ self.index_cued = True
137
+ if self.sector_list:
138
+ self.sector_list = self.sector_list[1:]
139
+
140
+
141
+ def reverse(self) -> None:
142
+
143
+ assert self.sector_list is None
144
+
145
+ was_index_cued = self.index_cued
146
+ flux_sum = sum(self.list)
147
+
148
+ self.index_cued = False
149
+ self.list.reverse()
150
+ self.index_list.reverse()
151
+
152
+ to_index = flux_sum - sum(self.index_list)
153
+ if to_index <= 0:
154
+ if to_index < 0:
155
+ self.list.insert(0, -to_index)
156
+ flux_sum += -to_index
157
+ self.index_list = self.index_list[1:]
158
+ self.index_cued = True
159
+ else:
160
+ self.index_list = [to_index] + self.index_list[:-1]
161
+
162
+ if was_index_cued:
163
+ self.index_list.append(flux_sum - sum(self.index_list))
164
+
165
+
166
+ def set_nr_revs(self, revs:int) -> None:
167
+
168
+ self.cue_at_index()
169
+ error.check(self.index_list,
170
+ 'Need at least one revolution to adjust # revolutions')
171
+
172
+ if len(self.index_list) > revs:
173
+ self.index_list = self.index_list[:revs]
174
+ if self.sector_list:
175
+ self.sector_list = self.sector_list[:revs]
176
+ to_index = sum(self.index_list)
177
+ for i in range(len(self.list)):
178
+ to_index -= self.list[i]
179
+ if to_index < 0:
180
+ self.list = self.list[:i]
181
+ break
182
+
183
+ while len(self.index_list) < revs:
184
+ nr = min(revs - len(self.index_list), len(self.index_list))
185
+ to_index = sum(self.index_list[:nr])
186
+ l = self.list
187
+ for i in range(len(l)):
188
+ to_index -= l[i]
189
+ if to_index < 0:
190
+ to_index += l[i]
191
+ l = l[:i]
192
+ break
193
+ if self.list:
194
+ self.list = l + [to_index + self.list[0]] + self.list[1:]
195
+ self.index_list = self.index_list[:nr] + self.index_list
196
+ if self.sector_list:
197
+ self.sector_list = self.sector_list[:nr] + self.sector_list
198
+
199
+ def flux_for_writeout(self, cue_at_index) -> WriteoutFlux:
200
+
201
+ # Splice at index unless we know better.
202
+ splice = 0 if self.splice is None else self.splice
203
+
204
+ error.check(self.index_cued,
205
+ "Cannot write non-index-cued raw flux")
206
+ error.check(splice == 0 or len(self.index_list) > 1,
207
+ "Cannot write single-revolution unaligned raw flux")
208
+ splice_at_index = (splice == 0)
209
+
210
+ # Copy the required amount of flux to a fresh list.
211
+ flux_list = []
212
+ to_index = self.index_list[0]
213
+ remain = to_index + splice
214
+ for f in self.list:
215
+ if f > remain:
216
+ break
217
+ flux_list.append(f)
218
+ remain -= f
219
+
220
+ if not cue_at_index:
221
+ # We will write more than one revolution and terminate the
222
+ # second revolution at the splice. Extend the start of the write
223
+ # with "safe" 4us sample values, in case the drive motor is a
224
+ # little fast.
225
+ if remain > 0:
226
+ flux_list.append(remain)
227
+ prepend = max(round(to_index/10 - splice), 0)
228
+ if prepend != 0:
229
+ four_us = max(self.sample_freq * 4e-6, 1)
230
+ flux_list = [four_us]*round(prepend/four_us) + flux_list
231
+ splice_at_index = False
232
+ elif splice_at_index:
233
+ # Extend with "safe" 4us sample values, to avoid unformatted area
234
+ # at end of track if drive motor is a little slow.
235
+ four_us = max(self.sample_freq * 4e-6, 1)
236
+ if remain > four_us:
237
+ flux_list.append(remain)
238
+ for i in range(round(to_index/(10*four_us))):
239
+ flux_list.append(four_us)
240
+ elif remain > 0:
241
+ # End the write exactly where specified.
242
+ flux_list.append(remain)
243
+
244
+ return WriteoutFlux(to_index, flux_list, self.sample_freq,
245
+ index_cued = cue_at_index,
246
+ terminate_at_index = splice_at_index)
247
+
248
+
249
+
250
+ def flux(self) -> Flux:
251
+ return self
252
+
253
+
254
+ def scale(self, factor) -> None:
255
+ """Scale up all flux and index timings by specified factor."""
256
+ self.sample_freq /= factor
257
+
258
+
259
+ @property
260
+ def ticks_per_rev(self) -> float:
261
+ """Mean time between index pulses, in sample ticks"""
262
+ try:
263
+ index_list = self.index_list
264
+ if not self.index_cued:
265
+ index_list = index_list[1:]
266
+ ticks_per_rev = sum(index_list) / len(index_list)
267
+ except:
268
+ ticks_per_rev = self._ticks_per_rev
269
+ return ticks_per_rev
270
+
271
+
272
+ @property
273
+ def time_per_rev(self) -> float:
274
+ """Mean time between index pulses, in seconds (float)"""
275
+ return self.ticks_per_rev / self.sample_freq
276
+
277
+
278
+ class WriteoutFlux:
279
+
280
+ def __init__(
281
+ self,
282
+ ticks_to_index: float,
283
+ flux_list: List[float],
284
+ sample_freq: float,
285
+ index_cued: bool,
286
+ terminate_at_index: bool
287
+ ) -> None:
288
+ self.ticks_to_index = ticks_to_index
289
+ self.list = flux_list
290
+ self.sample_freq = sample_freq
291
+ self.index_cued = index_cued
292
+ self.terminate_at_index = terminate_at_index
293
+
294
+
295
+ def __str__(self) -> str:
296
+ s = ("\nWriteoutFlux: %.2f MHz, %.2fms to index, %s\n"
297
+ " Total: %u samples, %.2fms"
298
+ % (self.sample_freq*1e-6,
299
+ self.ticks_to_index*1000/self.sample_freq,
300
+ ("Write all", "Terminate at index")[self.terminate_at_index],
301
+ len(self.list), sum(self.list)*1000/self.sample_freq))
302
+ return s
303
+
304
+
305
+ def summary_string(self) -> str:
306
+ s = ("Flux: %.1fms period, %.1f ms total, %s"
307
+ % (self.ticks_to_index*1000/self.sample_freq,
308
+ sum(self.list)*1000/self.sample_freq,
309
+ ("Write all", "Terminate at index")[self.terminate_at_index]))
310
+ return s
311
+
312
+
313
+ # Local variables:
314
+ # python-indent: 4
315
+ # End:
File without changes
@@ -0,0 +1,133 @@
1
+ # greaseweazle/image/a2r.py
2
+ #
3
+ # Applesauce 3.x image format.
4
+ #
5
+ # Written & released by Keir Fraser <keir.xen@gmail.com>
6
+ #
7
+ # This is free and unencumbered software released into the public domain.
8
+ # See the file COPYING for more details, or visit <http://unlicense.org>.
9
+
10
+ from typing import Dict, Tuple, Optional, List
11
+
12
+ import struct
13
+
14
+ from diskstack._vendor.greaseweazle import error
15
+ from diskstack._vendor.greaseweazle.flux import Flux
16
+ from .image import Image
17
+
18
+ class A2RCapType:
19
+ timing = 1
20
+ bits = 2
21
+ xtiming = 3
22
+
23
+
24
+ class A2RTrack:
25
+
26
+ def __init__(self, cyl: int, head: int, ps_per_tick: int):
27
+ self.cyl, self.head = cyl, head
28
+ self.sample_freq = 1e12 / ps_per_tick
29
+ self.caps: List[bytes] = list()
30
+
31
+
32
+ def add_cap(self, cap) -> None:
33
+ self.caps.append(cap)
34
+
35
+
36
+ def best_cap(self) -> bytes:
37
+ # Look for a trace with two index pulses. This is correct for the
38
+ # claimed 2.25 revolutions of an index-cued xtiming capture.
39
+ for dat in self.caps:
40
+ if dat[4] == 2:
41
+ return dat
42
+ return self.caps[-1]
43
+
44
+
45
+ def flux(self) -> Flux:
46
+ dat = self.best_cap()
47
+
48
+ # Decode the index list.
49
+ nidx = dat[4]
50
+ i = 5+nidx*4
51
+ index_list = list(struct.unpack(f'<{nidx}I', dat[5:i]))
52
+ for i in range(len(index_list)-1, 0, -1):
53
+ index_list[i] -= index_list[i-1]
54
+ ncap, = struct.unpack('<I', dat[i:i+4])
55
+ i += 4
56
+
57
+ # Decode the flux list.
58
+ flux_list: List[float] = []
59
+ acc = 0
60
+ for f in dat[i:i+ncap]:
61
+ acc += f
62
+ if f != 255:
63
+ flux_list.append(acc)
64
+ acc = 0
65
+ if acc != 0:
66
+ flux_list.append(acc)
67
+
68
+ flux = Flux(index_list, flux_list, self.sample_freq)
69
+ return flux
70
+
71
+
72
+ class A2R(Image):
73
+
74
+ read_only = True
75
+
76
+
77
+ def __init__(self, name: str, _fmt) -> None:
78
+ self.to_track: Dict[Tuple[int,int], A2RTrack] = dict()
79
+ self.filename = name
80
+
81
+
82
+ def process_rwcp(self, dat: bytes) -> None:
83
+
84
+ ps_per_tick, = struct.unpack('<I', dat[1:5])
85
+ i = 16
86
+
87
+ while dat[i] == ord('C'):
88
+
89
+ start = i
90
+
91
+ # Basic decoding, sufficient to skip unwanted capture types.
92
+ cap, loc, nidx = struct.unpack('<BHB', dat[i+1:i+5])
93
+ cyl, head = loc>>1, loc&1
94
+ i += 5 + nidx*4
95
+ ncap, = struct.unpack('<I', dat[i:i+4])
96
+ i += 4 + ncap
97
+
98
+ # Store away (x)timing captures for future decode.
99
+ if cap != A2RCapType.xtiming and cap != A2RCapType.timing:
100
+ continue
101
+ if not (cyl, head) in self.to_track:
102
+ t = A2RTrack(cyl, head, ps_per_tick)
103
+ self.to_track[cyl, head] = t
104
+ else:
105
+ t = self.to_track[cyl, head]
106
+ t.add_cap(dat[start:i])
107
+
108
+
109
+ def from_bytes(self, dat: bytes) -> None:
110
+
111
+ error.check(dat[:8] == b'A2R3\xff\x0a\x0d\x0a',
112
+ 'A2R: Invalid signature')
113
+ dat = dat[8:]
114
+
115
+ # Extract the RWCP chunk(s).
116
+ while len(dat) > 8:
117
+ id, sz = struct.unpack('<4sI', dat[:8])
118
+ dat = dat[8:]
119
+ if id == b'RWCP':
120
+ self.process_rwcp(dat)
121
+ dat = dat[sz:]
122
+
123
+
124
+ def get_track(self, cyl: int, side: int) -> Optional[Flux]:
125
+ if not (cyl, side) in self.to_track:
126
+ return None
127
+ track = self.to_track[cyl, side]
128
+ return track.flux()
129
+
130
+
131
+ # Local variables:
132
+ # python-indent: 4
133
+ # End: