Skip to content

Commit 4a6d57f

Browse files
author
Peter Kooiman
committed
Improve read timings. Attempt to add write support.
1 parent c6c6b84 commit 4a6d57f

1 file changed

Lines changed: 126 additions & 42 deletions

File tree

src/greaseweazle/codec/datageneral/datageneral.py

Lines changed: 126 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,23 @@
1414

1515
from greaseweazle import error
1616
from greaseweazle.codec import codec
17-
from greaseweazle.codec.ibm.ibm import decode, encode, fm_encode, mfm_encode
17+
from greaseweazle.codec.ibm.ibm import decode, encode, fm_encode
1818
from greaseweazle.track import MasterTrack, PLL, PLLTrack
1919
from greaseweazle.flux import HasFlux
2020

2121
default_revs = 1
2222

2323
bad_sector = b'-=[BAD SECTOR]=-'
2424

25-
mfm_sync = bitarray(endian='big')
26-
mfm_sync.frombytes(mfm_encode(encode(b'\x00\xfb')))
25+
min_sync = 1000
26+
max_sync = 0
27+
min_sync_zero = 1000
28+
max_sync_zero = 0
29+
min_datasync = 1000
30+
max_datasync = 0
2731

28-
fm_sync = bitarray(endian='big')
29-
fm_sync.frombytes(fm_encode(encode(b'\x00\x00\x00\x00\x00\x01')))
3032

3133

32-
#Sector starts with 23 * 16 zero bits, then a 0001 sync word followed by the adress field
33-
#There is an 18us "splice" between the address field and the following data field sync
34-
#The documentation is not very clear on the exact number of bits in the data sync field
35-
#The splice plus the data sync add up to a non-integer number of bits, so we
36-
#need to search for the data sync pattern to locate the data
37-
38-
#"Manual" FM encoding....
39-
fm_datasync = bitarray("101010101010101010101010101010101010101010101010101010101011")
40-
4134

4235
def csum(dat):
4336
#CRC with poly x^16 + x^8 + 1
@@ -49,31 +42,65 @@ def csum(dat):
4942
return y
5043

5144
class Mode(Enum):
52-
FM, MFM = range(2)
45+
FM = 0
5346
def __str__(self):
54-
NAMES = [ 'Data General FM', 'Data General MFM' ]
47+
NAMES = [ 'Data General FM']
5548
return f'{NAMES[self.value]}'
5649

5750
class DataGeneral(codec.Codec):
51+
'''
52+
Data General 8" floppy disk format, from DG document 015-000088-00, flowchart page G-4
53+
54+
Sector read:
55+
<sector index pulse> <704uS delay> <sector address sync bit> <16 bits sector address (preamble) <20 uS delay> <64 uS delay> <data sync bit> <data 512 bits><crc 16 bits>
56+
57+
Sector write:
58+
<sector index pulse> <704uS delay> <sector address sync bit> <16 bits sector address (preamble) <20 uS delay> <write zeros for 160 uS -> 40 zero bits> <data sync bit> <data 512 bits><crc 16 bits>
5859
60+
Formatting operation:
61+
<sector index pulse> <160 uS delay> <352 bits '0'> <0000000000000001> <16 bits sector address> <352 bits '0'> <3520 bits 'dont care'>
62+
63+
The actual number of zero bits before the sector address sync bit observed in disk images varies between 572 and 740
64+
65+
'''
66+
5967
time_per_rev = 0.166
6068

6169
verify_revs: float = default_revs
6270

