-- l1patch.lua -- runtime ROM patch for the level-1 HOST PORT test, plus a -- console capture, so level 1 can run past it and report the rest of the tests. -- -- Mechanism: manager.machine.memory.regions[':maincpu']:write_u16() writes into -- the host-side region buffer (bypasses the read-only CPU mapping). The -- on-disk ROM is never touched. Applied after the level-0 ROM checksum. -- -- VT1200_L1PATCH=1 enable the patch -- VT1200_L1PATCH_AT= frame to apply at (default 50) -- VT1200_L1PATCH_EXTRA=1 also patch the two extra L3FA753D0 checks -- VT1200_L1PATCH_VERDICT=1 also force L3FA6E4B0's return to 0 -- VT1200_L1PATCH_MAX= exit frame (default 3000) local machine = manager.machine local cpu = machine.devices[':maincpu'] local dbg = machine.debugger local mem = machine.memory if not dbg then print("ERROR: no debugger"); machine:exit(); return end local ROM_BIT_BASE = 0x3F200000 local region = mem.regions[':maincpu'] local function bitoff(a) return (a - ROM_BIT_BASE) >> 3 end local enabled = (os.getenv("VT1200_L1PATCH") ~= nil) local at = tonumber(os.getenv("VT1200_L1PATCH_AT") or "50") local maxframes = tonumber(os.getenv("VT1200_L1PATCH_MAX") or "3000") -- c0/c1 (and c6) are the report's three known failing checks; c2/c3 are the -- second-round checks the retry loop reaches once c0/c1 stop short-circuiting. local patches = { { 0x3FA73980, 0xCA04, 0xC004, "L3FA71BC0 flag c6 (SRA parity) -> skip store" }, { 0x3FA76060, 0xCB04, 0xC004, "L3FA753D0 flag c0 (OVR) -> skip store" }, { 0x3FA760F0, 0xCB04, 0xC004, "L3FA753D0 flag c1 (FFULL) -> skip store" }, } if os.getenv("VT1200_L1PATCH_EXTRA") then patches[#patches + 1] = { 0x3FA761F0, 0xCA04, 0xC004, "L3FA753D0 flag c3 (direct SRA read) -> skip store" } patches[#patches + 1] = { 0x3FA763A0, 0xCA04, 0xC004, "L3FA753D0 flag c2 (framing) -> skip store" } end if os.getenv("VT1200_L1PATCH_VERDICT") then patches[#patches + 1] = { 0x3FA71B60, 0x4D28, 0x5708, "L3FA6E4B0 verdict: move a9,a8 -> clr a8 (force return 0)" } end local function apply_patches() if not region then print("L1PATCH ERROR: no region"); return end print(string.format("L1PATCH applying %d patches", #patches)) for _, p in ipairs(patches) do local off = bitoff(p[1]) local before = region:read_u16(off) if before ~= p[2] then print(string.format("L1PATCH ERROR %08X: expected %04X found %04X", p[1], p[2], before)) else region:write_u16(off, p[3]) print(string.format("L1PATCH %08X off=%#010x %04X -> %04X (cpu reads %04X) %s", p[1], off, p[2], p[3], cpu.spaces['program']:read_u16(p[1]), p[4])) end end end -- console printer: a9 = message pointer (resolved offline) dbg:command('bpset 3fde00c0,,{ printf "PRINT pc=%08X a9=%08X a11=%08X\\n", pc, a9, a11 ; g }') -- host-port caller return (level-1 generic tester verdict) dbg:command('bpset 3fa84fe0,,{ printf "HOSTPORT-RET a8=%08X\\n", a8 ; g }') if enabled and at == 0 then apply_patches() end local pc = cpu.state['PC'] local n = 0 local hist, first, last = {}, {}, 0 emu.register_frame_done(function() n = n + 1 if enabled and n == at then apply_patches() end if os.getenv("VT1200_L1PATCH_TRACE") and n == tonumber(os.getenv("VT1200_L1PATCH_TRACE_AT") or "1") then dbg:command('trace ' .. os.getenv("VT1200_L1PATCH_TRACE")) end if os.getenv("VT1200_L1PATCH_TRACE") and n == (tonumber(os.getenv("VT1200_L1PATCH_TRACE_AT") or "1") + 1) then dbg:command('trace off') end local v = pc.value hist[v] = (hist[v] or 0) + 1 if not first[v] then first[v] = n end last = v if n >= maxframes then print(string.format("--- frames=%d last PC=%08X ---", n, last)) local list = {} for k, c in pairs(hist) do list[#list + 1] = { k, c } end table.sort(list, function(a, b) return a[2] > b[2] end) for i = 1, math.min(12, #list) do print(string.format(" PC=%08X samples=%d first_frame=%d", list[i][1], list[i][2], first[list[i][1]])) end machine:exit() end end, "vt1200-l1patch") dbg.visible_cpu = cpu dbg.execution_state = 'run'