18 Commits
Author SHA1 Message Date
arti 8f831eaf33 fix(video): model the real unit's VR319 monitor
The harness forced the VR150 monitor switch, so the firmware wrote the OS
screen at 1024 px / 128-byte rows.  VT1200_VRAM_REAL then re-read that
framebuffer at the VR319 pitch, which sheared the OS and wrapped the right
quarter of every row.

Model VR319 (switch 0x60) by default -- the real unit's mode -- so the
firmware itself programs 1280x1024 with a 256-byte row pitch (CONVDP=0x14).
The renderer's pitch follows VT1200_MONITOR, and the destination span now
comes from HEBLNK..HSBLNK instead of x=0, placing the active 1280 px at
x=336 in MAME's 1696-wide raster.

Verified: clean Terminal Manager at frame 600, legible faithful diagnostics
menu and level-1 error screens, tile mode PASS with tile_check 1280/1280
tiles, tablet/mouse pixel-identical.  VT1200_MONITOR=VR150 reproduces the
old geometry; the regression baseline moves 198 -> 199 (2 PCs) and is
recorded in harness/mame/README.md.
2026-10-31 20:53:51 +02:00
arti 5af949b338 fix(tiles): pack LSB first, the terminal's scanout order; render the real layout
The tile pipeline packed every row MSB first: pixel i -> bit 7 - i % 8. The
encoder and tile_check.py shared that convention, so every MAME replay check
passed while a display showed each group of eight pixels mirrored.

Evidence, from the real clip's own replay dump (vram-replay-200.bin, frame 200
of Bad Apple, 1,228,800 pixels):

  decoded MSB first   1,215 mismatched  <- what the pipeline produced
  decoded LSB first   3,113 mismatched  <- the mirrored picture, a sawtooth

and both the real unit and MAME decode LSB first: dwt/screenshot.py --real
("LSB-first bits", measured on live VRAM) and vt1200.cpp's
(vram[bit >> 4] >> (bit & 15)) & 1.  A zoomed edge of that dump showed the
comb of triangles; after this change the same edge is a smooth curve.

vidsrc.Rows.pack_row now sets bit i % 8 of byte i // 8.  The test that pins it
was written first and watched fail: it decodes the packed row with the
hardware's rule, which no MSB-first packer can pass.  The two older tests that
asserted MSB first were rewritten -- they were asserting the bug -- and the
synthetic clip now packs through a bit-reversal table, so the committed cache
is regenerated (PSNR 29.47 -> 31.30 dB).  The layout claims in tilecost.py,
vram_tile.asm and make_tiles.load_clip are corrected so the clip format states
the order instead of leaving it to whoever writes the frames.

Separately, MAME's renderer reads 128-byte rows while a payload targeting the
real unit writes 256-byte rows (vram_tile.asm DPTCH=0x800; c-hello/README.md:
"a payload writing at row*2048 bits lands on scanline row").  VT1200_VRAM_REAL=1
now draws the framebuffer at 2048 bits / 1280 px from x=0.  Off by default: the
Level-1 error text is only legible at the derived 128-byte pitch, and that is
what the faithful boot needs.  The branch only touches the bitmap, so the
regression baseline is unmoved (PROG_FRAMES=200 -> PC=3FDCE400, samples=198,
3 distinct PCs), and the default frame-3000 render of the Terminal Manager is
unchanged.

Verified end to end on the 3,292-frame Bad Apple clip: RX_OK=3196, TILES_DROP=0,
3292/3292 frames dumped and matched by tile_check.py, and the dumps decode to
1,379 / 4,125 mismatched pixels of 1,228,800 (quantisation) under the hardware
rule versus 3,101 / 9,793 under MSB.  MAME's own frame dumps with
VT1200_VRAM_REAL=1 show the clip unsheared.

