/* VPX link-adapter logging device (Tesla Rel 4.10 emulation, Phase 1) * * Impersonates the INMOS C012 transputer link adapter the Division VPX board * hung off the ISA bus, at its documented register map (host source: * sda4/DPL3/LINKIO.C, setLA): * * base+0 (0x150) inputData R byte board->host * base+1 (0x151) outputData W byte host->board * base+2 (0x152) inputStatus R bit0 = inbound byte available * base+3 (0x153) outputStatus R bit0 = ready to accept outbound byte * base+16 (0x160) resetRoot W board reset strobe / ok_to_fifo * base+17 (0x161) analyseRoot W board analyse strobe * * Phase 1 goal is DISCOVERY, not emulation: log every access so we can capture * the game's outbound boot conversation (reset -> transputer monitor download * -> i860 code/data segments) from the shipped binary. Status reads are * answered so the game keeps writing: * - outputStatus -> always ready (bit0=1) so OUTBYTE proceeds * - inputStatus -> no data (bit0=0) so the game doesn't read phantom input * - inputData -> 0xFF (logged) if the game reads anyway * * Enabled only when the environment variable VPXLOG is set (harmless when off). * Log file: $VPXLOG if it names a path, else "vpxlog.txt" in the CWD. */ #include "dosbox.h" #include "inout.h" #include "logging.h" #include #include #include static const io_port_t VPX_BASE = 0x150; static FILE *vpx_fp = NULL; static bool vpx_on = false; static unsigned long vpx_seq = 0; /* Coalesce runs of identical status polls so the log shows the conversation, * not millions of "read status = ready" lines. */ static io_port_t last_port = 0xFFFF; static unsigned last_val = 0xFFFFFFFF; static bool last_write = false; static unsigned long last_run = 0; static const char *reg_name(io_port_t port) { switch (port - VPX_BASE) { case 0: return "inputData"; case 1: return "outputData"; case 2: return "inputStatus"; case 3: return "outputStatus"; case 16: return "resetRoot"; case 17: return "analyseRoot"; default: return "?"; } } static void vpx_flush_run(void) { if (last_run == 0 || vpx_fp == NULL) return; if (last_run == 1) { fprintf(vpx_fp, "%8lu %s %-13s 0x%02X\n", vpx_seq++, last_write ? "W" : "R", reg_name(last_port), last_val & 0xFF); } else { fprintf(vpx_fp, "%8lu %s %-13s 0x%02X x%lu\n", vpx_seq++, last_write ? "W" : "R", reg_name(last_port), last_val & 0xFF, last_run); } fflush(vpx_fp); last_run = 0; } static void vpx_record(io_port_t port, unsigned val, bool write) { if (vpx_fp == NULL) return; /* Collapse identical consecutive accesses (typically status polls). */ if (port == last_port && val == last_val && write == last_write) { last_run++; return; } vpx_flush_run(); last_port = port; last_val = val; last_write = write; last_run = 1; /* Payload writes and reset/analyse strobes are the interesting events; * emit them immediately (don't wait for a differing access to flush). */ io_port_t off = port - VPX_BASE; if (write && (off == 1 || off == 16 || off == 17)) vpx_flush_run(); } static Bitu vpx_read(Bitu port, Bitu /*iolen*/) { io_port_t off = (io_port_t)port - VPX_BASE; Bitu ret; switch (off) { case 2: ret = 0x00; break; /* inputStatus: no inbound data */ case 3: ret = 0x01; break; /* outputStatus: always ready to send */ default: ret = 0xFF; break; /* inputData / unknown: open-bus */ } vpx_record((io_port_t)port, (unsigned)ret, false); return ret; } static void vpx_write(Bitu port, Bitu val, Bitu /*iolen*/) { vpx_record((io_port_t)port, (unsigned)val, true); } void VPXLOG_Init(void) { const char *env = getenv("VPXLOG"); if (env == NULL || env[0] == '\0') return; const char *path = (strchr(env, '/') || strchr(env, '\\') || strchr(env, '.')) ? env : "vpxlog.txt"; vpx_fp = fopen(path, "w"); if (vpx_fp == NULL) { LOG_MSG("VPXLOG: could not open log file '%s'", path); return; } vpx_on = true; /* Cover 0x150..0x153 and 0x160..0x161 with one 18-port range. */ IO_RegisterReadHandler(VPX_BASE, vpx_read, IO_MB, 18); IO_RegisterWriteHandler(VPX_BASE, vpx_write, IO_MB, 18); fprintf(vpx_fp, "# VPX link-adapter access log, base 0x%03X\n", VPX_BASE); fprintf(vpx_fp, "# seq dir register value [run-length]\n"); fflush(vpx_fp); LOG_MSG("VPXLOG: logging VPX link adapter at 0x%03X to '%s'", VPX_BASE, path); }