diff --git a/harness/mame/README.md b/harness/mame/README.md index 2c784c6..0972fc4 100644 --- a/harness/mame/README.md +++ b/harness/mame/README.md @@ -88,10 +88,33 @@ there is no expected-value table. The keyboard's `FD`/power-up reply is 4 bytes, the mouse's `T` reply is 4, the tablet's 5 — none is 10. A faithful LK401 would not help either: the init sequence's receiver-reset **flushes the FIFO**. -So no responder will be added. The real terminal *does* pass this test, so the -bytes exist on real hardware — they are simply not inferable from the ROM, and the -only honest way to close the question is to observe a real keyboard port at -power-up. +The real terminal *does* pass this test, so the bytes exist on real hardware — they +are simply not inferable from the ROM, and the only honest way to close the question +is to observe a real keyboard port at power-up. **No faithful responder can be +derived from any document.** + +So instead there is an **opt-in stand-in**, `VT1200_IO_PATH_STUB=1`. Read what it +is carefully, because it is easy to misread: + +* It is **not a keyboard model** and it is not any documented device. It stands in + for an **unknown test fixture on the keyboard port**. +* Its bytes are **arbitrary** (`0x00`). Nothing compares them: the routine stores + the received byte and never reads it back, and there is no expected-value table, + so only the **count** matters. Feeding a pattern would imply a protocol that does + not exist. +* It triggers on the exact thing the test does: the one `0xE1` write to DUART2-A + `THRA` (handler reg 3, window `0x0620000`, store at bit `3FDCE8B0`), then feeds + **10 bytes** back on the same channel's receive path via the SCN2681's own + `rx_fifo_push()`. It replies only while the channel is **polled** (`IMR = 0x00`, + the level-0 condition), so it cannot inject into the interrupt-enabled OS phase + or cause an interrupt there. +* It is **off by default**. A bare run still fails level 0 exactly as before and + says so. This is deliberate: the faithful configuration stays the default. + +With the stub on the boot clears level 0 and reaches **level 1's `HOST PORT - 15`** +(and `PRINTER PORT - 16`), which is the faithful ceiling. It does **not** produce a +booted OS: level 1 still fails on the DUART fidelity ceiling, so +`VT1200_SKIP_LEVEL1=1` remains the only route to X11. ### How far the OS gets @@ -123,7 +146,7 @@ behaviour**, and is **not** evidence about the real terminal's POST. The skip is deliberately **not** the default: the real machine runs level 1, so the faithful configuration stays the default and the switch is how we diverge. -## Environment knobs (16; down from 33) +## Environment knobs (17; down from 33) All opt-in, and all off by default — there is no longer a "working incantation" to remember, because everything the firmware needs to run is now the default. @@ -132,7 +155,8 @@ Instrumentation: `VT1200_DUARTLOG`, `VT1200_BOARD_LOG`, `VT1200_BOARD_MAP`, `VT1200_IRQLOG`, `VT1200_LOG_UNMAPPED`, `VT1200_DPYLOG` (+`_FRAMES`), `VT1200_DUMP_FRAME` (+`_FRAMES`), `VT1200_DUMP_RAM` (+`_FRAMES`), `VT1200_DUMP_VRAM` (+`_FRAMES`), `VT1200_DUMP_DIR`, `VT1200_NO_RENDER`. -Behaviour switch: `VT1200_SKIP_LEVEL1`. +Behaviour switches: `VT1200_SKIP_LEVEL1`, `VT1200_IO_PATH_STUB` (the level-0 +test-fixture stand-in described above — not a keyboard model, off by default). ## Regression baseline @@ -153,6 +177,21 @@ This has moved **twice**, both deliberately: menu. That looks like a regression and is not one — it is the removal of a false pass. +**The no-env value is unchanged by `VT1200_IO_PATH_STUB`** — stub off is the +default, so the `samples=198 (3 PCs)` line above still holds. The stub is a separate +observed configuration: + +``` +# VT1200_IO_PATH_STUB=1, progress.lua at 200 frames +PC=3FDCE400 samples=53 (75 distinct PCs) +``` + +That is a genuine move, not a false pass: with the count-only level-0 test +satisfied, the run leaves the level-0 wait and executes level-0/level-1 work, then +parks at **level 1's `HOST PORT - 15`** (verified from the console transcript: +`LEVEL1/2 TESTING **`, then `LEVEL1/2 ERROR -> HOST PORT - 15`). It is recorded as a +*different* baseline because it is opt-in; the default must stay `samples=198`. + It is a change detector, not a specification. If it moves without you meaning it to, find out why. diff --git a/harness/mame/vt1200.cpp b/harness/mame/vt1200.cpp index 97a3c96..5ba70f7 100644 --- a/harness/mame/vt1200.cpp +++ b/harness/mame/vt1200.cpp @@ -39,7 +39,10 @@ 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). + (see OS_BRINGUP_REPORT.md). VT1200_IO_PATH_STUB=1 adds an OPT-IN + stand-in for the unknown test fixture on that port (it is NOT a + keyboard model) so the count-only level-0 test can pass; the default + stays faithful and still fails. 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 @@ -101,6 +104,26 @@ the two phases' own routings. See reference/level1-bypass-analysis.md. + VT1200_IO_PATH_STUB=1 feed 10 bytes into DUART2 channel A after + the level-0 I/O data path test writes 0xE1 + to THRA. This is a STAND-IN for an unknown + test fixture on the keyboard port -- it is + NOT a keyboard model, and the bytes are + arbitrary (0x00) because the firmware checks + only the COUNT (it stores the received byte + and never compares it, and it does not + enable loopback). No documented + keyboard/mouse/tablet message is 10 bytes + long, so no documented device can satisfy + this test; see reference/level0-10byte-puzzle.md. + The stub replies only while the channel is + polled (IMR = 0x00), so it cannot inject + into the interrupt-enabled OS phase or + raise an interrupt there. Unset (the + default) the level-0 test fails honestly + and the boot stops in the diagnostics + menu. + Video: the driver paints the active scanlines itself, reading the framebuffer out of video RAM (see render_active_line). @@ -224,6 +247,16 @@ private: required_device m_duart2; // 0x0620000: keyboard(A)/mouse(B) bool m_duart1_int2 = false; // set by VT1200_SKIP_LEVEL1 + // VT1200_IO_PATH_STUB=1: opt-in stand-in for the unknown test fixture on + // the keyboard port, so level-0's count-only I/O data path test can pass. + // NOT a keyboard model. See iopath_stub_arm() and the README. + bool m_iopath_stub = false; + int m_iopath_supply = 0; // bytes still to feed for the current 0xE1 + u8 m_duart2_imr = 0; // last value written to DUART2 IMR (reg 5) + bool m_iopath_irq_reported = false; + void iopath_stub_arm(); // on the level-0 0xE1 THRA write + void iopath_stub_refill(); // after a channel-A RHRA read + // The four undecoded board windows, owned by the standalone model in // vt1200_board.h so it can be unit-tested without MAME. The driver only // forwards its window callbacks and configures the model from the env knobs. @@ -420,10 +453,17 @@ void vt1200_state::machine_start() m_duart1_int2 = true; } + // VT1200_IO_PATH_STUB=1: feed >=10 bytes on DUART2-A after the level-0 + // I/O data path test writes 0xE1 to THRA. Off by default: with it off the + // run still fails level 0 honestly and parks in the diagnostics menu. + if (getenv("VT1200_IO_PATH_STUB")) + m_iopath_stub = true; + 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=on (SCN2681 @0620000 kbd/mouse), skip_level1=%s\n", getenv("VT1200_SKIP_LEVEL1") ? "on (IP3 high, IRQ->INT2)" : "off (IP3 low, IRQ->INT1)"); + logerror("vt1200: io_path_stub=%s\n", m_iopath_stub ? "on (DUART2-A reply to 0xE1)" : "off (faithful)"); } void vt1200_state::machine_reset() @@ -432,6 +472,11 @@ void vt1200_state::machine_reset() fflush(m_log); if (m_unmapped_log) fflush(m_unmapped_log); + // The device resets IMR to 0; mirror that in our tracker and drop any + // in-flight stub reply. + m_duart2_imr = 0; + m_iopath_supply = 0; + m_iopath_irq_reported = false; } void vt1200_state::log_unmapped(const char *kind, u32 addr, const char *pc) @@ -616,9 +661,14 @@ u16 vt1200_state::duart2_r(offs_t off) const u32 a = VT1200_WIN2 | (u32(off) & 0xffff); if ((off & 0xffff) < 0x10) { - const u16 v = m_duart2->read(u32(off) & 0xf); + const u32 reg = u32(off) & 0xf; + const u16 v = m_duart2->read(reg); + // The stub tops the receive FIFO back up as RHRA (reg 3) is drained, + // so RXRDY stays asserted for all ten bytes (the FIFO is only 4 deep). + if (m_iopath_stub && reg == 3) + iopath_stub_refill(); log_pc_access('R', a, v); - log_duart(2, 'R', u32(off) & 0xf, v); + log_duart(2, 'R', reg, v); return v; } return board_read(2, off); @@ -629,14 +679,75 @@ void vt1200_state::duart2_w(offs_t off, u16 data) const u32 a = VT1200_WIN2 | (u32(off) & 0xffff); if ((off & 0xffff) < 0x10) { - m_duart2->write(u32(off) & 0xf, u8(data & 0xff)); + const u32 reg = u32(off) & 0xf; + m_duart2->write(reg, u8(data & 0xff)); + if (reg == 5) + m_duart2_imr = u8(data & 0xff); + // The level-0 I/O data path test's one data byte: 0xE1 to THRA. That + // is the trigger for the opt-in test-fixture stand-in (not a keyboard). + if (m_iopath_stub && reg == 3 && u8(data & 0xff) == 0xe1) + iopath_stub_arm(); log_pc_access('W', a, data); - log_duart(2, 'W', u32(off) & 0xf, data); + log_duart(2, 'W', reg, data); return; } board_write(2, off, data); } +// --- VT1200_IO_PATH_STUB: the level-0 test-fixture stand-in ----------------- +// +// The level-0 I/O data path test (L3FDCE470) writes ONE byte, 0xE1, to DUART2-A +// THRA and then requires EXACTLY 10 bytes to come back on the same channel. It +// never checks their content, never sets loopback, and transmits nothing else. +// No documented LK401/mouse/tablet message is 10 bytes long, and the routine's +// receiver-reset flushes the FIFO, so no documented device can satisfy it; see +// reference/level0-10byte-puzzle.md. This is therefore a stand-in for an +// UNKNOWN TEST FIXTURE on the keyboard port -- NOT a keyboard model -- and the +// bytes are deliberately arbitrary (0x00): only the COUNT is compared. +// +// Mechanism: duart_channel::rx_fifo_push() (the public channel API) instead of +// synthesising a serial bit stream on rx_a_w. A bit-level UART model would add +// timing surface without adding fidelity, because there is no document to be +// faithful to and the firmware ignores the values. The SCN2681 receive FIFO is +// only MC68681_RX_FIFO_SIZE+1 = 4 deep, so arm() pre-fills it and refill() adds +// one byte after every RHRA read, keeping RXRDY asserted until the ten bytes +// have been drained. At this point IMR = 0x00 (written by the channel +// initialiser), so pushing receive data cannot raise the chip's IRQ: +// update_interrupts() drives the line only when ISR & IMR != 0. +void vt1200_state::iopath_stub_arm() +{ + if (m_iopath_supply) + return; // a previous reply is still draining + // Only reply while the channel is polled (interrupts masked), which is + // exactly the level-0 condition. This keeps the stub from injecting into + // the interrupt-enabled OS phase (IMR = 0x33) and raising an interrupt + // there -- it is scoped to the one test it exists for. + if (m_duart2_imr != 0x00) + return; + m_iopath_supply = 10; + + const int fifo_depth = MC68681_RX_FIFO_SIZE + 1; + const int prefill = (m_iopath_supply < fifo_depth) ? m_iopath_supply : fifo_depth; + for (int i = 0; i < prefill; i++) + m_duart2->m_chanA->rx_fifo_push(0x00, 0); + m_iopath_supply -= prefill; + + osd_printf_info("vt1200: IO-PATH STUB: 0xE1 on DUART2-A -> feeding 10 arbitrary bytes\n"); +} + +void vt1200_state::iopath_stub_refill() +{ + if (m_iopath_supply <= 0) + return; + if (m_duart2_imr != 0x00) // interrupts got enabled: stop, don't arm + { + m_iopath_supply = 0; + return; + } + m_duart2->m_chanA->rx_fifo_push(0x00, 0); + m_iopath_supply--; +} + // The DUART's IRQ output is a single open-drain line per chip. Wire it to the // CPU's external interrupt input the firmware's vector trampoline expects: // line 0 -> TMS34010_INT1 (INTPEND 0x0002), line 1 -> TMS34010_INT2 (0x0004). @@ -658,6 +769,18 @@ void vt1200_state::duart1_irq_w(int state) void vt1200_state::duart2_irq_w(int state) { + // Under the stub, report the first DUART2 IRQ assertion with the ISR/IMR + // and how many injected bytes were outstanding. In the faithful level-0 + // run this line MUST NOT appear -- IMR = 0x00 there, so feeding receive + // data cannot interrupt. (It can appear under VT1200_SKIP_LEVEL1, where + // the OS enables DUART2 interrupts for its own traffic.) + if (state && m_iopath_stub && !m_iopath_irq_reported) + { + m_iopath_irq_reported = true; + const u32 isr = m_duart2->read(5) & 0xff; // side-effect-free read + osd_printf_info("vt1200: IO-PATH STUB: DUART2 IRQ asserted (isr=%02X imr=%02X supply=%d)\n", + isr, m_duart2_imr, m_iopath_supply); + } m_maincpu->set_input_line(1, state ? ASSERT_LINE : CLEAR_LINE); }