# RIO cockpit controls — passthrough tuning (Phase 5+)

The RIO (Remote Input/Output) is the cockpit control board on **COM1** (the
plasma display is COM2, handled later). DOSBox-X talks to a real RIO through
`serial1=directserial realport:COM1` (`game_rio.conf`). On this host the RIO is
a **Prolific USB-to-Serial adapter enumerated as COM1**.

## The analog-poll latency problem (solved)

The initial RIO *check* request tolerates latency and passed easily. But the
runtime control loop ([L4CTRL.CPP:1145](../CODE/RP/MUNGA_L4/L4CTRL.CPP)) sends
an **analog request** every cycle and, if the reply doesn't return inside its
window, logs `LBE4ControlsManager::Execute, lost RIO analog request` and
re-requests. The board itself refuses/drops comms if the ACK is late by more
than a few milliseconds — a hard real-time deadline.

Empirical result (2026-07-03): a **slower** CPU made the RIO fail **sooner**.
So the dominant latency was how fast the game processes the RIO packet and
emits the ACK, not the serial wiring. The fix:

```
[cpu]
core=dynamic     ; recompiler — many x faster than the 'normal' interpreter
cputype=pentium
cycles=max       ; full host speed
```

With `core=dynamic + cycles=max` the RIO **stays in sync** (the user confirmed
"the rio behaved"). Notes:
- `cycles=fixed 20000` / `150000` and `core=normal` were all too slow — the RIO
  dropped comms, faster at lower speeds.
- The DOSBox-X serial path is already low-latency on **transmit**
  (`directserial.cpp` `transmitByte` calls `SERIAL_sendchar` immediately).
- Prolific PL2303 has no adjustable `LatencyTimer` registry value (unlike
  FTDI); an FTDI adapter set to 1 ms latency would be the lowest-latency host
  option if ever needed.

## Receive-latency fork options (2026-07-03)

Even with the fast CPU, intermittent timeouts remained and the game would
drop into its **15-second analog retry fallback** — the source shows
`limit = 15.0; // 0.2` in L4CTRL.CPP, so the shipped binary re-requests very
slowly once replies stop arriving ("the polling is really slow"). Two
emulator receive latencies were fixed with new `directserial` options:

- **`rxpollus:<us>`** (50–1000, stock 1000): host-port receive poll tick.
  Stock DOSBox-X discovers inbound bytes on a 1 ms tick; 100 µs discovers
  them ~10× sooner. First validated result: the sim advanced and the camera
  moved in the render window.
- **`rxburst:<n>`** (1–64): stock DOSBox re-serializes each received byte at
  emulated wire speed (~1 ms/byte at 9600) even though the bytes already
  paid their wire time on the physical cable and sit in the host buffer — a
  15-byte analog reply gained ~14 ms of artificial latency, single-handedly
  blowing the RIO's few-ms ACK window. `rxburst:16` delivers buffered bytes
  16× faster.

`game_rio.conf` uses `realport:COM1 rxpollus:100 rxburst:16`.

## Crash-on-advance fixed: arena terrain shadows

With the RIO in sync the sim advances and the game crashed dereferencing the
**mech terrain shadow** renderable: `*_TSHD.BGF` live only in
`VIDEO/GEO/ARENA/` + `…/POLAR/`, which the object search path misses, so
`thr_tshd.bgf` failed to load and left a null renderable that the moving sim
eventually touched. Fix: the 11 `*_TSHD.BGF` files are copied from `ARENA/`
into `VIDEO/GEO/` in the working image (reversible, one file each). After
the fix the game runs sustained (500+ frames, no crash).

## Next wall: mission content (a game-data issue, not protocol/RIO)

With the RIO feeding real input, the simulation advances — and then the game
faults:

```
32loader runtime error: Unhandled exception
Exception 0E at 00FF:0040223B
Module 'BTL4OPT.EXE' section 'CODE' offset 0000123B
[..] 8B 4D 0C (mov ecx,[ebp+0C])  8B 11 (mov edx,[ecx])   ECX=2
The instruction referenced illegal address 00000002
```

A near-null pointer (`2`) dereference: a function got `2` where an object
pointer was expected. This is downstream of the failed content load logged
just before it:

```
Entity -1:63 class:42 couldn't figure out how to MakeEntityRenderables
L4VIDEO.cpp couldn't load object thr_tshd.bgf
```

`thr_tshd.bgf` exists in `VIDEO/GEO/ARENA/` and `VIDEO/GEO/POLAR/`, but not the
top `VIDEO/GEO/`. The game builds its object search path from **`objectpath`
entries in the mission notation file** ([L4VIDEO.CPP:1852](../CODE/RP/MUNGA_L4/L4VIDEO.CPP)),
i.e. from the `.egg`. The shipped `test.egg` is only mission parameters and
does not set up the arena object path, so arena-specific objects aren't found;
the entity gets a broken renderable and the sim eventually dereferences it.

**To progress past this needs a real mission/arena `.egg`** (or an object path
manually pointed at the selected arena's `VIDEO/GEO/<ARENA>` subdir). That is
content/mission configuration, separate from the VPX protocol, the renderer,
and the RIO — all of which now work.
