mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
103 lines
3.5 KiB
Lua
103 lines
3.5 KiB
Lua
-- gate_probe.lua -- Phase 1 of the rdebug exploit attempt.
|
|
--
|
|
-- * sample the two 16-bit config words at bit 0xAECD0 / 0xAECD2 across boot
|
|
-- * catch every write to that region (who writes 0x8070, and when)
|
|
-- * log the store instructions and the config-object pointers statically found
|
|
--
|
|
-- Diagnostic only: reads memory. Does not write anything.
|
|
--
|
|
-- Breakpoints/watchpoints are armed from the frame-1 callback, not at script
|
|
-- load: commands issued before dbg.execution_state='run' do not fire. Debugger
|
|
-- printf output requires -debuglog (it goes to ./debug.log, not stdout).
|
|
--
|
|
-- <mame> vt1200 -rompath <abs>/harness/mame/roms -video none -sound none \
|
|
-- -nothrottle -debug -debugger none -debuglog -nvram_directory <abs>/nvram \
|
|
-- -autoboot_script <abs>/gate_probe.lua -autoboot_delay 0
|
|
|
|
local machine = manager.machine
|
|
local cpu = machine.devices[':maincpu']
|
|
local dbg = machine.debugger
|
|
local space = cpu.spaces['program']
|
|
|
|
if not dbg then
|
|
print("GATE ERROR: debugger not enabled; pass -debug")
|
|
machine:exit()
|
|
return
|
|
end
|
|
dbg.visible_cpu = cpu
|
|
|
|
local function u16(a) return space:read_u16(a) end
|
|
local function u32(a) return space:read_u32(a) end
|
|
local function u8(a) return space:read_u8(a) end
|
|
|
|
local function str_at(bitptr, max)
|
|
local out = {}
|
|
for i = 0, (max or 96) - 1 do
|
|
local c = u8(bitptr + i * 8)
|
|
if c == 0 then break end
|
|
if c >= 32 and c < 127 then out[#out + 1] = string.char(c)
|
|
else out[#out + 1] = string.format("\\x%02X", c) end
|
|
end
|
|
return table.concat(out)
|
|
end
|
|
|
|
local samples = { [0]=true, [1]=true, [10]=true, [50]=true, [100]=true, [200]=true,
|
|
[600]=true, [1000]=true, [2000]=true, [3000]=true, [4000]=true, [5000]=true }
|
|
local frames = 0
|
|
local armed = false
|
|
|
|
local function arm()
|
|
-- Every write into the 8 bytes around the two words. wpset works in the
|
|
-- program space's address unit = 1 bit, so length 0x40 covers 8 bytes.
|
|
dbg:command('wpset 0xaecc0,0x40,w,,{ printf "WP cfg bit=%08X data=%04X pc=%08X\\n", wpaddr, wpdata, pc ; g }')
|
|
local stores = {
|
|
{ 0x3F329F00, "STORE aecd0" },
|
|
{ 0x3F32A050, "STORE aecd2" },
|
|
{ 0x3F32A1A0, "STORE aed51" },
|
|
{ 0x3F32A2F0, "STORE aed52" },
|
|
{ 0x3F32A440, "STORE aed53" },
|
|
{ 0x3F32A590, "STORE aed54" },
|
|
}
|
|
for _, s in ipairs(stores) do
|
|
dbg:command(string.format(
|
|
'bpset %08X,,{ printf "%s pc=%%08X a7=%%08X a11=%%08X a8=%%08X a13=%%08X\\n", pc, a7, a11, a8, a13 ; g }',
|
|
s[1], s[2]))
|
|
end
|
|
end
|
|
|
|
local function dump_items()
|
|
print("--- config item cells (values as the boot config code reads them)")
|
|
local slots = { 0x155830, 0x1565b0, 0x156c70, 0x157330, 0x1579f0,
|
|
0x1580b0, 0x152230, 0x1514b0, 0x11a4b0 }
|
|
for _, a in ipairs(slots) do
|
|
print(string.format(" @0x%06X = 0x%08X", a, u32(a)))
|
|
end
|
|
local t = {}
|
|
for i = 0, 15 do t[#t + 1] = string.format("%02X", u8(0xaec80 + i * 8)) end
|
|
print("--- bytes @bit 0xAEC80: " .. table.concat(t, " "))
|
|
print(string.format("--- word @0xAECD0 = 0x%04X, 2-bit fields: [0]=%d [1]=%d [2]=%d [3]=%d",
|
|
u16(0xaecd0), (u16(0xaecd0) >> 0) & 3, (u16(0xaecd0) >> 2) & 3,
|
|
(u16(0xaecd0) >> 4) & 3, (u16(0xaecd0) >> 6) & 3))
|
|
end
|
|
|
|
emu.register_frame_done(function()
|
|
frames = frames + 1
|
|
if frames == 1 and not armed then
|
|
armed = true
|
|
arm()
|
|
end
|
|
if samples[frames] then
|
|
print(string.format("SAMPLE frame %5d @0xAECD0=%04X @0xAECD2=%04X pc=%08X",
|
|
frames, u16(0xaecd0), u16(0xaecd2), cpu.state['PC'].value))
|
|
end
|
|
if frames == 3000 then
|
|
dump_items()
|
|
end
|
|
if frames >= tonumber(os.getenv("PROBE_END") or "5000") then
|
|
print(string.format("--- done at frame %d", frames))
|
|
machine:exit()
|
|
end
|
|
end, "vt1200-gate-probe")
|
|
|
|
dbg.execution_state = 'run'
|