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.
- diskstack/__init__.py +5 -0
- diskstack/_vendor/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/COPYING +24 -0
- diskstack/_vendor/greaseweazle/__init__.py +2 -0
- diskstack/_vendor/greaseweazle/codec/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/amiga/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/amiga/amigados.py +221 -0
- diskstack/_vendor/greaseweazle/codec/apple2/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/apple2/apple2_gcr.py +223 -0
- diskstack/_vendor/greaseweazle/codec/bitcell.py +117 -0
- diskstack/_vendor/greaseweazle/codec/codec.py +362 -0
- diskstack/_vendor/greaseweazle/codec/commodore/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/commodore/c64_gcr.py +229 -0
- diskstack/_vendor/greaseweazle/codec/datageneral/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/datageneral/datageneral.py +211 -0
- diskstack/_vendor/greaseweazle/codec/hp/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/hp/hp_mmfm.py +258 -0
- diskstack/_vendor/greaseweazle/codec/ibm/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/ibm/ibm.py +1005 -0
- diskstack/_vendor/greaseweazle/codec/macintosh/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/macintosh/mac_gcr.py +238 -0
- diskstack/_vendor/greaseweazle/codec/micropolis/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/micropolis/micropolis.py +233 -0
- diskstack/_vendor/greaseweazle/codec/northstar/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/codec/northstar/northstar.py +210 -0
- diskstack/_vendor/greaseweazle/data/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/data/diskdefs.cfg +81 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_acorn.cfg +119 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_akai.cfg +27 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_amiga.cfg +17 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_apple2.cfg +31 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_apricot.cfg +25 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_atari.cfg +33 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_atarist.cfg +76 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_coco.cfg +98 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_commodore.cfg +127 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_datageneral.cfg +8 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_dec.cfg +37 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_dragon.cfg +61 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_eagle.cfg +25 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_ensoniq.cfg +39 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_epson.cfg +95 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_gem.cfg +15 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_hp.cfg +25 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_ibm.cfg +140 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_kaypro.cfg +58 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_luxor.cfg +96 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_mac.cfg +71 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_micropolis.cfg +73 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_mm1.cfg +69 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_msx.cfg +45 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_northstar.cfg +37 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_occ1.cfg +23 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_olivetti.cfg +18 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_pc98.cfg +69 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_raw.cfg +25 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_rm.cfg +98 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_sci.cfg +15 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_sega.cfg +13 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_sharp.cfg +13 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_thomson.cfg +78 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_tsc.cfg +81 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_xerox.cfg +59 -0
- diskstack/_vendor/greaseweazle/data/diskdefs_zx.cfg +245 -0
- diskstack/_vendor/greaseweazle/error.py +19 -0
- diskstack/_vendor/greaseweazle/flux.py +315 -0
- diskstack/_vendor/greaseweazle/image/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/image/a2r.py +133 -0
- diskstack/_vendor/greaseweazle/image/acorn.py +28 -0
- diskstack/_vendor/greaseweazle/image/adf.py +15 -0
- diskstack/_vendor/greaseweazle/image/apple2.py +18 -0
- diskstack/_vendor/greaseweazle/image/apridisk.py +107 -0
- diskstack/_vendor/greaseweazle/image/caps.py +464 -0
- diskstack/_vendor/greaseweazle/image/d64.py +51 -0
- diskstack/_vendor/greaseweazle/image/d81.py +28 -0
- diskstack/_vendor/greaseweazle/image/d88.py +211 -0
- diskstack/_vendor/greaseweazle/image/dcp.py +45 -0
- diskstack/_vendor/greaseweazle/image/dim.py +53 -0
- diskstack/_vendor/greaseweazle/image/dmk.py +166 -0
- diskstack/_vendor/greaseweazle/image/dsk.py +31 -0
- diskstack/_vendor/greaseweazle/image/edsk.py +591 -0
- diskstack/_vendor/greaseweazle/image/fd.py +23 -0
- diskstack/_vendor/greaseweazle/image/fdi.py +41 -0
- diskstack/_vendor/greaseweazle/image/hdm.py +15 -0
- diskstack/_vendor/greaseweazle/image/hfe.py +742 -0
- diskstack/_vendor/greaseweazle/image/image.py +130 -0
- diskstack/_vendor/greaseweazle/image/imd.py +201 -0
- diskstack/_vendor/greaseweazle/image/img.py +111 -0
- diskstack/_vendor/greaseweazle/image/kryoflux.py +304 -0
- diskstack/_vendor/greaseweazle/image/mgt.py +15 -0
- diskstack/_vendor/greaseweazle/image/msa.py +153 -0
- diskstack/_vendor/greaseweazle/image/nfd.py +113 -0
- diskstack/_vendor/greaseweazle/image/nsi.py +47 -0
- diskstack/_vendor/greaseweazle/image/scp.py +471 -0
- diskstack/_vendor/greaseweazle/image/sf7.py +15 -0
- diskstack/_vendor/greaseweazle/image/sharp2d.py +15 -0
- diskstack/_vendor/greaseweazle/image/td0.py +141 -0
- diskstack/_vendor/greaseweazle/image/xdf.py +15 -0
- diskstack/_vendor/greaseweazle/optimised/__init__.py +22 -0
- diskstack/_vendor/greaseweazle/tools/__init__.py +0 -0
- diskstack/_vendor/greaseweazle/tools/util.py +133 -0
- diskstack/_vendor/greaseweazle/track.py +494 -0
- diskstack/cache.py +210 -0
- diskstack/candidates.py +396 -0
- diskstack/cli.py +348 -0
- diskstack/errors.py +5 -0
- diskstack/filler.py +29 -0
- diskstack/formats.py +381 -0
- diskstack/parallel.py +73 -0
- diskstack/report.py +353 -0
- diskstack/stack.py +317 -0
- diskstack-1.0.0.dist-info/METADATA +329 -0
- diskstack-1.0.0.dist-info/RECORD +119 -0
- diskstack-1.0.0.dist-info/WHEEL +5 -0
- diskstack-1.0.0.dist-info/entry_points.txt +2 -0
- diskstack-1.0.0.dist-info/licenses/LICENSE +21 -0
- diskstack-1.0.0.dist-info/licenses/NOTICE +55 -0
- diskstack-1.0.0.dist-info/licenses/diskstack/_vendor/greaseweazle/COPYING +24 -0
- diskstack-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# greaseweazle/codec/datageneral/datageneral.py
|
|
2
|
+
#
|
|
3
|
+
# Data General 8" floppy disk format, from DG document 015-000088-00
|
|
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
|
+
|
|
9
|
+
from typing import List, Optional
|
|
10
|
+
|
|
11
|
+
import struct, binascii # XXX
|
|
12
|
+
from bitarray import bitarray
|
|
13
|
+
import crcmod.predefined
|
|
14
|
+
import itertools as it
|
|
15
|
+
from enum import Enum
|
|
16
|
+
|
|
17
|
+
from diskstack._vendor.greaseweazle import error
|
|
18
|
+
from diskstack._vendor.greaseweazle.codec import codec
|
|
19
|
+
from diskstack._vendor.greaseweazle.codec.ibm.ibm import decode, encode, fm_encode
|
|
20
|
+
from diskstack._vendor.greaseweazle.track import MasterTrack, PLL, PLLTrack
|
|
21
|
+
from diskstack._vendor.greaseweazle.flux import HasFlux
|
|
22
|
+
|
|
23
|
+
default_revs = 1
|
|
24
|
+
|
|
25
|
+
bad_sector = b'-=[BAD SECTOR]=-'
|
|
26
|
+
|
|
27
|
+
sync_word = b'\x00\x01'
|
|
28
|
+
sync = bitarray(endian='big')
|
|
29
|
+
sync.frombytes(fm_encode(encode(sync_word)))
|
|
30
|
+
|
|
31
|
+
# CRC with poly x^16 + x^8 + 1
|
|
32
|
+
def csum(dat):
|
|
33
|
+
y = 0
|
|
34
|
+
# Need to "clock in" 8 extra bits to get the correct result
|
|
35
|
+
for x in dat + b'\x00':
|
|
36
|
+
y = ((y & 0xFF) ^ (y >> 8)) | (((y & 0xFF) ^ x) << 8)
|
|
37
|
+
return y
|
|
38
|
+
|
|
39
|
+
class DataGeneral(codec.Codec):
|
|
40
|
+
|
|
41
|
+
nsec = 8
|
|
42
|
+
time_per_rev = 60 / 360
|
|
43
|
+
|
|
44
|
+
verify_revs: float = default_revs
|
|
45
|
+
|
|
46
|
+
def __init__(self, cyl: int, head: int, config):
|
|
47
|
+
self.clock = 2e-6
|
|
48
|
+
self.bps = 512
|
|
49
|
+
self.cyl, self.head = cyl, head
|
|
50
|
+
self.config = config
|
|
51
|
+
self.sector: List[Optional[bytes]]
|
|
52
|
+
self.sector = [None] * self.nsec
|
|
53
|
+
|
|
54
|
+
def summary_string(self) -> str:
|
|
55
|
+
nsec, nbad = self.nsec, self.nr_missing()
|
|
56
|
+
s = "Data General 2F (%d/%d sectors)" % (nsec - nbad, nsec)
|
|
57
|
+
return s
|
|
58
|
+
|
|
59
|
+
# private
|
|
60
|
+
def add(self, sec_id, data) -> None:
|
|
61
|
+
assert not self.has_sec(sec_id)
|
|
62
|
+
self.sector[sec_id] = data
|
|
63
|
+
|
|
64
|
+
def has_sec(self, sec_id: int) -> bool:
|
|
65
|
+
return self.sector[sec_id] is not None
|
|
66
|
+
|
|
67
|
+
def nr_missing(self) -> int:
|
|
68
|
+
return len([sec for sec in self.sector if sec is None])
|
|
69
|
+
|
|
70
|
+
def get_img_track(self) -> bytearray:
|
|
71
|
+
tdat = bytearray()
|
|
72
|
+
for sec in self.sector:
|
|
73
|
+
tdat += sec if sec is not None else bad_sector * (self.bps//16)
|
|
74
|
+
return tdat
|
|
75
|
+
|
|
76
|
+
def set_img_track(self, tdat: bytes) -> int:
|
|
77
|
+
totsize = self.nsec * self.bps
|
|
78
|
+
if len(tdat) < totsize:
|
|
79
|
+
tdat += bytes(totsize - len(tdat))
|
|
80
|
+
for sec in range(self.nsec):
|
|
81
|
+
self.sector[sec] = tdat[sec*self.bps:(sec+1)*self.bps]
|
|
82
|
+
return totsize
|
|
83
|
+
|
|
84
|
+
def decode_flux(self, track: HasFlux, pll: Optional[PLL]=None) -> None:
|
|
85
|
+
|
|
86
|
+
flux = track.flux()
|
|
87
|
+
if flux.time_per_rev < self.time_per_rev / 2:
|
|
88
|
+
flux.identify_hard_sectors()
|
|
89
|
+
flux.cue_at_index()
|
|
90
|
+
raw = PLLTrack(time_per_rev = self.time_per_rev,
|
|
91
|
+
clock = self.clock, data = flux, pll = pll)
|
|
92
|
+
|
|
93
|
+
for rev in range(len(raw.revolutions)):
|
|
94
|
+
|
|
95
|
+
if self.nr_missing() == 0:
|
|
96
|
+
break
|
|
97
|
+
|
|
98
|
+
bits, _ = raw.get_revolution(rev)
|
|
99
|
+
|
|
100
|
+
hardsector_bits = raw.revolutions[rev].hardsector_bits
|
|
101
|
+
if hardsector_bits is not None:
|
|
102
|
+
hardsector_bits = list(it.accumulate(hardsector_bits))
|
|
103
|
+
# DG hard-sectored media has 4 holes per sector; 8 sectors
|
|
104
|
+
if len(hardsector_bits) == 32:
|
|
105
|
+
hardsector_bits = hardsector_bits[3::4]
|
|
106
|
+
else:
|
|
107
|
+
hardsector_bits = [len(bits)*(i+1)//self.nsec
|
|
108
|
+
for i in range(self.nsec)]
|
|
109
|
+
error.check(len(hardsector_bits) == self.nsec,
|
|
110
|
+
f'Data General: Unexpected number of sectors: '
|
|
111
|
+
f'{len(hardsector_bits)}')
|
|
112
|
+
hardsector_bits = [0] + hardsector_bits
|
|
113
|
+
|
|
114
|
+
for hsec_id in range(self.nsec):
|
|
115
|
+
|
|
116
|
+
s, e = hardsector_bits[hsec_id], hardsector_bits[hsec_id+1]
|
|
117
|
+
# Skip first 352 cells (704us), see Figure G-1
|
|
118
|
+
s += 352
|
|
119
|
+
|
|
120
|
+
offs = bits[s:e].search(sync)
|
|
121
|
+
if (off := next(offs, None)) is None:
|
|
122
|
+
continue
|
|
123
|
+
off += 2*16
|
|
124
|
+
h_off = off
|
|
125
|
+
|
|
126
|
+
# Read and check the preamble
|
|
127
|
+
preamble = decode(bits[s+off:s+off+2*16].tobytes())
|
|
128
|
+
if len(preamble) != 2:
|
|
129
|
+
continue
|
|
130
|
+
cyl = preamble[0] & 0x7F
|
|
131
|
+
sec_id = preamble[1] >> 2
|
|
132
|
+
if cyl != self.cyl or sec_id > self.nsec:
|
|
133
|
+
print('T%d.%d: Ignoring unexpected sector C:%d R:%d'
|
|
134
|
+
% (self.cyl, self.head, cyl, sec_id))
|
|
135
|
+
continue
|
|
136
|
+
if self.has_sec(sec_id):
|
|
137
|
+
break
|
|
138
|
+
|
|
139
|
+
# Skip 40 cells (80us) past the preamble word
|
|
140
|
+
d_off = off + 2*16 + 40
|
|
141
|
+
offs = bits[s+d_off:e].search(sync)
|
|
142
|
+
if (off := next(offs, None)) is None:
|
|
143
|
+
continue
|
|
144
|
+
off += d_off + 2*16
|
|
145
|
+
|
|
146
|
+
# Read and checksum the data
|
|
147
|
+
data = decode(bits[s+off:s+off+514*16].tobytes())
|
|
148
|
+
if len(data) != 514:
|
|
149
|
+
continue
|
|
150
|
+
read_csum = int.from_bytes(data[512:], 'big')
|
|
151
|
+
if csum(data[:512]) == read_csum:
|
|
152
|
+
self.add(sec_id, data[:512])
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def master_track(self) -> MasterTrack:
|
|
156
|
+
t = bytes()
|
|
157
|
+
slen = int((self.time_per_rev / self.clock / self.nsec / 16))
|
|
158
|
+
|
|
159
|
+
for sec_id in range(self.nsec):
|
|
160
|
+
# Format track layout, Figures F-4 & H-9
|
|
161
|
+
s = encode(bytes(22*2))
|
|
162
|
+
s += encode(sync_word)
|
|
163
|
+
|
|
164
|
+
s += encode(struct.pack('>BB', self.cyl & 0x7f, sec_id << 2))
|
|
165
|
+
|
|
166
|
+
# Delay 20us + 160us (Figure G-5) ~= 6 bytes
|
|
167
|
+
s += encode(bytes(4))
|
|
168
|
+
s += encode(sync_word)
|
|
169
|
+
|
|
170
|
+
sector = self.sector[sec_id]
|
|
171
|
+
data = bad_sector*(self.bps//16) if sector is None else sector
|
|
172
|
+
|
|
173
|
+
s += encode(data + csum(data).to_bytes(2, 'big'))
|
|
174
|
+
s += encode(bytes(slen - len(s)//2))
|
|
175
|
+
t += s
|
|
176
|
+
|
|
177
|
+
t = fm_encode(t)
|
|
178
|
+
|
|
179
|
+
track = MasterTrack(bits = t, time_per_rev = self.time_per_rev,
|
|
180
|
+
hardsector_bits = [slen*16] * self.nsec)
|
|
181
|
+
track.verify = self
|
|
182
|
+
return track
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def verify_track(self, flux):
|
|
186
|
+
readback_track = self.__class__(self.cyl, self.head, self.config)
|
|
187
|
+
readback_track.decode_flux(flux)
|
|
188
|
+
return (readback_track.nr_missing() == 0
|
|
189
|
+
and self.sector == readback_track.sector)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class DataGeneralDef(codec.TrackDef):
|
|
193
|
+
|
|
194
|
+
default_revs = default_revs
|
|
195
|
+
|
|
196
|
+
def __init__(self, format_name: str):
|
|
197
|
+
return
|
|
198
|
+
|
|
199
|
+
def add_param(self, key: str, val) -> None:
|
|
200
|
+
raise error.Fatal('unrecognised track option %s' % key)
|
|
201
|
+
|
|
202
|
+
def finalise(self) -> None:
|
|
203
|
+
return
|
|
204
|
+
|
|
205
|
+
def mk_track(self, cyl: int, head: int) -> DataGeneral:
|
|
206
|
+
return DataGeneral(cyl, head, self)
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
# Local variables:
|
|
210
|
+
# python-indent: 4
|
|
211
|
+
# End:
|
|
File without changes
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# greaseweazle/codec/hp/hp_mmfm.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
|
+
|
|
7
|
+
# From github:brouhaha/fluxtoimd/modulation.py:
|
|
8
|
+
#
|
|
9
|
+
# See the file COPYING for more details, or visit <http://unlicense.org>.
|
|
10
|
+
# An HP-proprietary M2FM floppy format, used by the HP 7902, 9885,
|
|
11
|
+
# and 9895 Flexible Disc Drives.
|
|
12
|
+
# Documentation:
|
|
13
|
+
# 9885 Flexible Disk Drive Service Manual
|
|
14
|
+
# Hewlett-Packard, September 1976, part number 09885-90030
|
|
15
|
+
# 7902A Disc Drive Preliminary Service Manual
|
|
16
|
+
# Hwelett Packard, May 1979, part number 07902-90060
|
|
17
|
+
# 7902A & C/9895K Flexible Disc Drive Service Documentation
|
|
18
|
+
# Hewlett-Packard, January 1981, part number 07902-90030
|
|
19
|
+
# 9895A Flexible Disc Memory Service Manual,
|
|
20
|
+
# Hewlett-Packard, February 1981, part number 09895-90030
|
|
21
|
+
# 9885: single-sided, 67 track, M2FM format only
|
|
22
|
+
# 7902: double-sided, 77 track, M2FM or IBM 3740 FM formats
|
|
23
|
+
# 9895: double-sided, 77 track, M2FM or IBM 3740 FM formats
|
|
24
|
+
|
|
25
|
+
from typing import List, Optional
|
|
26
|
+
|
|
27
|
+
import struct
|
|
28
|
+
from bitarray import bitarray
|
|
29
|
+
import crcmod.predefined
|
|
30
|
+
|
|
31
|
+
from diskstack._vendor.greaseweazle import error
|
|
32
|
+
from diskstack._vendor.greaseweazle.codec import codec
|
|
33
|
+
from diskstack._vendor.greaseweazle.codec.ibm.ibm import decode, encode, sec_map
|
|
34
|
+
from diskstack._vendor.greaseweazle.track import MasterTrack, PLL, PLLTrack
|
|
35
|
+
from diskstack._vendor.greaseweazle.flux import HasFlux
|
|
36
|
+
|
|
37
|
+
default_revs = 1.1
|
|
38
|
+
|
|
39
|
+
bad_sector = b'-=[BAD SECTOR]=-' * 16
|
|
40
|
+
|
|
41
|
+
# Syncs have an extra clock bit:
|
|
42
|
+
# ff 0e -> 55 55 22 54 -> 55 55 2a 54
|
|
43
|
+
sector_sync_bytes = b'\x55\x55\x2a\x54'
|
|
44
|
+
# ff 0a -> 55 55 22 44 -> 55 55 2a 44
|
|
45
|
+
data_sync_bytes = b'\x55\x55\x2a\x44'
|
|
46
|
+
|
|
47
|
+
sector_sync = bitarray(endian='big')
|
|
48
|
+
sector_sync.frombytes(sector_sync_bytes)
|
|
49
|
+
|
|
50
|
+
data_sync = bitarray(endian='big')
|
|
51
|
+
data_sync.frombytes(data_sync_bytes)
|
|
52
|
+
|
|
53
|
+
crc16 = crcmod.predefined.Crc('crc-ccitt-false')
|
|
54
|
+
|
|
55
|
+
rev_list = bytearray()
|
|
56
|
+
for x in range(256):
|
|
57
|
+
y = 0
|
|
58
|
+
for i in range(8):
|
|
59
|
+
y <<= 1
|
|
60
|
+
y |= x & 1
|
|
61
|
+
x >>= 1
|
|
62
|
+
rev_list.append(y)
|
|
63
|
+
|
|
64
|
+
def bitrev(x):
|
|
65
|
+
return rev_list[x]
|
|
66
|
+
|
|
67
|
+
# MMFM:
|
|
68
|
+
# Write clock bit iff
|
|
69
|
+
# 1. No preceding clock or data
|
|
70
|
+
# 2. No following data
|
|
71
|
+
mmfm_list = bytearray()
|
|
72
|
+
for x in range(512):
|
|
73
|
+
for i in [7,5,3,1]:
|
|
74
|
+
if x & (0xf << (i-1)) == 0:
|
|
75
|
+
x |= 1<<i
|
|
76
|
+
mmfm_list.append(x & 255)
|
|
77
|
+
|
|
78
|
+
def mmfm_encode(dat):
|
|
79
|
+
y, out = 0, bytearray()
|
|
80
|
+
for x in dat:
|
|
81
|
+
if (x & 0xaa) == 0:
|
|
82
|
+
x = mmfm_list[(y<<8) | x]
|
|
83
|
+
out.append(x)
|
|
84
|
+
y = 0 if (x & 3) == 0 else 1
|
|
85
|
+
return bytes(out)
|
|
86
|
+
|
|
87
|
+
class HPMMFM(codec.Codec):
|
|
88
|
+
|
|
89
|
+
time_per_rev = 60 / 360
|
|
90
|
+
clock = 1e-6
|
|
91
|
+
|
|
92
|
+
verify_revs = default_revs
|
|
93
|
+
|
|
94
|
+
def __init__(self, cyl: int, head: int, config):
|
|
95
|
+
self.cyl, self.head = cyl, head
|
|
96
|
+
self.config = config
|
|
97
|
+
self.sector: List[Optional[bytes]]
|
|
98
|
+
self.sector = [None] * self.nsec
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def nsec(self) -> int:
|
|
102
|
+
return self.config.secs
|
|
103
|
+
|
|
104
|
+
def summary_string(self) -> str:
|
|
105
|
+
nsec, nbad = self.nsec, self.nr_missing()
|
|
106
|
+
s = "HP MMFM (%d/%d sectors)" % (nsec - nbad, nsec)
|
|
107
|
+
return s
|
|
108
|
+
|
|
109
|
+
# private
|
|
110
|
+
def add(self, sec_id, data) -> None:
|
|
111
|
+
assert not self.has_sec(sec_id)
|
|
112
|
+
self.sector[sec_id] = data
|
|
113
|
+
|
|
114
|
+
def has_sec(self, sec_id: int) -> bool:
|
|
115
|
+
return self.sector[sec_id] is not None
|
|
116
|
+
|
|
117
|
+
def nr_missing(self) -> int:
|
|
118
|
+
return len([sec for sec in self.sector if sec is None])
|
|
119
|
+
|
|
120
|
+
def get_img_track(self) -> bytearray:
|
|
121
|
+
tdat = bytearray()
|
|
122
|
+
for sec in self.sector:
|
|
123
|
+
tdat += sec if sec is not None else bad_sector
|
|
124
|
+
return tdat
|
|
125
|
+
|
|
126
|
+
def set_img_track(self, tdat: bytes) -> int:
|
|
127
|
+
totsize = self.nsec * 256
|
|
128
|
+
if len(tdat) < totsize:
|
|
129
|
+
tdat += bytes(totsize - len(tdat))
|
|
130
|
+
for sec in range(self.nsec):
|
|
131
|
+
self.sector[sec] = tdat[sec*256:(sec+1)*256]
|
|
132
|
+
return totsize
|
|
133
|
+
|
|
134
|
+
def decode_flux(self, track: HasFlux, pll: Optional[PLL]=None) -> None:
|
|
135
|
+
raw = PLLTrack(time_per_rev = self.time_per_rev,
|
|
136
|
+
clock = self.clock, data = track, pll = pll)
|
|
137
|
+
bits, _ = raw.get_all_data()
|
|
138
|
+
|
|
139
|
+
for offs in bits.search(sector_sync):
|
|
140
|
+
|
|
141
|
+
if self.nr_missing() == 0:
|
|
142
|
+
break
|
|
143
|
+
|
|
144
|
+
offs += 2*16
|
|
145
|
+
idam = decode(bits[offs:offs+4*16].tobytes())
|
|
146
|
+
if len(idam) != 4:
|
|
147
|
+
continue
|
|
148
|
+
if crc16.new(idam).crcValue != 0:
|
|
149
|
+
continue
|
|
150
|
+
cyl = bitrev(idam[0])
|
|
151
|
+
sec_id = bitrev(idam[1])
|
|
152
|
+
head = (sec_id & 128) == 128
|
|
153
|
+
sec_id &= 127
|
|
154
|
+
if cyl != self.cyl or head != self.head or sec_id > self.nsec:
|
|
155
|
+
print('T%d.%d: Ignoring unexpected sector C:%d H:%d R:%d'
|
|
156
|
+
% (self.cyl, self.head, cyl, head, sec_id))
|
|
157
|
+
continue
|
|
158
|
+
if self.has_sec(sec_id):
|
|
159
|
+
continue
|
|
160
|
+
|
|
161
|
+
# Find data
|
|
162
|
+
offs += 8*16
|
|
163
|
+
dat_offs = list(bits[offs:offs+50*16].search(data_sync))
|
|
164
|
+
if len(dat_offs) != 1:
|
|
165
|
+
continue
|
|
166
|
+
offs += dat_offs[0] + 2*16
|
|
167
|
+
|
|
168
|
+
sec = decode(bits[offs:offs+258*16].tobytes())
|
|
169
|
+
if len(sec) != 258:
|
|
170
|
+
continue
|
|
171
|
+
if crc16.new(sec).crcValue != 0:
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
# bit swap, and byte swap
|
|
175
|
+
sec = bytes(map(lambda x: bitrev(x), sec[:256]))
|
|
176
|
+
sec = struct.pack('<128H', *struct.unpack('>128H', sec))
|
|
177
|
+
|
|
178
|
+
self.add(sec_id, sec)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def master_track(self) -> MasterTrack:
|
|
182
|
+
|
|
183
|
+
# Post-index track gap.
|
|
184
|
+
t = encode(bytes(100))
|
|
185
|
+
|
|
186
|
+
for sec_id in sec_map(self.nsec, self.config.interleave,
|
|
187
|
+
self.config.cskew, self.config.hskew,
|
|
188
|
+
self.cyl, self.head):
|
|
189
|
+
sector = self.sector[sec_id]
|
|
190
|
+
data = bad_sector if sector is None else sector
|
|
191
|
+
# Header
|
|
192
|
+
t += encode(bytes([0xff]*3)) + sector_sync_bytes
|
|
193
|
+
idam = bytes(map(lambda x: bitrev(x),
|
|
194
|
+
[self.cyl, sec_id | (self.head << 7)]))
|
|
195
|
+
idam += struct.pack('>H', crc16.new(idam).crcValue)
|
|
196
|
+
t += encode(idam)
|
|
197
|
+
t += encode(bytes(1 + 16 + 4))
|
|
198
|
+
# Data
|
|
199
|
+
t += encode(bytes([0xff]*3)) + data_sync_bytes
|
|
200
|
+
data = struct.pack('<128H', *struct.unpack('>128H', data))
|
|
201
|
+
data = bytes(map(lambda x: bitrev(x), data))
|
|
202
|
+
data += struct.pack('>H', crc16.new(data).crcValue)
|
|
203
|
+
t += encode(data)
|
|
204
|
+
t += encode(bytes(1 + 34 + 4))
|
|
205
|
+
|
|
206
|
+
# Add the pre-index gap.
|
|
207
|
+
tlen = int((self.time_per_rev / self.clock)) & ~31
|
|
208
|
+
t += encode(bytes((tlen//8-len(t))//2))
|
|
209
|
+
|
|
210
|
+
t = mmfm_encode(t)
|
|
211
|
+
|
|
212
|
+
track = MasterTrack(bits = t, time_per_rev = self.time_per_rev)
|
|
213
|
+
track.verify = self
|
|
214
|
+
return track
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def verify_track(self, flux):
|
|
218
|
+
readback_track = self.__class__(self.cyl, self.head, self.config)
|
|
219
|
+
readback_track.decode_flux(flux)
|
|
220
|
+
return (readback_track.nr_missing() == 0
|
|
221
|
+
and self.sector == readback_track.sector)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class HPMMFMDef(codec.TrackDef):
|
|
225
|
+
|
|
226
|
+
default_revs = default_revs
|
|
227
|
+
|
|
228
|
+
def __init__(self, format_name: str):
|
|
229
|
+
self.interleave = 1
|
|
230
|
+
self.cskew, self.hskew = 0, 0
|
|
231
|
+
self.secs: Optional[int] = None
|
|
232
|
+
self.finalised = False
|
|
233
|
+
|
|
234
|
+
def add_param(self, key: str, val) -> None:
|
|
235
|
+
if key == 'secs':
|
|
236
|
+
val = int(val)
|
|
237
|
+
self.secs = val
|
|
238
|
+
elif key in ['interleave', 'cskew', 'hskew']:
|
|
239
|
+
n = int(val)
|
|
240
|
+
error.check(0 <= n <= 255, '%s out of range' % key)
|
|
241
|
+
setattr(self, key, n)
|
|
242
|
+
else:
|
|
243
|
+
raise error.Fatal('unrecognised track option %s' % key)
|
|
244
|
+
|
|
245
|
+
def finalise(self) -> None:
|
|
246
|
+
if self.finalised:
|
|
247
|
+
return
|
|
248
|
+
error.check(self.secs is not None,
|
|
249
|
+
'number of sectors not specified')
|
|
250
|
+
self.finalised = True
|
|
251
|
+
|
|
252
|
+
def mk_track(self, cyl: int, head: int) -> HPMMFM:
|
|
253
|
+
return HPMMFM(cyl, head, self)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
# Local variables:
|
|
257
|
+
# python-indent: 4
|
|
258
|
+
# End:
|
|
File without changes
|