RIO working: serial wire tap, conf tuning, four-patch writeup

The RIO cockpit board now runs sustained sessions with button mashing;
dropouts self-heal in ~1-3s. Documented in RIO-NOTES.md:

- directserial RIO_TAP=<path> (host env): logs every TX/RX byte with
  host-us + emu-ms timestamps, plus config/RTS/DTR/break lines. This
  instrument found every root cause below.
- Confs: rxburst:16 restored (no-burst reply pacing made the game ACK
  ~14ms late -> board dropped on the first long analog stream; the old
  'rxburst corrupts boot' belief was the then-unpatched PCSPAK crash).
  priority=highest,highest (unfocused DOSBox was demoted and blew the
  ACK deadline).
- BTL4OPT.EXE patch lineage (in ALPHA_1/, zip left pristine):
  v2 full DISABLE_AND_DIE NOPs (v1 left the IRQ/RTS-retract prologue
  live -> first protocol error deafened the driver), v3 TXMAXIDLE 4->32
  (kills the button-press ACK-window livelock), v4 analog retry limit
  15s->0.5s (dev value was 0.2s; recovery now near-instant).
- Board firmware patch plan recorded for the EPROM-dump route.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-04 00:22:14 -05:00
co-authored by Claude Fable 5
parent aea8b3eab4
commit 377047f37d
5 changed files with 215 additions and 3 deletions
@@ -29,9 +29,32 @@
#include "libserial.h"
#include <chrono>
/* This is a serial passthrough class. Its amazingly simple to */
/* write now that the serial ports themselves were abstracted out */
static double tap_now_us() {
using namespace std::chrono;
return duration<double, std::micro>(
steady_clock::now().time_since_epoch()).count();
}
/* One log line per byte on the wire. Host-relative microseconds tell the
* real-time story (the RIO's ACK deadline is wall clock); PIC_FullIndex()
* (emulated ms) ties each byte to game-side timing. */
void CDirectSerial::tapLine(char dir, unsigned val, unsigned err) {
if (!tap_fp) return;
double us = tap_now_us() - tap_t0_us;
if (err)
fprintf(tap_fp, "%12.1f %11.3f %c %02X err=%02X\n",
us, PIC_FullIndex(), dir, val, err);
else
fprintf(tap_fp, "%12.1f %11.3f %c %02X\n",
us, PIC_FullIndex(), dir, val);
fflush(tap_fp);
}
CDirectSerial::CDirectSerial (Bitu id, CommandLine* cmd)
:CSerial (id, cmd) {
InstallationSuccessful = false;
@@ -86,6 +109,22 @@ CDirectSerial::CDirectSerial (Bitu id, CommandLine* cmd)
(unsigned)rx_burst);
}
// RIO_TAP=<path> (host env, like the VPX_* vars): append one line per
// TX/RX byte. Appends across runs; each open writes a header line.
const char *tp = getenv("RIO_TAP");
if (tp && tp[0]) {
tap_fp = fopen(tp, "a");
if (tap_fp) {
tap_t0_us = tap_now_us();
fprintf(tap_fp, "# COM%d tap open (host-us rel | emu-ms | dir | byte)\n",
(int)COMNUMBER);
fflush(tap_fp);
LOG_MSG("Serial%d: RIO_TAP logging to '%s'", (int)COMNUMBER, tp);
} else {
LOG_MSG("Serial%d: RIO_TAP cannot open '%s'", (int)COMNUMBER, tp);
}
}
CSerial::Init_Registers();
InstallationSuccessful = true;
rx_state = D_RX_IDLE;
@@ -94,6 +133,7 @@ CDirectSerial::CDirectSerial (Bitu id, CommandLine* cmd)
CDirectSerial::~CDirectSerial () {
if(comport) SERIAL_close(comport);
if(tap_fp) fclose(tap_fp);
// We do not use own events so we don't have to clear them.
}
@@ -293,6 +333,7 @@ void CDirectSerial::handleUpperEvent(uint16_t type) {
bool CDirectSerial::doReceive() {
int value = SERIAL_getextchar(comport);
if(value) {
tapLine('R', (unsigned)(value & 0xff), (unsigned)((value >> 8) & 0xff));
receiveByteEx((uint8_t)(value&0xff),(uint8_t)((value&0xff00)>>8));
return true;
}
@@ -326,6 +367,12 @@ void CDirectSerial::updatePortConfig (uint16_t divider, uint8_t lcr) {
else stopbits = SERIAL_2STOP;
} else stopbits = SERIAL_1STOP;
if (tap_fp) {
fprintf(tap_fp, "# %12.1f %11.3f config %u baud %d%c%d\n",
tap_now_us() - tap_t0_us, PIC_FullIndex(),
(unsigned)baudrate, (int)bytelength, (char)parity, (int)stopbits);
fflush(tap_fp);
}
if(!SERIAL_setCommParameters(comport, (int)baudrate, (char)parity, (char)stopbits, (char)bytelength)) {
#if SERIAL_DEBUG
log_ser(dbg_aux,"Serial port settings not supported by host." );
@@ -346,6 +393,7 @@ void CDirectSerial::updateMSR () {
}
void CDirectSerial::transmitByte (uint8_t val, bool first) {
tapLine('T', val, 0);
if(!SERIAL_sendchar(comport, (char)val))
LOG_MSG("Serial%d: COM port error: write failed!", (int)COMNUMBER);
if(first) setEvent(SERIAL_THR_EVENT, bytetime/8);
@@ -355,20 +403,41 @@ void CDirectSerial::transmitByte (uint8_t val, bool first) {
// setBreak(val) switches break on or off
void CDirectSerial::setBreak (bool value) {
if (tap_fp) {
fprintf(tap_fp, "# %12.1f %11.3f break=%d\n",
tap_now_us() - tap_t0_us, PIC_FullIndex(), value ? 1 : 0);
fflush(tap_fp);
}
SERIAL_setBREAK(comport,value);
}
// updateModemControlLines(mcr) sets DTR and RTS.
// updateModemControlLines(mcr) sets DTR and RTS.
void CDirectSerial::setRTSDTR(bool rts, bool dtr) {
if (tap_fp) {
fprintf(tap_fp, "# %12.1f %11.3f rts=%d dtr=%d\n",
tap_now_us() - tap_t0_us, PIC_FullIndex(),
rts ? 1 : 0, dtr ? 1 : 0);
fflush(tap_fp);
}
SERIAL_setRTS(comport,rts);
SERIAL_setDTR(comport,dtr);
}
void CDirectSerial::setRTS(bool val) {
if (tap_fp) {
fprintf(tap_fp, "# %12.1f %11.3f rts=%d\n",
tap_now_us() - tap_t0_us, PIC_FullIndex(), val ? 1 : 0);
fflush(tap_fp);
}
SERIAL_setRTS(comport,val);
}
void CDirectSerial::setDTR(bool val) {
if (tap_fp) {
fprintf(tap_fp, "# %12.1f %11.3f dtr=%d\n",
tap_now_us() - tap_t0_us, PIC_FullIndex(), val ? 1 : 0);
fflush(tap_fp);
}
SERIAL_setDTR(comport,val);
}
@@ -68,6 +68,12 @@ private:
// sitting in the host buffer. n>1 delivers buffered bytes n times
// faster, cutting multi-byte reply latency (RIO analog packets).
float rx_burst_div = 1.0f;
// RIO_TAP=<path> (host env): wire-level byte log ("<host-us> <emu-ms>
// T|R <hex>") to verify whether RIO requests reach the wire and when
// replies come back. NULL when disabled.
FILE *tap_fp = nullptr;
double tap_t0_us = 0.0;
void tapLine(char dir, unsigned val, unsigned err);
bool doReceive();
#if SERIAL_DEBUG