6371
def __init__(self, cyl: int, head: int, config):
64-
if config.mode is Mode.FM:
65-
self.clock = 2e-6
66-
self.bps = 512
67-
self.sync = fm_sync
68-
self.datasync = fm_datasync
69-
self.presync_bytes = 21
70-
self.sync_bytes = 6
71-
else:
72-
self.clock = 2e-6
73-
self.bps = 512
74-
self.sync = mfm_sync
75-
self.presync_bytes = 34
76-
self.sync_bytes = 2
72+
73+
self.clock = 2e-6
74+
self.bps = 512
75+
76+
self.address_sync = b'\x00\x01'
77+
self.data_sync = b'\x00\x01'
78+
79+
self.address_sync_fmbits = bitarray(endian='big')
80+
self.address_sync_fmbits.frombytes(fm_encode(encode(self.address_sync)))
81+
82+
self.data_sync_fmbits = bitarray(endian='big')
83+
self.data_sync_fmbits.frombytes(fm_encode(encode(self.data_sync)))
84+
85+
#According to the documentation, there is a 704uS delay before the drive reads the sync word for the sector address (preamble)
86+
#This is equivalent to 704 presync FM bits followed by 0x0001 (32 FM bits) sync word
87+
#The actual number of FM bits before the sector address sync word observed in disk images varies between 572 and 740
88+
self.pre_addresssync_read_fm_bits = 560
89+
#self.pre_addresssync_read_fm_bits = 100
90+
91+
#self.pre_addresssync_write_fm_bits = 704
92+
self.pre_addresssync_write_fm_bytes = 44
93+
94+
#there is a 20us + 64us delay before the data sync bit is read corresponding to a total of 84 FM bits + 2 fm sync bits
95+
#When writing, there is a 20uS delay, then 40 zero bits followed by a one bit (the sync bit) are written
96+
#The actual number of zero (FM) bits before the data sync bit observed in disk images varies between 97 and 98
97+
#equivalent to 66 FM bits followed by 0x0001 (32 FM bits) syncword. For reading, use minimum of 60 FM bits before the syncword
98+
99+
self.pre_datasync_read_fm_bits = 60
100+
#self.pre_datasync_write_fm_bits = 150 #20 + 160 + 2 = 150 presync + 32 sync word 0x0001
101+
self.pre_datasync_write_fm_bytes = 5 #20 + 160 + 2 = 150 presync + 32 sync word 0x0001
102+
103+
77104
self.cyl, self.head = cyl, head
78105
self.config = config
79106
self.sector: List[Optional[bytes]]
@@ -114,6 +141,13 @@ def set_img_track(self, tdat: bytes) -> int:
114141
return totsize
115142

116143
def decode_flux(self, track: HasFlux, pll: Optional[PLL]=None) -> None:
144+
global min_sync
145+
global max_sync
146+
global min_sync_zero
147+
global max_sync_zero
148+
global min_datasync
149+
global max_datasync
150+
117151
flux = track.flux()
118152
if flux.time_per_rev < self.time_per_rev / 2:
119153
flux.identify_hard_sectors()
@@ -148,23 +182,62 @@ def decode_flux(self, track: HasFlux, pll: Optional[PLL]=None) -> None:
148182
#print(bits[s:e])
149183
data = decode(bits[s:e].tobytes())
150184
#print(data.hex())
151-
offs = bits[s:e].search(self.sync)
185+
186+
#Start searching for sync after a minimum delay of pre_addresssync_read_bits
187+
offs = bits[s + self.pre_addresssync_read_fm_bits:e].search(self.address_sync_fmbits)
152188
if (off := next(offs, None)) is None:
153189
continue
154-
off += (self.sync_bytes) * 16
190+
191+
numsync = self.pre_addresssync_read_fm_bits + off + len(self.address_sync_fmbits)
192+
#Get offset of first preamble bit
193+
off += self.pre_addresssync_read_fm_bits + len(self.address_sync_fmbits)
194+
195+
196+
#Reed 2 byte preamb;e
155197
data = decode(bits[s+off:s+off+2*16].tobytes())
156-
#print(data.hex())
198+
199+
#Extract track# and sector#
157200
track = data[0] & 0x7F
158201
sector = data[1] >> 2
159-
#print(f'Track {track}, Sector {sector}')
202+
203+
204+
if numsync < min_sync:
205+
min_sync =numsync
206+
if numsync > max_sync:
207+
max_sync = numsync
160208

