From cb95c229dae21a2658fe03d1cb8a054f2da8023b Mon Sep 17 00:00:00 2001 From: Cyd Date: Sun, 19 Jul 2026 15:02:22 -0500 Subject: [PATCH] =?UTF-8?q?Firmware:=20--e0thresh=20patch=20=E2=80=94=20ga?= =?UTF-8?q?te=20the=20E0=20error=20display=20(default=20N=3D5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stock $D5F2 repaints the cockpit display to the E0 counter readout on the FIRST increment of $3187/$3184/$3185, so one benign give-up shows E0000001 forever. New opt-in make_patch.py edit 5 hijacks the render's LDX #$2038 into a 30-byte cave at $E000: all three counters below N -> exit via the routine's own epilogue (registers restored, display untouched); any >= N -> resume the render. Counters still accumulate. Built + disassembly-verified (not yet burned): RIOv4_2_patched_e0t5.bin (9600) and RIOv4_2_patched_31250v2_e0t5.bin (FastRIO). Docs updated. Co-Authored-By: Claude Fable 5 --- RIOv4_2-ANALYSIS.md | 27 + RIOv4_2_patched_31250v2_e0t5.bin | 39 + RIOv4_2_patched_31250v2_e0t5.disasm.asm | 13115 ++++++++++++++++++++++ RIOv4_2_patched_e0t5.bin | 39 + RIOv4_2_patched_e0t5.disasm.asm | 13115 ++++++++++++++++++++++ make_patch.py | 34 + 6 files changed, 26369 insertions(+) create mode 100644 RIOv4_2_patched_31250v2_e0t5.bin create mode 100644 RIOv4_2_patched_31250v2_e0t5.disasm.asm create mode 100644 RIOv4_2_patched_e0t5.bin create mode 100644 RIOv4_2_patched_e0t5.disasm.asm diff --git a/RIOv4_2-ANALYSIS.md b/RIOv4_2-ANALYSIS.md index 24817407..96e78ff3 100644 --- a/RIOv4_2-ANALYSIS.md +++ b/RIOv4_2-ANALYSIS.md @@ -459,3 +459,30 @@ detail in [`docs/hardware/display-board-1408.md`](../docs/hardware/display-board - **RUN LED = buffered `AS*`** (address strobe): it indicates "clock alive", not "software alive" — it stays lit even inside the `dEAd` loop. TX/RX LEDs are buffered `SER_OUT`/`SER_IN`. + +### E0-display threshold patch (edit 5, `--e0thresh[=N]`) + +Stock firmware has **no threshold**: the first increment of any of the +three displayed counters repaints the cockpit display from `F0000000` to +the `E0` readout and it never reverts — so a single benign reply +give-up (e.g. one lost ACK healed by host-side stop-and-wait) leaves a +permanent `E0000001`. `make_patch.py --e0thresh=N` (default 5) gates the +renderer: `$D5F4`'s `LDX #$2038` becomes `JMP $E000`, and a 30-byte cave +at `$E000` (erased region, clear of the `$DFF0` wedge stub) compares +`$3187`/`$3184`/`$3185` against N — all below ⇒ exit through the +routine's own epilogue (`$D61D`, registers restored, display untouched); +any at/above ⇒ resume the render at `$D5F7`. Counters still accumulate +regardless, so the diagnostic history is intact — the display just stays +quiet until real trouble (N events since power-on). + +Built variants (wedge fix + threshold 5): + +| image | config | sha256 (first 12) | +|---|---|---| +| `RIOv4_2_patched_e0t5.bin` | 9600, native-game compatible | `9c21ac7199fb` | +| `RIOv4_2_patched_31250v2_e0t5.bin` | 31250 + widened ACK-wait (FastRIO) | `b43032b016d7` | + +**Not yet burned or bench-verified.** To verify on hardware: burn, then +force reply give-ups (e.g. kill the host mid-poll repeatedly) — the +display must stay `F0000000` through the 4th event and flip to +`E00000 05` on the 5th; `RIO_TAP`/mash regression as usual. diff --git a/RIOv4_2_patched_31250v2_e0t5.bin b/RIOv4_2_patched_31250v2_e0t5.bin new file mode 100644 index 00000000..73faccb0 --- /dev/null +++ b/RIOv4_2_patched_31250v2_e0t5.bin @@ -0,0 +1,39 @@ +$"f qFLiÐÙƽôýâë,ʽ5ӽ$$ J K M 4 7  N$ɽ|$!$B$C$G$E$I$F$J$$K$$H$1111% %!%"Ty1y~y˽y~<<67̽4hɜ$F$E32889}$I'|$J$J&$!'м$J$I9̭#""9    =  =   @#""9 #""9^ q9oZ&J&9í$E#; @&    @ @#; $I#;%;<<76 %;% +&~8 & +&G ~8& +&6 &0~$') +&  &&~8$!˧A%523889~;&F(;'~O& ̽;'~^& ;'u~o& 4;'d~€ & h;'S~‘& ɜ;'B~¢&%ұB;'\~¹&ҽJ;'H~Ё&*p;'4~˧; '~A|,$!9&ν~;'~~%~ÐÙi M9 K~S J91Ԣ%9 P۰d< 1 18Z&9" + 32< 1 18Z&z 3&9`!b>9H#ν>9H!½>9$>9H$T>9H$>9$Ļ3U9%08 (9 >9 >9oZ&9   Π)Π!Π#Π%Π' 9 Π) 9 Π! 9 Π# 9 Π% 9 Π' 9 + '>9$"91D1X 9i&$"1''X' +''9~~ƿâë$$!J1Ѱ$ɽ1S~bNJǵ$!99˽ 9~ƾi&1'~ƏLiÐÙƽôýâë,$$ J K M 4 7  N$ɽ|$!$B$C$G$E$I$F$J$$K$$H$1111% %!%"~ƾ&z~ƾ&Ċ~ƾ&Ě~ƾ&Ī~ƾ&ĺ9i&1$Gi&1$K$GH$?967<<-A-B;8832967<<-A-B$!;8832967<<-A-B%%;8832967<<-A-B%%;8832967<<-A-B1;8832967<<-A-B1;8832967<<-A-B1;8832967<<$!'0 '% +%-B -A<%%8z%&;88329$!'-A$ -B 7;9$!'#%-A$ -B% J;9$!'#%-A$ -B% K;99 LΠ( Π8 !ʗ #ʰ &   + 9 LΠ Π0 !ʗ #ʰ &    9 LΠ" Π2 !ʗ  #ʰ &     9 LΠ$ Π4 !ʗ  #ʰ &     9 LΠ& Π6 !ʗ  #ʰ &     9 ' L&#  H$ H~ʖ H~ʖ&#  H$ H~ʖ H~ʖ&#  H$ H~ʖ H~ʖ &#  H$ H~ʖ H~ʖ&  H$ H ~ʖ H 9  & ! %9< 89O_ % & #'|   ҽ9<6$!& 8  H  H 6I289<6$!& 8 % H & H 6I289 HDDDD9O L H 9 8962962962962962967<<  N P !̲8832967<<  N : P2: :!̲88329  4 4&~&O~&̀~ͱ "Z& 5 +< 1 18Z&z 5&z 4&9  + + 9 + +  9 + +   9 + + 9  6 5 NZ'D~&r~| z 5&z 6&967<< !:&7< 1 1 : N& J8~d K8$ Ȝ~l$ o8832967<< !:'@< 1 1 : N& J8~κ K8$ Ȝ~Ȇ$ J%o88329<6$!& 8 K H O L6I289 M& +$ ~$ # M& +$ +~0$ +967<< 7$B N$!P϶| NV!P϶| N܈!P϶ܺ!P϶| N!P϶!P϶P!P϶| N| N| N݂!P϶| Nݴ!P϶883299D967<<!R!PZ& 6 +< 1 18Z&z 6&8832967<9H#ν>9H!½>9$>9H$T>9H$>9$Ļ3U9%08 (9 >9 >9oZ&9   Π)Π!Π#Π%Π' 9 Π) 9 Π! 9 Π# 9 Π% 9 Π' 9 + '>9$"91D1X 9i&$"1''X' +''9~~ƿâë$$!J1Ѱ$ɽ1S~bNJǵ$!99˽ 9~ƾi&1'~ƏLiÐÙƽôýâë,$$ J K M 4 7  N$ɽ|$!$B$C$G$E$I$F$J$$K$$H$1111% %!%"~ƾ&z~ƾ&Ċ~ƾ&Ě~ƾ&Ī~ƾ&ĺ9i&1$Gi&1$K$GH$?967<<-A-B;8832967<<-A-B$!;8832967<<-A-B%%;8832967<<-A-B%%;8832967<<-A-B1;8832967<<-A-B1;8832967<<-A-B1;8832967<<$!'0 '% +%-B -A<%%8z%&;88329$!'-A$ -B 7;9$!'#%-A$ -B% J;9$!'#%-A$ -B% K;99 LΠ( Π8 !ʗ #ʰ &   + 9 LΠ Π0 !ʗ #ʰ &    9 LΠ" Π2 !ʗ  #ʰ &     9 LΠ$ Π4 !ʗ  #ʰ &     9 LΠ& Π6 !ʗ  #ʰ &     9 ' L&#  H$ H~ʖ H~ʖ&#  H$ H~ʖ H~ʖ&#  H$ H~ʖ H~ʖ &#  H$ H~ʖ H~ʖ&  H$ H ~ʖ H 9  & ! %9< 89O_ % & #'|   ҽ9<6$!& 8  H  H 6I289<6$!& 8 % H & H 6I289 HDDDD9O L H 9 8962962962962962967<<  N P !̲8832967<<  N : P2: :!̲88329  4 4&~&O~&̀~ͱ "Z& 5 +< 1 18Z&z 5&z 4&9  + + 9 + +  9 + +   9 + + 9  6 5 NZ'D~&r~| z 5&z 6&967<< !:&7< 1 1 : N& J8~d K8$ Ȝ~l$ o8832967<< !:'@< 1 1 : N& J8~κ K8$ Ȝ~Ȇ$ J%o88329<6$!& 8 K H O L6I289 M& +$ ~$ # M& +$ +~0$ +967<< 7$B N$!P϶| NV!P϶| N܈!P϶ܺ!P϶| N!P϶!P϶P!P϶| N| N| N݂!P϶| Nݴ!P϶883299D967<<!R!PZ& 6 +< 1 18Z&z 6&8832967< 0 else "RIOv4_2.bin" if len(args) > 1: @@ -50,6 +57,8 @@ elif BAUD_NAME: DST = f"RIOv4_2_patched_{BAUD_NAME}.bin" else: DST = "RIOv4_2_patched.bin" +if E0T is not None and len(args) <= 1: + DST = DST[:-4] + f"_e0t{E0T}.bin" d = bytearray(open(SRC, "rb").read()) assert len(d) == 0x10000, f"expected 64KB image, got {len(d)}" @@ -107,6 +116,31 @@ if WIDEN_ACKWAIT: "expected LDAA $317B ; CMPA #imm at $D9E3" patch(0xD9E7, [0x04], [ACKWAIT_VAL]) +# --- edit 5 (--e0thresh=N): threshold-gate the E0 error display ----------- +# Stock behavior: every increment of $3187 (TX-ring overflow), $3184 (reply +# retransmit) or $3185 (reply teardown/give-up) immediately calls $D5F2, +# which paints 'E0'+counters over the F0 banner — so ONE benign give-up +# shows E0000001 forever. Gate the render: skip unless any counter >= N. +# $D5F2 prologue (PSHX/PSHA) has already run when we take over its +# LDX #$2038; the cave may clobber A/X because both exits restore them — +# below-threshold leaves via the routine's own epilogue at $D61D +# (PULA/PULX/RTS), at-or-above resumes the render at $D5F7. +if E0T is not None: + patch(0xD5F4, [0xCE, 0x20, 0x38], [0x7E, 0xE0, 0x00]) # LDX #$2038 -> JMP $E000 + cave = [0xB6, 0x31, 0x87, # $E000 LDAA $3187 (ring overflows) + 0x81, E0T, # $E003 CMPA #N + 0x24, 0x11, # $E005 BCC $E018 (>=N -> render) + 0xB6, 0x31, 0x84, # $E007 LDAA $3184 (reply retransmits) + 0x81, E0T, # $E00A CMPA #N + 0x24, 0x0A, # $E00C BCC $E018 + 0xB6, 0x31, 0x85, # $E00E LDAA $3185 (teardowns/give-ups) + 0x81, E0T, # $E011 CMPA #N + 0x24, 0x03, # $E013 BCC $E018 + 0x7E, 0xD6, 0x1D, # $E015 JMP $D61D (below threshold: epilogue) + 0xCE, 0x20, 0x38, # $E018 LDX #$2038 (displaced instruction) + 0x7E, 0xD5, 0xF7] # $E01B JMP $D5F7 (resume render) + patch(0xE000, [0xFF] * len(cave), cave) + open(DST, "wb").write(d) new_sha = hashlib.sha256(d).hexdigest() # byte-diff report