diff --git a/harness/mame/OS_BRINGUP_REPORT.md b/harness/mame/OS_BRINGUP_REPORT.md index dc7fdd1..d5e3ffa 100644 --- a/harness/mame/OS_BRINGUP_REPORT.md +++ b/harness/mame/OS_BRINGUP_REPORT.md @@ -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= 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 ( must exist): +env VT1200_SKIP_LEVEL1=1 VT1200_DUMP_FRAMES=3000 \ + VT1200_DUMP_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. diff --git a/harness/mame/duart2_default_tm_frame3000.png b/harness/mame/duart2_default_tm_frame3000.png new file mode 100644 index 0000000..7b568bd Binary files /dev/null and b/harness/mame/duart2_default_tm_frame3000.png differ diff --git a/harness/mame/vt1200.cpp b/harness/mame/vt1200.cpp index 0532ab6..97a3c96 100644 --- a/harness/mame/vt1200.cpp +++ b/harness/mame/vt1200.cpp @@ -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