The stub run does not park at HOST PORT - 15. Both DUART failures are reported,
the board LED blinks once (finite L3F208660), TYPE 'GO' TO CONTINUE is printed
(no input is read), and the level-1 runner continues ('CONTINUING THE TESTS').
The runner never returns to 3F204440, so 3F208060/3F208650 are never executed.
The run then wedges at frame ~2839 in a DUART1 counter-ready interrupt storm:
the ISR polls DUART1 reg 5 (bit 0x06400050) and always reads 0x08; the counter
runs in timer mode (ACR=0x60) with CTUR/CTLR never programmed, so MAME re-arms
at 1 tick and the interrupt re-asserts every ~229 CPU cycles. Frames at 3500 /
6000 / 12000 are byte-identical; RAM differs by one byte.
Corrects README.md and annotates GATE_CLEANUP_REPORT.md, BRINGUP_LOG.md and
DUART_REPORT.md. Adds a console+PC/milestone instrument and the finding report.
8.5 KiB
The stub run does NOT park at HOST PORT — it continues, then dies in a DUART1 counter storm
Date: 2026-10-16. Configuration: ./run.sh stub (VT1200_IO_PATH_STUB=1), no other
env. ROM vt1200_v22.bin md5 b46a3e442ccb86134d2356bd70d22fd2 (unmodified).
This report corrects a claim that stood as fact in
reference/level1-bypass-analysis.md Q4 and shaped the wording of
harness/mame/README.md: "a level-1 failure is fatal: the runner returns
nonzero → 3F204440 jane L3F208060 → an infinite board-LED loop at
3F208650 jruc L3F208060." Measured, that is wrong for the DUART failures. (The file
reference/level1-bypass-analysis.md is outside this task's owned paths, so its
Q4 text is not edited here; this report is the correction of record and the
owner should annotate it in place.)
All timings are screen frames (the firmware's own refresh), sampled once per
emulated frame. Instrumentation: harness/mame/console_frames.lua,
harness/mame/wall_hist.lua, and breakpoints on the sites named below; frames
and VRAM/RAM dumped via the driver's VT1200_DUMP_* knobs. Console output is the
firmware's own printer (0x3FDE00C0), captured to MAME debug.log with
-debuglog; the on-screen text is from the frame dumps.
Timeline (across time, not one snapshot)
| frame | what happens |
|---|---|
| 0–1 | level 0 passes (stub); level-1 runner entered (3FDF72D0, called from 3F204400); LEVEL1/2 TESTING ** printed |
| 470–481 | HOST PORT test runs; it enables DUART1 interrupts (IMR = 0x07→0x01→0x07, channel A) |
| 484 | HOST PORT fails: dispatcher 3FDF0630 code 0x400 → handler 3FDE9D70 prints LEVEL1/2 ERROR -> HOST PORT - 15, dumps BAUD ERROR 00900040 / FRAME & OVERRUN ERROR 00030000 / INTERRUPT REG BIT 00000002, prints BLINKING SYSTEM BOARD LED, calls L3F208660 (one finite two-digit blink, error code 0x15) |
| 484–1534 | the blink's dsjs delay loops dominate the CPU (hottest PC 3F2088B0, etc.) |
| 1534 | handler prints TYPE 'GO' TO CONTINUE, then jauc L3FDF1010 |
| 1534→1627 | L3FDF1010 takes the return branch (status word *A13(0xFFA0) is zero) → the runner continues to the next test |
| 1613–1624 | PRINTER PORT test runs; enables DUART1 channel-B interrupts (IMR = 0x70→0x10→0x70) |
| 1627 | PRINTER PORT fails: code 0x800 → LEVEL1/2 ERROR -> PRINTER PORT - 16, same three diagnostics, BLINKING SYSTEM BOARD LED, L3F208660 blink (code 0x16); on-screen text reads CONTINUING THE TESTS |
| 2759 | TYPE 'GO' TO CONTINUE again; returns via L3FDF1010; runner continues and draws the weave |
| 2839 | the next level-1 test enables only the DUART1 counter-ready interrupt: L3FA78A10 writes IMR = 0x08 (last IMR write of the run) |
| 2839→end | DUART1 counter-ready interrupt storm. Frame samples are 100 % inside the DUART1 interrupt path (00000F60 INT1 trampoline → 3FA92F40 → dispatcher L3FA78D90); the DUART1 IRQ re-asserts every ~229 CPU cycles |
| 3500, 6000, 12000 | frame dumps are byte-identical; RAM at 3500 vs 12000 differs in one byte (offset 0x4E105). Still storming at frame 20000 |
So the owner's two observations are exact: (1) after each DUART failure, after a finite delay (~1050 frames ≈ 15 s of the blink), it continues; (2) then it gets stuck — at frame 2839.
The "continues" is not a timeout and not the prompt
The error handler L3FDE9D70 ends
3fdea470: move a11, *a14+, 1
3fdea490: calla L3F208660 ; ONE finite blink of the LED error code
3fdea4e0: movi 0x3fdcd2a8, a9 ; "TYPE 'GO' TO CONTINUE"
3fdea520: calla L3FDE00C0 ; print it
3fdea550: jauc L3FDF1010
and the shared exit is
L3FDF1010:
3fdf1010: move *a13(0xffa0), a11, 1
3fdf1030: jreq L3FDF10A0 ; status == 0 -> skip the blink, return
3fdf1040: move a14, -*sp, 1
3fdf1070: calla L3FDF1270 ; status != 0 -> the INFINITE LED blink
L3FDF10A0:
3fdf10a0: ... rets 2
Facts:
3F208060/3F208650are never executed in this run. Breakpoints on both fired 0 times over 20 000 frames; so did3F204440(the boot loop'sjane L3F208060) and3FDF1070(the call into the truly infinite blinkL3FDF1270).- The blink that is seen (
3F2088B0/3F208950/3F208A80/3F208B20) is insideL3F208660, a finite one-shot blink. The earlier "LED loop" conclusion came from sampling a 900–3000 frame window that ended during the second blink and reading its dominant PC as a wedge. L3FDF1270is an infinite loop (3fdf1a80: jruc L3FDF13B0), but it is only entered when the status word is nonzero. In the observed DUART path the status word is zero, soL3FDF1010returns and the runner proceeds.TYPE 'GO' TO CONTINUEis printed, not waited on: nothing reads DUART2, the keyboard, or any input after it, and no input is faked. The runner continues on its own. There is no keypress and no timeout.
What is actually true: a level-1 failure is reported and the runner
continues; it is not by itself fatal. A level-1 failure becomes an infinite
blink only via L3FDF1010 → L3FDF1270, i.e. when the status word
*A13(0xFFA0) is nonzero, which did not happen for these two DUART failures.
The runner also never returns to the boot loop (3F204440 never fires), so
3F208060 plays no part in this path at all.
The wall, named
In the steady state (frames 2839 → 20 000) the only DUART accesses are a repeating pair, ~229 CPU cycles apart:
| logged PC | instruction | access | value |
|---|---|---|---|
3FA78E30 |
3FA78E10 move *a11(0x50), a7, 0 |
DUART1 ISR, reg 5, firmware bit address 0x06400050 |
0x0008 (bit 3 = counter/timer ready) |
3FA7B5E0 |
3FA7B5C0 move *a7(0xf0), a7, 0 |
DUART1 reg 0xF, firmware bit address 0x064000F0 (write-only; reads 0xFF) |
0x00FF |
(The logged PC trails the memory instruction by one word — TMS34010 prefetch — so the reading instruction is the one quoted.)
L3FA78D90(the DUART1 ISR dispatcher) computespending = ISR & IMRinto RAM0x270820, tests bit 2, then 6, then 1, later bit 3 at3FA7B4D0, clears the software bit, and reads reg0xF. But the hardware ISR bit 3 is set again before the read even retires, andretire-enters immediately.- Cause: the counter is in timer mode —
L3FA71CB0writesACR = 0x60(reg 4) once — and CTUR/CTLR (regs 6/7) are never written, so the reload value is0. MAME'smc68681re-arms withstd::max(CTR, 1)= 1 tick at the 3.6864 MHz DUART clock (271 ns), so the counter-ready interrupt is re-asserted far faster than the ~229-cycle ISR can clear it. The interrupt is therefore permanently pending and the CPU never runs mainline code again. - What it waits for: ISR bit 3 (
0x08) to be clear, i.e.ISR & IMR == 0. It never is. - What is missing: a sane counter/timer period. With the reload left at
0, the model produces the fastest possible tick rather than a long one; the firmware's counter interrupt becomes a storm. This lives in the MAMEmc68681model (devices/machine/mc68681.cpp), which is out of scope here (MAME tree is not owned by this task) — reported, not changed. The local MAME edits (harness/mame/mame_edits/) are unrelated (Rx overrun latch, TxRDY timing); this is upstreammax(CTR,1)behaviour.
Next wall
DUART1 counter-ready interrupt storm. CPU is 100 % inside the DUART1 INT1
ISR; it polls DUART1 ISR (reg 5, bit 0x06400050) and always reads 0x08
(counter ready); the counter runs in timer mode (ACR = 0x60) with an
unprogrammed reload (CTUR/CTLR = 0), so the interrupt re-arms every ~229 CPU
cycles and can never be cleared. Nothing else executes; the screen has been
frozen since frame 2839.
Reproduce:
M=/home/arti/code/k-space/mame0289/vt1200
# transcript + milestones (writes debug.log)
VT1200_IO_PATH_STUB=1 CF_FRAMES=20000 $M vt1200 -rompath harness/mame/roms \
-video none -sound none -nothrottle -debug -debugger none -debuglog \
-autoboot_script harness/mame/console_frames.lua -autoboot_delay 0
# final-state histogram, plus DUART access log
VT1200_IO_PATH_STUB=1 W_START=5000 W_END=12000 \
VT1200_DUARTLOG=/tmp/duart.log $M vt1200 -rompath harness/mame/roms \
-video none -sound none -nothrottle -debug -debugger none \
-autoboot_script harness/mame/wall_hist.lua -autoboot_delay 0
# frames at increasing times
VT1200_IO_PATH_STUB=1 VT1200_DUMP_FRAMES=500,1627,2800,3500,12000 \
VT1200_DUMP_DIR=/tmp VT1200_DUMP_VRAM_FRAMES=500,2800,12000 \
$M vt1200 -rompath harness/mame/roms -video none -sound none -nothrottle \
-seconds_to_run 200