#!/usr/bin/env python3 """Check the 68000's load-time output against tools/bench/dlxload.py, byte for byte. python3 tools/bench/verify_load.py [--out tmp/load] The 68000 ran src/player/load.i over the RAW container header; tools/bench/ load.lua read the results back out of emulated RAM and out of the PALETTE REGISTERS. This compares them with what the host-side transforms produce. Byte-for-byte and not "close enough", for both halves: * the codebooks are indices, so a single wrong byte is a wrong COLOUR in every block that uses that codeword, in every frame of the scene. * the palette words carry the shared LSB the encoder's 1.96 dB (FINDINGS 23.3) depends on, and a wrong choice of it is invisible in a diff of the picture's SHAPE -- it is a slightly wrong colour, which is exactly the sort of thing that gets attributed to the codec. The darkest-entry index is checked too: it is what the letterbox is filled with until the encoder reserves a black entry (23.4, still open), and it comes out of an argmin whose tie-break has to match numpy's -- first index wins. """ import sys, argparse sys.path.insert(0, "tools/encoder") sys.path.insert(0, "tools/bench") from dlx import DLX import dlxload as DL ap = argparse.ArgumentParser() ap.add_argument("container") ap.add_argument("--out", default="tmp/load") ap.add_argument("--log", default="tmp/load_check.log", help="the rig's log, for the DARK= line it printed") a = ap.parse_args() d = DLX(a.container) cb1, cb4 = DL.expand_codebooks(d) palb, dark, _ = DL.pack_palette(d) want = cb1.tobytes() + cb4.tobytes() + palb.tobytes() got = open(a.out + "_out.bin", "rb").read() if len(got) != len(want): sys.exit(f"FAIL: the 68000 produced {len(got)} B, expected {len(want)}") n1, n4 = cb1.nbytes, cb4.nbytes sections = (("CB1", 0, n1), ("CB4", n1, n1 + n4), ("palette", n1 + n4, len(want))) bad = 0 for name, lo, hi in sections: diff = [i for i in range(lo, hi) if got[i] != want[i]] if diff: bad += len(diff) i = diff[0] print(f"FAIL: {name}: {len(diff)}/{hi-lo} bytes differ; first at " f"+{i-lo} (68000 {got[i]:#04x}, dlxload {want[i]:#04x})") else: print(f" OK {name}: {hi-lo} B identical to dlxload.py") # The rig prints the index the 68000 chose; parse it rather than re-deriving, # so a rig that failed to read LDARK cannot pass by silence. got_dark = None for line in open(a.log, "rb").read().decode("utf-8", "replace").splitlines(): if "DARK=" in line: got_dark = int(line.split("DARK=")[1].split()[0].rstrip(",")) if got_dark is None: sys.exit("FAIL: the rig printed no DARK= line -- it did not reach the dump") if got_dark != dark: sys.exit(f"FAIL: darkest palette entry: 68000 says {got_dark}, dlxload says {dark}") print(f" OK darkest entry {dark}, chosen by the same argmin tie-break") if bad: sys.exit(f"FAIL: {bad} bytes differ in total") print(f"OK the 68000 reproduced all {len(want)} B of load-time output exactly " f"(P1 codebooks, P2 palette)")