From 96b206895b039fe41b1ea6ca15c6199dc7d64c53 Mon Sep 17 00:00:00 2001 From: Jeff Epler Date: Tue, 2 Apr 2024 15:46:24 -0500 Subject: [PATCH] Add support for floppsy direction pin & circuitpython changes this **incompatible change** is related to https://github.com/adafruit/circuitpython/pull/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. --- adafruit_floppy.py | 95 +++++++++++++++++++++++------------------- examples/floppy_vfs.py | 66 ++++++++++++++++++++--------- 2 files changed, 97 insertions(+), 64 deletions(-) diff --git a/adafruit_floppy.py b/adafruit_floppy.py index 1e816ec..1de4826 100644 --- a/adafruit_floppy.py +++ b/adafruit_floppy.py @@ -50,7 +50,7 @@ def _sleep_ms(interval): _sleep_deadline_ms(ticks_add(ticks_ms(), interval)) -class MFMFloppy: # pylint: disable=too-many-instance-attributes +class Floppy: # pylint: disable=too-many-instance-attributes """Interface with floppy disk drive hardware""" _track: typing.Optional[int] @@ -71,6 +71,7 @@ def __init__( readypin: microcontroller.Pin, wrdatapin: typing.Optional[microcontroller.Pin] = None, wrgatepin: typing.Optional[microcontroller.Pin] = None, + floppydirectionpin: typing.Optional[microcontroller.Pin] = None, ) -> None: self._density = DigitalInOut(densitypin) self._density.pull = Pull.UP @@ -97,6 +98,10 @@ def __init__( self._ready = DigitalInOut(readypin) self._ready.pull = Pull.UP + self._floppydirection = _optionaldigitalinout(floppydirectionpin) + if self._floppydirection: + self._floppydirection.switch_to_output(True) + self._track = None def _do_step(self, direction, count): @@ -122,6 +127,7 @@ def find_track0(self): for _ in range(250): if not self._track0.value: self._track = 0 + self._check_inpos() return self._do_step(_STEP_OUT, 1) raise RuntimeError("Could not reach track 0") @@ -131,7 +137,7 @@ def _check_inpos(self) -> None: drive_says_track0 = not self._track0.value we_think_track0 = track == 0 if drive_says_track0 != we_think_track0: - raise RuntimeError("Drive lost position") + raise RuntimeError(f"Drive lost position (target={track}, track0 sensor {drive_says_track0})") @property def track(self) -> typing.Optional[int]: @@ -150,7 +156,7 @@ def track(self, track: int) -> None: delta = track - self.track if delta < 0: self._do_step(_STEP_OUT, -delta) - else: + elif delta > 0: self._do_step(_STEP_IN, delta) self._track = track @@ -210,24 +216,6 @@ def flux_readinto(self, buf: "circuitpython_typing.WritableBuffer") -> int: :return: The actual number of bytes of read""" return floppyio.flux_readinto(buf, self._rddata, self._index) - def mfm_readinto(self, buf: "circuitpython_typing.WriteableBuffer") -> int: - """Read mfm blocks into the buffer. - - The track is assumed to consist of 512-byte sectors. - - The function returns when all sectors have been successfully read, or - a number of index pulses have occurred. Due to technical limitations, this - process may not be interruptible by KeyboardInterrupt. - - :param buf: Read data into this buffer. Must be a multiple of 512. - :return: The actual number of sectors read - """ - return floppyio.mfm_readinto( - buf, - self._rddata, - self._index, - ) - class FloppyBlockDevice: """Wrap an MFMFloppy object into a block device suitable for `storage.VfsFat` @@ -243,37 +231,46 @@ class FloppyBlockDevice: import storage import adafruit_floppy - floppy = adafruit_floppy.MFMFloppy(...) + floppy = adafruit_floppy.Floppy(...) block_device = adafruit_floppy.FloppyBlockDevice(floppy) vfs = storage.VfsFat(f) storage.mount(vfs, '/floppy') print(os.listdir("/floppy")) """ - def __init__(self, floppy, heads=2, sectors=18, tracks=80): + def __init__(self, floppy, heads=2, sectors=18, tracks=80, flux_buffer=None, t1_nom_ns: float=1000): self.floppy = floppy self.heads = heads self.sectors = sectors self.tracks = tracks + self.flux_buffer = flux_buffer or buffer(sectors * 12 * 512) self.track0side0_cache = memoryview(bytearray(sectors * 512)) + self.track0side0_validity = bytearray(sectors) + self.track_cache = memoryview(bytearray(sectors * 512)) + self.track_validity = bytearray(sectors) - self.floppy.track = 0 - self.floppy.head = 0 - floppyio.mfm_readinto(self.track0side0_cache, floppy._rddata, floppy._index) + self._t2_5_max = round(2.5 * t1_nom_ns * floppyio.samplerate * 1e-9) + self._t3_5_max = round(3.5 * t1_nom_ns * floppyio.samplerate * 1e-9) + + self._track_read(self.track0side0_cache, self.track0side0_validity, 0, 0) - self.track_cache = memoryview(bytearray(sectors * 512)) self.cached_track = -1 self.cached_side = -1 + def deinit(self): - """Deinitialize this object (does nothing)""" + """Deinitialize this object""" + self.floppy.deinit() + del self.flux_buffer + del self.track0side0_cache + del self.track_validity def sync(self): """Write out any pending data to disk (does nothing)""" def writeblocks(self, start, buf): # pylint: disable=no-self-use """Write to the floppy (always raises an exception)""" - raise IOError("Read-only filesystem") + raise OSError("Read-only filesystem") def count(self): """Return the floppy capacity in 512-byte units""" @@ -287,27 +284,37 @@ def readblocks(self, start_block, buf): def _readblock(self, block, buf): if block > self.count(): - raise IOError("Read past end of media") + raise OSError("Read past end of media") track = block // (self.heads * self.sectors) block %= self.heads * self.sectors side = block // (self.sectors) block %= self.sectors - trackdata = self._get_track_data(track, side) + trackdata, validity = self._get_track_data(track, side) + if not validity[block]: + raise OSError(f"Failed to read sector {track}/{side}/{block}") buf[:] = trackdata[block * 512 : (block + 1) * 512] def _get_track_data(self, track, side): if track == 0 and side == 0: - return self.track0side0_cache + return self.track0side0_cache, self.track0side0_validity if track != self.cached_track or side != self.cached_side: - self.floppy.selected = True - self.floppy.spin = True - self.floppy.track = track - self.floppy.side = side - self.floppy.mfm_readinto( - self.track_cache, - ) - self.floppy.spin = False - self.floppy.selected = False - self.cached_track = track - self.cached_side = side - return self.track_cache + self._track_read(self.track_cache, self.track_validity, track, side) + return self.track_cache, self.track_validity + + def _track_read(self, track_data, validity, track, side): + self.floppy.selected = True + self.floppy.spin = True + self.floppy.track = track + self.floppy.side = side + self._mfm_readinto(track_data, validity) + self.floppy.spin = False + self.floppy.selected = False + self.cached_track = track + self.cached_side = side + + def _mfm_readinto(self, track_data, validity): + for i in range(5): + self.floppy.flux_readinto(self.flux_buffer) + print("timing bins", self._t2_5_max, self._t3_5_max) + n = floppyio.mfm_readinto(track_data, self.flux_buffer, self._t2_5_max, self._t3_5_max, validity, i==0) + if n == self.sectors: break diff --git a/examples/floppy_vfs.py b/examples/floppy_vfs.py index fe722f7..42c7998 100644 --- a/examples/floppy_vfs.py +++ b/examples/floppy_vfs.py @@ -3,8 +3,12 @@ # # SPDX-License-Identifier: Unlicense -# On an Adafruit Feather M4 or Adafruit Feather RP2040 with Floppy Featherwing, -# print the root directory listing of a 1.44MB floppy +# On an Adafruit Floppsy, Adafruit Feather M4 or Adafruit Feather RP2040 with +# Floppy Featherwing, print the root directory listing of a 1.44MB floppy + +# Leave this line here, this memory must be allocated as early as possible to avoid +# memory fragmentation +flux_buffer = bytearray(110000) import os import storage @@ -12,30 +16,52 @@ import adafruit_datetime as datetime import adafruit_floppy -D24 = getattr(board, "D24") or getattr(board, "A4") -D25 = getattr(board, "D25") or getattr(board, "A5") - epoch = datetime.datetime(1970, 1, 1) ST_SIZE = 6 ST_TIME = 7 SV_BFREE = 3 -floppy = adafruit_floppy.MFMFloppy( - densitypin=board.A1, - indexpin=D25, - selectpin=board.A0, - motorpin=board.A2, - directionpin=board.A3, - steppin=D24, - track0pin=board.D10, - protectpin=board.D11, - rddatapin=board.D9, - sidepin=board.D6, - readypin=board.D5, -) - -f = adafruit_floppy.FloppyBlockDevice(floppy, sectors=18) +if hasattr(board, 'DENSITY'): # floppsy + floppy = adafruit_floppy.Floppy( + densitypin=board.DENSITY, + indexpin=board.INDEX, + selectpin=board.SELECT, + motorpin=board.MOTOR, + directionpin=board.DIRECTION, + steppin=board.STEP, + track0pin=board.TRACK0, + protectpin=board.WRPROT, + rddatapin=board.RDDATA, + sidepin=board.SIDE, + readypin=board.READY, + floppydirectionpin=board.FLOPPY_DIRECTION, + ) + +else: + floppy = adafruit_floppy.Floppy( + densitypin=board.A1, + indexpin=D25, + selectpin=board.A0, + motorpin=board.A2, + directionpin=board.A3, + steppin=D24, + track0pin=board.D10, + protectpin=board.D11, + rddatapin=board.D9, + sidepin=board.D6, + readypin=board.D5, + flux_buffer=flux_buffer + ) + +floppy.find_track0() +print(sum(floppy._index.value for _ in range(100_000))) +print(floppy.flux_readinto(flux_buffer)) +print(sum(flux_buffer)) + +f = adafruit_floppy.FloppyBlockDevice(floppy, sectors=18, + flux_buffer=flux_buffer + ) vfs = storage.VfsFat(f) storage.mount(vfs, "/floppy")