Get a real Dragon's Lair frame onto the emulated X68000

First pixels on an actual X68000 screen. Everything up to now was Python-side
or a headless -video none run, which cannot snapshot at all.

The blocker was not the video controller. The IPL leaves CRTC R20 = 0x0B16,
and bit 11 is "G-VRAM set to buffer", which makes MAME's draw_gfx() return
early. GVRAM writes still land and read back correctly while the layer is
invisible, so six attempts at $E82400/$E82500/$E82600 all rendered black with
every register holding the value I intended.

Two more facts, both confirmed against MAME 0.277 source rather than assumed:

- $E8E001 monitor contrast is left at 14 by the IPL, scaling all output to
  93.3%. The player must set it to 15. Contrast 0 blanks the screen, which is
  a free fade-to-black for scene transitions.
- The palette word is GGGGGRRRRRBBBBBI with a shared LSB, expanded as
  pal6bit((field<<1)|I). With contrast at 15 the render is pixel-exact, not
  merely close, which also confirms the 1024-byte GVRAM line stride.

That exactness gives a new quality ceiling: the 15-bit+I palette alone costs
38.88 dB against the 24-bit palettised source, the same order as the scsi
profile's own codec error. scsi is close to display-transparent on hardware,
which bounds how much further it is worth raising.

Unblocks next step 2, the 68000 decoder skeleton, which now has a known-good
reference image to diff against.

Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
prosolis
2026-08-23 13:12:27 -07:00
parent 22c67f1cb8
commit b322e84cd4
7 changed files with 198 additions and 3 deletions
+29
View File
@@ -0,0 +1,29 @@
#!/usr/bin/env python3
"""Frame -> flat (RGB888 palette + index plane) blob for the MAME Lua loader.
Packing into the X68000 palette word is done Lua-side on purpose: the exact
channel order is a hardware fact we intend to CONFIRM BY EYE, not assume, so it
has to be cheap to change without regenerating the blob.
"""
import sys, struct, glob
import numpy as np
from PIL import Image
src, out = sys.argv[1], sys.argv[2]
f = sorted(glob.glob(f"{src}/*.png"))[int(sys.argv[3]) if len(sys.argv) > 3 else 0]
im = Image.open(f).convert("RGB")
W, H = im.size
q = im.quantize(colors=256, method=Image.MEDIANCUT, dither=Image.NONE)
pal = np.array(q.getpalette()[:256*3], dtype=np.uint8).reshape(256, 3)
idx = np.asarray(q, dtype=np.uint8)
with open(out, "wb") as fh:
fh.write(b"DLXR")
fh.write(struct.pack(">HH", W, H))
fh.write(pal.tobytes())
fh.write(idx.tobytes())
# reference PNG of exactly what the X68000 should display
Image.fromarray(pal[idx]).save(out.replace(".bin", "_ref.png"))
print(f"src={f} {W}x{H} colors={len(np.unique(idx))} -> {out}")