Add the terminal's actual input parts to the harness as device models on the already-modelled SCN2681 at DUART2: a DEC LK401 keyboard (channel A, 4800 8N1) and a DEC VSXXX-AA mouse (channel B, 4800 8O1), both driven by MAME's host input and wired bit-by-bit to the DUART (rx_a_w/rx_b_w and a_tx_cb/b_tx_cb). Route: the docs, not MAME's lk201. MAME's lk201 device executes the 68HC05 firmware 23-001s9-00.bin, which is not in our ROM set (the downloaded zip only carries the 8051 23-004M2-00.bin, MAME's unused second region), so route 1 is unusable without sourcing that ROM. The models follow PROTOCOL.md: the keyboard answers FD with the 4-byte self-test and reports documented keycodes honouring the division modes; the mouse answers T with its 4-byte self-test and streams 3-byte incremental reports. One physical-layer detail was required: the SCN2681 receiver mis-samples the first byte after a receiver reset unless the line is explicitly idle-high before the start bit. Both models now hold TxD at mark when idle (which is faithful anyway) -- isolated evidence: bit-banging 0x55 into rx_a_w gives RHRA=D5 with no idle-high, RHRA=55 with four idle-high bits. Verified (VT1200_SKIP_LEVEL1=1, input_inject.lua): the OS reads the keyboard keycode 0xC2 (A) and ~900 bytes of VSXXX reports (dx=+16/+32); after the self-test it switches the mouse to stream mode; the X11 cursor arrow is visible at frame 4400 and has moved by frame 4600. Nothing was tuned for the level-0 I/O data-path test: with no env the boot still fails it honestly and the regression baseline is unchanged (PC=3FDCE400 samples=198, 3 PCs). VT1200_IO_PATH_STUB=1 (53/75) and the skip run (Terminal Manager at frame 3000) are also unchanged. ROM md5 unchanged. See harness/mame/INPUT_REPORT.md.
9.3 KiB
Keyboard & mouse as real devices on DUART2 — report
Adds the VT1200's actual input parts to the harness as device models on the already-modelled SCN2681 at DUART2: a DEC LK401 keyboard on channel A and a DEC VSXXX-AA mouse on channel B. Both are driven by MAME's host input and talk to the DUART through its genuine bit-level serial lines.
Status: works. The X11 cursor moves with the mouse and keystrokes reach the OS. Existing configurations are unchanged.
1. Route chosen: LK401 from the docs (route 2)
The brief offered two routes. Route 1 (MAME's own lk201 device + firmware
ROM) is not usable, and the ROM check is decisive:
- MAME's
lk201device (src/mame/dec/lk201.cpp) runs an M68HC05EG and loads23-001s9-00.bin(8 KiB, CRCbe293c51) from itslk201_cpuregion. - The firmware zip downloaded during the protocol research
(
reference/keyboard-mouse/bitsavers-23-004M2-00_LK201AA_Firmware.zip) contains only23-004M2-00.BIN(4096 bytes, SHA19744c561…, CRC35dd04c6). That is exactly MAME's second ROM — the 805123-004m2.8051h.e1— which thelk201device does not execute (it is the red-LED LK201 variant; the device models the 6805 green-LED one). - So MAME's
lk201would need a ROM we do not have. Running it is impossible without sourcing23-001s9-00.bin.
Route 2 it is: an HLE model of the documented LK401/LK201 host protocol from
reference/keyboard-mouse/PROTOCOL.md, driving the real DUART wires. This also
matches the actual fitted part (LK401) rather than an LK201, and needs no
external ROM. Had route 1 been available it would have had the honest drawback
of speaking LK201 keycodes — for this OS, which reads the keyboard over the
protocol and does not care which family the keycodes came from, that would
probably not have mattered in practice; but it is moot because the ROM is
missing.
The mouse has no MAME device, so the VSXXX-AA is modelled either way (it is small).
2. What was implemented
Both devices live in harness/mame/vt1200.cpp (inside the owned driver, so the
MAME tree gains no files), derive from
device_buffered_serial_interface<>, and are wired bit-by-bit:
keyboard tx_handler -> duart2.rx_a_w (device TX -> DUART RX)
mouse tx_handler -> duart2.rx_b_w
duart2.a_tx_cb / b_tx_cb -> device rx_w (DUART TX -> device RX)
So the framing (keyboard 4800 8N1, mouse 4800 8O1) and the baud timing are the DUART's own; there is no byte-level shortcut.
LK401 keyboard (vt1200_lk401_device), protocol from PROTOCOL.md:
- Answers
FDwith the documented 4-byte self-test01 00 00 00(and runs it 70 ms after power-up, as the real part does).ABreturns the same ID bytes. - Parses the command set:
D3,E9(LK401 mode),13 p/11 p(LEDs), keyclick/bell/auto-repeat commands,CB,8B, and the division mode-set commands (acknowledged withBA). - Reports key transitions as single keycodes (> 0x40) from the documented
table (
netbsd-lk201-keycodes.html+elkay/docs/keymap.md), honouring each division's programmed mode (down-only / auto-repeat-down / down-up) and sendingB3(all-ups) when the last down-up key is released.B4metronome ticks implement auto-repeat. LK401-only keys (RShift/LAlt/RAlt/RCompose) are gated on theE9command. - Host input: one MAME keyboard port per key (
:lk401:K0…K107) withPORT_CODEandPORT_CHAR, polled at 120 Hz.
VSXXX-AA mouse (vt1200_vsxxxaa_device), protocol from PROTOCOL.md §4:
- Answers
T(and power-up) with the 4-byte self-test reportA0 02 00 00(header 101 + revision 0; manufacturer 0; device code 0b0010 = mouse; error 0; no buttons). - Implements stream mode (
R), prompt mode (D/P), and theSformat command. Reports80 | SX SY L M R,|dx|,|dy|. - Host input: relative
IPT_MOUSE_X/IPT_MOUSE_Yplus three buttons; deltas are turned into incremental reports (Y inverted for DEC coordinates).
The one non-obvious fix: idle-high TxD
The SCN2681's device_serial_interface receiver has a phase dependency: if the
first edge it sees after a receiver reset is the start bit, with no explicit
idle-high beforehand, it synchronises late and mis-samples the byte. Isolated
evidence — bit-banging 0x55 (8N1, 4800) straight into duart2.rx_a_w with the
CPU halted:
| preceding bits | SRA | RHRA |
|---|---|---|
| none | 0D |
D5 |
| 4 idle-high | 0D |
55 |
The fix is faithful anyway: a UART's TxD idles at mark (high). Both device models now hold the line high whenever they are not mid-byte, which is what makes the link work. This is the only place a physical-layer detail had to be made explicit.
3. Verification
3.1 Does the cursor move / do keystrokes register? — YES
Run: VT1200_SKIP_LEVEL1=1 (the only configuration that reaches the OS),
headless, with harness/mame/input_inject.lua injecting input.
- Keystroke registers. The script presses and releases
A(:lk401:K26) at frame 4450. The OS's DUART2 poller reads the keycode0xC2(the documented LK401 code forA) from channel A at emulatedt=62.2856 s(2 R reg=3 val=00C2 pc=3FB42710). The protocol delivers keystrokes to the OS. - Mouse reports reach the OS. The script walks the mouse 16–32 units/frame.
The OS reads ~900 bytes of well-formed VSXXX reports from channel B; the raw
stream is
98 10 00/98 20 00= header100, SX=1, SY=1, no buttons, dx=+16/+32, dy=0. - The cursor moves. The X server's X11 pointer arrow is visible at
(768,472)at frame 4400 and has left that location by frame 4600 (frames 4700 and 5000 are byte-identical, i.e. it settled). Evidence images:input_cursor_before_f4400.png,input_cursor_after_f4600.png(full frames, downscaled) andinput_cursor_band_before_f4400.png/input_cursor_band_after_f4600.png(3× zoom of the memory-bar band the pointer sat on). - The mouse self-test now decodes cleanly and the OS switches the mouse to
incremental stream mode (
R, THRB) — before the idle-high fix the self-test was garbled (0x27…) and the OS never sentR. - After
FD, the OS reads the keyboard self-test as01 00 00 00and the mouse self-test asA0 02 00 00.
The pointer's travel under continuous injection is small in absolute terms because it ends up clamped at the Terminal Manager window border; the point is that it is the X server's own cursor and it tracks the device.
3.2 Level-0 test outcome — unchanged (report only)
The level-0 I/O data-path outcome did not change. With no environment the boot still fails level 0's count-only 10-byte test and parks in the diagnostics menu; the regression baseline is byte-for-byte the same:
# no env, progress.lua at 200 frames
PC=3FDCE400 samples=198 (3 distinct PCs)
This is the expected, honest result and does not contradict
reference/level0-10byte-puzzle.md: the keyboard's power-up self-test is
4 bytes (and is sent before the DUART channel is configured, so it is dropped),
0x11 0x8C and 0xE1 provoke no reply, and the test flushes the receiver — so
the real device contributes 0 bytes to the count, never 10. Nothing was
tuned to make that test pass.
3.3 Existing configurations still hold
| configuration | expected | observed | changed? |
|---|---|---|---|
| no env | level 0 fails honestly, menu | PC=3FDCE400 samples=198 (3 PCs) |
no |
VT1200_IO_PATH_STUB=1 |
reaches level 1 | PC=3FDCE400 samples=53 (75 PCs) |
no |
VT1200_SKIP_LEVEL1=1 |
X11 weave + Terminal Manager | frame 3000 = Terminal Manager | no |
ROM md5 b46a3e442ccb86134d2356bd70d22fd2 — unchanged.
No baseline moved, so no baseline change needs recording. In particular the keyboard's power-up transmission does not perturb the faithful boot: it happens at 70 ms, before the OS programs DUART2, and the level-0 test's flush would consume it anyway.
The stub run's level-1 outcome is also unchanged (still the DUART1 counter-ready interrupt storm), so the new DUART2 devices do not alter the level-1 path.
4. Caveats / what is not done yet
- HLE, not firmware. The keyboard models what the LK401 firmware does; it
does not execute it. If
23-001s9-00.bin(or an LK401 ROM) is ever sourced, route 1 could replace this. - Keycode source. Keycodes/divisions come from the NetBSD LK201 table and
elkay's LK401 map; the LK401 firmware's exact ID bytes are unknown, so the
self-test uses the documented healthy
01 00 00 00. - Auto-repeat is a simple 500 ms / 20 Hz model — enough to be correct in shape, not measured against hardware.
0xB9ambiguity. PerPROTOCOL.mdit is "disable Ctrl-key click" (no reply); NetBSD also lists it as "inhibit transmission" (replyB7). The no-reply reading was chosen because the VT1200 OS never sends it.- The mouse reports are read by the OS's poller; the exact pointer acceleration/clamping is the terminal software's, not ours.
5. Files
harness/mame/vt1200.cpp— the two device models, input ports, and DUART2 wiring (the only source file changed).harness/mame/input_probe.lua— lists the device ports/fields (the tags Lua needs).harness/mame/input_inject.lua— drives the mouse and one key from Lua for the acceptance test.harness/mame/input_cursor_*.png— the frame evidence above.- New opt-in knob
VT1200_INPUT_LOG=<path>— per-byte device TX/RX trace.