Final gate passed: 120s mash at 549 presses/min sustained (heaviest stress run of the campaign) — zero wedges, counters flat, NAK 0, all 247 resends healed. RIOv4_3rc1 renamed to RIOv4_3.bin (same bytes, sha dc59bd51cae3...); README/ANALYSIS promote it, production table updated (native-game cabinets -> RIOv4_3.bin; FastRIO 4.3 variant is build-on-demand with the documented flag set + acceptance ladder). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
7.9 KiB
Display Board 1408 — outputs, and what the RIO shows on it
Complete definition of the RIO External Display board (P/N 1408 Rev. 1,
DSP_1408 sheet 7), from the schematic, the board photos, the GAL decode,
and the firmware disassembly (RIOv4_2.disasm.asm).
Outputs: light only — the board is electrically write-only
No signal leaves the board toward the RIO or anything else. J1 has no
readback line, matching the GAL's write-only DSP_SEL* decode, and the
PC↔RIO protocol has no display command — only the firmware writes it.
1. Eight multiplexed 7-segment digits (U2–U9, LTS313A, red, common-cathode)
The ICM7228B (U1) does all driving. HEX/B/SHD is strapped high via 2.7 K
(R1): hexadecimal decode mode, always enabled — each digit renders 0–F
plus a decimal point; no raw-segment access exists as built.
- Segment bus — U1 pins 16, 17, 20, 18, 21, 22, 23, 15 drive
SEGA–SEGG,SEGDP, shared by all digits (digit pins A–G, DP = 10, 9, 8, 5, 4, 2, 3, 7). Segments source the multiplexed current (~20 mA peak). - Digit commons — U1 outputs
DSP1–DSP8(pins 4, 25, 3, 1, 26, 2, 27, 24) each sink one digit's tied-together commons (CC1+CC2, pins 6+1).DSP1→U2 (leftmost) …DSP8→U9 (rightmost). - Multiplex scanning is internal to the ICM7228; the host only writes digit registers.
From the 68HC11 the digits are bytes at $A000–$A007 (AS0-2 =
A0–A2, strobed by DSP_SEL* = E & /R/W-qualified, arriving as the
7228's /WR on J1 pin 7). Data format per write: bits 0–3 = hex value
(MD[00:03]→ID0-3), bit 4 = decimal point (MD[04]→ID7; DP lights
when the bit is low — the firmware always sets it, so the DP is never
used). $A000 = leftmost digit (U2), $A007 = rightmost (U9); the
firmware's render buffer ($2038, routine $CB49) fills right-to-left so
multi-digit hex numbers read naturally.
2. Twelve passive monitor LEDs on the "IBM printer port" header (J2) — an input, not an output
J2 (26-pin IDC, standard LPT-ribbon mapping to a DB25) is electrically
isolated from the display circuit; its DD nets touch nothing but LEDs
and 1 K resistors referenced to the PC's own ground (IBM_GND). With a PC
parallel port plugged in, the LEDs show: D0–D7 = data bits (J2 pins
3, 5, 7, 9, 11, 13, 15, 17), D8 = /STROBE (pin 1), D9–D11 =
/AUTOFD, /INIT, /SELECTIN (pins 2, 6, 8). The LPT status returns are
strapped to ground so a PC streams without waiting. It is a built-in
parallel-port activity monitor / bench tool, powered entirely by the PC.
3. Nothing else
J1 (14-pin, from the RIO's EXTERNAL DISPLAY port) is all input:
MD[00:04] on pins 2, 4, 6, 8, 10; AS0-2 on 1, 3, 5; BF_DSP_SEL* on
7; GND on 9, 11, 13; VCC on 12, 14.
What the RIO firmware displays (v4.2)
All display writes go through $CB49 (buffer render) or five canned
patterns at $CB7C–$CC52, driven from four contexts: initialization,
the built-in test mode, the serial error/crash displays, and
the CheckRequest self-test (all below). During normal, healthy
gameplay the display is static.
Normal operation: F0000000 — written at cold boot ($C068) and warm
re-init ($C64B), then left alone.
Test mode — entered by typing the keypad sequence A, 9, E on a
cockpit keypad ($C1CB tracks the sequence at $20F8); entry sets flag
$2421, shows F1000000, and notifies the PC (TestModeChange, cmd
0x8C — see PROTOCOL.md). Within test mode the next
keypad digit selects a sub-test; F ends a sub-test (back to
F1000000), D exits test mode (back to F0000000).
Display format convention: left pair = test number, right = value, zeros as filler; the decimal points are never used.
| Key | Display | Test |
|---|---|---|
| (entry) | F1000000 |
test-mode banner |
0 |
00000000 |
encoder-counter reset: presets the five 16-bit accumulators to $FAFA and re-reads all HCTL-2016s ($C446) |
1 |
0100hhll |
axis test, channel 01 — counter at $A028/$A038 |
2 |
0200hhll |
axis test, channel 02 — $A020/$A030 |
3 |
0300hhll |
axis test, channel 03 — $A022/$A032 |
C |
0C00hhll |
axis test, channel 0C — $A024/$A034 |
4 |
0400hhll |
axis test, channel 04 — $A026/$A036 |
5 |
050000nn |
button test — nn = index of the pressed input (scan at $D004 over the pod input state at $2152), with lamp feedback |
6 |
06000003 |
lamp test, pattern 3 ($D34A) — right digit is the pattern code |
7 |
07000001 |
lamp test, pattern 1 ($D370) |
8 |
080000kk |
keypad test — kk = keypad status byte $204B, refreshed while scanning ($CC7E) |
In the axis tests hhll is the live 16-bit quadrature count in hex
(high:low = $201D:$201E, alternating with the raw latched bytes
$2026:$2025 as the loop runs) — spin the stick/throttle/pedal encoder
and watch the number move. The channel IDs are literally the keypad keys
that start them, which is why the fourth axis reads 0C.
The CheckRequest self-test leftover: 04000000
The host CheckRequest (0x80) triggers a full self-test in the
handler at $C5A6: it sets the same display flag test mode uses
($2421), brackets itself with TestModeChange 0x8C notifications,
flashes a lamp pattern, scans the pods, and sweeps the five encoder
channels — rendering each channel's cc00hhll frame as it goes. It
clears the flag on exit but never repaints the display, so after
every status check the digits are left showing the last frame:
channel 4, usually 04000000 with idle encoders. It is a stale
snapshot (axis movement does not update it — bench-confirmed
2026-07-19), purely cosmetic, and the board keeps operating normally.
Any reset repaints F0000000.
Error displays (the third use)
Two more display writers live in the serial machinery — the display is also the RIO's error annunciator:
E0serial-diagnostics readout ($D5F2) —E0xxyyzzwherexx= TX-ring overflow count ($3187, outbound byte dropped because the ring was full,$D66A),yy= give-up count ($3184,$D96A— reply cycles whose 5 timeout retransmits all went unACKed),zz= reply-teardown count ($3185,$DA2F— any reply cycle that needed at least one retransmission; the counter behind a loneE0000001). Semantics bench-confirmed 2026-07-19: display readE0000105after one give-up + five teardowns. Each increment site immediately re-renders, so the display live-updates as serial trouble accumulates. If a cockpit's display readsE0…instead ofF0000000, the serial link has been dropping or retrying. Note there is no threshold in stock firmware — the first event paintsE0000001permanently; the RIO 4.3 firmware (make_patch.py --e0thresh, see RIOv4_2-ANALYSIS.md) keeps the display quiet until a counter reaches 5, and repaints honestly after every check self-test.dEAdcrash screen ($DAB8, loop at$DAB2) — the TX-ISR ring drain ($D8C2) looks each outgoing command byte up in an 8-entry dispatch table at$3158; an unmatched byte means internal state corruption, and the firmware jumps to an infinite loop that renders#$DE #$AD→dEAdfollowed by RAM bytes$0000/$0001(nothing in this build writes them — vestigial debug data). The board is dead until reset; there is no watchdog to rescue it (the MAX690's WDI is not connected to the CPU and no strobe code exists in the firmware).
Cross-references
- README.md — board family, photos, schematic page map
- gal/README.md —
DSP_SEL*decode ($A000-$A00F, write-only) recovered from the GAL fuse map rio-firmware/RIOv4_2.disasm.asm— display routines$CB20/$CB36/$CB49(buffer render),$CB7C(F0),$CBA7(F1),$CBD2/$CBFD(lamp-test banners),$CC28(zeros), test dispatcher$C241, entry-sequence tracker$C1CB