Arcade (COH/SC2): boot on a single reset after a card switch; fix a stuck-core1 hang on card switch; add version/ls/stat serial commands - #104
Conversation
|
Thanks for the PR.😊 EDIT:
|
bbb0c37 to
842aa87
Compare
|
Thanks for the quick look. All three points addressed, branch force-pushed (head 842aa87): Retargeted to No more direct
The replies of the new serial commands ( RAM. Linker memory report,
Take your time with the review - and if it helps I can run any variant you build on the two arcade boards here. |
|
I finally had the chance to give the content a good look. I would like to split it into 2 parts
|
…rd switch On every card switch the firmware refuses the next five card-identifier requests so a PS2's mcman drops its cache. Arcade BIOSes (Namco System 246/256: the COH and SC2 variants) identify the card once at boot and give up, so the first boot after any switch fails with "Boot Program Error" and a second reset is needed. Those boards have no mcman cache to protect. Keep the five refusals armed through the switch and the card load - they also guard the load against stray commands, and zeroing the counter at switch time hung the firmware on a System 256 when switching away from a game that was actively using the card (4 of 5 attempts, 0 of 2 on stock). Once the card manager reports the new card loaded, an Arcade (COH) or Conquest (SC2) variant clears the counter. Retail and Proto are unchanged. Tested on real Namco System 246 and System 256 hardware with a Raspberry Pi driving the SD2PSX over USB-CDC: 72 of 72 games on the 246 and the whole 256 library boot on a single reset release after a card switch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B1gpWeX4ssmodsaa4oUt6x
Reproducible on stock 1.4.0 (seen on two units, two boards): switch cards
while the console is mid-transaction and gets reset - core1 sits in
receive() waiting for a byte that never comes, watching only the
console-reset flag, so it never acknowledges the exit request. The 1s
recovery in ps2_memory_card_exit() then hard-resets core1 and relaunches
it, and the relaunched core re-claims PIO state machines still claimed
by the dead run and panics ('No PIO state machines are available'), once
a second, forever: dead card, dead USB, only a power cycle helps. Silent
on release builds.
- receive() and mc_respond() also honour the exit request
- the recovery path unclaims the PIO, releases the dirty spinlock the
dead core may have held for an in-flight PSRAM DMA, and re-arms its
timeout instead of resetting core1 on every loop iteration
- each card-switch step prints over USB in DEBUG_USB_UART builds only
Verified on a Namco System 256: six switches away from a running game
(the sequence that hung the firmware four times out of five before this)
all survived, four of them logged as caught mid-command; 125 switches in
an automated run were all answered.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
842aa87 to
0bdf96c
Compare
|
Thanks for the detailed review, and sorry for the delay getting back to you. Split, rename, and the unload() call: all done, and the branch has been rebased/force-pushed on top of current
The two remaining points (the Over 16 switches, it hung 3 times (~19%):
Given that, I'd like to keep both of these as they are in the PR rather than change them - but I'm very open to a different fix for the underlying concerns you raised (the deselection semantics on |
|
Some steps that may help root causing the issue: If the sd2psx side becomes unresponsive (like in the OLED menu etc) this typically means that Core 0 has stalled somewhere - probably your error case 1. If console side becomes unresponsive it usually means that either the PIOs have locked up, or (more often) that card switching has somehow locked up (ping-pong exit logic). Would it be possible for you to check with a logic analyser, if the SEL line stays LO during a longer period / when this issue happens? You are explicitly checking for receiving an exit (card switch) during the receive function. On deselction of the card, the reset flag is set through an interrupt, so even if the card does not immediately step out of the receive loop on an exit request, it will do after deselection. Therefore usually the sequence for a card switch should be:
|
|
Let me see what I can wire up to the card and get some additional data on the what the SEL line is doing. I will also check if the menu is responsive on the card when an error has occured. Will reply back with more data! |
|
Following up on my last comment with better data. To be precise about terms: everywhere below, "the shipped fix" means upstream Every row below is the shipped fix with only the stated change applied, driven through the real load path (what the web UI/OLED actually triggers), judged by whether the switch completes:
So it splits cleanly: the retry-counter change is fine - guarding On the mechanism - I went back and checked with an oscilloscope on the actual SEL line during real hangs (probed at the test point behind the card-edge connector, pulse-width triggered on anything over 50ms). SEL does not stay low for any extended period during a hang - a >50ms low-pulse trigger never once fired across all the hangs I caught, only on some unrelated slow-but-successful selections. So I don't think Given the retry-counter result, want me to update the PR to use your version there and keep the |
Summary
Three commits from running sd2psXtd as the security-dongle source on real Namco System 246 / System 256 arcade boards (a Raspberry Pi drives the SD2PSX over its USB-CDC serial port and selects a card per game). All three are on top of
1.4.0(identical in content tomainat the time), and the resulting tree has been running as1.4.0-coh3on two units since 2026-09-04.ps2: arcade variants no longer need a second console reset after a card switch- the reason we started. Variant-gated: only the Arcade (COH) and Conquest (SC2) variants change; Retail and Proto behave exactly as before.serial: add 'ls [path]' and 'stat <path>'- lets a host check a card image exists on the SD card before switching to it. Optional; harmless on its own.ps2: make a card switch survive a core1 that is stuck mid-command- a bug in stock 1.4.0 that hits every variant. This one may be worth taking even if you decline the rest; it is the last commit so it can be cherry-picked, though two of its hunks sit in code the first commit touches (ps2_mmceman.c), so it will need a small adjustment on its own.1. Arcade boards need two resets per card switch (stock behaviour)
On every card switch the firmware refuses the next five card-identifier requests so a PS2's
mcmandrops its cache (ps2_mmceman.c, the retry counter). An arcade BIOS identifies the card once at boot and gives up, so on a 246/256 the first boot after any switch ends in "Boot Program Error" and a second reset is needed. Those boards have nomcmancache to protect.The fix keeps the five refusals armed through the switch and the card load - they also guard the load against stray commands - and clears the counter once the card manager reports the new card loaded, on Arcade/Conquest variants only. Our first attempt zeroed the counter at switch time instead; that hung the firmware on a System 256 when switching away from a game that was actively using the card (4 of 5 attempts, versus 0 of 2 on stock), which is how we found the bug in section 3. The commit here is the corrected version.
The same commit adds a
versionserial command:Stock answers
Unknown command., which is how the host tells the two apart.Tested: every game in both libraries, one card switch each, one reset release, judged at the cabinet monitor:
2.
lsandstatover serialBoth answer
Busy: card operation in progresswhile a card switch or card creation is running. The motivation: when a host selects a card index that is not on the SD card the firmware quietly creates a blank one and the arcade board boots into "Boot Program Error" with nothing in any log to say why. Withstatthe host can refuse the switch instead.3. Stock bug: a card switch while core1 is mid-command kills the device
Reproducible on stock 1.4.0, seen on two SD2PSX units and two arcade boards: switch cards while the console is mid-transaction and gets reset.
core1sits inreceive()waiting for a byte that never comes, watching only the console-reset flag, so it never acknowledges the exit request. The one-second recovery inps2_memory_card_exit()then hard-resets core1 and relaunches it; the relaunched core re-claims PIO state machines still claimed by the dead run and panics withNo PIO state machines are available- once a second, forever. From outside: dead card, dead USB serial, only a power cycle helps. Silent on release builds, which is why it looked like a random hang.The fix:
receive()andmc_respond()also honour the exit request.DEBUG_USB_UARTbuilds only (that is how the sequence was pinned down;SW1..SW8andReset at <cmd>).Tested: on a System 256, six switches away from a running game - the sequence that hung the firmware four times out of five before - all survived, four of them logged as caught mid-command. Across the two library runs, 125 + ~100 further switches with no hang.
Build
Built with the recipe from
.github/workflows/build_nightly.ymlon macOS (cmake, ninja,arm-none-eabi-gccwith newlib):An unpatched build from the same tree reproduces the upstream
1.4.0version string, which is how the toolchain was proven before patching. Both the Release and the-DDEBUG_USB_UART=ONvariants of this tree have been flashed and run.Notes for review
-coh<N>suffix in the version string above is just our local tag name (git describeon our tree); nothing in the code depends on it.Co-Authored-By: Claude Fable 5.1because the patches were written with Anthropic's Claude Code in the loop, against hardware, with the results above checked by a person at the cabinet. Happy to reword or squash however you prefer.Thanks for sd2psXtd - it is the reason a 20-year-old Namco board can switch between 137 games from a menu.