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>
9.2 KiB
Phase 2 — Boot Protocol Emulation: Progress
Status: boot handshake + i860 download + FIFO transport solved; blocked on one
unknown constant in velocirender_sync. Phase 5 (RIO) validated on real hardware.
Update 2 (2026-07-02): FIFO transport working; RIO validated
-
Phase 5 — RIO passthrough validated with real hardware. A physical RIO was attached to the host's COM1 (Prolific USB-serial). With
serial1=directserial realport:COM1(seerio.conf) andL4CONTROLS=RIO, the game's 1996 RIO driver (L4RIO.CPP, 9600 baud) talks to the real board: theRIO never came back from check request?error disappears, and the expected activity was observed on the RIO board. The cockpit control path works end-to-end, unmodified, through DOSBox-X serial passthrough. -
FIFO fast path decoded and implemented (send side). The board is dual-register: the render protocol switches to a FIFO channel after the i860 boots. From
OUTSW.ASM+ the capture: each message writes anoutswtag byte tooutputData, then pumps the payload as 16-bit words to the FIFO data port, which DOSBox-X sees as byte writes to 0x154 (low) / 0x155 (high). The payload begins with the 4-byte action. The device now parses this and extracts the action of each FIFO message (confirmed:vr_init=0 with the/device~…args string, thenvr_create=1 with node-type0x2D). -
Handler-specific reply actions (from board source).
velocirender_receivereads replies on the slow path (already served); the device echoes the sent action, with per-handler exceptions found inVRENDER/VR_REMOT.C:velocirender_initandvelocirender_statisticsdo*data = 1, so their reply action is 1, not the sent action. The device implements this map. -
Remaining blocker — one constant. The game reaches
velocirender_sync, which sendsvr_init(action 0) and checks the reply action against a specific value that is neither 0 (echo) nor 1 (the DPL3 board'sinitreturn) — so the Rel 4.10 board returns a different value than the DPL3 dev-tree source. Determining it needs the Rel 4.10 LIBDPL/VRENDER source or a disassembly ofBTL4OPT.EXEaround the format string at file offset 0x107772 ("unexpected action %d returned in velocirender_sync"). The device has an experiment hook (VPX_INIT_REPLY=<n>) to try candidate values.
Original Update 1 analysis (handshake + i860 download) follows.
Status: substantial progress; boot handshake + i860 download solved; render transport (FIFO fast path) identified as the remaining sub-protocol.
The VPX device (vpx-device/vpxlog.cpp, env VPX_RESPOND=1) now drives the
production BattleTech v4.10 (ALPHA_1 image) through the entire transputer/i860
boot sequence — far past the Phase 1 wall — and reaches the live renderer
protocol. Each layer below was solved by reading the normative sources on the
drive (sda4/DPL3/VR_COMMS.C, DPL_HOST.C, VRENDER/VR_REMOT.C, LINKIO.C,
OUTSW.ASM) and iterating against the running binary.
Solved
1. iserver boot handshake
After the monitor (VRENDMON.BTL) download, boot_xputer() calls
startup_handshake(), which does 3 iserver transactions (Phil's comment in
VR_COMMS.C: "the C run-time boots up and does 3 iserver transactions"). Each
is a receive_protocol() read of a 4-byte little-endian length/route word
(bit31=iserver, low16=payload length) + payload, answered by iserver_action();
only success is checked. The device feeds 3 well-formed iserver "version"
requests (tag 42, no request-content dependency) and the handshake completes.
2. i860 renderer download
boot_860() then streams the i860 image (VRNOSTEX.MNG) as framed renderer
messages on the slow byte path: vr_860args (21), vr_860code (18, ×118),
vr_860data (19, ×37), vr_860bss (20, ×9). The device parses the outbound
frame stream ([length_word:4][action:4][data], nb = length_word & 0xffff)
and absorbs them, staying byte-aligned throughout.
3. The echo-action reply model (validated against board source)
Renderer request/reply calls (velocirender_create/delete/flush/sync in
DPL_HOST.C) send an action and require the same action echoed back. The
board side confirms this exactly: VRENDER/VR_REMOT.C remote_velocirender()
replies with its data buffer whose first word is still the received action
(reply() → dN_send). The device tracks the last outbound action and echoes
it; this is the general model for every render call, not a one-off.
The remaining sub-protocol: the FIFO fast path
After the i860 boots, the host stops using the slow byte path and switches to a
FIFO fast path for the render protocol. This is why the current build still
stops at velocirender_sync — the device only implements the slow path
(0x150–0x153).
Discovered mechanics (from the capture + OUTSW.ASM + LINKIO.C):
- The VPX board is dual-register: beyond the C012 slow link
(0x150 inputData / 0x151 outputData / 0x152 inputStatus / 0x153 outputStatus),
there is a FIFO data port reached at 0x154/0x155 (observed) and an
ok_to_fifostatus at 0x160. send_FIFO_protocol()/velocirender_transmit()(whenuse_fifo) call the assemblyoutsw()(OUTSW.ASM): it writes a0x40"use fifo" tag byte, thenadd dx,3andREP OUTSW— pumping the message as 16-bit words into the FIFO port. In the capture these land as alternating byte writes to 0x154/0x155 and decode to the init/args string (e.g./device~0x150~/video~svga~…).- The host switches to
use_fifoonce the board assertsok_to_fifo(fifo_ok_status()reads 0x160 bit0); see the commented wait loop inboot_xputer().
Next step (concrete)
Extend the device to implement the FIFO transport:
- Answer
ok_to_fifo(0x160 bit0) at the right time so the host enablesuse_fifo. - Accept FIFO writes: the
0x40tag, then word-stream data at the FIFO port (0x154/0x155). Reassemble into[length_word][action][data]messages (same framing as the slow path). - Serve FIFO reads for replies:
velocirender_receive()in FIFO mode reads the reply (length_word +[action][node]) from the FIFO/status ports. Echo the sent action (model already validated), returning meaningful node handles forvr_createso latervr_delete/geometry ops have valid remotes. - Then the render loop (
vr_draw_scene+ frame-ack,velocirender_frameack()) — the Phase 3 boundary, where geometry/material/texture commands can be decoded into the OpenGL backend (formats already implemented inrestoration/divformats.py).
Reproduce
set VPXLOG=C:\VWE\TeslaRel410\emulator\vpxresp.txt
set VPX_RESPOND=1
emulator\src\src\dosbox-x.exe -conf emulator\respond.conf
Console progression now: BattleTech v4.10 → BTL4Application → RIO check →
(handshake, i860 download succeed) → unexpected action N in velocirender_sync
(the FIFO-path render handshake, the next target). The log's # note lines
trace the device's decisions (handshake requests fed, frames parsed, actions
echoed, mystery ports resolved to 0x154/0x155).
Update 3 (2026-07-03): full VPX protocol working — game reaches content load
Disassembling BTL4OPT.EXE (a PE-in-DOS32RTM binary; CODE @VA 0x401000,
velocirender_sync @0x48D220) cracked the last protocol unknowns and the
emulated board now carries the game through the entire boot + render
protocol into its own game logic:
-
velocirender_sync (was the blocker): from the disassembly, it sends action 0x2D (a Rel4.10 sync/ping outside the DPL3 enum, which is why my
<24filter dropped it) with data[token,0], then checkscmp token, reply.node[0]at 0x48D271 — the "unexpected action %d" message only prints the received action; the real test is the echoed data token. Device now replies to 0x2D withnode[0] = token. Sync passes. -
Speculative-poll gating:
velocirender_transmitcallshandle_iserver_stuff()which polls inputStatus once and proceeds if not-ready. Feeding a reply into those polls corrupted alignment. The device now only feeds a reply after several consecutive empty polls (a blockinginRecordspin), not single speculative checks. -
Frame-ack:
velocirender_frameackexpects a message with action 9 (vr_draw_scene). The device tracks an outstandingdraw_scenesend and replies 9 to the frame-ack. Frame-ack passes.
With these, the game now: passes sync, loads the renderer config, builds the
scene (create/flush/list_add/draw_scene), completes frame-ack, and enters its
own content loader (L4VIDEO.cpp), where it tries to load BattleTech models
(buttee.bgf, mslr.bgf, …). The remaining errors are content packaging /
paths (the minimal test.egg lacks these objects) and an empty renderer
config (dpldflt.ini) — not VPX protocol issues.
The VPX board emulation is functionally complete for boot + scene setup +
frame-ack. Next: (a) run against a full mission/content set (a real .egg
or the pod's content tree) so objects load; (b) provide a proper DPL config;
(c) Phase 3 — decode the geometry/material/texture commands already flowing over
the FIFO into the OpenGL backend (formats in restoration/divformats.py) to put
actual pixels on screen.