PR #38 (Fixes #37) made uci_wait_idle wall-clock-bounded via CIA1 TOD. The other two spin-wait helpers in src/net/uci/uci_cmd.s — uci_push_wait and uci_end_cmd — are still unbounded. If the FPGA wedges during a CMD_PUSH or after CMD_CTRL=END, the C64 hangs indefinitely instead of surfacing a clean error.
No such wedge has been observed in the field yet, which is why this wasn't bundled into PR #38 — but the same defect class applies and the fix is mechanical.
Suggested fix
Use uci_wait_idle from PR #38 (commit 4d839d0, now on master at f907b20) as the template:
- Sample CIA1 TOD
$DC08-$DC0B at entry. Read order: HOUR ($DC0B, latches) → MIN → SEC → TENTHS ($DC08, unlatches). Wrong order returns garbage. - Each spin pass: re-latch + re-read TENTHS, increment elapsed counter on transition, bail with
C=1 after the budget is exhausted (5 s = 50 transitions matches the existing helper). - On bail:
net_last_error = UCI_ERR_WAIT_TIMEOUT (or a new UCI_ERR_PUSH_TIMEOUT / UCI_ERR_END_TIMEOUT if you want to discriminate at the caller level — uci_errors.inc is the home). - State in two SMC bytes inside the routine to match the file's no-ZP convention.
- Update all callers to
bcs out and surface the failure (don't swallow). Grep the tree for jsr uci_push_wait / jsr uci_end_cmd to find them.
Why CIA1 TOD (not cycle-counted)
See CLAUDE.md "Design note — bounded timeouts must use wall-clock time". The fences around every UCI register access make per-iteration cost scale with CPU speed; a cycle-counted budget that's ample at 1 MHz collapses at 48 MHz. TOD ticks at 10 Hz independent of turbo. Prior cycle-counted attempt on feat/net-drain-abi broke DHCP at turbo and was abandoned.
Test plan
After patching, the existing UCI test sequence (tools/uci/{boot_check,phase2_check,phase3_tcp_echo,test_http_local,test_https_local,test_https_print_body}.py) should still all pass. Inducing an actual wedge to verify the new bail path is harder — would likely need a synthetic test that holds CMD_CTRL high or similar firmware-side disruption.
Priority
Low — defense-in-depth. Real wedges have only been seen in the OPEN_TCP path that PR #38 already covered. File this so the work isn't forgotten when someone next touches uci_cmd.s.
PR #38 (Fixes #37) made
uci_wait_idlewall-clock-bounded via CIA1 TOD. The other two spin-wait helpers insrc/net/uci/uci_cmd.s—uci_push_waitanduci_end_cmd— are still unbounded. If the FPGA wedges during a CMD_PUSH or after CMD_CTRL=END, the C64 hangs indefinitely instead of surfacing a clean error.No such wedge has been observed in the field yet, which is why this wasn't bundled into PR #38 — but the same defect class applies and the fix is mechanical.
Suggested fix
Use
uci_wait_idlefrom PR #38 (commit 4d839d0, now on master at f907b20) as the template:$DC08-$DC0Bat entry. Read order: HOUR ($DC0B, latches) → MIN → SEC → TENTHS ($DC08, unlatches). Wrong order returns garbage.C=1after the budget is exhausted (5 s = 50 transitions matches the existing helper).net_last_error = UCI_ERR_WAIT_TIMEOUT(or a newUCI_ERR_PUSH_TIMEOUT/UCI_ERR_END_TIMEOUTif you want to discriminate at the caller level —uci_errors.incis the home).bcsout and surface the failure (don't swallow). Grep the tree forjsr uci_push_wait/jsr uci_end_cmdto find them.Why CIA1 TOD (not cycle-counted)
See CLAUDE.md "Design note — bounded timeouts must use wall-clock time". The fences around every UCI register access make per-iteration cost scale with CPU speed; a cycle-counted budget that's ample at 1 MHz collapses at 48 MHz. TOD ticks at 10 Hz independent of turbo. Prior cycle-counted attempt on
feat/net-drain-abibroke DHCP at turbo and was abandoned.Test plan
After patching, the existing UCI test sequence (
tools/uci/{boot_check,phase2_check,phase3_tcp_echo,test_http_local,test_https_local,test_https_print_body}.py) should still all pass. Inducing an actual wedge to verify the new bail path is harder — would likely need a synthetic test that holds CMD_CTRL high or similar firmware-side disruption.Priority
Low — defense-in-depth. Real wedges have only been seen in the OPEN_TCP path that PR #38 already covered. File this so the work isn't forgotten when someone next touches
uci_cmd.s.