From e13575d3acb9ba752123172a175b2f139f3a499a Mon Sep 17 00:00:00 2001 From: Cyd Date: Thu, 2 Jul 2026 23:41:49 -0500 Subject: [PATCH] Phase 2: FIFO transport working + RIO validated on real hardware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- emulator/PHASE2-PROGRESS.md | 41 ++++++++++++++++++++++++++ emulator/rio.conf | 30 +++++++++++++++++++ emulator/vpx-device/vpxlog.cpp | 54 ++++++++++++++++++++++++++++++---- 3 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 emulator/rio.conf diff --git a/emulator/PHASE2-PROGRESS.md b/emulator/PHASE2-PROGRESS.md index c080ebd..2c32674 100644 --- a/emulator/PHASE2-PROGRESS.md +++ b/emulator/PHASE2-PROGRESS.md @@ -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=`) 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.** diff --git a/emulator/rio.conf b/emulator/rio.conf new file mode 100644 index 0000000..424d473 --- /dev/null +++ b/emulator/rio.conf @@ -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 diff --git a/emulator/vpx-device/vpxlog.cpp b/emulator/vpx-device/vpxlog.cpp index 367a0a0..95bbae1 100644 --- a/emulator/vpx-device/vpxlog.cpp +++ b/emulator/vpx-device/vpxlog.cpp @@ -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); } }