Skip to content

Add support for floppsy direction pin & circuitpython changes #11

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 68 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,9 @@ 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 +158,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,26 +218,8 @@ 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:
class FloppyBlockDevice: # pylint: disable=too-many-instance-attributes
"""Wrap an MFMFloppy object into a block device suitable for `storage.VfsFat`

The default heads/sectors/tracks setting are for 3.5", 1.44MB floppies.
Expand All @@ -243,37 +233,53 @@ 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__( # pylint: disable=too-many-arguments
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 bytearray(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"""
Expand All @@ -287,27 +293,45 @@ 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._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,
)
self.floppy.spin = False
self.floppy.selected = False
self.cached_track = track
self.cached_side = side
return self.track_cache
if n == self.sectors:
break
65 changes: 45 additions & 20 deletions examples/floppy_vfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,64 @@
#
# 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)

# pylint: disable=wrong-import-position
import os
import storage
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:
D24 = getattr(board, "D24") or getattr(board, "A4")
D25 = getattr(board, "D25") or getattr(board, "A5")

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()

f = adafruit_floppy.FloppyBlockDevice(floppy, sectors=18, flux_buffer=flux_buffer)

vfs = storage.VfsFat(f)
storage.mount(vfs, "/floppy")
Expand Down
Loading