harness: model DUART2 by default (drop the VT1200_DUART2 gate)

The real VT1200 board has a DUART2 (SCN2681) at 0x0620000 for the keyboard
(ch A) and mouse (ch B) with INT2 wired.  Map it unconditionally instead of
opting in via VT1200_DUART2; the OS's DUART interrupt dispatcher needs a real
read-only ISR distinct from the write-only IMR.

Verified with frame dumps: with ONLY VT1200_SKIP_LEVEL1=1 the Terminal Manager
V2.2 dialog now appears (frame 3000; VRAM md5 73c3c9a1..., identical to the old
skip+DUART2 run).

This does NOT keep the faithful no-env path: level 0's I/O data path test
(L3FDCE470) transmits 0xE1 to DUART2-A and drains the receiver until it has read
10 bytes (SRA RxRDY).  The board write-back latch used to alias CSRA/RHRA and
faked that (baseline first divergence: R 06200001 pc=3FDCE670 = 0x0099 vs the
real chip's 0x000C), so the firmware dropped into the interactive diagnostics
menu.  No faithful device we have (LK401 sends at most 4 bytes) supplies the 10
bytes; an external TX->RX loopback does not change the transmit count.  See
OS_BRINGUP_REPORT.md section 8 (DONE_WITH_CONCERNS).

The OS keyboard/mouse driver does initialise: skip-phase OS code programs
DUART2 (MR1A=0x13, CSRA=0x99, IMR=0x33, ...) and sends FD to the keyboard plus
the documented LED/click/bell/auto-repeat commands, and 'T' to the mouse.
This commit is contained in:
SDD committed 2026-10-16 13:59:09 +03:00
1 parent 6ce84b86f2
commit 1ca4817745
3 files changed
+173 -15

No files matched your search

+151
View File
@@ -14,6 +14,13 @@ the write-only interrupt-mask register (IMR) — same chip register address,
GUI's Terminal Manager **dialog box** — the reference video's ~0:48 target —
appears.
> **2026-10-16 update (§8): DUART2 is now modelled by default.** The
> `VT1200_DUART2` gate is gone; a bare `VT1200_SKIP_LEVEL1=1` run now reaches
> the Terminal Manager (§8.1). This *does* break the faithful no-env path:
> level 0's I/O data path test needs received data on DUART2-A that the old
> board write-back latch faked (§8.2). Read §8 before relying on the
> "no-env → HOST PORT - 15" baseline elsewhere in this file.
---
## 1. Progressing or stuck — evidence across time
@@ -305,3 +312,147 @@ documented combination `VT1200_SKIP_LEVEL1=1 VT1200_DUART2=1`.
auto-exposure, not content.
* NVRAM contents/initialisation policy — only that the OS reads it and falls
back to system defaults.
---
# 8. DUART2 promoted to a default — what it fixes and what it breaks
Date: 2026-10-16 (later). Change: `harness/mame/vt1200.cpp` maps MAME's
`SCN2681` at `0x0620000` (keyboard ch A / mouse ch B) **unconditionally**; the
`VT1200_DUART2` gate is deleted. ROM untouched (`md5
b46a3e442ccb86134d2356bd70d22fd2`).
## 8.1 The OS now reaches the GUI with only `VT1200_SKIP_LEVEL1=1`
One run, no `VT1200_DUART2` anywhere:
```sh
env VT1200_SKIP_LEVEL1=1 VT1200_DUMP_FRAMES=3000,10000 \
VT1200_DUMP_VRAM_FRAMES=3000,10000 VT1200_DUMP_DIR=<dir> <mame> vt1200 ...
```
| frame | lit | video-RAM md5 |
|---|---|---|
| 3000 | 461172 (39.9879%) | `73c3c9a1f13378d3ccf1e092aa47fa60` |
| 10000 | 460143 (39.8986%) | `4ff38c8c782684c562369dc47fb8a781` |
Video-RAM md5s identical to the report's `VT1200_SKIP_LEVEL1=1 VT1200_DUART2=1`
run — a bare skip run now *is* that run. **The dumped frame (I rendered
`vt1200-frame-3000.ppm` to `duart2_default_tm_frame3000.png`) shows the
Terminal Manager V2.2 box** — title bar "Terminal Manager V2.2: Copyright ©
1991 Digital Equipment Corporation", Session/Create/Customize/Print Screen,
Messages ("Using local fonts. / User settings invalid. / System defaults
loaded."), Ethernet Address `00-00-00-00-00-00`, LAT Host Name
`LAT_00000000000`, the Memory Remaining bar (3104), Secure Reset Count: 0, the
mouse cursor, and the bottom banner "All Diagnostics Passed". This matches
`reference/vt1200-boot-video/real_t52.png`. Frame 10000 differs, so the machine
is still running (not frozen).
## 8.2 Level 0: the earlier claim is **REAL** — and the exact test is named
The claim "a real DUART2 diverts level 0 to the interactive diagnostics menu"
**holds**. The inference that it "looks wrong" came from the *skip* run, but
`VT1200_SKIP_LEVEL1` skips level 0 entirely, so that run says nothing about it.
* **Console, no env at all now** (`cap.lua`): the transcript is no longer
`LEVEL1/2 TESTING` / `LEVEL1/2 ERROR -> HOST PORT - 15`; it is the
**interactive diagnostics menu** — `VERSION 2.08 - SELECTED TEST TYPE:` and
the A–Z test list (`D - KEYBOARD TEST`, `H - DUART2, CHANNEL A (KEYBOARD)
TEST`, …). So the faithful path is **broken** by this change.
* **Exact test.** Level-0's **I/O data path test**, routine `L3FDCE470`, called
from `3F3DFA80` with window base `0x0620000` (DUART2 ch A). It runs at
`cyc≈1.494 M`, *before* the baseline's `LEVEL1/2 TESTING` print at
`cyc=2.119 M` (confirmed with a cycle-stamped printer breakpoint), i.e. it is
a level-0 test, not level 1.
* **What it waits for.** It initialises the channel (`MR1A=0x13, MR2A=0x07,
CSRA=0x99, CRA enable`, *normal* mode — **not** local loopback), transmits
`0xE1` to THRA (`pc=3FDCE8D0`), and then drains the receiver: loop
`3FDCE6C0`/`3FDCE930` reads RHRA, delays 100, and repeats **while SRA bit 0
(RXRDY) is set**, up to **10 bytes**; the pass branch is taken at
`3FDCEAE0` only when the count is exactly 10, otherwise it falls through to
the menu display. With a real chip and nothing on the wire, RXRDY never
sets (`SRA=0x000C` = TXRDY|TXEMT, bit 0 clear) and the test fails.
* **Why the baseline passed.** The first differing access is
`R 06200001 pc=3FDCE670`: baseline (board model) returned `0x0099` — the
value last written to CSRA, whose bit 0 is set — so the driver drained 10
copies of the latched RHRA `0x008C` and hit count 10. A real chip returns
`0x000C`. The write-back latch was an **accidental loopback** that faked the
test; modelling the real device removes the fake and exposes the gap.
* **The LK401 model would not fix it.** The firmware transmits only three bytes
before this test (`0x11 0x8C` = LEDs off, then `0xE1` = disable auto-repeat);
none of these has a documented LK401 reply, and the keyboard's power-up /
`FD` self-test report is **4 bytes** (`reference/keyboard-mouse/PROTOCOL.md`
§3.5; `vt100net-lk401.txt` §3.3.4.6). The test drains until it has read
**10**. I also wired an external DUART2-A TX→RX loopback
(`a_tx_cb`→`rx_a_w`, the old `VT1200_DUART_LB2`) as a diagnostic: the
firmware's transmit count is still 3 and the menu still appears. So no
faithful device/protocol model we have supplies the 10 bytes; restoring the
no-env path would require re-introducing a read-back fake, which is exactly
what this change removes.
## 8.3 Does the OS keyboard/mouse driver initialise? — **Yes**
`VT1200_SKIP_LEVEL1=1` with `VT1200_DUARTLOG`, first 40 s: OS code (`pc` in the
OS range, e.g. `3F6F3990`…`3F6F3CA0`, then `3FB43550`) drives `0x0620xxx`:
```
W 0620000D=0000 OP, W 0620000E=000C, W 0620000F=0004 ; output port setup
W 06200002=002A/003A/005A/0010 (CRA) , W 0620000A … ; channel resets
W 06200000=0013 MR1A , W 06200008=0007 MR1B
W 06200004=0060 ACR
W 06200001=0099 CSRA , W 06200009=0099 CSRB
W 06200000=0007 MR2A , W 06200008=0007 MR2B
W 06200005=0033 IMR (both channels Rx/Tx)
W 06200002=0009 CRA , W 0620000A=0009 CRB ; enable Tx+Rx+A
… then the driver's command traffic (pc=3FB43550):
W 06200003=00FD FD -> keyboard: jump to power-up / self-test
W 06200003=0013 0080, 001B 0086, 0023 0084, 00E3 ; LED / click / bell / auto-repeat
W 0620000B=0054 'T' -> mouse: self-test & identify
```
Every one of those keyboard bytes is a documented LK401 command and `'T'` is
the documented VSXXX mouse self-test (`PROTOCOL.md` §3.3, §4.2) — so the
driver initialises **and speaks the real protocol**. **Input still does not
work** (no keyboard/mouse is attached, and nothing is faked); the chip receives
no reply, which is the honest state.
## 8.4 Baselines
* **no env** → **CHANGED**: no longer POST + level 1 to `HOST PORT - 15`; now
level-0 I/O data path fails and the machine sits in the diagnostics menu
(§8.2). `progress.lua` 200 frames: **3 distinct PCs**, hot
`PC=3FDCE400 samples=198` (the menu's key-wait delay loop). This replaces the
`PC=3FDCE400 samples=53 / 78 distinct PCs` baseline recorded in
`GATE_CLEANUP_REPORT.md`; the baseline is a change detector, and this change
is deliberate.
* **`VT1200_SKIP_LEVEL1=1`** → **intact and extended**: the X11 weave still
paints first and the run now progresses through it to the Terminal Manager
(§8.1). Frame/VRAM md5s unchanged from the old skip+DUART2 run.
* `progress.lua` unchanged.
## 8.5 Reproduce
```sh
M=/home/arti/code/k-space/mame0289/vt1200
R="-rompath harness/mame/roms -sound none -skip_gameinfo -video none -nothrottle"
# GUI with ONLY the skip gate (<dir> must exist):
env VT1200_SKIP_LEVEL1=1 VT1200_DUMP_FRAMES=3000 \
VT1200_DUMP_DIR=<dir> $M vt1200 $R -seconds_to_run 60
# Level-0 divert, with no env at all:
env CAP_MAX=2500 $M vt1200 -rompath harness/mame/roms -sound none \
-skip_gameinfo -video none -nothrottle -debug -debugger none -debuglog \
-autoboot_script harness/mame/cap.lua -autoboot_delay 0 -seconds_to_run 60
python3 harness/mame/resolve_console.py debug.log harness/mame/roms/vt1200/vt1200_v22.bin
# OS keyboard/mouse driver init:
env VT1200_SKIP_LEVEL1=1 VT1200_DUARTLOG=harness/mame/scratch/duart_skip.log \
$M vt1200 $R -seconds_to_run 40
```
**Bottom line.** The faithfulness fix works and the GUI is now reachable with
one env var — but it invalidates the "no env reaches `HOST PORT - 15`"
constraint, for a real, named reason (level-0 I/O data path test needs 10
received bytes on DUART2-A). The previous no-env baseline only reached level 1
because the board latch faked that test. I did **not** fake it back.
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

+22 -15
View File
@@ -32,8 +32,16 @@
the reset-vector page at logical bit 0xffffffe0 resolves to the last
four bytes of the image (file offset 0x1bfffc).
What is NOT modelled: LANCE Ethernet, DUART2 (0x0620000 = keyboard/mouse;
opt in with VT1200_DUART2=1), and the board system logic beyond the
* MAME's SCN2681 at DUART2 (0x0620000 = keyboard (ch A) / mouse (ch B)),
wired to INT2. This is modelled unconditionally because the real
board has it and the OS's DUART interrupt dispatcher needs a real
chip. NOTE: with a real DUART2 and nothing attached to the keyboard
port, the *faithful* (no-env) boot now fails the level-0 I/O data path
test and drops into the interactive diagnostics menu; before DUART2
was modelled the board write-back latch accidentally faked that test
(see OS_BRINGUP_REPORT.md).
What is NOT modelled: LANCE Ethernet and the board system logic beyond the
register model. The firmware therefore still stops at the level-1 HOST
PORT test and wedges in the system-board LED loop; that is expected.
@@ -96,11 +104,8 @@
Video: the driver paints the active scanlines itself, reading the
framebuffer out of video RAM (see render_active_line).
VT1200_DUART2=1 additionally maps MAME's SCN2681 at 0x0620000
(keyboard/mouse). It is off by default because mapping both DUARTs
diverts the level-0 keyboard path.
See FULL_BOOT_ESTIMATE.md section 4b for measured results.
See FULL_BOOT_ESTIMATE.md section 4b for measured results and
OS_BRINGUP_REPORT.md for the DUART2 / level-0 analysis.
***************************************************************************/
@@ -186,8 +191,8 @@ public:
u16 board3_r(offs_t off); void board3_w(offs_t off, u16 data);
// The two DUARTs. DUART1 = host (ch A) + printer (ch B) at window
// 0x0640000 and is always mapped; DUART2 = keyboard (ch A) + mouse (ch B)
// at window 0x0620000 is opt-in via VT1200_DUART2.
// 0x0640000; DUART2 = keyboard (ch A) + mouse (ch B) at window 0x0620000.
// Both are mapped unconditionally.
u16 duart1_r(offs_t off); void duart1_w(offs_t off, u16 data);
u16 duart2_r(offs_t off); void duart2_w(offs_t off, u16 data);
@@ -417,8 +422,7 @@ void vt1200_state::machine_start()
logerror("vt1200: board model=handshake busy_ticks=2 switch=duart-ip4/5/6 log=%s render=%d dump=%ld\n",
m_log ? "on" : "off", m_render ? 1 : 0, m_dump_frame);
logerror("vt1200: duart1=on (SCN2681 @0640000 host/printer), duart2=%s skip_level1=%s\n",
getenv("VT1200_DUART2") ? "on (kbd/mouse)" : "off",
logerror("vt1200: duart1=on (SCN2681 @0640000 host/printer), duart2=on (SCN2681 @0620000 kbd/mouse), skip_level1=%s\n",
getenv("VT1200_SKIP_LEVEL1") ? "on (IP3 high, IRQ->INT2)" : "off (IP3 low, IRQ->INT1)");
}
@@ -505,11 +509,14 @@ void vt1200_state::program_map(address_map &map)
// board-window model so it wins the register slice (map precedence is
// last-wins); the model still serves any other register in the same window.
// Base in map units is the bit address; 16 registers at 2 bytes each = 32
// bytes = 0x100 bit units. DUART2 (0x0620000 = keyboard/mouse) is opt-in
// via VT1200_DUART2: mapping both diverts the level-0 keyboard path.
// bytes = 0x100 bit units. DUART2 (0x0620000 = keyboard/mouse) is mapped
// the same way, unconditionally.
map(VT1200_WIN3, VT1200_WIN3 + 0xff).rw(FUNC(vt1200_state::duart1_r), FUNC(vt1200_state::duart1_w));
if (getenv("VT1200_DUART2"))
map(VT1200_WIN2, VT1200_WIN2 + 0xff).rw(FUNC(vt1200_state::duart2_r), FUNC(vt1200_state::duart2_w));
// DUART2 (0x0620000 = keyboard/mouse) is mapped unconditionally: the real
// board has it, and the OS's DUART dispatcher requires a real read-only ISR
// distinct from the write-only IMR (the board write-back latch aliased them,
// producing a phantom interrupt that wedged the OS at the X11 weave).
map(VT1200_WIN2, VT1200_WIN2 + 0xff).rw(FUNC(vt1200_state::duart2_r), FUNC(vt1200_state::duart2_w));
}
// MAME may hand us either an entry-relative offset (0..0xffff) or the absolute
// address; BOARD_BASE is 0x10000-aligned, so ORing the low 16 bits is correct