Files
Dragon-s-Lair-X68k/tools/bench/show_frame256.lua
T
prosolis 34f9ee341d A real 256x256 CRTC mode, derived not recalled; palette ceiling was 2 dB low
Session 3 left the harness on the IPL's 768x512 text timing because no CRTC
values had been derived and guessing them was the failure mode to avoid. This
derives them from MAME 0.277's divisor ladder instead, and the derivation is
self-checking: the 256-wide mode runs at div 6 against the 768 mode's div 2, so
htotal is exactly 1104/3 = 368 dots and every horizontal register divides by
three with no remainder. Only the blanking split rounds. Verified by snapshot:
native 256x512, active area pixel-exact, x=512 wrap gone.

Two things fell out that change numbers elsewhere:

- The palette's shared LSB I must be chosen per entry, not hardcoded to 1.
  Doing so lifts the display ceiling from 38.85 to 40.81 dB and is the only way
  to reach true black at all, since pal6bit(1) = 4. 102 of 256 entries want
  I = 0, so this is not a corner case. Supersedes FINDINGS 22.4; scsi has ~2 dB
  more headroom than that section claimed. The encoder does not do this yet.

- Letterboxing costs a palette entry: GVRAM cleared to zero shows entry 0, and
  a free mediancut palette puts a real image colour there. 255 colours plus a
  reserved black, via prep_frame.py --reserve-black.

MAME's graphics double-scan is phase-shifted one raster line (it halves the
absolute scanline and vbegin is odd), which produced a false failure before it
was understood; the regression test now asserts the shifted pairing explicitly.

Still Lua-side. No 68000 instruction has drawn a pixel; the 38% blit estimate
remains unvalidated. What this buys is a defined geometry for the decoder to
write into: 256 words per row, 1024-byte stride, rows 32..223.

Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
2026-08-23 13:27:17 -07:00

73 lines
2.7 KiB
Lua

-- Same as show_frame.lua, but sets a REAL 256x256 CRTC mode instead of
-- borrowing the IPL's 768x512 text timing. Proves the mode table in
-- crtc_mode.lua and removes the x=512 wrap of FINDINGS 22.5.
M=manager.machine; SP=M.devices[":maincpu"].spaces["program"]; SUB=nil
local function load_mode()
for _,p in ipairs{"../tools/bench/crtc_mode.lua","tools/bench/crtc_mode.lua","crtc_mode.lua"} do
local f=loadfile(p); if f then return f() end
end
error("crtc_mode.lua not found")
end
local MODE = load_mode()
local GVRAM, GPAL = 0xC00000, 0xE82000
local f=io.open("frame256.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
local YOFF = (MODE.height - H) // 2 -- letterbox 192 rows inside 256
-- GGGGGRRRRRBBBBBI, confirmed from x68k_v.cpp. The LSB "I" is SHARED by all
-- three channels: each renders as pal6bit((field<<1)|I). Hardcoding I=1 (as
-- show_frame.lua does) makes true black unreachable -- pal6bit(1) = 4 -- so I
-- is chosen per entry to minimise summed squared error over R,G,B.
local function pal6(v) return ((v<<2)|(v>>4)) & 0xff end
local function pack(r,g,b)
local f = {r>>3, g>>3, b>>3}
local best, bestI = nil, 1
for I=0,1 do
local e=0
for c=1,3 do
local want = ({r,g,b})[c]
local d = pal6((f[c]<<1)|I) - want
e = e + d*d
end
if best==nil or e<best then best,bestI = e,I end
end
return (f[2]<<11)|(f[1]<<6)|(f[3]<<1)|bestI
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
MODE.apply(SP)
-- clear the letterbox rows: GVRAM holds IPL leftovers, not zeros
for y=0,MODE.height-1 do
if y<YOFF or y>=YOFF+H then
local base=GVRAM+y*1024
for x=0,MODE.width-1 do SP:write_u16(base+x*2,0) end
end
end
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+YOFF)*1024
for x=0,W-1 do SP:write_u16(base+x*2, B(row+x)) end
end
print(string.format("[256] R00-R08 %d %d %d %d %d %d %d %d %d R20=%04X yoff=%d t=%.3f",
SP:read_u16(0xE80000),SP:read_u16(0xE80002),SP:read_u16(0xE80004),SP:read_u16(0xE80006),
SP:read_u16(0xE80008),SP:read_u16(0xE8000A),SP:read_u16(0xE8000C),SP:read_u16(0xE8000E),
SP:read_u16(0xE80010),SP:read_u16(0xE80028), YOFF, t))
st,tp="painted",t
elseif st=="painted" and t>tp+0.30 then
M.video:snapshot(); print("[256] snapshot"); st="done"; M:exit()
end
end)