Files
SDD 6cbb362bb9 harness: VT1200_DUART1_INT2 -- route DUART1's IRQ to INT2 (A/B knob)
The OS's DUART1 driver services the 0x6400000 window from the INT2 stub
(0x0F30 -> 0x3FA93070); with the default INT1 wiring the DUART1 TxRDY IRQ is
handed to the INT1 stub's 0x5800000 board-DMA handler (0x3F7151F0 ->
0x3F9D5B40), which never reads the DUART ISR, so INTPEND's INT1 bit stays
latched and the CPU re-enters the handler ~387x per frame.

Empirical A/B (level-1-skipped boot via VT1200_DUART_IP3):
  default (INT1): INT1 stub entered 387x in one frame; screen frozen.
  VT1200_DUART1_INT2=1: zero interrupt entries in a one-frame trace;
    the OS reads DUART1 ISR (reg5) at pc=3FB43900 right after the IRQ.
INTENB reads 0x0606 (INT1|INT2|HI|DI), so the routing (not the enable mask)
is what identifies the line.  Opt-in; default unchanged.

osprobe.lua: also print INTENB and DUART1 MR1A/CSRA in the globals dump.
2026-10-16 13:01:40 +03:00

106 lines
3.9 KiB
Lua

-- osprobe.lua -- observe the DUART1-IP3 gate and the OS cold-start.
--
-- Reports:
-- * whether the boot gate at 0x3F2042D0 takes the OS branch (0x3F2043B0)
-- instead of the level-1 runner (0x3F204400),
-- * whether the OS cold-start at 0x3F5788C0 is reached,
-- * a per-frame PC histogram so the parked PC (the first wall) is explicit.
--
-- <mame> vt1200 -rompath roms ... -debug -debugger none -debuglog \
-- -autoboot_script harness/mame/osprobe.lua -autoboot_delay 0
-- OS_FRAMES overrides the frame budget (default 3000).
local machine = manager.machine
local cpu = machine.devices[':maincpu']
local dbg = machine.debugger
local pc = cpu.state['PC']
if not dbg then print("ERROR: debugger not enabled; pass -debug"); machine:exit(); return end
-- Empirical: what memory does the driver actually give the CPU?
do
local shares = machine.memory.shares
local tags = {}
for t, _ in pairs(shares) do tags[#tags + 1] = t end
table.sort(tags)
for _, t in ipairs(tags) do
local s = shares[t]
print(string.format("SHARE %s size=%d bytes bytewidth=%d length=%d",
tostring(s.tag), s.size, s.bytewidth, s.length))
end
end
local maxframes = tonumber(os.getenv("OS_FRAMES") or "3000")
local recent = tonumber(os.getenv("OS_RECENT") or "300")
local frames = 0
local hist = {}
local first = {}
local lastpc = 0
local rhist = {}
local rfirst = {}
local gate = { skip = 0, run = 0, os = 0 }
local function bp(addr, body)
dbg:command('bpset ' .. addr .. ',,{ ' .. body .. ' ; g }')
end
bp("3f2043b0", 'printf "GATE-SKIP-BRANCH\\n"')
bp("3f204400", 'printf "GATE-RUN-LEVEL1\\n"')
bp("3f5788c0", 'printf "OS-COLD-START pc=%08X\\n", pc')
bp("3fdf72d0", 'printf "LEVEL1-RUNNER-ENTER\\n"')
emu.register_frame_done(function()
frames = frames + 1
if os.getenv("OS_TRACE") and frames == tonumber(os.getenv("OS_TRACE_AT") or "1") then
dbg:command('trace ' .. os.getenv("OS_TRACE"))
print("TRACE-ON " .. os.getenv("OS_TRACE"))
end
if os.getenv("OS_TRACE") and frames == (tonumber(os.getenv("OS_TRACE_AT") or "1") + tonumber(os.getenv("OS_TRACE_FRAMES") or "1")) then
dbg:command('trace off')
print("TRACE-OFF")
end
if os.getenv("OS_GLOBALS_AT") and frames == tonumber(os.getenv("OS_GLOBALS_AT")) then
local p = cpu.spaces['program']
for _, g in ipairs({0x23cbf0, 0x23cc50, 0x23cc30, 0x1b3900, 0x1e9420, 0x16f0}) do
local v = p:read_u32(g)
print(string.format("GLOBAL @%08X = %08X", g, v))
end
-- TMS34010 on-chip INTENB (0xC0000110): which external lines are enabled.
print(string.format("INTENB = %04X", p:read_u16(0xc0000110)))
-- DUART1 MR1A (reg0) and CSRA (reg2) are side-effect-free reads.
print(string.format("DUART1 MR1A=%02X CSRA=%02X", p:read_u16(0x6400000) & 0xff, p:read_u16(0x6400020) & 0xff))
end
local v = pc.value
hist[v] = (hist[v] or 0) + 1
if not first[v] then first[v] = frames end
lastpc = v
if frames > maxframes - recent then
rhist[v] = (rhist[v] or 0) + 1
if not rfirst[v] then rfirst[v] = frames end
end
if frames >= maxframes then
local n = 0
for _ in pairs(hist) do n = n + 1 end
print(string.format("--- frames=%d distinct_pcs=%d ---", frames, n))
print("--- hottest PCs (one sample per frame) ---")
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(20, #list) do
print(string.format(" PC=%08X samples=%d first_frame=%d",
list[i][1], list[i][2], first[list[i][1]]))
end
print(string.format("--- last frame PC=%08X ; last %d frames ---", lastpc, recent))
local rlist = {}
for k, c in pairs(rhist) do rlist[#rlist + 1] = { k, c } end
table.sort(rlist, function(a, b) return a[2] > b[2] end)
for i = 1, math.min(12, #rlist) do
print(string.format(" PARK PC=%08X samples=%d first_frame=%d",
rlist[i][1], rlist[i][2], rfirst[rlist[i][1]]))
end
machine:exit()
end
end, "vt1200-osprobe")
dbg.visible_cpu = cpu
dbg.execution_state = 'run'