Session 1: hardware research, content measurement, codec decision, MAME harness

Verified GVRAM is one word-access per pixel in ALL color modes; chose 256-color
256x192 with movem.l bursts (page 1 sacrificed as double-buffer).

Measured 8 scenes from the Blu-ray source: blit costs under 8% of the 12fps
cycle budget, so I/O is the bottleneck, not CPU. Naive delta+RLE reaches only
3.2:1 (365 KB/s, 470MB) -> decision to use 4x4 vector quantization (~30 KB/s).

"Shot on twos" assumption failed: the transfer has zero duplicate frames, so
12fps requires explicit decimation.

Documents three false measurement results and their root causes (per-frame
Floyd-Steinberg dithering, temporal denoise, exact-match dedupe on noisy source).

MAME Lua injection harness works and is reusable for cycle-cost measurement;
the IOCS _B_READ disk benchmark is blocked returning -1.

Claude-Session: https://claude.ai/code/session_01194oWYW8DQXK1SZ2DnChW6
This commit is contained in:
reala-misaki
2026-08-23 11:23:49 -07:00
commit 65112b9305
19 changed files with 842 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
local function s(x) return tostring(x) end
print("[API] add_machine_frame_notifier = "..s(emu.add_machine_frame_notifier))
print("[API] add_machine_periodic_notifier = "..s(emu.add_machine_periodic_notifier))
print("[API] add_machine_reset_notifier = "..s(emu.add_machine_reset_notifier))
local m=manager.machine
print("[API] time.seconds="..s(m.time.seconds).." atto="..s(m.time.attoseconds))
print("[API] machine.exit="..s(m.exit))
for k,v in pairs(emu) do if tostring(k):find("notif") then print("[API] emu."..s(k)) end end
+53
View File
@@ -0,0 +1,53 @@
M = manager.machine
CPU = M.devices[":maincpu"]
SP = CPU.spaces["program"]
FLAG,STAT,NREAD,BUF = 0x18000,0x18004,0x18008,0x20000
TOTAL = 16*65536
local f=io.open("bench.bin","rb"); local d=f:read("a"); f:close()
CODE={} ; for i=1,#d do CODE[i]=string.byte(d,i) end
print("[BENCH] loaded bench.bin bytes="..#d)
STATE,T0,NFIRE = "wait",nil,0
local function T() local t=M.time return t.seconds + t.attoseconds/1e18 end
local function P(s) print("[BENCH] "..s) end
local function tick()
NFIRE = NFIRE + 1
if NFIRE==1 then P("notifier firing OK") end
local t=T()
if STATE=="wait" then
if t < 5.0 then return end
for i=1,#CODE do SP:write_u8(0x10000+i-1, CODE[i]) end
SP:write_u32(FLAG,0); SP:write_u32(STAT,0); SP:write_u32(NREAD,0); SP:write_u32(BUF,0)
CPU.state["SR"].value=0x2000
CPU.state["SP"].value=0x8000
CPU.state["PC"].value=0x10000
STATE="run"
P(string.format("injected t=%.3f frames=%d",t,NFIRE))
return
end
if STATE=="run" then
local fl=SP:read_u32(FLAG)
if fl==1 and not T0 then T0=t; P(string.format("started t=%.4f",t)) end
if fl==0xFF then
STATE="done"
local dt=t-(T0 or t)
P(string.format("DONE reads=%d status=%08X",SP:read_u32(NREAD),SP:read_u32(STAT)))
P(string.format("elapsed=%.4f s bytes=%d",dt,TOTAL))
if dt>0 then P(string.format("THROUGHPUT = %.1f KB/s",TOTAL/1024/dt)) end
P(string.format("buf=%08X %08X",SP:read_u32(BUF),SP:read_u32(BUF+4)))
M:exit()
elseif fl==0xEE then
STATE="done"; P(string.format("READ FAILED status=%08X reads=%d",SP:read_u32(STAT),SP:read_u32(NREAD))); M:exit()
elseif t>25 then
STATE="done"
P(string.format("TIMEOUT flag=%08X status=%08X reads=%d PC=%08X SR=%04X",
fl,SP:read_u32(STAT),SP:read_u32(NREAD),CPU.state["PC"].value,CPU.state["SR"].value))
M:exit()
end
end
end
SUB = emu.add_machine_frame_notifier(function()
local ok,err = pcall(tick)
if not ok then print("[BENCH] LUA ERROR: "..tostring(err)); M:exit() end
end)
print("[BENCH] subscription="..tostring(SUB))
+36
View File
@@ -0,0 +1,36 @@
FLAG = $18000
STAT = $18004
NREAD = $18008
BUF = $20000
CHUNK = 65536 ; bytes per read
RECS = CHUNK/256 ; 256-byte records per read
NCHUNK = 16 ; 16 x 64KB = 1MB total
org $10000
start:
move.l #1,FLAG.l ; signal: started
move.l #NCHUNK,d6
moveq #0,d5 ; record position
moveq #0,d4 ; completed count
loop:
movem.l d4-d6,-(sp)
moveq #$46,d0 ; _B_READ
move.l #$80000000,d1 ; PDA=$80 (SASI unit0), mode=0
move.l d5,d2 ; position (records)
move.l #CHUNK,d3 ; byte count
lea BUF,a1
trap #15
movem.l (sp)+,d4-d6
move.l d0,STAT.l ; last status
tst.l d0
bmi.s failed
addq.l #1,d4
move.l d4,NREAD.l
add.l #RECS,d5
subq.l #1,d6
bne.s loop
move.l #$FF,FLAG.l ; signal: done OK
stop: bra.s stop
failed:
move.l #$EE,FLAG.l ; signal: error
bra.s stop
+24
View File
@@ -0,0 +1,24 @@
M=manager.machine; CPU=M.devices[":maincpu"]; SP=CPU.spaces["program"]
local f=io.open("one.bin","rb"); local d=f:read("a"); f:close()
CODE={}; for i=1,#d do CODE[i]=string.byte(d,i) end
STATE="wait"
local function T() local t=M.time return t.seconds+t.attoseconds/1e18 end
local function tick()
local t=T()
if STATE=="wait" then
if t<5.0 then return end
for i=1,#CODE do SP:write_u8(0x10000+i-1,CODE[i]) end
SP:write_u32(0x18000,0); SP:write_u32(0x18004,0x5A5A5A5A); SP:write_u32(0x20000,0)
CPU.state["SR"].value=0x2000; CPU.state["SP"].value=0x8000; CPU.state["PC"].value=0x10000
STATE="run"; return
end
local fl=SP:read_u32(0x18000)
if fl==0xFF or t>20 then
STATE="done"
print(string.format("[ONE] %s status=%08X buf=%08X",
os.getenv("SZLABEL") or "?", SP:read_u32(0x18004), SP:read_u32(0x20000)))
M:exit()
end
end
SUB=emu.add_machine_frame_notifier(function()
local ok,e=pcall(tick); if not ok then print("[ONE] ERR "..tostring(e)); M:exit() end end)
+14
View File
@@ -0,0 +1,14 @@
FLAG=$18000
STAT=$18004
BUF=$20000
org $10000
start: move.l #1,FLAG.l
moveq #$46,d0
move.l #$80000000,d1
moveq #0,d2
move.l #256,d3
lea BUF,a1
trap #15
move.l d0,STAT.l
move.l #$FF,FLAG.l
stop: bra.s stop
+5
View File
@@ -0,0 +1,5 @@
local cpu = manager.machine.devices[":maincpu"]
local names={}
for k,v in pairs(cpu.state) do names[#names+1]=tostring(k) end
table.sort(names)
print("[REGS] "..table.concat(names,", "))
+32
View File
@@ -0,0 +1,32 @@
M=manager.machine; CPU=M.devices[":maincpu"]; SP=CPU.spaces["program"]
local f=io.open("sweep.bin","rb"); local d=f:read("a"); f:close()
CODE={}; for i=1,#d do CODE[i]=string.byte(d,i) end
STATE="wait"
local function T() local t=M.time return t.seconds+t.attoseconds/1e18 end
local function tick()
local t=T()
if STATE=="wait" then
if t<5.0 then return end
for i=1,#CODE do SP:write_u8(0x10000+i-1,CODE[i]) end
SP:write_u32(0x18000,0)
for i=0,31 do SP:write_u32(0x18100+i*4,0x5A5A5A5A) end
CPU.state["SR"].value=0x2000; CPU.state["SP"].value=0x8000; CPU.state["PC"].value=0x10000
STATE="run"; print("[SW] injected")
return
end
if STATE=="run" then
local fl=SP:read_u32(0x18000)
if fl==0xFF or t>25 then
STATE="done"
print("[SW] flag="..string.format("%X",fl))
for i=0,15 do
local a=SP:read_u32(0x18100+i*8)
local b=SP:read_u32(0x18100+i*8+4)
print(string.format("[SW] PDA=%02X encA(<<24)=%08X encB(<<8)=%08X",0x80+i,a,b))
end
M:exit()
end
end
end
SUB=emu.add_machine_frame_notifier(function()
local ok,err=pcall(tick); if not ok then print("[SW] ERR "..tostring(err)); M:exit() end end)
+34
View File
@@ -0,0 +1,34 @@
FLAG = $18000
RESULT = $18100
BUF = $20000
org $10000
start:
move.l #1,FLAG.l
lea RESULT,a2
moveq #0,d4
outer:
move.l d4,d1 ; encoding A: PDA in bits 31-24
add.l #$80,d1
swap d1
lsl.l #8,d1
bsr.s doread
move.l d0,(a2)+
move.l d4,d1 ; encoding B: PDA in bits 15-8
add.l #$80,d1
lsl.l #8,d1
bsr.s doread
move.l d0,(a2)+
addq.l #1,d4
cmpi.l #16,d4
bne.s outer
move.l #$FF,FLAG.l
stop: bra.s stop
doread:
movem.l d2-d7/a2-a6,-(sp)
moveq #$46,d0
moveq #0,d2
move.l #256,d3
lea BUF,a1
trap #15
movem.l (sp)+,d2-d7/a2-a6
rts