Emulator: VDB heads mirror plain-VGA modes (analog splitter pass-through)

The VDB is an analog splitter: the game's packed-16bpp trick routes the two
framebuffer bytes through the board palettes at 0x300/8/10, but any plain
VGA mode passes the S3's own DAC signal to every head -- no VDB register
traffic at all. The VGL_LABS test suite draws its secondary-display
patterns in 640x480x8 and programs nothing at 0x300-0x31A (verified from a
full session log), so the packed-only decode showed black heads. pal_draw
now branches: M_LIN8 decodes through the VGA DAC (full color on the color
heads, single-wire luminance on the mono MFD windows); 16bpp keeps the
packed VDB-palette decode.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Cyd
2026-07-17 07:30:09 -05:00
co-authored by Claude Fable 5
parent a6713ec1bd
commit e02a346bb0
+37 -15
View File
@@ -1384,6 +1384,18 @@ static void pal_draw(HDC dc, int g, int cw, int ch, bool dump) {
fflush(vpx_fp);
}
}
/* The VDB is an ANALOG splitter: the game's packed-16bpp trick routes the
* two framebuffer bytes through the BOARD's palettes (0x300/8/10), but any
* plain VGA mode (the VGL_LABS test suite draws its secondary-display
* patterns in 640x480x8) passes through the S3's OWN DAC and every head
* simply mirrors that signal -- no VDB register traffic at all (verified
* 2026-07-17: TSTALL programs nothing at 0x300-0x31A). Model both: 16bpp
* = packed decode via vdb_pal; 8bpp = VGA-DAC mirror, with the mono MFD
* heads showing their single color wire's luminance. */
const bool lin8 = (vga.mode == M_LIN8);
if (lin8) stride = (vga.draw.address_add >= (Bitu)W &&
vga.draw.address_add <= (Bitu)W * 4)
? vga.draw.address_add : (Bitu)W;
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
@@ -1393,22 +1405,32 @@ static void pal_draw(HDC dc, int g, int cw, int ch, bool dump) {
for (int y = 0; y < H; y++) {
Bitu ofs = start + (Bitu)y * stride;
unsigned char *d = &img[(size_t)y * W * 3];
for (int x = 0; x < W; x++, ofs += 2, d += 3) {
uint16_t px = *(const uint16_t *)(fb + (ofs & mask));
unsigned idx; int pg, chn = -1;
if (g <= 2) { idx = px & 0xFFu; pg = g; } /* bits 0-7 via pal0/1/2 */
else if (g <= 4) { idx = (px >> 8u) & 0xFFu; pg = g - 2; } /* bits 8-15 via pal1/2 */
else { idx = (px >> 8u) & 0xFFu; /* wins 5-9: one mono MFD */
pg = (g <= 6) ? 1 : 2; /* = one color wire of a */
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];
if (chn < 0) {
d[0] = (unsigned char)((e[0] << 2) | (e[0] >> 4)); /* 6-bit DAC -> 8-bit R */
d[1] = (unsigned char)((e[1] << 2) | (e[1] >> 4)); /* G */
d[2] = (unsigned char)((e[2] << 2) | (e[2] >> 4)); /* B */
for (int x = 0; x < W; x++, ofs += (lin8 ? 1 : 2), d += 3) {
unsigned char e6[3]; int chn = -1;
if (g > 4) chn = (g <= 6) ? g - 5 : g - 7; /* mono wire # */
if (lin8) {
/* analog mirror: byte -> the VGA's own DAC */
unsigned idx = fb[ofs & mask];
e6[0] = (unsigned char)vga.dac.rgb[idx].red;
e6[1] = (unsigned char)vga.dac.rgb[idx].green;
e6[2] = (unsigned char)vga.dac.rgb[idx].blue;
} else {
unsigned char v = (unsigned char)((e[chn] << 2) | (e[chn] >> 4));
uint16_t px = *(const uint16_t *)(fb + (ofs & mask));
unsigned idx; int pg;
if (g <= 2) { idx = px & 0xFFu; pg = g; } /* bits 0-7 via pal0/1/2 */
else if (g <= 4) { idx = (px >> 8u) & 0xFFu; pg = g - 2; } /* bits 8-15 via pal1/2 */
else { idx = (px >> 8u) & 0xFFu; /* wins 5-9: one mono MFD */
pg = (g <= 6) ? 1 : 2; } /* = one wire of a head */
if (apply_mask) idx &= vdb_pal[pg].mask;
const unsigned char *e = &vdb_pal[pg].ram[idx * 3u];
e6[0] = e[0]; e6[1] = e[1]; e6[2] = e[2];
}
if (chn < 0) {
d[0] = (unsigned char)((e6[0] << 2) | (e6[0] >> 4)); /* 6-bit DAC -> 8-bit R */
d[1] = (unsigned char)((e6[1] << 2) | (e6[1] >> 4)); /* G */
d[2] = (unsigned char)((e6[2] << 2) | (e6[2] >> 4)); /* B */
} else {
unsigned char v = (unsigned char)((e6[chn] << 2) | (e6[chn] >> 4));
d[0] = (unsigned char)(v >> 3); /* mono MFD: green-phosphor tube, */
d[1] = v; /* wire level = beam brightness */
d[2] = (unsigned char)(v >> 3);