From 6a595cc56588e9fadecf0c4457f9b330b522c15b Mon Sep 17 00:00:00 2001 From: Cyd Date: Thu, 9 Jul 2026 16:57:12 -0500 Subject: [PATCH] RP VDB SOLVED: gauge bank granularity 4KB->64KB (L4GAUGE.INI) -- full displays RP's cockpit displays decoded near-empty (top-strip only, a copy off-screen at VRAM 0x100000) while BT's were full. Root cause was NOT the VDB decode (byte split/palettes/stride all correct, proven by BT) but an SVGA bank-switch GRANULARITY mismatch: RP's REL410/RP/GAUGE/L4GAUGE.INI [640x480x16] used granularityInKB=4 vs BT's 64 on the same STB Horizon+ (CL-GD5434). DOSBox-X's CL-GD5434 bank emulation handles 64KB granularity (BT full) but mishandles the 4KB-granular paged gauge writes, scattering them off-screen. FIX (config, no rebuild): RP L4GAUGE.INI granularityInKB 4->64 (matches BT, same card). VERIFIED live: VRAM content scan went from 0x0+0x100000 (top-strip + off-screen) to the FULL framebuffer 0x0..0x90000 populated; radar + upper-center MFD confirmed correct, rest under reference check. Also commits the VDB diagnostic tooling that found it (vpxlog.cpp, all default-off): VDB_SCAN (VRAM content locator -- decisive), VDB_PALDUMP, pixel-mask value logging, VDB_REALSTRIDE + live mode/stride/start logging, VDB_BASE read-base override, VDB_APPLYMASK DAC-mask honoring. Full writeup in VDB-NOTES.md. Co-Authored-By: Claude Fable 5 --- ALPHA_1/REL410/RP/GAUGE/L4GAUGE.INI | 2 +- emulator/VDB-NOTES.md | 73 +++++++++++++++++++---------- emulator/vpx-device/vpxlog.cpp | 57 +++++++++++++++++++++- 3 files changed, 105 insertions(+), 27 deletions(-) diff --git a/ALPHA_1/REL410/RP/GAUGE/L4GAUGE.INI b/ALPHA_1/REL410/RP/GAUGE/L4GAUGE.INI index 535958c..4a4db29 100644 --- a/ALPHA_1/REL410/RP/GAUGE/L4GAUGE.INI +++ b/ALPHA_1/REL410/RP/GAUGE/L4GAUGE.INI @@ -20,7 +20,7 @@ mode=273 ;0111h width=640 height=480 sizeInKB=64 -granularityInKB=4 +granularityInKB=64 bytesPerLine=1280 pageFcnPtr=796182 ;C000:2616 special=0 diff --git a/emulator/VDB-NOTES.md b/emulator/VDB-NOTES.md index c6f9841..8fd3416 100644 --- a/emulator/VDB-NOTES.md +++ b/emulator/VDB-NOTES.md @@ -97,26 +97,35 @@ dumps each group's 768-byte CLUT; pixel-mask writes now log their value; masks default 0xFF; `VDB_APPLYMASK=1` ANDs the index with the mask (correct DAC emulation, default off = BT-identical). -## RP vs BT — the OPEN decode problem (2026-07-10) +## RP vs BT — the decode problem, SOLVED (2026-07-10) -Same board, same driver. Captured live for RP (VDB_PALDUMP): framebuffer -identical (mode 0x111, 640x480); **Secondary/pal0 loads ALL ZEROS** (BT drives -it as the dynamic color radar) -> our radar head is black; Aux1=green, -Aux2=RGB are driven; the RP MFD heads decode as SPARSE fragments in the top -strip. So under the shared mechanism, RP simply puts NO display in the low -byte (Secondary empty is legitimate), and its high-byte MFD content is not -landing where our BT-tuned head/channel split expects. +**ROOT CAUSE: an SVGA bank-switch GRANULARITY mismatch, NOT a VDB decode bug.** +RP's `REL410/RP/GAUGE/L4GAUGE.INI` `[640x480x16]` mode was byte-identical to +BT's EXCEPT `granularityInKB=4` (RP) vs `=64` (BT) -- same card (STB Horizon+ += CL-GD5434). The MUNGA gauge renderer (L4GREND.CPP reads L4GAUGE -> +L4GAUGE.INI -> SVGA16 ctor with that granularity) uses it to compute VESA +bank/page numbers for its paged writes down the gauge framebuffer. DOSBox-X's +CL-GD5434 bank emulation handles 64KB granularity correctly (BT gauges full) +but MISHANDLES the 4KB-granular paging, so RP's gauge writes scattered/landed +off-screen -- symptom: the visible page held only a top ~51-row strip while a +copy sat at VRAM 0x100000, and the heads decoded near-empty. Everything else +(low/high byte split, 3 palettes, stride, the whole VDB model) was ALREADY +CORRECT -- proven by BT and by the fix. -**Operator's steer: the card is dumb, so the fix is glaringly simple** — not -a driver-logic difference. Leading suspects to check next (empirically, like -the first BT pass): (a) the framebuffer PAGE/`real_start` — is RP double- -buffering so we read a stale/wrong SVGA page? (`SVGASetPage` exists); (b) the -device DUMP path stopped writing win*.bmp for RP (fires in pal_draw) — worth -finding why, it may reveal the head-render path isn't running the same; -(c) which bit groups/channels RP's displays actually claim (dump a BT vs RP -Aux palette side-by-side; the ramp/channel structure shows the bit layout); -(d) confirm the low/high byte order and the 640x480 vs a smaller gauge region -(top-strip content hints RP may draw gauges in fewer rows). +**FIX (config, no rebuild): set RP's L4GAUGE.INI `[640x480x16]` +`granularityInKB=64` to match BT.** Safe because it's the same card BT drives +at 64KB. VERIFIED live 2026-07-10: VRAM content scan went from `0x0 + 0x100000` +(top-strip + off-screen) to the FULL framebuffer `0x0..0x90000` all populated; +operator confirmed the radar + upper-center MFD render correctly (others being +checked vs reference). RP cockpit displays now decode like BT's. + +The tooling that found it (all in vpxlog.cpp, default-off diagnostics, kept): +`VDB_PALDUMP` (dump the 3 CLUTs), pixel-mask value logging, `VDB_REALSTRIDE` ++ live scan_len/addr_add/real_start/mode logging, `VDB_SCAN` (VRAM +content-region locator -- THE decisive tool), `VDB_BASE` (read-base override), +`VDB_APPLYMASK` (honor the DAC pixel-mask). Note: proper long-term fix could +instead be DOSBox-X honoring 4KB CL-GD5434 bank granularity, but the INI match +is correct and sufficient. ## Physical hardware (documented from the operator's sample, 2026-07-10) @@ -140,12 +149,28 @@ CONFIRMS the reverse-engineering below the chip level: connector that fans out to the 5 monochrome MFDs** (= Aux1/0x30A R,G = 2 lower MFDs, Aux2/0x312 R,G,B = 3 upper MFDs; 5 mono wires total, one channel unused). The DB25 fan-out is the "octopus/pentapus" cable. -- **Reset-tie component**: a part wired in PARALLEL with the PC's front-panel - RESET switch — either a remote-reboot path OR a means to force the VDB into - a known state when the PC is reset (so the CPLD/DACs re-init cleanly). - OPERATOR RESEARCHING. (Plausible: the CPLD needs a hard reset synchronized - with the host, since it has no software reset in the 0x300-0x31A map -- only - clock on/off; a reset tie guarantees the splitter isn't left mid-state.) +- **Host hard-reset circuit (remote reboot)**: an **HSSR-8060** — a 6-pin + opto-isolated solid-state relay (MOSFET output, Agilent/Broadcom) — with its + **output switch (pins 4 & 6) wired to a 2-pin header silk-screened + `RESET_OUT`**, which is in parallel with the PC's front-panel RESET switch. + So energizing the SSR's input LED = "pressing reset" = a hardware reboot of + the pod's host PC. **What drives the SSR input is still unknown (operator + researching).** This is almost certainly a REMOTE/AUTOMATED REBOOT path: an + arcade operator (or the ops console over the net) could hard-reset a wedged + pod without physically touching it. Candidate input drivers to trace: (a) the + MACH130 CPLD decoding a "reset" write in the 0x300-0x31A I/O space (a soft + command -> hard reset, e.g. a watchdog or console-commanded reboot); (b) a + discrete line from the network/console board; (c) a watchdog timer. NOTE for + the emulator: our HLE has no reset-out port yet; if the driver ever writes a + reset trigger it'd appear as an unhandled 0x300-0x31A write — worth watching + the vpxresp log for writes outside the known palette/clock offsets. + **RESOLVED (operator, 2026-07-10): the SSR INPUT is driven from a spare pair + on the RADAR display's 9-pin video cable.** So it is NOT software/CPLD-driven + — it's a COCKPIT-LOCAL reset button: the reset signal is carried UP the radar + cable's unused conductors from a button in the cockpit down to the SSR, which + closes RESET_OUT across the PC front-panel reset. Lets the operator hard- + reboot a wedged pod PC from the cockpit without opening the chassis. Pure + hardware; nothing for the emulator to model. ### Still worth capturing for the archive - Photos of each chip + the board silkscreen; the DB25 pinout (which pin -> diff --git a/emulator/vpx-device/vpxlog.cpp b/emulator/vpx-device/vpxlog.cpp index a334289..987c12f 100644 --- a/emulator/vpx-device/vpxlog.cpp +++ b/emulator/vpx-device/vpxlog.cpp @@ -1334,7 +1334,56 @@ static void pal_draw(HDC dc, int g, int cw, int ch, bool dump) { Bitu mask = vga.draw.linear_mask ? vga.draw.linear_mask : (vga.mem.memsize ? vga.mem.memsize - 1 : 0); Bitu start = vga.config.real_start; /* visible page start (bytes) */ + /* VDB_BASE=: override the read base. RP renders its cockpit displays + * to an OFF-SCREEN page (content found at 0x100000 by VDB_SCAN) while the + * visible page (real_start) is near-empty; the real VDB is fed from that + * page. Point us at it. (Proper fix = track the CL-GD5434 start register.) */ + { + static const char *be = getenv("VDB_BASE"); + if (be && *be) start = (Bitu)strtoul(be, NULL, 0); + } + /* VDB_REALSTRIDE=1: use the CRT's actual per-scanline byte advance instead + * of the hardcoded 640*2. RP may set a wider CL-GD5434 pitch or page-flip; + * BT uses a plain 1280 so this is a no-op there. Logged once/sec so we can + * see the real scan_len/addr_add/start and whether the page flips. */ + static const bool real_stride = getenv("VDB_REALSTRIDE") != NULL; Bitu stride = (Bitu)W * 2; /* 16bpp */ + if (real_stride && vga.draw.address_add >= (Bitu)W*2 + && vga.draw.address_add <= (Bitu)W*8) { + stride = vga.draw.address_add; + } + if (vpx_fp && g == 0) { + static double last_log = 0.0; double nowt = (double)GetTickCount(); + if (nowt - last_log > 1000.0) { + last_log = nowt; + fprintf(vpx_fp, "# VDB pal_draw: real_start=0x%X scan_len*2=%u " + "addr_add=%u using_stride=%u LIVE mode=%d w=%u h=%u memsize=%uK\n", + (unsigned)start, (unsigned)(vga.config.scan_len*2u), + (unsigned)vga.draw.address_add, (unsigned)stride, + (int)vga.mode, (unsigned)vga.draw.width, + (unsigned)vga.draw.height, (unsigned)(vga.mem.memsize/1024)); + /* VDB_SCAN=1: find WHERE RP's display content actually lives in the + * Cirrus VRAM. The visible page (real_start) looks near-empty for RP + * -- so scan the whole framebuffer in 64K windows and report which + * hold significant non-black 16bpp pixels. */ + if (fb && getenv("VDB_SCAN")) { + Bitu msz = vga.mem.memsize ? vga.mem.memsize : 0; + const Bitu WIN = 0x10000; + fprintf(vpx_fp, "# VRAM content scan (64K windows w/ >2%% lit):"); + for (Bitu base = 0; base + WIN <= msz && base < 0x400000; base += WIN) { + unsigned lit = 0; + for (Bitu o = base; o < base + WIN; o += 2*64) { /* sample */ + if (*(const uint16_t *)(fb + (o & mask))) lit++; + } + if (lit > (WIN/(2*64))/50) /* >2% of samples lit */ + fprintf(vpx_fp, " 0x%X(%u%%)", (unsigned)base, + (unsigned)(lit*100/(WIN/(2*64)))); + } + fprintf(vpx_fp, "\n"); + } + fflush(vpx_fp); + } + } if (fb) { /* win0 = framebuffer LOW byte (bits 0-7) through pal0; win3/win4 = * framebuffer HIGH byte (bits 8-15) through pal1/pal2. All as 8-bit @@ -2518,10 +2567,14 @@ static void vdb_write(Bitu port, Bitu val, Bitu /*iolen*/) { if (port == 0x319) { vdb_splitter_on = false; vdb_note("splitter clock OFF (0x319)"); return; } if (port == 0x31A) { vdb_splitter_on = true; vdb_note("splitter clock ON (0x31A)"); if (vpx_fp && CurMode) { flush_run(); - fprintf(vpx_fp, "# VDB framebuffer mode: 0x%X type=%d %ux%u pitch=%u\n", + fprintf(vpx_fp, "# VDB framebuffer mode: 0x%X type=%d %ux%u pitch=%u" + " REAL scan_len*2=%u addr_add=%u real_start=0x%X\n", (unsigned)CurMode->mode, (int)CurMode->type, (unsigned)CurMode->swidth, (unsigned)CurMode->sheight, - (unsigned)CurMode->pitch); fflush(vpx_fp); } + (unsigned)CurMode->pitch, + (unsigned)(vga.config.scan_len * 2u), + (unsigned)vga.draw.address_add, + (unsigned)vga.config.real_start); fflush(vpx_fp); } return; } int g = vdb_group_of(off); if (g < 0) return;