#!/usr/bin/env python3 """Is the 68000 decoder's output pixel-exact against the reference decoder? python3 tools/bench/verify_decode.py [--snap tmp/snap_decode] Checks tmp/snap_decode/x68000/0000.png -- the screen after src/player/decode.s has decoded every frame of the container in sequence -- against tools/encoder/dlx.py's reconstruction of the final frame. This is a stronger test than the blit regression it is modelled on. The blit proved the 68000 could COPY a frame; this proves it can PARSE one. And because the decoder is temporally recursive -- a SKIP block is a claim that the previous frame is still in GVRAM -- the last frame of a sequential run is only correct if every frame before it was, so a single comparison audits all of them. """ import argparse, sys sys.path.insert(0, "tools/encoder") import numpy as np from PIL import Image from dlx import DLX ap = argparse.ArgumentParser() ap.add_argument("container") ap.add_argument("--snap", default="tmp/snap_decode") # The harness can only load as much of a container as fits in the emulated # machine's RAM, so it may have decoded a PREFIX (tools/bench/prep_dlx.py # --ram). Compare against the same prefix, or the reference runs ahead of the # 68000 and reports a mismatch that is an artefact of the rig. ap.add_argument("--nframes", type=int, default=None, help="frames the 68000 actually decoded (default: all)") a = ap.parse_args() d = DLX(a.container) NF = a.nframes if a.nframes is not None else d.nframes if NF > d.nframes: sys.exit(f"--nframes {NF} exceeds the container's {d.nframes}") canvas = np.zeros((d.H, d.W), np.uint8) for f in range(NF): d.paint(canvas, f) pal = d.pal.astype(int) p6 = lambda v: ((v << 2) | (v >> 4)) & 0xFF fl = pal >> 3 render = lambda I: p6((fl << 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)[canvas] s = np.asarray(Image.open(f"{a.snap}/x68000/0000.png").convert("RGB")).astype(int) fail = [] if s.shape[:2] != (512, 256): fail.append(f"1. geometry: expected 512x256, got {s.shape[1]}x{s.shape[0]}") else: 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] - d.H) // 2 act = g[yoff:yoff+d.H] if not np.array_equal(act, exp): diff = abs(act - exp) bad = diff.any(2) by, bx = np.where(bad) blocks = sorted(set(zip((by//4).tolist(), (bx//4).tolist()))) fail.append(f"3. frame {NF-1} not pixel-exact: {bad.sum()} px in " f"{len(blocks)} blocks differ, maxdiff {diff.max()}; " f"first block (by={blocks[0][0]}, bx={blocks[0][1]})") for x in fail: print("FAIL " + x) if fail: sys.exit(1) print(f"OK {NF} frames decoded on the 68000, final frame pixel-exact " f"against tools/encoder/dlx.py") print(f" {d.W}x{d.H}, {d.nb} blocks/frame, k1={d.k1} k4={d.k4}, " f"all four block modes exercised")