#!/usr/bin/env python3 """Regression test for the 256x256 CRTC mode (docs/FINDINGS 23). Checks tmp/snap256/x68000/0000.png against tmp/frame256.bin: 1. native snapshot is 256x512 -- 256 dots, and 512 active scanlines of a 568-line 31.5kHz raster carrying 256 double-scanned graphics rows 2. double-scan pairing is (1,2),(3,4),... -- MAME halves the ABSOLUTE scanline (x68k_v.cpp get_gfx_pixel) and vbegin=41 is odd, so snapshot row 0 is a lone half-line and even rows are gfx rows 0..255 3. the 192 active rows are PIXEL-EXACT against the palette pushed through GGGGGRRRRRBBBBBI with I chosen per entry by minimum squared error 4. the letterbox bars are TRUE black -- needs both a reserved index-0 black entry AND I=0 on it, since pal6bit(1) = 4, not 0 """ import struct, sys import numpy as np from PIL import Image s = np.asarray(Image.open("tmp/snap256/x68000/0000.png").convert("RGB")).astype(int) d = open("tmp/frame256.bin", "rb").read() W, H = struct.unpack(">HH", d[4:8]) pal = np.frombuffer(d[8:8+768], np.uint8).reshape(256, 3).astype(int) idx = np.frombuffer(d[8+768:8+768+W*H], np.uint8).reshape(H, W) p6 = lambda v: ((v << 2) | (v >> 4)) & 0xFF f = pal >> 3 render = lambda I: p6((f << 1) | I[:, None]) I = (((render(np.ones(256, int)) - pal) ** 2).sum(1) < ((render(np.zeros(256, int)) - pal) ** 2).sum(1)).astype(int) exp = render(I)[idx] fail = [] if s.shape[:2] != (512, 256): fail.append(f"1. geometry: expected 512x256, got {s.shape[1]}x{s.shape[0]}") if not all(np.array_equal(s[i], s[i+1]) for i in range(1, s.shape[0]-1, 2)): fail.append("2. double-scan pairing (1,2),(3,4),... broken") g = s[0::2] yoff = (g.shape[0] - H) // 2 act = g[yoff:yoff+H] if not np.array_equal(act, exp): diff = abs(act - exp) fail.append(f"3. active area not pixel-exact: maxdiff {diff.max()}, " f"{diff.any(2).sum()} px differ") bars = np.concatenate([g[:yoff], g[yoff+H:]]) if bars.max() != 0: fail.append(f"4. letterbox not true black: max channel {bars.max()}") for x in fail: print("FAIL " + x) if fail: sys.exit(1) mse = ((act - pal[idx]) ** 2).mean() print(f"OK 256x512 native, double-scan exact, active {W}x{H} pixel-exact, " f"letterbox true black") print(f" palette ceiling vs 24-bit palettised source: " f"{10*np.log10(255**2/mse):.2f} dB ({(I==0).sum()}/256 entries use I=0)")