VDB: honor the VGA-DAC pixel-mask (RP drives it; BT left it 0xFF); park Mac mirror

RP experiment finding: RP runs on the BT pod emulator (shared MUNGA stack) but
its cockpit VDB heads mis-decode. Root causes captured live (VDB_PALDUMP +
vpxresp log): (1) framebuffer identical to BT (mode 0x111, 640x480); (2) RP
loads pal0 (BT's dynamic color-radar palette) as ALL ZEROS -> radar head black;
(3) RP actively drives the VGA-DAC pixel-mask register that BT leaves at 0xFF,
and pal_draw ignored it -> likely the MFD garble.

Device change: pixel-mask writes now log their VALUE on change
("# VDB pixel-mask[gN] = 0xXX"); pal_draw ANDs the palette index with the
group mask under VDB_APPLYMASK=1 (default off = BT-identical); masks default to
0xFF in vdb_reset so an un-driven group still decodes. Built clean under MINGW64.

Also: Mac Console 4.10 retired from regular use (host .NET console is the daily
driver); tap2_mirror.py PARKED with revive notes for console A/B only. The
SendToRxAdapters registry stays set (serves the .NET host console).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-09 14:53:22 -05:00
co-authored by Claude Fable 5
parent dd2ae57647
commit 68ca9b6f15
3 changed files with 38 additions and 2 deletions
+8
View File
@@ -533,6 +533,14 @@ Revert: `Remove-ItemProperty ... -Name SendToRxAdapters` + service
bounce + pod relaunch. Host connect/disconnect churns netnub (exit + bounce + pod relaunch. Host connect/disconnect churns netnub (exit +
~60s relaunch) -- expected. ~60s relaunch) -- expected.
**MAC CONSOLE RETIRED / MIRROR PARKED (2026-07-10, operator decision):** the
SheepShaver Mac Console 4.10 is no longer in regular use -- the host-side .NET
TeslaConsole is the daily driver (direct pod link, no mirror). tap2_mirror.py
is PARKED, kept only for running the Mac console alongside the .NET one for
console A/B testing; revive per its header + the section below. The
SendToRxAdapters registry stays SET (it serves the .NET host console). Nothing
to revert unless a Mac-console A/B is needed.
**BOTH CONSOLES AT ONCE — WORKING (2026-07-10, `net-tools/tap2_mirror.py`):** **BOTH CONSOLES AT ONCE — WORKING (2026-07-10, `net-tools/tap2_mirror.py`):**
keep SendToRxAdapters set (host console works) and run the mirror keep SendToRxAdapters set (host console works) and run the mirror
daemon, which turns TAP2's free application side into a userspace daemon, which turns TAP2's free application side into a userspace
+11 -1
View File
@@ -1,5 +1,15 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Pod->Mac mirror for the SendToRx era: with Npcap's SendToRxAdapters set """[PARKED 2026-07-10] The SheepShaver/Mac Console 4.10 is retired from
regular use -- the modern host-side .NET TeslaConsole is the daily driver
(it talks to the pod directly via the bridge IP + SendToRxAdapters; no
mirror needed). Keep this tool for the ONE case it exists for: running the
1996 Mac console ALONGSIDE the .NET console for A/B testing. To revive: start
a pod, then `pythonw net-tools/tap2_mirror.py`, and bring up SheepShaver +
Console 4.10 (see NET-NOTES.md "BOTH CONSOLES AT ONCE"). Verified working
2026-07-09 (Console 4.10 ran a full mission through it). Constants at top
(pod NE2000 MAC, TAP2 GUID) may need refreshing if the adapters change.
----------------------------------------------------------------------------
Pod->Mac mirror for the SendToRx era: with Npcap's SendToRxAdapters set
on the Network Bridge (so the host .NET console hears the pod), the pod's on the Network Bridge (so the host .NET console hears the pod), the pod's
injected frames no longer reach the TAP members -- SheepShaver goes deaf. injected frames no longer reach the TAP members -- SheepShaver goes deaf.
Every other path still works. This daemon closes the one missing hop: Every other path still works. This daemon closes the one missing hop:
+19 -1
View File
@@ -1326,6 +1326,10 @@ static void write_bmp(const char *path, const unsigned char *rgb, int W, int H)
static void pal_draw(HDC dc, int g, int cw, int ch, bool dump) { static void pal_draw(HDC dc, int g, int cw, int ch, bool dump) {
const int W = 640, H = 480; const int W = 640, H = 480;
static unsigned char img[640 * 480 * 3]; static unsigned char img[640 * 480 * 3];
/* VDB_APPLYMASK=1: AND the palette index with the group's VGA-DAC
* pixel-mask (default 0xFF = no-op). RP drives this mask actively where
* BT leaves it 0xFF, so honoring it may unscramble RP's MFD heads. */
static const bool apply_mask = getenv("VDB_APPLYMASK") != NULL;
const uint8_t *fb = vga.mem.linear; const uint8_t *fb = vga.mem.linear;
Bitu mask = vga.draw.linear_mask ? vga.draw.linear_mask Bitu mask = vga.draw.linear_mask ? vga.draw.linear_mask
: (vga.mem.memsize ? vga.mem.memsize - 1 : 0); : (vga.mem.memsize ? vga.mem.memsize - 1 : 0);
@@ -1348,6 +1352,7 @@ static void pal_draw(HDC dc, int g, int cw, int ch, bool dump) {
else { idx = (px >> 8u) & 0xFFu; /* wins 5-9: one mono MFD */ else { idx = (px >> 8u) & 0xFFu; /* wins 5-9: one mono MFD */
pg = (g <= 6) ? 1 : 2; /* = one color wire of a */ pg = (g <= 6) ? 1 : 2; /* = one color wire of a */
chn = (g <= 6) ? g - 5 : g - 7; } /* head (pentapus split) */ chn = (g <= 6) ? g - 5 : g - 7; } /* head (pentapus split) */
if (apply_mask) idx &= vdb_pal[pg].mask;
const unsigned char *e = &vdb_pal[pg].ram[idx * 3u]; const unsigned char *e = &vdb_pal[pg].ram[idx * 3u];
if (chn < 0) { if (chn < 0) {
d[0] = (unsigned char)((e[0] << 2) | (e[0] >> 4)); /* 6-bit DAC -> 8-bit R */ d[0] = (unsigned char)((e[0] << 2) | (e[0] >> 4)); /* 6-bit DAC -> 8-bit R */
@@ -2533,7 +2538,17 @@ static void vdb_write(Bitu port, Bitu val, Bitu /*iolen*/) {
vdb_data_count++; vdb_data_count++;
break; break;
case 0: /* pixel-mask */ case 0: /* pixel-mask */
p.mask = v; vdb_note("pixel-mask set"); /* RP drives this VGA-DAC pixel-mask register actively (BT leaves
* it at 0xFF); log the VALUE on change so we can see how RP gates
* the palette index, and honor it in pal_draw under VDB_APPLYMASK. */
if (v != p.mask) {
p.mask = v;
if (vpx_fp) { vdb_flush_data(); flush_run();
fprintf(vpx_fp, "# VDB pixel-mask[g%d] = 0x%02X\n", g, v);
fflush(vpx_fp); }
} else {
p.mask = v;
}
break; break;
case 1: /* read-address */ case 1: /* read-address */
p.raddr = v; p.sub = 0; p.raddr = v; p.sub = 0;
@@ -2554,6 +2569,9 @@ static Bitu vdb_read(Bitu port, Bitu /*iolen*/) {
} }
static void vdb_reset(void) { static void vdb_reset(void) {
memset(vdb_pal, 0, sizeof vdb_pal); memset(vdb_pal, 0, sizeof vdb_pal);
/* pixel-mask defaults to 0xFF (no gating) so an un-set group decodes
* normally; the game overwrites it if it drives the mask (RP does). */
for (int g = 0; g < 3; g++) vdb_pal[g].mask = 0xFF;
vdb_splitter_on = false; vdb_data_group = -1; vdb_data_count = 0; vdb_splitter_on = false; vdb_data_group = -1; vdb_data_count = 0;
} }