A bare 'vt1200 vt1200' now boots exactly as the old six-variable incantation
(VRAM, handshake busy=2, DUART1, monitor switch) did, so the no-env run is no
longer a silently-broken machine.
Promoted (5): VT1200_VRAM, VT1200_BOARD_MODEL, VT1200_BOARD_BUSY_TICKS,
VT1200_DUART, VT1200_DUART_SEL (folded to DUART1-only; DUART2 is the new
opt-in VT1200_DUART2).
Deleted (15): VT1200_CAL/_VID4M/_W8, VT1200_BOARD_RAM/_DEFAULT/_BUSY_W2/
_BUSY_W3/_DMA_TICKS/_SWITCH, VT1200_DUART_LB1/LB2, VT1200_LOWLOG,
VT1200_VRAMLOG, VT1200_STRUCTLOG, VT1200_DUART1_INT2.
VT1200_BOARD_SWITCH is superseded: 0x0640000D is the DUART1 IP register, and
the DUART mapping wins the last-wins slice, so the board model switch was
never consulted (verified: identical output with and without it).
Renamed: VT1200_DUART_IP3 -> VT1200_SKIP_LEVEL1, which now also selects the
OS-phase DUART1 IRQ routing (INT2) instead of POST's INT1, removing the
standalone VT1200_DUART1_INT2 knob. The skip gate itself stays opt-in.
Evidence (equivalence): frame-160/200/600 md5, progress.lua at 200/1500
frames, and the cap.lua console transcript from the no-env run are
byte-identical to the incantation run. Re-baseline: no-env 200-frame hottest
PC 3FDCE400 samples 199 (1 PC) -> samples 53 (78 PCs). ROM md5 unchanged
b46a3e442ccb86134d2356bd70d22fd2. Header comment block updated; see
harness/mame/GATE_CLEANUP_REPORT.md.
vt1200.cpp declared PALETTE(config, m_palette).set_entries(256) with no init
delegate, so render_active_line()'s pens 0/1 rendered in whatever MAME's
default palette held -- reported as red on screen. Use
palette_device::MONOCHROME (emupal.cpp: palette_init_monochrome) with two
entries, so the colour is ours and deterministic.
Verified via a real MAME screenshot (screen:snapshot(), which renders through
the palette -- dump_frame()'s PPM is straight from bitmap pens and cannot show
colour): pal_mono_frame600.png is exactly black (0,0,0) + white (255,255,255).
palverify.lua takes the screenshot; the frame is the level-1 error screen.
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.
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).
MAME's TMS34010 renderer does not read VRAM: tms340x0_ind16 calls the
driver's scanline callback for the active region and paints only the
blanking regions. Our callback drew nothing, so the active area was
never painted and every frame dump was all-black.
Add render_active_line(), called from scanline(): it reads the
framebuffer out of the video-RAM share at bit 0x04000000 with a measured
1024-bit (0x40-word, 128-byte) row pitch and LSB-first bit order within
each 16-bit word. The pitch comes from the firmware's own character
blitter (PIXBLT at pc=3FDF9A00 advances its destination by 0x40 words per
glyph row); at the 160-byte pitch MAME derived from HEBLNK/HSBLNK/HTOTAL
the text is sheared and unreadable.
Validated: frame 160 shows the level-1 CP437 character table legibly,
frame 600 the level-1 error screen. VT1200_DPYLOG added as an opt-in
per-scanline display-params dump.
Regression (no VT1200_* env): PC=3FDCE400 samples=199 -- holds.
See harness/mame/RENDER_REPORT.md.
Root cause of the missing THRA write (HOST PORT test): the level-1 host-port
test's transmit is ISR-driven, and the DUART interrupt was never wired to the
CPU. The firmware installs RAM trampolines in the fixed vector table (INT1
vector 0xffffffc0 -> stub at bit 0x0f60 = JA 0x3FA92F40, which calls the DUART
ISR L3FA78D90 with (2, 0x270860, 0x6400000)). Counters: INTENB reaches 0x0006
(INT1|INT2) at the test, so interrupts are enabled.
vt1200.cpp: wire m_duart1/m_duart2 irq_cb() to CPU external interrupt 1/2
(duart1_irq_w/duart2_irq_w), gated on VT1200_DUART. Result: THRA writes go
from 0 to 20 per run; the test's SRA-overrun and RHRA-pattern checks now pass.
MAME tree (mc68681.cpp/.h), two datasheet-backed corrections with revert notes:
1. TxRDY was asserted mid-character ("not until the end of start bit time");
the datasheet asserts it at the THR->TSR transfer. Corrected.
2. The overrun error was latched when the overrunning character *completed*
(rx_fifo_push); the datasheet latches it "upon receipt of the start bit of
the new (overrunning) character". Corrected via duart_channel::rx_w().
This clears every check in the RXCHECK sub-test (flags 1 -> 0).
Acceptance is NOT met: cap.lua still prints 'LEVEL1/2 ERROR -> HOST PORT - 15'.
The next failure is the preceding L3FA71BC0 (DUART baud/timer) sub-test
returning 6; its flag-set sites were breakpointed and never fired. Regression
holds (no VT1200_* env -> PC=3FDCE400 samples=199). Details, numbers and the
datasheet audit: harness/mame/DUART_REPORT.md sections FINAL FINDINGS / UPDATE.
Adds SCN2681 devices at 0x0640000 (DUART1 = host/printer) and 0x0620000
(DUART2 = keyboard/mouse), gated on VT1200_DUART, with the remap
verified as duart_register = handler_off (duart_byte = off*2, value in
the low byte of the firmware's 16-bit move). Optional external TX->RX
loopback (VT1200_DUART_LB1/2) models the loopback connectors; the
monitor switch is driven on IP4-IP6. New VT1200_DUARTLOG gives a full
non-deduplicated DUART access log.
Result: the level-1 HOST PORT test is reached and executed against the
real DUART, but still reports 'LEVEL1/2 ERROR -> HOST PORT - 15'. The
test reads SRA/RHRA and requires a 5-byte external pattern
(0xAA,0xFF,0xAA,0x55) plus SRA overrun, yet the firmware never writes
the transmit register (0 writes to reg 3/B on either DUART in 1400
frames), so no internal or external loopback can supply it. See
harness/mame/DUART_REPORT.md.
Regression holds: no VT1200_* env -> PC=3FDCE400 samples=199.
The dispatch status is 0x100 (printed as '22' in the message). A driver-side
transparent low-RAM write log (VT1200_LOWLOG) shows the slot at bit 0x16F0 is
written by the store at 3FDF9090 from a13(0x10), which L3FA7C050 sets to the
literal 0x100 when the memory test L3F2018B0 returns non-zero. L3F2018B0 is the
level-1 video-memory test (base bit 0x04000000, count 0x20000). Its failing
checks: the 16-bit walking-bit read-back at 0x04000000 (baseline, unmapped) and,
with video RAM mapped, the REFCNT (0xC00001F0) 'must change' check.
Closed cross-check (single run): the catch-all and VT1200_CAL proxies are
directly comparable (both base 0) and resolve identically; last-wins holds
for same- and mismatched-width overlaps. The apparent conflict was a units
error: a .rw(FUNC(offs_t)) handler receives (bitaddr-base)>>4 (tms34010
Width=1, AddrShift=3), so the catch-all logs bitaddr>>4. Its 0x400000..0x40D7FF
is bit 0x04000000..0x040D7FF0 (bytes 0x800000..0x81AFFE), outside the declared
4 MiB RAM -- genuinely unmapped. The old 0x00400000 proxy was 16x too low.
Catch-all reproduces the committed log byte-for-byte and does not perturb
coverage (56 / PC=3F208950 with and without). Regression holds: PC=3FDCE400
samples=199. Report: harness/mame/XCROSS_REPORT.md