Skip to content

Commit 96b2068

Browse files
committed
Add support for floppsy direction pin & circuitpython changes
this **incompatible change** is related to adafruit/circuitpython#9135 When MFM decoding, a separate buffer of flux data is required. Due to its size, this buffer should be created early in the program execution flow.
1 parent 4713293 commit 96b2068

File tree

2 files changed

+97
-64
lines changed

2 files changed

+97
-64
lines changed

adafruit_floppy.py

+51-44
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def _sleep_ms(interval):
5050
_sleep_deadline_ms(ticks_add(ticks_ms(), interval))
5151

5252

53-
class MFMFloppy: # pylint: disable=too-many-instance-attributes
53+
class Floppy: # pylint: disable=too-many-instance-attributes
5454
"""Interface with floppy disk drive hardware"""
5555

5656
_track: typing.Optional[int]
@@ -71,6 +71,7 @@ def __init__(
7171
readypin: microcontroller.Pin,
7272
wrdatapin: typing.Optional[microcontroller.Pin] = None,
7373
wrgatepin: typing.Optional[microcontroller.Pin] = None,
74+
floppydirectionpin: typing.Optional[microcontroller.Pin] = None,
7475
) -> None:
7576
self._density = DigitalInOut(densitypin)
7677
self._density.pull = Pull.UP
@@ -97,6 +98,10 @@ def __init__(
9798
self._ready = DigitalInOut(readypin)
9899
self._ready.pull = Pull.UP
99100

101+
self._floppydirection = _optionaldigitalinout(floppydirectionpin)
102+
if self._floppydirection:
103+
self._floppydirection.switch_to_output(True)
104+
100105
self._track = None
101106

102107
def _do_step(self, direction, count):
@@ -122,6 +127,7 @@ def find_track0(self):
122127
for _ in range(250):
123128
if not self._track0.value:
124129
self._track = 0
130+
self._check_inpos()
125131
return
126132
self._do_step(_STEP_OUT, 1)
127133
raise RuntimeError("Could not reach track 0")
@@ -131,7 +137,7 @@ def _check_inpos(self) -> None:
131137
drive_says_track0 = not self._track0.value
132138
we_think_track0 = track == 0
133139
if drive_says_track0 != we_think_track0:
134-
raise RuntimeError("Drive lost position")
140+
raise RuntimeError(f"Drive lost position (target={track}, track0 sensor {drive_says_track0})")
135141

136142
@property
137143
def track(self) -> typing.Optional[int]:
@@ -150,7 +156,7 @@ def track(self, track: int) -> None:
150156
delta = track - self.track
151157
if delta < 0:
152158
self._do_step(_STEP_OUT, -delta)
153-
else:
159+
elif delta > 0:
154160
self._do_step(_STEP_IN, delta)
155161

156162
self._track = track
@@ -210,24 +216,6 @@ def flux_readinto(self, buf: "circuitpython_typing.WritableBuffer") -> int:
210216
:return: The actual number of bytes of read"""
211217
return floppyio.flux_readinto(buf, self._rddata, self._index)
212218

213-
def mfm_readinto(self, buf: "circuitpython_typing.WriteableBuffer") -> int:
214-
"""Read mfm blocks into the buffer.
215-
216-
The track is assumed to consist of 512-byte sectors.
217-
218-
The function returns when all sectors have been successfully read, or
219-
a number of index pulses have occurred. Due to technical limitations, this
220-
process may not be interruptible by KeyboardInterrupt.
221-
222-
:param buf: Read data into this buffer. Must be a multiple of 512.
223-
:return: The actual number of sectors read
224-
"""
225-
return floppyio.mfm_readinto(
226-
buf,
227-
self._rddata,
228-
self._index,
229-
)
230-
231219

232220
class FloppyBlockDevice:
233221
"""Wrap an MFMFloppy object into a block device suitable for `storage.VfsFat`
@@ -243,37 +231,46 @@ class FloppyBlockDevice:
243231
import storage
244232
import adafruit_floppy
245233
246-
floppy = adafruit_floppy.MFMFloppy(...)
234+
floppy = adafruit_floppy.Floppy(...)
247235
block_device = adafruit_floppy.FloppyBlockDevice(floppy)
248236
vfs = storage.VfsFat(f)
249237
storage.mount(vfs, '/floppy')
250238
print(os.listdir("/floppy"))
251239
"""
252240

253-
def __init__(self, floppy, heads=2, sectors=18, tracks=80):
241+
def __init__(self, floppy, heads=2, sectors=18, tracks=80, flux_buffer=None, t1_nom_ns: float=1000):
254242
self.floppy = floppy
255243
self.heads = heads
256244
self.sectors = sectors
257245
self.tracks = tracks
246+
self.flux_buffer = flux_buffer or buffer(sectors * 12 * 512)
258247
self.track0side0_cache = memoryview(bytearray(sectors * 512))
248+
self.track0side0_validity = bytearray(sectors)
249+
self.track_cache = memoryview(bytearray(sectors * 512))
250+
self.track_validity = bytearray(sectors)
259251

260-
self.floppy.track = 0
261-
self.floppy.head = 0
262-
floppyio.mfm_readinto(self.track0side0_cache, floppy._rddata, floppy._index)
252+
self._t2_5_max = round(2.5 * t1_nom_ns * floppyio.samplerate * 1e-9)
253+
self._t3_5_max = round(3.5 * t1_nom_ns * floppyio.samplerate * 1e-9)
254+
255+
self._track_read(self.track0side0_cache, self.track0side0_validity, 0, 0)
263256

264-
self.track_cache = memoryview(bytearray(sectors * 512))
265257
self.cached_track = -1
266258
self.cached_side = -1
267259

260+
268261
def deinit(self):
269-
"""Deinitialize this object (does nothing)"""
262+
"""Deinitialize this object"""
263+
self.floppy.deinit()
264+
del self.flux_buffer
265+
del self.track0side0_cache
266+
del self.track_validity
270267

271268
def sync(self):
272269
"""Write out any pending data to disk (does nothing)"""
273270

274271
def writeblocks(self, start, buf): # pylint: disable=no-self-use
275272
"""Write to the floppy (always raises an exception)"""
276-
raise IOError("Read-only filesystem")
273+
raise OSError("Read-only filesystem")
277274

278275
def count(self):
279276
"""Return the floppy capacity in 512-byte units"""
@@ -287,27 +284,37 @@ def readblocks(self, start_block, buf):
287284

288285
def _readblock(self, block, buf):
289286
if block > self.count():
290-
raise IOError("Read past end of media")
287+
raise OSError("Read past end of media")
291288
track = block // (self.heads * self.sectors)
292289
block %= self.heads * self.sectors
293290
side = block // (self.sectors)
294291
block %= self.sectors
295-
trackdata = self._get_track_data(track, side)
292+
trackdata, validity = self._get_track_data(track, side)
293+
if not validity[block]:
294+
raise OSError(f"Failed to read sector {track}/{side}/{block}")
296295
buf[:] = trackdata[block * 512 : (block + 1) * 512]
297296

298297
def _get_track_data(self, track, side):
299298
if track == 0 and side == 0:
300-
return self.track0side0_cache
299+
return self.track0side0_cache, self.track0side0_validity
301300
if track != self.cached_track or side != self.cached_side:
302-
self.floppy.selected = True
303-
self.floppy.spin = True
304-
self.floppy.track = track
305-
self.floppy.side = side
306-
self.floppy.mfm_readinto(
307-
self.track_cache,
308-
)
309-
self.floppy.spin = False
310-
self.floppy.selected = False
311-
self.cached_track = track
312-
self.cached_side = side
313-
return self.track_cache
301+
self._track_read(self.track_cache, self.track_validity, track, side)
302+
return self.track_cache, self.track_validity
303+
304+
def _track_read(self, track_data, validity, track, side):
305+
self.floppy.selected = True
306+
self.floppy.spin = True
307+
self.floppy.track = track
308+
self.floppy.side = side
309+
self._mfm_readinto(track_data, validity)
310+
self.floppy.spin = False
311+
self.floppy.selected = False
312+
self.cached_track = track
313+
self.cached_side = side
314+
315+
def _mfm_readinto(self, track_data, validity):
316+
for i in range(5):
317+
self.floppy.flux_readinto(self.flux_buffer)
318+
print("timing bins", self._t2_5_max, self._t3_5_max)
319+
n = floppyio.mfm_readinto(track_data, self.flux_buffer, self._t2_5_max, self._t3_5_max, validity, i==0)
320+
if n == self.sectors: break

examples/floppy_vfs.py

+46-20
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,65 @@
33
#
44
# SPDX-License-Identifier: Unlicense
55

6-
# On an Adafruit Feather M4 or Adafruit Feather RP2040 with Floppy Featherwing,
7-
# print the root directory listing of a 1.44MB floppy
6+
# On an Adafruit Floppsy, Adafruit Feather M4 or Adafruit Feather RP2040 with
7+
# Floppy Featherwing, print the root directory listing of a 1.44MB floppy
8+
9+
# Leave this line here, this memory must be allocated as early as possible to avoid
10+
# memory fragmentation
11+
flux_buffer = bytearray(110000)
812

913
import os
1014
import storage
1115
import board
1216
import adafruit_datetime as datetime
1317
import adafruit_floppy
1418

15-
D24 = getattr(board, "D24") or getattr(board, "A4")
16-
D25 = getattr(board, "D25") or getattr(board, "A5")
17-
1819
epoch = datetime.datetime(1970, 1, 1)
1920

2021
ST_SIZE = 6
2122
ST_TIME = 7
2223
SV_BFREE = 3
2324

24-
floppy = adafruit_floppy.MFMFloppy(
25-
densitypin=board.A1,
26-
indexpin=D25,
27-
selectpin=board.A0,
28-
motorpin=board.A2,
29-
directionpin=board.A3,
30-
steppin=D24,
31-
track0pin=board.D10,
32-
protectpin=board.D11,
33-
rddatapin=board.D9,
34-
sidepin=board.D6,
35-
readypin=board.D5,
36-
)
37-
38-
f = adafruit_floppy.FloppyBlockDevice(floppy, sectors=18)
25+
if hasattr(board, 'DENSITY'): # floppsy
26+
floppy = adafruit_floppy.Floppy(
27+
densitypin=board.DENSITY,
28+
indexpin=board.INDEX,
29+
selectpin=board.SELECT,
30+
motorpin=board.MOTOR,
31+
directionpin=board.DIRECTION,
32+
steppin=board.STEP,
33+
track0pin=board.TRACK0,
34+
protectpin=board.WRPROT,
35+
rddatapin=board.RDDATA,
36+
sidepin=board.SIDE,
37+
readypin=board.READY,
38+
floppydirectionpin=board.FLOPPY_DIRECTION,
39+
)
40+
41+
else:
42+
floppy = adafruit_floppy.Floppy(
43+
densitypin=board.A1,
44+
indexpin=D25,
45+
selectpin=board.A0,
46+
motorpin=board.A2,
47+
directionpin=board.A3,
48+
steppin=D24,
49+
track0pin=board.D10,
50+
protectpin=board.D11,
51+
rddatapin=board.D9,
52+
sidepin=board.D6,
53+
readypin=board.D5,
54+
flux_buffer=flux_buffer
55+
)
56+
57+
floppy.find_track0()
58+
print(sum(floppy._index.value for _ in range(100_000)))
59+
print(floppy.flux_readinto(flux_buffer))
60+
print(sum(flux_buffer))
61+
62+
f = adafruit_floppy.FloppyBlockDevice(floppy, sectors=18,
63+
flux_buffer=flux_buffer
64+
)
3965

4066
vfs = storage.VfsFat(f)
4167
storage.mount(vfs, "/floppy")

0 commit comments

Comments
 (0)