mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
The firmware's tftp_open hangs in its resolver from the rdebug process, so the payload speaks RFC 783 over the firmware socket API: socket(2,2,0), sendto(RRQ), recvfrom(DATA)/ACK into a malloc'd buffer, close. Verified live: 600 B fetched from 192.168.99.1 into the malloc buffer, server RRQ logged, and rdebug peek matches probe.bin byte-for-byte. Notes: dwt/notes/TCPIP.md (function table, ABI, limits) and the loader/transport hand-off in dwt/notes/LOADER_CROSS.md.
106 lines
3.8 KiB
Lua
106 lines
3.8 KiB
Lua
-- probe.lua -- watch the dwt/tcpip payloads from inside MAME.
|
|
--
|
|
-- Samples marker cells and the CPU PC once per frame. This is independent of
|
|
-- the terminal's network stack, so it still reports after a payload has wedged
|
|
-- rdebug or TCP. PCs inside the payload or the socket/TFTP code are printed,
|
|
-- which is how a hang is localised.
|
|
--
|
|
-- <mame> vt1200 ... -debug -debugger none \
|
|
-- -autoboot_script dwt/tcpip/probe.lua -autoboot_delay 0
|
|
--
|
|
-- Stop with PROBE_END=<frame> (default 20000).
|
|
|
|
local machine = manager.machine
|
|
local cpu = machine.devices[':maincpu']
|
|
local prog = cpu.spaces['program']
|
|
|
|
local function hex(v) return string.format("%08X", v & 0xFFFFFFFF) end
|
|
|
|
local M = {
|
|
-- tfetch.asm
|
|
stage = 0x01871000, fd = 0x01871020, buf = 0x01871040,
|
|
total = 0x01871060, last = 0x01871080, done = 0x018710A0,
|
|
close = 0x018710C0, sp = 0x01871200, a14 = 0x01871220,
|
|
-- launcher.asm
|
|
lstage = 0x01872500, pcb = 0x01872520,
|
|
-- socktest.asm
|
|
sstage = 0x01874000, sfd = 0x01874020,
|
|
-- tftpc.asm
|
|
cstage = 0x0187C000, cfd = 0x0187C020, cbuf = 0x0187C040,
|
|
ctotal = 0x0187C060, clast = 0x0187C080, cblock = 0x0187C0A0,
|
|
cdone = 0x0187C0C0,
|
|
}
|
|
|
|
local END_AT = tonumber(os.getenv("PROBE_END") or "1000000")
|
|
local last = {}
|
|
local frames = 0
|
|
local pc_lines = 0
|
|
local checked = false
|
|
|
|
-- Ground truth for the fetch: read the malloc'd buffer from inside MAME and
|
|
-- compare it with the file the server holds. This avoids trusting rdebug peek.
|
|
local function check_buffer(buf, total)
|
|
local f = io.open("dwt/tcpip/probe.bin", "rb")
|
|
if not f then print("PROBE: no probe.bin to compare") return end
|
|
local want = f:read("*a")
|
|
f:close()
|
|
local n = math.min(total, #want)
|
|
local bad, first = 0, -1
|
|
for i = 0, n - 1 do
|
|
local got = prog:read_u8(buf + i * 8)
|
|
if got ~= want:byte(i + 1) then
|
|
bad = bad + 1
|
|
if first < 0 then first = i end
|
|
end
|
|
end
|
|
print(string.format("PROBE BUFFER buf=%s total=%d file=%d bad=%d first_bad=%d",
|
|
hex(buf), total, #want, bad, first))
|
|
end
|
|
|
|
local function in_range(pc, lo, hi) return pc >= lo and pc < hi end
|
|
|
|
local function sample()
|
|
frames = frames + 1
|
|
local vals = {}
|
|
for k, a in pairs(M) do vals[k] = prog:read_u32(a) end
|
|
local pc = cpu.state['PC'].value
|
|
local changed = false
|
|
for k, v in pairs(vals) do
|
|
if last[k] ~= v then changed = true end
|
|
end
|
|
local interesting = in_range(pc, 0x01870000, 0x01874000)
|
|
or in_range(pc, 0x3F5C0000, 0x3F5F0000)
|
|
or in_range(pc, 0x3F700000, 0x3F710000)
|
|
if changed or interesting or frames % 120 == 1 then
|
|
if interesting then pc_lines = pc_lines + 1 end
|
|
if not interesting or pc_lines <= 400 then
|
|
print(string.format("PROBE f=%d pc=%s stage=%s fd=%s buf=%s total=%s last=%s done=%s close=%s sp=%s a14=%s lstage=%s pcb=%s sstage=%s sfd=%s",
|
|
frames, hex(pc), hex(vals.stage), hex(vals.fd), hex(vals.buf),
|
|
hex(vals.total), hex(vals.last), hex(vals.done),
|
|
hex(vals.close), hex(vals.sp), hex(vals.a14),
|
|
hex(vals.lstage), hex(vals.pcb), hex(vals.sstage), hex(vals.sfd)))
|
|
print(string.format("TCPC cstage=%s cfd=%s cbuf=%s ctotal=%s clast=%s cblock=%s cdone=%s",
|
|
hex(vals.cstage), hex(vals.cfd), hex(vals.cbuf), hex(vals.ctotal),
|
|
hex(vals.clast), hex(vals.cblock), hex(vals.cdone)))
|
|
end
|
|
end
|
|
if vals.cdone == 0x05A5A5A5 and not checked then
|
|
checked = true
|
|
check_buffer(vals.cbuf, vals.ctotal)
|
|
end
|
|
last = vals
|
|
if frames >= END_AT then
|
|
print("PROBE end")
|
|
machine:exit()
|
|
end
|
|
end
|
|
|
|
if not machine.debugger then
|
|
print("PROBE ERROR: pass -debug")
|
|
machine:exit()
|
|
return
|
|
end
|
|
|
|
emu.register_frame_done(sample, "probe")
|
|
print("PROBE armed")
|