Lighting: stride-8/9 vertices carry a normal (floats 3-5, uv at 6-7). The
backend transforms normals by the instance rotation and lights with a single
directional sun (GL_LIGHT0, smooth, two-sided, GL_COLOR_MATERIAL). Terrain
and hulls shade instead of reading flat.
LOD: the lod flush has no switch distances -- the host keeps the active LOD
at the child-list head and re-orders over the wire, so children[0] is right
and LOD changes come through as the sim runs.
The crash that halted the game once the RIO drove the sim was not ours: fault
at 0047E1D1 writing 0xFFFFFFFF is the RIO driver's DISABLE_AND_DIE debug
macro (PCSPAK.ASM, DIE_ON_ERROR=1), which deliberately faults on a serial-tx
anomaly -- error 3 = body char >0x7F during a RIO packet, i.e. a glitch on a
board reset. Release build (DIE_ON_ERROR=0) compiles it out and recovers
(and al,07Fh). patch_btl4opt.py NOPs all 12 sites by signature (with .orig
backup) = release behavior; game then survives RIO resets (1200+ frames).
(ALPHA_1 is git-ignored, so the tool ships, not the patched binary.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
The full DPL hierarchy the game uses (vs flyk's flat scene) is now decoded
and rendered:
- stride-aware set_geom_verts (header word 3 = floats/vertex: 3/4/5/8/9;
mech meshes carry normals + UVs)
- instances are list_add children of DCS nodes; instance flush field 4 ->
object; object->lod->geogroup->geometry; dcs_link builds the articulation
tree of 4x4s (payload floats 4..19, row-major, row 3 = translation)
- game world is y-down (DCS matrices carry a reflection); projection flips
x (Division mirror) and y
render_game.py reconstructs a captured game stream offline: the mission
arena (10km, 246 instances, 330 geometries), the player's Thor at the
camera, six enemy mechs 1.5km north -- game-mech-decoded.png shows one with
real hull/armor/glass materials; game-cockpit-decoded.png the cockpit view.
The live backend (vpxlog.cpp) gained the same traversal and now draws the
game's out-the-window view in real time (game-live-gl.png): sky, arena
floor to the horizon, own gun barrels at frame bottom.
Next: texturing (action-26 texel maps + UVs), lighting from wire normals,
per-frame articulation once the RIO drives the sim.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The VDB is the ISA card that fans the PC's Cirrus Logic framebuffer to the six
secondary cockpit displays (5 mono + 1 color), dividing the pixel clock. From
the driver (CODE/RP/MUNGA_L4/L4SVGA16.ASM, L4VB16.CPP; "Adam's port decoder
design" -- Adam G., VWE hardware):
0x300/0x308/0x310 three VGA-DAC-style palette groups
0x31A / 0x319 splitter high-color clock divider ON / OFF
New emulated VDB device in vpxlog.cpp at I/O 0x300-0x31A (active when logging,
disable with VDB=0): decodes palette write-address/data/mask/read-address and
the clock strobes, recording palette contents + splitter state so the six-
display encoding can be decoded later. Validated: with gauges enabled
(gauge.conf, setenv arg4=g) the game issues "splitter clock ON (0x31A)" ->
captured. The board is write-only (game never reads it, per driver + owner),
so its absence is not the crash.
Crash diagnosis (BTL4OPT.EXE CODE+0x123B): the faulting routine is a heap
free() with boundary-tag coalescing; it was handed a garbage block pointer
(value 2). Symptom of upstream corruption, reached only when the RIO is in
sync and the sim advances. HISTORY.md gains a "Cockpit display hardware -- the
VDB" section with the attribution.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Root cause: vpx_max_postboot_acks=200, a Phase-2 bring-up guard, capped how
many post-boot replies the device would ever feed. A real BattleTech session
issues an unbounded stream of sync/frame/render replies and aborted with
"velocirender_receive timed out - sends_wo_rcv" at exactly the 200th ack
(143 sync + 55 frame-ack + 2 render). Cap is now effectively unlimited
(0x7fffffff; override VPX_MAX_ACKS for diagnostics).
After the fix the full game (game.conf, RIO disabled, cycles=max) runs
indefinitely: 2500+ syncs, 1264+ frame-acks, no abort, into L4VIDEO content
load (Thor mech + terrain from ALPHA_1/REL410/BT/VIDEO/GEO -- 841 real .bgf
models; test.egg is only mission params). Window still shows background:
the game uses the full DPL hierarchy (instance/object/lod/geogroup + DCS
transforms) which the flat flyk-tuned scene walk doesn't traverse yet -- that
is Phase 3d, documented in PHASE3-PROGRESS.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The emulated VPX board now draws frames in real time. The FIFO burst
assembler feeds an in-process DPL scene store; each vr_draw_scene publishes a
frame snapshot to a dedicated WGL window thread that renders it fixed-function.
- vpxlog.cpp: scene_burst() decode + VScene store + rt_main GL window thread;
glFrustum from the view-node window rect, glScalef(-1,1,1) for Division's
mirrored screen-x, action-31 camera as modelview. No build change (opengl32
already linked).
- Validated: flyk divrgb.scn opens the "VPX VelociRender (emulated)" window
and draws the SMPTE bars live via the real camera.spl spline camera
(divrgb-live-gl.png), matching the offline render_capture.py decode.
- Game path (alpha1.conf) opens the window + draws background but hits the
pre-existing production btdpl.ini vr_sync timeout (sends_wo_rcv) -- not a 3b
issue. Remaining work tracked in PHASE3-PROGRESS.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- vpxlog.cpp: VPX_FIFODUMP=<path> records every FIFO burst ('VPXM' records)
- decode_fifodump.py: action census + payload dumps of a capture
- render_capture.py: reconstruct the DPL scene graph from a capture and
software-render each draw_scene frame (camera, view, materials, geometry
all taken from the wire)
- divrgb.conf + divrgb.fifodump: flyk divrgb.scn capture fixture
- divrgb-decoded.png / divrgb-frame0.png: first images ever produced from
the Rel 4.10 VPX protocol without a real board -- the textbook SMPTE
color-bar pattern, validating verts/conns/materials/camera in one shot
- PHASE3-PROGRESS.md: the established Rel 4.10 wire protocol (action map,
node types, message layouts); RENDER-HARNESS.md updated
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Disassembled BTL4OPT.EXE (PE-in-DOS32RTM) to crack the last unknowns:
- velocirender_sync (@0x48D220): sends action 0x2D with a data token and
checks cmp token, reply.node[0] (the 'unexpected action' text only prints
the action; the real check is the echoed token). Device now echoes the
token in node[0]. Sync passes.
- Speculative-poll gating: only feed a reply after consecutive empty polls
(a blocking inRecord spin), not single handle_iserver_stuff() checks.
- Frame-ack: velocirender_frameack expects action 9 (vr_draw_scene); device
tracks outstanding draw_scene and replies 9. Frame-ack passes.
The game now passes sync, loads renderer config, builds the scene
(create/flush/list_add/draw_scene), completes frame-ack, and enters its own
content loader (L4VIDEO.cpp) loading BattleTech models. Remaining issues are
content packaging/paths and an empty DPL config -- not VPX protocol. The
board emulation is functionally complete for boot + scene setup + frame-ack.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
RIO (Phase 5) validated with real hardware: with a physical RIO on
COM1 and serial1=directserial realport:COM1, the game's 1996 RIO
driver talks to the board — the 'RIO never came back' error is gone
and expected activity was seen on the RIO board.
VPX FIFO fast path decoded (OUTSW.ASM + capture): after i860 boot the
render protocol pumps 16-bit words to a FIFO data port seen as
0x154/0x155 byte writes, payload starting with the 4-byte action. The
device now extracts FIFO message actions (confirmed vr_init=0 with the
args string, vr_create=1 with node type 0x2D) and echoes replies with
handler-specific overrides from board source VR_REMOT.C (init/statistics
reply action = 1).
Blocked on one unknown: velocirender_sync checks the init reply against
a constant that is neither 0 nor 1, so the Rel 4.10 board differs from
the DPL3 dev source. Needs the Rel 4.10 LIBDPL source or disassembly of
BTL4OPT.EXE at the format string (file offset 0x107772). Device has a
VPX_INIT_REPLY experiment hook for candidate values.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The VPX responder device now drives production BattleTech v4.10
through the full transputer/i860 boot, far past the Phase 1 wall:
- iserver boot handshake solved: feeds 3 well-formed 'version' (tag 42)
iserver requests, satisfying startup_handshake()'s 3 transactions.
- i860 download solved: parses the outbound framed renderer messages
(vr_860args/code/data/bss) and absorbs them, staying byte-aligned.
- echo-action reply model validated against board source
(VRENDER/VR_REMOT.C reply() echoes the received action); device
tracks and echoes the last outbound action generally.
Remaining sub-protocol identified and characterized: after i860 boot
the host switches to a FIFO fast path (OUTSW.ASM: 0x40 tag + REP OUTSW
16-bit words to FIFO port 0x154/0x155, gated by ok_to_fifo at 0x160).
The current build stops at velocirender_sync because the device only
implements the slow byte path. PHASE2-PROGRESS.md documents the exact
next steps (FIFO transport + render loop -> Phase 3 OpenGL backend).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Built DOSBox-X from source with a custom VPX link-adapter logging
device (vpx-device/vpxlog.cpp) and captured the game's outbound boot
sequence. Findings:
- Register map confirmed against LINKIO.C: outputData 0x151,
outputStatus 0x153 (polled bit0), resetRoot 0x160, analyseRoot 0x161.
- Reset preamble captured exactly (analyse=0, reset 0->1->0 + status inits).
- The game streams 85298 bytes to outputData that are BYTE-FOR-BYTE
identical to VRENDMON.BTL; first byte 0xF0 = transputer boot-from-link
primary-bootstrap length. Protocol stage 1 (reset + monitor download)
fully characterized; no hidden bulk/interrupt path.
- Production cockpit dump ALPHA_1 added (git-ignored): its BT is v1.1.0.6
with a VRENDMON.BTL byte-identical to the captured stream, so this
result reflects the exact cockpit software. ALPHA_1 is the reference
image going forward (carries RP + production pod/network boot chain).
Adds analyze_capture.py, capture.conf, PHASE1-RESULTS.md. DOSBox-X
source tree and capture artifacts are git-ignored.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>