M7: a real floppy through the Liron card

/ UniDisk 3.5 controller / updated 2026-10-12 smartport liron rp2350 prodos floppy m7

The Sony mechanism is behind the SmartPort code now. PR#5 boots ProDOS off a real 800K floppy through my board, Copy II Plus writes files to it, and a small test program gets byte-identical STATUS and READ replies from my board and from the working UniDisk, with a disk in, write-protected and with the drive empty.

Everything wired for M7: the Pico and level shifters on the breadboard, the Sony mechanism on its ribbon, the scope probes on the host bus.
Everything wired for M7: the Pico and level shifters on the breadboard, the Sony mechanism on its ribbon, the scope probes on the host bus.

A test disk my board didn’t write

Booting REF800K would only get as far as “UNABLE TO LOAD PRODOS”, and I didn’t trust that as a pass. So I built /M7TEST on the Mac and wrote it to a floppy through the working UniDisk with ADTPro. That way my board has to read a disk something else wrote.

It boots ProDOS 2.4.1, and a STARTUP program prints a sentinel line, BLOADs four 16 KB files of known content and sums each one with a small 6502 routine. DATA1-3 sit on tracks 2-6. A 1,307-block filler file pushes DATA4 out to track 72, so the boot has to seek across the disk.

PR#5 off a real floppy through my board: the STARTUP sentinel and all four checksums, DATA4 on track 72.
PR#5 off a real floppy through my board: the STARTUP sentinel and all four checksums, DATA4 on track 72.

For that boot the board logged 223 commands with no errors and no retries. ACK went out 4 to 6 us after /WRREQ rose, the same as with the RAM disk in M6.

The card waits as long as the drive needs

Each SmartPort call becomes a request to the mechanism code on the Pico’s second core, and the first core holds ACK low until the answer is back. The Liron card’s reply loop has no timeout, so a seek fits. Over the session:

CountHold-off
Commands that needed the mechanism109median 198 ms, 90 % under 595 ms, longest 844 ms
WRITEBLOCKs (each verified on the next revolution)16114 to 725 ms
Reads served from the track bufferthe restabout 0 ms

The 844 ms one was a read after the motor had stopped, so it includes the spin-up. Copy II Plus copied README from the slot 6 disk onto /M7TEST with 16 WRITEBLOCKs, all good.

Copy II Plus catalog of /M7TEST after copying README onto it through my board.
Copy II Plus catalog of /M7TEST after copying README onto it through my board.

My board and the UniDisk give the same bytes

SPCOMPARE is a short Applesoft program on the test disk. It calls the slot 5 SmartPort entry directly and prints STATUS $00, $03 (the device info block), $05 and $01, then blocks 0-2 and block 1600 with an error code and a checksum. I ran it on both drives with the same disk in three states:

DiskSTATUS $00Blocks 0-2Block 1600
In, writableF8 40 06 00$00, sums 56491 / 0 / 15100$2D bad block
Write-protected (window open)FC 40 06 00$00, same sums$2D
Drive emptyE8 40 06 00$2F no disk$2F

Every screen matched except one byte. STATUS $05 on the UniDisk ends in $FF, which is the drive ROM’s zero page $5D, and mine sent $00. I changed it, and the screens are identical now.

SPCOMPARE on the working UniDisk: disk in (top), then the drive empty (bottom).
SPCOMPARE on the working UniDisk: disk in (top), then the drive empty (bottom).
SPCOMPARE on my board with the drive empty: the same bytes as the UniDisk.
SPCOMPARE on my board with the drive empty: the same bytes as the UniDisk.

The block 1600 row with no disk shows the drive ROM’s order of checks. It looks for the disk first and only then at the block number, so an empty drive answers $2F even for a block that doesn’t exist. If your replacement controller returns $2D where a UniDisk returns $2F for an out-of-range block with no disk in, check the disk first. The UniDisk does.

The first run was an I/O error from my current limit

The first PR#5 came back with I/O ERROR. The board logged $27 after exactly 2000 ms, which is the motor spin-up timeout: the mechanism never reported ready. My bench supply was still set to 0.03 to 0.05 A from the host-side work, and the rail collapsed as soon as the motor started. The same dip dropped the Pico’s USB and left the level shifter reading all ones.

The mechanism needs real current. M2 measured 0.14 A on +5 and 0.13 A on +12 with the motor running, and the eject motor wants 0.5 A on +12. With the limits at 0.5 A and 1.0 A it booted on the next try.

While the rail was sagging, the card sat with REQ high and never read. My firmware took that as “please send the reply again” and re-sent the stale error about 190 times in 10 seconds. It’s capped at 5 re-sends per reply now, which is also how many times the card tries to read one.

Open threads