Files
CydandClaude Fable 5 939ea17d17 Phase 2 COMPLETE: full VPX protocol working; game reaches content loader
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>
2026-07-03 00:04:33 -05:00

9.2 KiB
Raw Permalink Blame History

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 (see rio.conf) and L4CONTROLS=RIO, the game's 1996 RIO driver (L4RIO.CPP, 9600 baud) talks to the real board: the RIO 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 an outsw tag byte to outputData, 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, then vr_create=1 with node-type 0x2D).

  • Handler-specific reply actions (from board source). velocirender_receive reads replies on the slow path (already served); the device echoes the sent action, with per-handler exceptions found in VRENDER/VR_REMOT.C: velocirender_init and velocirender_statistics do *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 sends vr_init (action 0) and checks the reply action against a specific value that is neither 0 (echo) nor 1 (the DPL3 board's init return) — 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 of BTL4OPT.EXE around 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 (0x1500x153).

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_fifo status at 0x160.
  • send_FIFO_protocol() / velocirender_transmit() (when use_fifo) call the assembly outsw() (OUTSW.ASM): it writes a 0x40 "use fifo" tag byte, then add dx,3 and REP 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_fifo once the board asserts ok_to_fifo (fifo_ok_status() reads 0x160 bit0); see the commented wait loop in boot_xputer().

Next step (concrete)

Extend the device to implement the FIFO transport:

  1. Answer ok_to_fifo (0x160 bit0) at the right time so the host enables use_fifo.
  2. Accept FIFO writes: the 0x40 tag, then word-stream data at the FIFO port (0x154/0x155). Reassemble into [length_word][action][data] messages (same framing as the slow path).
  3. 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 for vr_create so later vr_delete/geometry ops have valid remotes.
  4. 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 in restoration/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.10BTL4Application → 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:

  1. 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 <24 filter dropped it) with data [token,0], then checks cmp 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 with node[0] = token. Sync passes.

  2. Speculative-poll gating: velocirender_transmit calls handle_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 blocking inRecord spin), not single speculative checks.

  3. Frame-ack: velocirender_frameack expects a message with action 9 (vr_draw_scene). The device tracks an outstanding draw_scene send 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.