# 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 three contexts: initialization,
the built-in **test mode**, and the **serial error/crash displays**
(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](../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`.

## Error displays (the third use)

Two more display writers live in the serial machinery — the display is
also the RIO's **error annunciator**:

- **`E0` serial-diagnostics readout** (`$D5F2`) — `E0xxyyzz` where
  `xx` = 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 lone `E0000001`).
  Semantics bench-confirmed 2026-07-19: display read `E0000105` after
  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 reads `E0…` instead of `F0000000`, the serial link
  has been dropping or retrying. Note there is **no threshold in stock
  firmware** — the first event paints `E0000001` permanently; the
  `_e0t5` patched images (`make_patch.py --e0thresh`, see
  [RIOv4_2-ANALYSIS.md](../../rio-firmware/RIOv4_2-ANALYSIS.md)) keep
  the display quiet until a counter reaches 5.
- **`dEAd` crash 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` → **`dEAd`** followed 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](README.md) — board family, photos, schematic page map
- [gal/README.md](gal/README.md) — `DSP_SEL*` decode (`$A000-$A00F`,
  write-only) recovered from the GAL fuse map
- [`rio-firmware/RIOv4_2.disasm.asm`](../../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`
