Files

79 lines
3.7 KiB
Lua

-- live_probe.lua -- emulator-side evidence for the rdebug TCP attempt.
--
-- Armed from the frame-1 callback (commands before execution do not fire).
-- Prints to ./debug.log because the harness is started with -debuglog.
-- `dump` writes the exact memory the firmware works on.
local machine = manager.machine
local cpu = machine.devices[':maincpu']
local dbg = machine.debugger
local space = cpu.spaces['program']
if not dbg then machine:exit(); return end
dbg.visible_cpu = cpu
local D = "/home/arti/code/k-space/vt1200/dwt/evidence/run-live"
local MAGIC1 = 0x01810000
local MAGIC2 = 0x01810010
local COUNT = 0x01810020
local VRAM = 0x04019040
local f = 0
local armed = false
local done_at = nil
local function report_magic(tag)
print(string.format("LIVE %s: MAGIC1=%04X MAGIC2=%04X COUNT=%04X",
tag, space:read_u16(MAGIC1), space:read_u16(MAGIC2), space:read_u16(COUNT)))
local filled, holes = 0, 0
for r = 0, 63 do
for c = 0, 15 do
if space:read_u16(VRAM + r * 1024 + c * 16) == 0xFFFF then
filled = filled + 1
else
holes = holes + 1
end
end
end
print(string.format("LIVE %s: VRAM block %d/1024 words FFFF, %d holes", tag, filled, holes))
end
emu.register_frame_done(function()
f = f + 1
if f == 1 and not armed then
armed = true
dbg:command('bpset 3F58AD60,,{ printf "LIVE accept_debug_conn entry pc=%08X\\n", pc ; g }')
dbg:command('bpset 3F59F320,,{ printf "LIVE dispatcher entry pc=%08X\\n", pc ; g }')
dbg:command('bpset 3F59FDC0,,{ printf "LIVE dwt_prf challenge entry pc=%08X\\n", pc ; g }')
dbg:command('bpset 3F5A0200,,{ printf "LIVE challenge sent a13=%08X\\n", a13 ; dump ' .. D .. '/chal_sent.bin,a13,64 ; g }')
-- transform called with the reply buffer (a13+16 bytes = +128 bits)
dbg:command('bpset 3F5A0740,,{ printf "LIVE pre-transform a13=%08X reply=%08X\\n", a13, d@(a13+128) ; dump ' .. D .. '/reply_in.bin,a13+128,64 ; g }')
-- inside 0x3F5ABA40: after the first font render, a13 holds its 8-byte locals
dbg:command('bpset 3F5ABB00,,{ printf "LIVE cipher-in a13=%08X keyptr=%08X sboxptr=%08X\\n", a13, d@0x1c0260, d@0x1c0200 ; dump ' .. D .. '/cipher_in.bin,a13,64 ; dump ' .. D .. '/key.bin,d@0x1c0260,256 ; dump ' .. D .. '/sbox.bin,d@0x1c0200,4096 ; g }')
-- inside 0x3F5ABA40: locals hold the ciphertext before the final render
dbg:command('bpset 3F5AC620,,{ printf "LIVE cipher-out a13=%08X keyptr=%08X\\n", a13, d@0x1c0260 ; dump ' .. D .. '/cipher_out.bin,a13,64 ; g }')
dbg:command('bpset 3F5A08E0,,{ printf "LIVE memcmp-args a9=%08X a11=%08X\\n", a9, a11 ; dump ' .. D .. '/cmp_a9.bin,a9,64 ; dump ' .. D .. '/cmp_a11.bin,a11,64 ; g }')
-- DEBUGGER-ASSISTED: force the challenge comparison to "equal" so the
-- real poke/jump handlers can be exercised over TCP. This is NOT a
-- remote bypass -- it is only here to test the loader behind gate B.
dbg:command('bpset 3F5A0920,,{ printf "LIVE challenge memcmp FORCED a8=%08X -> 0\\n", a8 ; a8 = 0 ; g }')
dbg:command('bpset 3F5A1B50,,{ printf "LIVE rdebug_poke entry pc=%08X\\n", pc ; g }')
dbg:command('bpset 3F5A3890,,{ printf "LIVE rdebug_jump entry pc=%08X\\n", pc ; g }')
print("LIVE armed emulator-side breakpoints")
end
if f % 500 == 0 then
print(string.format("LIVE frame %5d word@0xAECD0=%04X field2..3=%d",
f, space:read_u16(0xaecd0), (space:read_u16(0xaecd0) >> 2) & 3))
end
if done_at == nil and space:read_u16(MAGIC1) == 0xDEAD then
done_at = f
report_magic("payload-ran")
local ok, err = pcall(function()
machine.screens[1]:snapshot(D .. "/loader_frame.png")
end)
print("LIVE snapshot " .. (ok and "saved" or ("failed: " .. tostring(err))))
end
if done_at and f >= done_at + 120 then
print(string.format("LIVE exiting at frame %d (payload seen at %d)", f, done_at))
machine:exit()
end
end, "vt1200-live-probe")
dbg.execution_state = 'run'