Revert note for the driver: delete the m_render_real branch and its getenv and
vt1200.cpp returns to the measured 128-byte renderer byte for byte.  No second
reader reviewed that citation; it is quoted in the comment for the next one.
2026-10-31 18:06:14 +02:00
SDD 1608a68cdb hello-world: the TMS34010 SDK assembles code that runs on the emulated VT1200
An 82-byte payload written in GSPA assembly, linked at bit 01800000h and
loaded into main RAM by harness/mame/hello.lua, executes on the CPU: it
writes DEAD/BEEF and a 1024-iteration counter into RAM and fills a 256x64
block in video RAM, which appears in the frame dump at x 256..511,
y 136..199.  Entry is a debugger breakpoint on the level-0 menu idle loop,
not a delivery mechanism.  Includes the build script, a COFF .text
extractor (gsprom's hex addresses wrap at 64 KB), the run log, RAM excerpt,
frame crops including a no-load control, and HELLO_WORLD_REPORT.md.
2026-10-18 01:20:38 +03:00
arti 7efb4880f0 harness: the TFTP font 'Disk full' is the terminal's own tftp_close, not a storage or map bug
A tcpdump on the tap plus a live execution trace settle the 'Disk full or
allocation exceeded' abort: the TERMINAL sends TFTP error 3, from
tftp_close (0x3F5EC860), which reports an incomplete read (the last DATA
block read was a full 512-byte block) using its fixed text for code 3. It
is not a capacity or disk condition.

The font buffers (path/index read buffer bit 0x9316D0, fos parser stream
buffer 0x227FD0, parsed-font cache 0x227F50) are all in main RAM
(0x00000000-0x01FFFFFF), mapped and writable; the writes land in RAM dumps.
No second map overlap exists, so the NVR fix did not cause this. vt1200.cpp
is unchanged and every baseline is intact.

New: fonttrace.lua, fontprobe.lua, two evidence pcaps, FONT_STORAGE_REPORT.md.
2026-10-16 22:03:16 +03:00
arti d4a3ccc42c harness: model the battery-backed NVR at bit 0x6000000
The terminal's settings NVR was not modelled: 0x6000000 fell inside the
video-memory mapping (0x04000000..0x1fffffff), so NVR reads returned stale
framebuffer words and writes were lost.  That is the owner's 'User settings
invalid' plus 'settings do not persist' (and the font path is stored here).
Map a persisted NVRAM over the 0x800-word settings block; MAME now saves and
reloads it (nvram/vt1200/nvram), verified write->restart->read.  No MAME-tree
change.
2026-10-16 17:25:04 +03:00
arti b6da987aae harness: dump the LANCE DMA; the C - LANCE TEST's failing frame carries no FCS by design
The failing loopback case is a negative test: its TX buffer deliberately has
no FCS, and the test expects a CRC error.  The DTCR=1 case that does carry an
FCS is delivered intact and passes, so the DMA is not dropping four bytes.
The sole remaining mismatch is one bit: the test wants RMD1=0x6B00
(ERR|FRAM|CRC|STP|ENP) and MAME's am79c90 reports 0x4B00 -- it never sets
RMD1_FRAM.  No MAME-tree change made; reported for a second reader.
2026-10-16 17:12:02 +03:00
SDD 60b0b02e3c run.sh+README: fix the stale tablet comment; amend rule 2 with the orchestrator's own collision (uncommitted edits get swept too) 2026-10-16 16:41:16 +03:00
SDD 634246ecb1 harness: LANCE Ethernet -- register file, 8-byte address ROM, INT1 and byte<->bit DMA
Milestone 1 of docs/superpowers/plans/2026-10-16-lance-ethernet.md.

Modelled:
  * MAME am79c90 (AM79C90) at the firmware's two windows -- bit 0x5900000 is
    RAP, 0x5800000 is RDP; every other offset in those windows still falls
    through to the board model.
  * the Ethernet address ROM at bit 0x6800000, as EIGHT bytes.  The plan said
    six; the firmware's reader (0x3FDD7520) actually loops i = 0..7 and then
    validates a folded ones-complement checksum over bytes 0..5 against
    bytes 6..7 (0x3FDD78B0).  For the MAC 08:00:2b:16:4b:5a that is 0xC186, so
    the ROM is 08 00 2b 16 4b 5a c1 86.  A six-byte ROM returns expected = 0
    and fails LEVEL1/2 ERROR -> ETHERNET ROM CHECKSUM - 23.  The checksum
    bytes are derived from the firmware's own algorithm (documented in
    LANCE_REPORT.md); they are not read off a real PROM.
  * the interrupt on INT1, matching the ISR 0x3FA93400 and INTENB bit 1.
  * bus-master DMA, translating the LANCE's byte address to the TMS34010's
    bit-addressed main RAM (byte B -> word B>>1), cross-checked against the
    firmware's own CSR1 = 0xFF60 = RAM word 0x7FB0.
  * VT1200_LANCELOG to trace the register/ROM accesses.

Observed with the firmware's own diagnostics menu (driven by the real LK401
model; new menu_keys.lua): the M -- ETHERNET ROM TEST now PASSES.  The
C -- LANCE TEST runs the chip for real -- the firmware's init block
(mode 0x004C = LOOP|INTL|DTCR, PADR 08:00:2b:16:4b:5a) is read via the new DMA,
INIT completes (CSR0 = 0x6FE3, IDON), the chip transmits and receives -- but its
internal-loopback receive still reports a CRC error under MAME (RCV descriptor
1 = 0x4B00, message count 0).  Cause: MAME's receive() runs the FCS check when
MODE_LOOP && MODE_DTCR (am79c90.cpp:263) even though DTCR means no FCS was
appended.  No MAME change was made; quoted and reported in LANCE_REPORT.md.

The OS (skip gate) reads the ROM MAC, programs CSR1/2/3, INITs, sees IDON,
STRTs and polls CSR0; the Terminal Manager now shows Ethernet Address
08-00-2B-16-4B-5A (was 00-00-00-00-00-00) and LAT_08002B164B5A.

Baselines unchanged: no-env samples=198 (3 PCs), VT1200_IO_PATH_STUB samples=53
(75 PCs).  ROM md5 unchanged.  No MAME tree changes.
2026-10-16 16:39:21 +03:00
SDD 6d8097360c harness: the VSXXX-AB tablet is the default pointing device; VT1200_MOUSE=1 forces the faithful mouse back
The tablet was opt-in (VT1200_TABLET=1) with the mouse as the default.  Invert
it: the tablet is selected with no environment, and VT1200_MOUSE=1 (a
value-aware inverse switch, so VT1200_MOUSE=0 selects the tablet) restores the
VSXXX-AA mouse.

