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
30 lines
1.0 KiB
Python
30 lines
1.0 KiB
Python
#!/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}")
|