Skip to content

Commit

Permalink
Add support for floppsy direction pin & circuitpython changes
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jepler committed Apr 2, 2024
1 parent 4713293 commit 96b2068
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 64 deletions.
95 changes: 51 additions & 44 deletions adafruit_floppy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
Expand All @@ -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):
Expand All @@ -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")
Expand All @@ -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]:
Expand All @@ -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
Expand Down Expand Up @@ -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`
Expand All @@ -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

Check failure on line 243 in adafruit_floppy.py

View workflow job for this annotation

GitHub Actions / test

Too many arguments (7/5)

Check failure on line 243 in adafruit_floppy.py

View workflow job for this annotation

GitHub Actions / test

Too many arguments (7/5)
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)

Check failure on line 256 in adafruit_floppy.py

View workflow job for this annotation

GitHub Actions / test

Undefined variable 'buffer'

Check failure on line 256 in adafruit_floppy.py

View workflow job for this annotation

GitHub Actions / test

Undefined variable 'buffer'
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"""
Expand All @@ -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
66 changes: 46 additions & 20 deletions examples/floppy_vfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,65 @@
#
# 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

Check failure on line 13 in examples/floppy_vfs.py

View workflow job for this annotation

GitHub Actions / test

Import "import os" should be placed at the top of the module

Check failure on line 13 in examples/floppy_vfs.py

View workflow job for this annotation

GitHub Actions / test

Import "import os" should be placed at the top of the module
import storage

Check failure on line 14 in examples/floppy_vfs.py

View workflow job for this annotation

GitHub Actions / test

Import "import storage" should be placed at the top of the module

Check failure on line 14 in examples/floppy_vfs.py

View workflow job for this annotation

GitHub Actions / test

Import "import storage" should be placed at the top of the module
import board
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")
Expand Down

0 comments on commit 96b2068

Please sign in to comment.