The real terminal almost certainly has a mouse, so this is a DELIBERATE
divergence from the target machine, documented in README.md alongside
VT1200_SKIP_LEVEL1 and VT1200_IO_PATH_STUB.  It is on by default because the OS
is only reachable through the skip gate and there the tablet's absolute reports
are what make the Terminal Manager usable.

Baselines re-measured, both unchanged:
  * no-env progress.lua at 200 frames: PC=3FDCE400 samples=198 (3 PCs) -> same.
    DUART2-B is never addressed in that window (the T/R exchange is OS-phase
    only), so the reply bytes A0 04 00 00 vs A0 02 00 00 and the 5- vs 3-byte
    framing do not reach the CPU.
  * VT1200_SKIP_LEVEL1=1 frame 3000: pixel-identical for tablet and
    VT1200_MOUSE=1 (magick AE=0); both reproduce the committed
    tablet_cursor_*.png frames exactly.

VT1200_TABLET is now a no-op; run.sh's tablet mode is reported to the owner for
rewording (its comments are his).
2026-10-16 16:19:46 +03:00
arti 065f946e2f harness: VSXXX-AB tablet on DUART2, driven by the absolute host pointer
The terminal tells the two pointing devices apart from the 'T' self-test
device code (0010 mouse / 0100 tablet) and switches report framing on it:
3-byte relative for the mouse, 5-byte absolute (header 110) for the tablet.
Verified against the running OS: with the tablet it reads the self-test as
A0 04 00 00 and then reads RHRB in groups of 5 (groups of 3 for the mouse).

Refactor the VSXXX model into a shared base + mouse/tablet subclasses and
select the device on DUART2-B with VT1200_TABLET=1 (default: the mouse, so
the mouse stays and is selectable, not replaced).  The tablet reports its
documented 0..2199 active area (VCB02 D.1) and reads the host pointer
through IPT_LIGHTGUN_X/Y, MAME's absolute control, so the emulated cursor
follows the host pointer positionally: corner (0,0) -> (194,38), centre
(1100,1100) -> (704,467), corner (2199,2199) -> (1214,898), reproducible
pixel-for-pixel on a repeated coordinate.  Driving it from the host mouse
needs '-lightgun -lightgun_device mouse' (documented for run.sh; run.sh is
not edited here).

The mouse and keyboard are unchanged, and all baselines hold: no env
PC=3FDCE400 samples=198 (3 PCs); VT1200_IO_PATH_STUB=1 samples=53 (75 PCs);
ROM md5 unchanged.  Nothing is tuned to satisfy level 0's I/O data path test.
2026-10-16 16:07:36 +03:00
arti 02acabb7ef harness: real LK401 keyboard + VSXXX-AA mouse devices on DUART2
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.
2026-10-16 15:33:30 +03:00
arti 41454d5d4c harness: the stub level-1 run continues past both DUART failures; correct the "fatal/LED loop" claim
The stub run does not park at HOST PORT - 15. Both DUART failures are reported,
the board LED blinks once (finite L3F208660), TYPE 'GO' TO CONTINUE is printed
(no input is read), and the level-1 runner continues ('CONTINUING THE TESTS').
The runner never returns to 3F204440, so 3F208060/3F208650 are never executed.

The run then wedges at frame ~2839 in a DUART1 counter-ready interrupt storm:
the ISR polls DUART1 reg 5 (bit 0x06400050) and always reads 0x08; the counter
runs in timer mode (ACR=0x60) with CTUR/CTLR never programmed, so MAME re-arms
at 1 tick and the interrupt re-asserts every ~229 CPU cycles. Frames at 3500 /
6000 / 12000 are byte-identical; RAM differs by one byte.

Corrects README.md and annotates GATE_CLEANUP_REPORT.md, BRINGUP_LOG.md and
DUART_REPORT.md. Adds a console+PC/milestone instrument and the finding report.
2026-10-16 14:56:47 +03:00
SDD f7caf9aed8 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.
2026-10-16 14:31:20 +03:00
SDD 215fd573db docs: level-0 I/O test is not honestly satisfiable by any documented device; record why and the only honest route to close it 2026-10-16 14:17:10 +03:00
SDD 47dac6b283 docs: DUART2 modelled; Terminal Manager reached; the bare run's old level-1 pass was a latch artefact (honest failure now) 2026-10-16 14:00:23 +03:00
SDD 855f3e85e9 docs: the OS DOES paint -- skip run boots to a running X11 server (root weave confirmed in a frame dump); correct the claim I wrote 2026-10-16 13:26:27 +03:00
SDD c3faa7729d docs: correct stale README - DUARTs/VRAM are modelled, fix memory_access template args, add gate list and new baseline 2026-10-16 13:19:25 +03:00
SDD acb92d3e44 checkpoint: VT1200 reverse-engineering workspace before board-model work 2026-10-16 01:37:50 +03:00