209+
numsynczero = 0
161210

162-
dataoffs = bits[s+off+2*16:e].search(self.datasync)
211+
for kk in range(s + numsync - 4, s, -2):
212+
if bits[kk:kk + 2] != bitarray('10'):
213+
break
214+
numsynczero += 2
215+
216+
if numsynczero < min_sync_zero:
217+
min_sync_zero =numsynczero
218+
if numsynczero > max_sync_zero:
219+
max_sync_zero = numsynczero
220+
#print(f'Track {track}, Sector {sector}, after {numsync} sync bits, {numsynczero} zero value sync bits, min sync {min_sync}, max sync {max_sync}, min zero {min_sync_zero}, max zero {max_sync_zero}')
221+
222+
dsyncstart = off + 2*16
223+
#Skip to the start of the data sync, skip over 2 byte preamble plus minimum delay of pre_datasync_read_bits
224+
off += 2*16 + self.pre_datasync_read_fm_bits
225+
dataoffs = bits[s+off:e].search(self.data_sync_fmbits)
163226

164227
if (dataoff := next(dataoffs, None)) is None:
165228
continue
229+
230+
dataoff += off + len(self.data_sync_fmbits)
231+
numdsync = dataoff - dsyncstart
232+
233+
if numdsync < min_datasync:
234+
min_datasync = numdsync
235+
if numdsync > max_datasync:
236+
max_datasync = numdsync
237+
238+
#print(f'Data sync {numdsync} bits, min {min_datasync}, max {max_datasync}')
166239

167-
data = decode(bits[s+off+2*16 + dataoff + len(self.datasync) :s+off+2*16 + dataoff + len(self.datasync) + 518*16].tobytes())
240+
data = decode(bits[s + dataoff:s + dataoff + 518*16].tobytes())
168241
#print(data[:512].hex())
169242
#print(data[512:514].hex())
170243
#print(data[514:].hex())
@@ -177,20 +250,33 @@ def decode_flux(self, track: HasFlux, pll: Optional[PLL]=None) -> None:
177250

178251

179252
def master_track(self) -> MasterTrack:
180-
raise error.Fatal('NOT IMPLEMENTED')
181253
t = bytes()
182254
slen = int((self.time_per_rev / self.clock / self.nsec / 16))
183255

184256
for sec_id in range(self.nsec):
185-
s = encode(bytes(self.presync_bytes))
186-
s += encode(b'\xfb' * self.sync_bytes)
257+
#
258+
s = encode(b'\x00' * self.pre_addresssync_write_fm_bytes)
259+
s += encode(self.address_sync)
260+
261+
262+
187263
sector = self.sector[sec_id]
264+
trackid = self.cyl & 7
265+
sectorenc = (sec_id & 0xF) << 2
266+
preamble = struct.pack('>BB', trackid, sectorenc)
267+
268+
s += encode(preamble)
269+
270+
s += encode(b'\x00' * self.pre_datasync_write_fm_bytes)
271+
s += encode(self.data_sync)
272+
188273
data = bad_sector*(self.bps//16) if sector is None else sector
189-
s += encode(data + bytes([csum(data)]))
274+
275+
s += encode(data + csum(data).to_bytes(2, 'big'))
190276
s += encode(bytes(slen - len(s)//2))
191277
t += s
192278

193-
t = mfm_encode(t) if self.config.mode is Mode.MFM else fm_encode(t)
279+
t = fm_encode(t)
194280

195281
hardsector_bits = [slen*16*i for i in range(self.nsec)]
196282

@@ -222,8 +308,6 @@ def add_param(self, key: str, val) -> None:
222308
elif key == 'mode':
223309
if val == 'fm':
224310
self.mode = Mode.FM
225-
elif val == 'mfm':
226-
self.mode = Mode.MFM
227311
else:
228312
raise error.Fatal('unrecognised mode %s' % val)
229313
else:

0 commit comments

Comments
 (0)