From c19c603d773491faaf0509a1855a22ad38d3e2a6 Mon Sep 17 00:00:00 2001 From: SDD Date: Fri, 16 Oct 2026 12:55:36 +0300 Subject: [PATCH] harness: VT1200_DUART_IP3 -- drive DUART1 IP3 high to take the OS cold-start gate Add an opt-in knob (VT1200_DUART_IP3=1) that asserts DUART1 input-port bit 3. The POST gate at bit 0x3F2042D0 tests IP3 (@0x64000D0 bit 3) and, when it is high and the 0x6200000 status handshake did not return 0x55, branches to the OS cold-start 0x3F5788C0 (jauc), skipping the whole level-1 batch: the sole caller of the level-1 runner 0x3FDF72D0 is 0x3F204400, inside the skipped region. Verified from the raw opcodes against MAME's tms34010 opcode table (BTST_K sets Z=NOT bit; jane=JNE absolute-long) and empirically. Default behaviour is unchanged (no env -> the gate is not taken). The real terminal reads IP3 low, so this is a modelled board input, not a faithful POST path; results from a level-1-skipped harness are not evidence about the real hardware's POST, but are evidence about the OS/graphics route. osprobe.lua: gate/OS/Park probe (breakpoints on 0x3F2043B0 / 0x3F204400 / 0x3F5788C0, per-frame PC histogram, optional instruction trace and RAM-global dump). --- harness/mame/osprobe.lua | 101 +++++++++++++++++++++++++++++++++++++++ harness/mame/vt1200.cpp | 17 +++++++ 2 files changed, 118 insertions(+) create mode 100644 harness/mame/osprobe.lua diff --git a/harness/mame/osprobe.lua b/harness/mame/osprobe.lua new file mode 100644 index 0000000..d88739f --- /dev/null +++ b/harness/mame/osprobe.lua @@ -0,0 +1,101 @@ +-- 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. +-- +-- 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 + 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' diff --git a/harness/mame/vt1200.cpp b/harness/mame/vt1200.cpp index ff7cb0b..8d4e736 100644 --- a/harness/mame/vt1200.cpp +++ b/harness/mame/vt1200.cpp @@ -53,6 +53,18 @@ 0x0620000; default "12". VT1200_DUART_LB1/LB2=1 external TX->RX loopback on DUART1 / DUART2. VT1200_DUARTLOG= every DUART access (not de-duplicated). + VT1200_DUART_IP3=1 drive DUART1 IP3 (input-port bit 3) high. + The boot gate at bit 0x3F2042D0 tests IP3 + (firmware @0x64000D0 bit 3) and, when set + and the 0x6200000 status handshake did not + return 0x55, branches to the OS cold-start + 0x3F5788C0 -- skipping ALL of the level-1 + batch (the sole caller of the level-1 + runner 0x3FDF72D0 is 0x3F204400, inside the + skipped region). This is a modelled board + input, not a ROM change: the real terminal + reads IP3 low. See + reference/level1-bypass-analysis.md. Video: the driver paints the active scanlines itself, reading the framebuffer out of video RAM (see render_active_line). VT1200_NO_RENDER=1 @@ -467,6 +479,11 @@ void vt1200_state::machine_start() m_duart1->ip4_w(1); m_duart1->ip5_w(1); m_duart1->ip6_w(1); + // VT1200_DUART_IP3=1 drives DUART1 IP3 high, which makes the + // boot gate at bit 0x3F2042D0 take the OS cold-start branch + // (0x3F5788C0) and skip the whole level-1 self-test batch. + if (getenv("VT1200_DUART_IP3")) + m_duart1->ip3_w(1); } }