diff --git a/README.md b/README.md index 4d5bf5b6..71a8defe 100644 --- a/README.md +++ b/README.md @@ -40,3 +40,13 @@ disassemble as 68HC11 from `$C000` with the vector entries as roots, find 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) + +- **`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`. diff --git a/RIOv4_2-ANALYSIS.md b/RIOv4_2-ANALYSIS.md index d95d3f52..59443dd1 100644 --- a/RIOv4_2-ANALYSIS.md +++ b/RIOv4_2-ANALYSIS.md @@ -148,6 +148,31 @@ three regions with no downstream desync: Flash `RIOv4_2_patched.bin` directly to the W27C512 (DIP-28). This is static verification only; dynamic proof still needs the burned chip. +### 31250-baud variant (2026-07-17) — `RIOv4_2_patched_31250.bin` + +`make_patch.py --baud31250` adds **one byte** to the wedge patch: the SCI +init at `$D62A` (`LDAA #$30 ; STAA $102B BAUD`) becomes `LDAA #$02`. +BAUD `$30` = prescale ÷13, divider ÷1 → 2 MHz E-clock / (16·13) = 9615 +("9600"); `$02` = prescale ÷1, divider ÷4 → 2 MHz / (16·4) = **31250 +exactly** (3.3× faster; analog exchange ~15 ms → ~4.6 ms; 96-lamp burst +~0.4 s → ~0.12 s; every collision window shrinks 3×). The BAUD write +confirms the 8 MHz crystal / 2 MHz E-clock — 19200/38400 are unreachable. + +sha256 `9f866cf353d04906e1d3e3847b6375eaae251c987b656f68f093e61ba1bd545b`, +24 bytes changed (the 23 wedge bytes + `$D62B: 30→02`). Re-disassembly +diff vs the classic patched image shows exactly the one operand line. + +**Caveats:** 31250 is non-standard — FTDI-class USB adapters make it +exactly, classic 16550 UARTs cannot; the **native games still speak +9600**, so a 31250 chip is bench/RIOJoy-only until Firestorm/Red Planet +get BTL4OPT-style baud patches; and the 2 MHz HC11's RX ISR has ~640 +cycles/byte at this rate (fine) — do NOT be tempted to `$01`/62500 or +`$00`/125 k without cycle-counting the `$D630` ISR worst path first. +PC side: `SerialPortTransport` takes a baud parameter and the +monitor/mash tools accept `--baud 31250`. Validate the wedge patch at +9600 FIRST (one variable at a time), then A/B this chip with +`--mash COM1 300 --label patched-31250 --baud 31250`. + ### Validation plan (when a chip is available) Burn the two edits to a W27C512, socket it (preserve the original AMD chip), then run the `RIO_TAP` two-handed 8-button mash test. Expect: no diff --git a/RIOv4_2_patched_31250.bin b/RIOv4_2_patched_31250.bin new file mode 100644 index 00000000..f3a374b8 --- /dev/null +++ b/RIOv4_2_patched_31250.bin @@ -0,0 +1,38 @@ +$"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< RIOv4_2_patched_31250.bin): the init at $D62A loads BAUD ($102B) with +$30 (prescale /13, divider /1 -> 2MHz E / (16*13) = 9615). $02 selects +prescale /1, divider /4 -> 2MHz / (16*4) = 31250 exactly. One operand +byte at $D62B. NOTE: 31250 is a non-standard rate — FTDI-class USB +adapters produce it exactly; classic 16550 UARTs cannot. The native +games still expect 9600, so a 31250 chip is bench/RIOJoy-only until the +games are patched. + Each edit asserts the exact original bytes first, so a wrong assumption aborts instead of corrupting the image. Address == file offset. """ import sys, hashlib -SRC = sys.argv[1] if len(sys.argv) > 1 else "RIOv4_2.bin" -DST = sys.argv[2] if len(sys.argv) > 2 else "RIOv4_2_patched.bin" +BAUD31250 = "--baud31250" in sys.argv +args = [a for a in sys.argv[1:] if not a.startswith("--")] +SRC = args[0] if len(args) > 0 else "RIOv4_2.bin" +DST = args[1] if len(args) > 1 else ( + "RIOv4_2_patched_31250.bin" if BAUD31250 else "RIOv4_2_patched.bin") d = bytearray(open(SRC, "rb").read()) assert len(d) == 0x10000, f"expected 64KB image, got {len(d)}" @@ -52,6 +64,14 @@ patch(0xDA21, [0x7F,0x25,0x21, 0x7F,0x25,0x22, 0x01,0x01,0x01,0x01,0x01,0x01,0x01]) assert d[0xDA2E] == 0x39, "RTS at $DA2E must be intact" +# --- edit 3 (--baud31250 only): SCI 9600 -> 31250 -------------------------- +# $D62A 86 30 LDAA #$30 (SCP=/13, SCR=/1) -> 86 02 (SCP=/1, SCR=/4) +# then STAA $102B (BAUD). 2MHz E-clock: 2e6/(16*13)=9615 -> 2e6/(16*4)=31250. +if BAUD31250: + assert d[0xD62A] == 0x86 and bytes(d[0xD62C:0xD62F]) == bytes([0xB7,0x10,0x2B]), \ + "expected LDAA #imm ; STAA $102B at $D62A" + patch(0xD62B, [0x30], [0x02]) + open(DST, "wb").write(d) new_sha = hashlib.sha256(d).hexdigest() # byte-diff report