mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
harness: opt-in VT1200_IO_PATH_STUB stand-in for the level-0 keyboard-port test fixture
The level-0 I/O data path test writes one 0xE1 byte to DUART2-A THRA and demands exactly 10 bytes back; it checks only the count and never compares the content, and no documented keyboard/mouse/tablet message is 10 bytes. Add an opt-in stand-in that watches for that 0xE1 write and feeds 10 arbitrary (0x00) bytes on the channel's receive path via the SCN2681's public rx_fifo_push(), refilling the 4-deep FIFO on each RHRA read. This is explicitly NOT a keyboard model: it stands in for an unknown test fixture on the keyboard port. It replies only while the channel is polled (IMR=0x00), so it raises no interrupt; with the stub on the run clears level 0 and reaches level 1's HOST PORT - 15, but still no OS (SKIP_LEVEL1 remains the only route to X11). Default is unchanged: no env still fails level 0 honestly (progress.lua samples=198/3 PCs); stub-on is 53/75 PCs. See harness/mame/README.md.
This commit is contained in:
1 parent
215fd573db
commit
f7caf9aed8
2 files changed
+173
-11
No files matched your search
+45
-6
@@ -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.
|
||||
|
||||
|
||||
+128
-5
@@ -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<scn2681_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);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user