- VPX_COCKPIT=1 places the four VGA heads as borderless windows:
Division main 800x600 @ 0,0; radar (win0) 640x480 @ 800,0; MFD heads
win3/win4 640x480 @ 1440,0 / 2080,0 (heads 3+4 are driver-spanned
into one 1280x480 canvas). VPX_MAIN / VPX_WIN<g> = x,y[,w,h]
overrides any window without a rebuild; default debug layout kept.
- Remove exploratory win1/win2 (bits 0-7 via static palettes); keep the
0/3/4 numbering so win<g>.bmp dump names stay stable.
- Sync commit copy with the build tree: per-head palette decode in
pal_draw() and device-side BMP capture (VPX_DUMPDIR).
- RIO-NOTES: document the PCSPAK DISABLE_AND_DIE patch (error-3 crash
on RIO resets); restore the terrain-shadow section heading it ate.
- Add gauge_rio{,_log}.conf launch configs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
6.0 KiB
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) 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/150000andcore=normalwere all too slow — the RIO dropped comms, faster at lower speeds.- The DOSBox-X serial path is already low-latency on transmit
(
directserial.cpptransmitBytecallsSERIAL_sendcharimmediately). - Prolific PL2303 has no adjustable
LatencyTimerregistry 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:16delivers buffered bytes 16× faster.
game_rio.conf uses realport:COM1 rxpollus:100 rxburst:16.
The crash-to-desktop: PCSPAK's DISABLE_AND_DIE (patched)
The recurring hard fault (Exception 0E, write to 0xFFFFFFFF-ish, e.g. at
BTL4OPT CODE+0x7D1D1 with EAX=3) is deliberate: the RIO serial packet
driver (CODE/RP/MUNGA_L4/PCSPAK.ASM) was shipped built with
DIE_ON_ERROR equ 1, which compiles a DISABLE_AND_DIE <code> debugging
macro at 12 error sites — it retracts the UART IRQ, EOIs the PIC, and then
"crashes loudly" by writing the error code to address 0xFFFFFFFF.
Our crash is error 3 (PCSPAK.ASM:1630): a byte ≥ 0x80 found in the TX
ring body (the protocol reserves high-bit bytes for commands). It gets hit
via the ACK/NAK-interrupt → restart path — exactly what physical RIO resets
and timeout storms exercise. On clean pod serial timing this never fired; on
a USB-serial rig with resets it does.
The source's release configuration (DIE_ON_ERROR equ 0) makes the macro
empty and the code recovers (the next instruction masks the byte with
and al,7Fh and continues). We reproduce that intended behavior by patching
all 12 die sequences (50 52 BA FF FF FF FF B8 xx 00 00 00 89 02 → NOPs) in
the working image's BTL4OPT.EXE. Original preserved as BTL4OPT.EXE.orig.
Error-code map (from PCSPAK.ASM): 0/1/2 rx framing states, 3 tx body >0x7F,
4/5 tx state.
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),
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.