Files
vt1200/harness/mame/l1patch.lua
T
SDD 9d912ae084 harness: l1patch.lua -- runtime level-1 host-port patch + test-map probe
Runtime region-writes (on-disk ROM untouched) applied after the level-0 ROM
checksum:
  three known failing checks: 3FA73980 CA04->C004, 3FA76060 CB04->C004,
    3FA760F0 CB04->C004 (skip the c6/c0/c1 flag stores)
  VT1200_L1PATCH_EXTRA adds the second-round checks 3FA761F0 / 3FA763A0
    (the retry loop reaches c3/c2 once c0/c1 stop short-circuiting)
  VT1200_L1PATCH_VERDICT optionally forces L3FA6E4B0's return (3FA71B60
    4D28 move a9,a8 -> 5708 clr a8)

With the five branch patches the HOST PORT test's caller returns 0 and no
'HOST PORT - 15' is printed, but level 1 does not complete: the CPU stays in
the DUART ISR chain (INT1 0x0F60 -> 3FA92F40 -> L3FA78D90 -> 3FA78xxx-3FA7Bxxx)
for >19000 frames with no error.  l1p_pie_frame1500.png is a frame dump of the
self-test circle/pie -- the renderer draws curves, fills and blits correctly.
2026-10-16 13:09:45 +03:00

95 lines
4.0 KiB
Lua

-- 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=<n> 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=<n> 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'