Phase 2: FIFO transport working + RIO validated on real hardware

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>
This commit is contained in:
Cyd
2026-07-02 23:41:49 -05:00
co-authored by Claude Fable 5
parent f236d15d51
commit e13575d3ac
3 changed files with 120 additions and 5 deletions
+41
View File
@@ -1,5 +1,46 @@
# 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.**
+30
View File
@@ -0,0 +1,30 @@
[sdl]
output=opengl
[dosbox]
memsize=32
machine=svga_s3
[cpu]
core=normal
cputype=pentium
cycles=20000
# Phase 5 VALIDATED 2026-07-02: with a real RIO on the host's COM1, this
# passthrough makes the game's 1996 RIO driver talk to the physical board —
# the "RIO never came back from check request?" error disappears and the
# expected activity is observed on the RIO board.
[serial]
serial1=directserial realport:COM1
serial2=disabled
[autoexec]
mount c "image"
c:
set L4CONTROLS=RIO,KEYBOARD
set L4TIMER=
set BLASTER=A220 I5 D1 H5 P330 T6
set VIDEOFORMAT=svga
set TEMP=c:\
set DPLARG=/tranny~.\vrendmon.btl~/i860~.\vrnostex.mng~/device~0x150~/video~svga~/pipes~1~/qual~0x14~/system_tex~0~
32rtm.exe -x
btl4opt.exe -egg test.egg
32rtm.exe -u
echo RIO-DONE
pause
+49 -5
View File
@@ -94,7 +94,7 @@ static void parse_out_byte(unsigned char v) {
/* diagnostic: log small control frames (skip 512B i860 chunks) */
if (vpx_fp && wf_last_nb <= 64) {
flush_run();
fprintf(vpx_fp, "# FRAME action=%u nb=%d\n", wf_action, wf_last_nb);
fprintf(vpx_fp, "# FRAME action=%u nb=%d (slow)\n", wf_action, wf_last_nb);
fflush(vpx_fp);
}
wf_payload_left = -1;
@@ -102,6 +102,34 @@ static void parse_out_byte(unsigned char v) {
}
}
/* ---- FIFO fast-path action extraction ---------------------------------- *
* After the i860 boots the host switches to the FIFO path (OUTSW.ASM): each
* message writes an outsw tag byte to outputData, then pumps the payload as
* 16-bit words to the FIFO data port (observed at 0x154/0x155, low/high). The
* payload of a control message begins with the 4-byte action. We arm on each
* outputData write and capture the next 4 FIFO bytes; if they form a valid
* action code (< 32, the vr_action enum range) we treat it as the action to
* echo. Data runs (e.g. text/coords) yield large values and are ignored. */
static bool fifo_arm = false;
static int fifo_cap_pos = 0;
static unsigned fifo_cap = 0;
static const unsigned VR_ACTION_MAX = 24; /* enum has 24 actions (0..23) */
static void fifo_arm_action(void) { fifo_arm = true; fifo_cap_pos = 0; fifo_cap = 0; }
static void parse_fifo_byte(unsigned char v) {
if (!fifo_arm) return;
fifo_cap |= ((unsigned)v) << (fifo_cap_pos * 8);
if (++fifo_cap_pos == 4) {
fifo_arm = false;
if (vpx_fp) { flush_run();
fprintf(vpx_fp, "# FIFOCAP 0x%08X%s\n", fifo_cap,
fifo_cap < VR_ACTION_MAX ? " (action)" : "");
fflush(vpx_fp); }
if (fifo_cap < VR_ACTION_MAX) last_action = fifo_cap;
}
}
/* A well-formed iserver "version" request (VR_COMMS.C iserver_action case 42):
* length_word = nb | (sender<<16) | 0x80000000, little-endian; payload = tag.
* nb is padded even for iserver; use nb=2, payload {0x2a,0x00}. sender=0. */
@@ -209,11 +237,23 @@ static Bitu vpx_read(Bitu port, Bitu /*iolen*/) {
/* game is reading -> expects a renderer reply
* (first is the vr_init ack after i860 boot). */
if (postboot_acks < vpx_max_postboot_acks) {
queue_render_ack((unsigned char)(last_action & 0xff));
/* Reply action is handler-specific (board side,
* VR_REMOT.C): most handlers preserve data[0] (echo
* the sent action), but a few overwrite it:
* vr_init_action(0) -> *data = 1
* vr_statistics_action(15) -> *data = 1
* everything else echoes. */
unsigned reply = last_action;
if (last_action == 0 /*init*/ ||
last_action == 15 /*statistics*/) reply = 1;
/* experiment hook: override init reply action */
{ const char *o = getenv("VPX_INIT_REPLY");
if (o && last_action == 0) reply = (unsigned)atoi(o); }
queue_render_ack((unsigned char)(reply & 0xff));
postboot_acks++;
if (vpx_fp) { flush_run();
fprintf(vpx_fp, "# post-boot: echo action %u\n",
last_action); fflush(vpx_fp); }
fprintf(vpx_fp, "# post-boot: reply action %u (sent %u)\n",
reply, last_action); fflush(vpx_fp); }
ret = 0x01;
}
}
@@ -251,10 +291,14 @@ static void vpx_write(Bitu port, Bitu val, Bitu iolen) {
postboot_acks = 0; in_len = in_pos = 0;
parse_frames = false; wf_need_len = 4; wf_lenbuf = 0; wf_lenshift = 0;
wf_payload_left = -1; wf_action_pos = 0; wf_action = 0; last_action = 0;
fifo_arm = false; fifo_cap_pos = 0; fifo_cap = 0;
note("board reset");
} else if (off == 1) {
saw_write = true; /* outputData: a download/response byte */
if (parse_frames) parse_out_byte((unsigned char)val);
if (parse_frames) { parse_out_byte((unsigned char)val); fifo_arm_action(); }
} else if (off == 4 || off == 5) {
/* FIFO data port (link B): payload words, low/high bytes. */
if (parse_frames) parse_fifo_byte((unsigned char)val);
}
}