RIOv4_3rc1 candidate: wedge fix + E0 threshold + check-display repaint; archive prior images

New edit 6 (--checkrepaint, requires --e0thresh): hooks the CheckRequest
handler's final notify ($C5E4) through a 10-byte cave at $E020 that
repaints F0000000 after the self-test and re-renders the E0 readout only
when a counter is at/over threshold — no more stale 04000000 after
status checks. Candidate = 9600 native-game-compatible base + edits
1-2/5/6, 68 bytes vs stock, disassembly-verified, awaiting burn; on
verification it will be christened RIO 4.3.

All prior patched generations (wedge-only, 31250/31250v2, 62500/125000
science builds, e0t5 pair) moved to rio-firmware/archive/ with their
disassemblies; RIOv4_2.bin stays at top level as the pristine patch
source.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-19 16:13:13 -05:00
co-authored by Claude Fable 5
parent a9ae6ff0c8
commit c29a9d5343
19 changed files with 13214 additions and 22 deletions
+33 -18
View File
@@ -41,22 +41,37 @@ the SCI state machine (protocol constants FC=ACK FD=NAK FE=RESTART
FF=IDLE, idle-reload-4 patterns), patch the early-ACK/error wedge path or
widen its window, burn a new EPROM, keep this original safe.
## Patched images (built by make_patch.py; see RIOv4_2-ANALYSIS.md)
## Current work candidate — `RIOv4_3rc1.bin`
- **`RIOv4_2_patched.bin`** — the reply-latch wedge fix, 23 bytes changed.
sha256 `3fc8170caf60e2580641724ff995176c93c4f2e706f31487beded8233142493f`.
- **`RIOv4_2_patched_31250.bin`** (`make_patch.py --baud31250`) — wedge fix
+ SCI retuned 9600 → 31250 baud (one more byte, `$D62B: 30→02`).
sha256 `9f866cf353d04906e1d3e3847b6375eaae251c987b656f68f093e61ba1bd545b`.
Bench/RIOJoy-only until the native games get baud patches; needs an
FTDI-class adapter (16550s can't make 31250). Test tools take `--baud 31250`.
- **Speed-ladder variants** (`_31250v2`, `_62500`, `_125000`) and bench
verdicts: see RIOv4_2-ANALYSIS.md — 31250v2 is the certified FastRIO
build; 62500/125000 were science runs (CPU is the wall).
- **`RIOv4_2_patched_e0t5.bin` / `RIOv4_2_patched_31250v2_e0t5.bin`**
(`--e0thresh=5`) — adds a threshold to the cockpit display's `E0`
serial-error readout, which stock firmware paints on the *first*
counter event (`E0000001` forever after one benign give-up). Display
stays `F0000000` until any error counter reaches 5; counters still
accumulate. 30-byte cave at `$E000`, disassembly-verified, **not yet
burned/bench-verified**. Details in RIOv4_2-ANALYSIS.md.
Built by `make_patch.py --e0thresh=5 --checkrepaint RIOv4_2.bin
RIOv4_3rc1.bin`; sha256
`dc59bd51cae34781cf42f338e44a020b249a4030acc3c584af916aee9ca881e3`
(68 bytes changed vs stock). 9600 baud, native-game compatible. On the
stock v4.2 base it carries:
1. **Reply-latch wedge fix** (edits 1-2) — bench-certified.
2. **E0-display threshold, N=5** (edit 5) — bench-certified 2026-07-19
(held `F0000000` sub-threshold, flipped to `E0000105` at the 5th
teardown; mash regression clean).
3. **Check self-test display repaint** (edit 6, NEW) — the CheckRequest
handler previously left a stale `04000000` frame on the display; a
10-byte cave at `$E020` now repaints `F0000000` after every check,
then re-renders the `E0` readout only if a counter is at/over
threshold. **Needs on-hardware verification**: after a burn, any
version+check exchange (tray connect, monitor, mash start) must
leave the display reading `F0000000`, not `04000000`; then re-run
`--e0test` (expect the certified behavior unchanged) and a mash
spot-check.
Once edit 6 is verified on the cockpit, this image is christened
**RIO 4.3**.
## Archive
All prior generations live in [`archive/`](archive/) with their
disassemblies: `RIOv4_2_patched` (wedge fix only), `_31250`/`_31250v2`
(FastRIO speed builds), `_62500`/`_125000` (speed-ladder science runs),
and the `_e0t5` pair (threshold without check-repaint; the 9600 one is
in the socket as of 2026-07-19 until rc1 is burned). Bench verdicts and
per-edit history: RIOv4_2-ANALYSIS.md. The pristine dump `RIOv4_2.bin`
stays at top level — it is the source every patch builds from.
+9 -4
View File
@@ -516,7 +516,12 @@ last thing rendered), enqueues the fault reports, clears the flag
(`$C5E1`), notifies `0x8C` again — and **returns without repainting the
display**. Every status check therefore leaves a stale
`04 00 <ch4 count>` snapshot (idle: `04000000`) on the digits. The
board is fully healthy afterwards; no reset is needed. Proper fix, if
the cosmetic bothers: a ~10-byte cave off `$C5E4` that repaints `F0`
(`JSR $CB7C`) and re-renders the E0 readout when over threshold
(`JSR $D5F2`) — candidate for the next burn, not yet built.
board is fully healthy afterwards; no reset is needed. Proper fix:
**edit 6** (`make_patch.py --checkrepaint`) — a 10-byte cave at `$E020`
hooked from the handler's final notify (`$C5E4`): `JSR $C70E` (original
notify), `JSR $CB7C` (repaint `F0000000`), `JSR $D5F2` (re-render the
E0 readout, which the edit-5 gate suppresses below threshold), `RTS`.
Built into **`RIOv4_3rc1.bin`** (with edits 1-2 and 5; sha256
`dc59bd51cae3…`) — awaiting burn + on-hardware verification, after
which the image is christened RIO 4.3. Prior generations moved to
`archive/`.
+39
View File
File diff suppressed because one or more lines are too long
+13109
View File
File diff suppressed because it is too large Load Diff
+24
View File
@@ -47,6 +47,11 @@ for a in sys.argv:
E0T = int(a.split("=", 1)[1]) if "=" in a else 5
assert E0T is None or 1 <= E0T <= 255, f"--e0thresh out of range: {E0T}"
# --checkrepaint: repaint the display after the CheckRequest self-test (edit 6).
CHECKREPAINT = "--checkrepaint" in sys.argv
assert not CHECKREPAINT or E0T is not None, \
"--checkrepaint requires --e0thresh (it re-renders E0 via the gated $D5F2)"
args = [a for a in sys.argv[1:] if not a.startswith("--")]
SRC = args[0] if len(args) > 0 else "RIOv4_2.bin"
if len(args) > 1:
@@ -59,6 +64,8 @@ else:
DST = "RIOv4_2_patched.bin"
if E0T is not None and len(args) <= 1:
DST = DST[:-4] + f"_e0t{E0T}.bin"
if CHECKREPAINT and len(args) <= 1:
DST = DST[:-4] + "_ckr.bin"
d = bytearray(open(SRC, "rb").read())
assert len(d) == 0x10000, f"expected 64KB image, got {len(d)}"
@@ -141,6 +148,23 @@ if E0T is not None:
0x7E, 0xD5, 0xF7] # $E01B JMP $D5F7 (resume render)
patch(0xE000, [0xFF] * len(cave), cave)
# --- edit 6 (--checkrepaint): repaint the display after the check self-test --
# The CheckRequest handler ($C5A6) renders its five-channel encoder sweep to
# the cockpit display (it sets the test-display flag $2421 for the duration)
# and returns without repainting, leaving a stale '04 00 <ch4 count>' frame
# (idle: 04000000) after every status check. Hook its final TestModeChange
# notify ($C5E4 JSR $C70E) through a cave that also repaints F0000000
# ($CB7C) and then re-renders the E0 error readout via $D5F2 — which, with
# the edit-5 gate, paints only when a counter is at/over threshold. Display
# after any check: F0000000 when healthy, E0... when not.
if CHECKREPAINT:
patch(0xC5E4, [0xBD, 0xC7, 0x0E], [0xBD, 0xE0, 0x20]) # JSR $C70E -> JSR $E020
cave6 = [0xBD, 0xC7, 0x0E, # $E020 JSR $C70E (original notify, kept)
0xBD, 0xCB, 0x7C, # $E023 JSR $CB7C (repaint F0000000)
0xBD, 0xD5, 0xF2, # $E026 JSR $D5F2 (E0 readout iff >= threshold)
0x39] # $E029 RTS
patch(0xE020, [0xFF] * len(cave6), cave6)
open(DST, "wb").write(d)
new_sha = hashlib.sha256(d).hexdigest()
# byte-diff report