Files
Dragon-s-Lair-X68k/tools/bench/show_frame.lua
T
prosolis b322e84cd4 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
2026-08-23 13:12:27 -07:00

48 lines
2.0 KiB
Lua

-- Real frame, 256-colour, with the ACTUAL display gate from MAME's source:
-- CRTC R20 bit 11 = "G-VRAM set to buffer" -> graphics layer invisible.
-- CRTC R20 bits 9-8 = colour setup: 0x0100 = 256-colour, low byte per word.
-- The IPL leaves R20 = 0x0B16 (buffered + 65536c), which is why every earlier
-- attempt rendered black no matter what the video controller said.
M=manager.machine; SP=M.devices[":maincpu"].spaces["program"]; SUB=nil
local CRTC20 = 0xE80000 + 20*2
local GVRAM, GPAL = 0xC00000, 0xE82000
local f=io.open("frame.bin","rb"); local d=f:read("a"); f:close()
local function B(i) return string.byte(d,i) end
local W,H = B(5)*256+B(6), B(7)*256+B(8)
local PAL0, PIX0 = 9, 9+256*3
-- GGGGGRRRRRBBBBBI, confirmed from x68k_v.cpp
local function pack(r,g,b) return ((g>>3)<<11)|((r>>3)<<6)|((b>>3)<<1)|1 end
local function T() local t=M.time; return t.seconds+t.attoseconds/1e18 end
local st,tp="wait",nil
SUB = emu.add_machine_frame_notifier(function()
local t=T()
if st=="wait" then
if t<3.0 then return end
local old = SP:read_u16(CRTC20)
local new = (old & ~0x0B00) | 0x0100 -- clear buffer bit, select 256c
SP:write_u16(CRTC20, new)
SP:write_u16(0xE82400, 0x0001) -- video ctrl: 256 colours
SP:write_u16(0xE82600, 0x001F) -- graphics + pages on, text off
SP:write_u8(0xE8E001, 15) -- monitor contrast: IPL leaves it at 14
for c=0,255 do
local o=PAL0+c*3
SP:write_u16(GPAL+c*2, pack(B(o),B(o+1),B(o+2)))
end
for y=0,H-1 do
local row,base = PIX0+y*W, GVRAM+y*1024
for x=0,W-1 do SP:write_u16(base+x*2, B(row+x)) end
end
print(string.format("[SHOW3] R20 %04X->%04X E82400=%04X E82600=%04X gv=%04X t=%.3f",
old, SP:read_u16(CRTC20), SP:read_u16(0xE82400), SP:read_u16(0xE82600),
SP:read_u16(GVRAM), t))
st,tp="painted",t
elseif st=="painted" and t>tp+0.30 then
M.video:snapshot(); print("[SHOW3] snapshot"); st="done"; M:exit()
end
end)