M6: ProDOS boots from my board

/ UniDisk 3.5 controller / updated 2026-10-12 smartport liron rp2350 pio prodos m6

ProDOS 2.4.1 boots from my board now. I typed PR#5, the Liron card found one SmartPort device, read about 140 blocks off it and left me at the BASIC.SYSTEM prompt. The disk is an 800K ProDOS image in the Pico’s flash with a RAM overlay for writes, so anything I save is gone at power off. The Sony mechanism isn’t in the loop yet. That’s M7.

The boot came off /M6BOOT

CAT lists /M6BOOT, a volume name that only exists in the flash image, with PRODOS and BASIC.SYSTEM on it. The board keeps counters, and for that boot they read:

Count
Packets received150
ACKs / replies sent150 / 150
Checksum errors, timeouts, re-reads0
INIT / STATUS / READBLOCK3 / 3 / 144

INIT gave the board ID 1 and its reply carried status $40, which tells the card nothing is plugged in behind it.

Writing works too. SAVE HELLO sent 5 WRITEBLOCKs and changed 3 blocks: the file, the directory and the free block bitmap. Free blocks went from 1537 to 1536. NEW, LOAD HELLO, LIST, RUN printed HELLO.

HELLO saved to the board's RAM disk, loaded back and run through the Liron card.
HELLO saved to the board’s RAM disk, loaded back and run through the Liron card.

What the Liron card waits for

The card ROM (liron-if.asm) sets every deadline on the host side. Counting its poll loops at 1.023 MHz gives these:

Card stepLimit
ACK high before it sends a packet50 polls, about 540 us, then it retries (3000 tries, INIT gets 1)
ACK low after its write underrun9 polls, about 105 us, then it drops REQ and retries
ACK high before it reads the replyno timeout
C3 in the replywithin 30 nibbles

The third row is how a drive holds the host off. It keeps ACK low while it works, and the card sits in that loop for as long as it takes. A floppy seek in M7 fits there. The 105 us window after the host’s last byte is the only tight one.

On the wire, the host writes a 1 as a level change on WR, in 3.911 us cells (28 clocks of the IWM’s 7.159 MHz). Going back, the card’s IWM only counts falling edges on RD, so the board sends each 1 as a 1 us low pulse. A PIO program on the receive side arms on /WRREQ going low and starts on the first edge, so the CPU never has to catch the start of a packet. Every byte is 8 cells from that first edge, so the last byte (C8) is complete half a cell before the host’s last cell ends. ACK can go out about as soon as the host stops.

If you’re building a SmartPort device for a Liron card and it says NO DEVICE CONNECTED, check that ACK goes low within about 105 us of /WRREQ rising. The card does not wait longer.

The first packet after a reset was ACKed 31 us late

I put /WRREQ, ACK and REQ on the scope and triggered on /WRREQ rising. A READBLOCK during boot got its ACK 6.7 us after the rise. The INIT right after a PR#5 bus reset took 30.9 us. That’s still inside the card’s window, but it was five times the others.

INIT after PR#5, code in flash: ACK (magenta) falls 30.9 us after /WRREQ (yellow) rises. CH3 is REQ. 20 us/div.
INIT after PR#5, code in flash: ACK (magenta) falls 30.9 us after /WRREQ (yellow) rises. CH3 is REQ. 20 us/div.

The firmware’s own latency number for that same INIT was -3 us, meaning ACK before /WRREQ rose. It sampled /WRREQ at the top of its loop while the line was still low, then spent about 30 us on the last chunk and the checksum before setting ACK. So it never saw the rise come first. It now uses the last time it saw /WRREQ low, which makes the number an upper bound.

My guess for the 30 us is flash cache misses: the receive and decode code had dropped out of the cache after a quiet spell on the bus. I moved that path into RAM and the same INIT came in at 5.3 us. The firmware reported 6 us for it.

Same INIT with the receive path in RAM: ACK falls 5.3 us after /WRREQ rises. 20 us/div.
Same INIT with the receive path in RAM: ACK falls 5.3 us after /WRREQ rises. 20 us/div.
PacketCode/WRREQ rise to ACK low
READBLOCK during bootflash6.68 us
INIT after PR#5flash30.93 us
INIT after PR#5RAM5.29 us

Numbers are from the 500 MSa/s waveform data, not read off the screen.

Two bench problems on the side

Neither had anything to do with SmartPort.

The firmware went from 64 KB to 864 KB with the disk image in it, and OpenOCD timed out erasing it (“failed erasing sectors 0 to 216”). A second try left the flash half erased. An OpenOCD rescue reset first, then program, worked, so make flash does that now. My guess is core 1 kept running from flash during the long erase.

Twice the IIe wouldn’t start after I power cycled it with the Pico left running. Once the Pico’s host inputs read all ones afterwards. A cold boot of everything fixed it both times. I didn’t chase it. The Pico now gets switched off with everything else, and on power up it comes up first, then the board, then the IIe.

Open threads