Texturing (validated live -- game-live-textured.png, the ravine's brown dirt terrain): the wire model is Division's intensity+ramp scheme. action 26 uploads 8-bit intensity texels ([node][nbytes][w][h] + rows; type 13 = texture); texmap (12) references the texture; material (11) references its texmap and a ramp (14: lo/hi RGB); texel color = lerp(lo, hi, i/255). The backend bakes RGBA per material, uploads to GL, maps with wire UVs (stride-5: floats 3-4; stride-8/9: floats 6-7). Serial (directserial fork options, tracked in vpx-device/serialport/): - rxpollus:<us> -- receive poll tick (stock 1ms); 100us discovers inbound bytes ~10x sooner. Validated: sim advanced, camera moved with real RIO. - rxburst:<n> -- stock DOSBox re-serializes received bytes at emulated wire speed (~1ms/byte at 9600) though they already paid wire time on the real cable; a 15-byte analog reply gained ~14ms, blowing the RIO's few-ms ACK window and dropping the game into its 15-second retry fallback (L4CTRL.CPP limit=15.0 //0.2). rxburst:16 delivers buffered bytes 16x faster. game_rio.conf: realport:COM1 rxpollus:100 rxburst:16. Crash-on-advance fixed: *_TSHD.BGF terrain shadows live only in arena subdirs the search path misses; null shadow renderable was dereferenced once the sim moved. All 11 copied into VIDEO/GEO in the working image; game now runs sustained (560+ frames, textured, no crash). Details in RIO-NOTES.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.7 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.
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.