Commit Graph
102 Commits
Author SHA1 Message Date
arti 0563cb08e8 udpstream: LANCE command payload -- peek/poke/jump/reboot, benchmark, driver-matched RX
lance_cmd.c is a payload that drives the LANCE with its own code (no ROM
calls) and answers a command protocol on ethertype 0x88B6, so a unit whose
chip we have taken over can still be inspected:

  read (peek), stats, poke, jump, reboot

The reply path was dead for a while and the cause was not the chip: the RX
ring had been grown to 16 entries (0x3020..0x309F) while TXR stayed at
0x3060, so all eight TX descriptors lay inside the RX ring.  The chip's RX
write-back set OWN in what we believed were TX descriptors, and every reply
was discarded by transmit_poll() as "owned but not start of packet"
(r 00309A 8000 / w 00309A 0000 in the DMA log).  TX ring moved to 0x30C0,
with a compile-time guard so the rings cannot overlap again.

Receive path aligned with the three drivers available locally (4.4BSD
pmax if_le.c, NetBSD am7990.c, Linux 7990.c):

  * STP *and* ENP required on one descriptor, else the frame is counted as
    chained and dropped rather than parsed from the middle of a buffer;
  * the 4-byte FCS the chip writes into the buffer is subtracted, as all
    three do (rmd3 - 4);
  * re-arm restores RMD2 and clears RMD3, ownership written last;
  * TX kick is TDMD|INEA (no reference driver re-writes STRT);
  * a TX slot still owned is skipped instead of clobbered.

RX buffers are 1526 B so a full 1514-byte MTU frame fits one descriptor;
1500 B would have chained it.

vtcmd.py drives all of it; bench-sweep.sh sweeps size x rate.  Measured on
the real VT1200 (10 Mbit half-duplex hub): 856-858 fps of 1514-byte frames
at 99.9-100% delivery, i.e. wire limited, and 1500 fps of 60-byte frames at
99.8-99.9%.  NETWORK_REPORT.md sections 68-72 record the evidence, including
the 64-descriptor build that died with an illegal opcode on the unit and was
reverted.
2026-10-30 16:57:15 +02:00
arti e51c7b94cf NETWORK_REPORT: the broadcast payload works on the real VT1200 (TX 5, RX 5)
Panel read off the unit after loading lance_bcast.coff with the host sending
0x88B5 broadcasts: TXD=5, RXF=5, ET=0x88B5, CSR0=0x0073 (STRT|TXON|RXON|INEA)
and REST=0x0073. First exchange with a real peer using only our own driver
code.

Two display bugs found by reading it: the glyph table had no 'X' (so TXD/RXF
printed as 'T D'/'R F') and the three MAC words were drawn on top of each
other. Both fixed: X added, MAC on three rows, REST moved to y=878.
2026-10-30 16:24:06 +02:00
arti f251672d5d udpstream: payload sends and receives raw Ethernet with its own LANCE driver
lance_bcast.c drives the chip entirely on its own -- init block, PADR/LADRF,
eight TX and eight RX descriptors, INIT/IDON/STRT, TDMD, completion polling --
and test-bcast.sh runs it in MAME and reports:

  TXD=6  RXF=5  ET=0x88B5  source MAC 0xC6FC  ok=1  PASS

The tap capture separates the directions: six 60-byte 0x88b5 broadcasts from
08:00:2b:16:4b:5a (our payload) and 49 from the host. RXF counts host frames
(early stop at five) and the recorded source MAC is the host's.

Fixes behind it: per-frame TX descriptor (the chip advances its own ring
position), cfg-net needed for a tap to exist at all, pkill -x instead of -f,
and two bit-vs-byte addressing bugs.
2026-10-30 16:21:02 +02:00
arti dfc968e906 udpstream: lance_bcast -- raw Ethernet broadcast TX + RX with no ROM code
Normal (promiscuous) mode instead of loopback: programs its own init block,
PADR and LADRF, arms eight RX descriptors, transmits six 60-byte broadcast
frames (ethertype 0x88B5) through its own TX ring with TDMD kicks, then counts
received frames and records the last source MAC and ethertype.

Everything is the payload's own code -- CSR accesses, init block, rings,
descriptors, DMA results. Panel: TXD (transmitted), RXF (received), ET (last
ethertype), MAC (last source), CSR0, REST.
2026-10-30 16:05:31 +02:00
arti e3d04d6459 NETWORK_REPORT: correct 64.3 -- the Ethernet Port Error banner is a clean-boot condition
The terminal shows 'Ethernet Port Error - 24' after a clean boot while its port
works, so it is the firmware's own Ethernet self-test result (which also fails
in MAME per LANCE_LOOPBACK_REPORT), not damage from the payload.

Consequences: the banner is not evidence about the takeover, and the 'needs a
power cycle' claim now rests only on the runs that crashed; for the REST=0x0073
build the network was never checked after the run.
2026-10-30 16:03:19 +02:00
arti b6f193262a udpstream: drop the old white-box ok/tx-done indicators
They were drawn at y=850, overlapping the REST text row (846-867), which is
what shows as a stray box over REST in the unit photo. The text panel already
reports OK as YES/NO.
2026-10-30 16:02:40 +02:00
arti 4dda186241 NETWORK_REPORT: text panel on the unit -- REST=0x0073 shows the chip hand-back works
Photo images/20261030_155855.JPG shows CSR0/CSR1=0x07B3, TMD1=0x8300,
RMD1=0x0300, MCNT=0x001C, OK=YES (loopback reproduced with the text build) and
REST=0x0073 = INIT|STRT|TXON|RXON|INEA, i.e. the chip is running again after
the payload's 0x7F41 -> 0x0042 hand-back.

The OS however shows 'Ethernet Port Error - 24' and stays off the network, so
the recovery failure is in the firmware's driver state, not in the LANCE. A
takeover run therefore still costs a power cycle.
2026-10-30 16:01:58 +02:00
arti 895e059383 build-lance.sh: fail the build if .text overlaps the marker block
The text panel grew .text past MARK and the payload overwrote its own code
with cell() writes (illegal opcode on the unit). The build now parses
build/lance.map and aborts if the end of .text reaches MARK, printing the
remaining gap.
2026-10-30 15:58:16 +02:00
arti c0ae3ecd27 fix crash: marker block overlapped .text after the text panel grew it
.text went from 0x018C6140 to 0x018CACD0, so the marker cells at 0x018C8000
landed inside the payload's own code; cell() then overwrote instructions and
the CPU faulted with an illegal opcode before drawing anything.

MARK moved to 0x018D0000 (~2.6 KB past the end of .text).
2026-10-30 15:57:30 +02:00
arti 3cd03cdc19 fix crash: 0x23CC50 is the RDP pointer, not a CSR0 shadow
The restore wrote 0x0042 into 0x23CC50, overwriting the driver's RDP pointer
(0x05800000); the next CSR access then targeted 0x0042 and the terminal died
with an illegal opcode. The TX kick does move @0x23CC50,a3 / move *a3,a5 --
it reads the live CSR0 and ORs TDMD, there is no shadow cell.

Report: section 10 corrected, section 62 added with the disassembly and the
consequence.
2026-10-30 15:56:08 +02:00
arti 2ec9787435 udpstream: text panel (labelled hex) and an instrumented chip restore
The panel now draws labelled hex text instead of 16 white boxes: a 5x7 glyph
table for 0-9/A-F plus the letters the labels need, built at runtime because
this toolchain never runs .cinit. Rows: CSR0, CSR1, TMD1, RMD1, MCNT, OK and
REST (CSR0 after handing the chip back), on a 32px pitch.

The restore now matches the firmware's own init: CSR0 = 0x7F41
(INIT | INEA | all status bits write-1-to-clear) instead of a plain INIT, then
poll IDON, then 0x0042 (STRT | INEA), plus a retry if TXON does not come up and
an update of the driver's CSR0 shadow cell 0x23CC50.

Note: on the first post-fix run the network still did not recover, so the
restore remains a convenience at best; results are read from the panel.
2026-10-30 15:54:03 +02:00
arti 53ff57fe45 NETWORK_REPORT: confirmed on real silicon -- lance_fixlen works, loopback completes
Panel read off the VT1200 after loading lance_fixlen.coff (via
dwt/loader/hostload.py): CSR0=0x7B3, TMD1=0x8300, RMD1=0x0300 (OWN clear,
STP|ENP), MCNT=0x001C, ok=1 -- byte-identical to the MAME reference
(0x7B3/0x8300/0x0300/0x1C/1).

So the firmware's init/ring/descriptor/TDMD sequence is confirmed on the
machine, and the earlier failure (CSR0=0x1BB, TMD1=0x8338, no RX) was the
payload's descriptor-length encoding: the field must be -len (0xFFE8/0xFA0A),
which MAME masked via get_buf_length()'s 0xF000 OR.

Also records that the payload kills the network while it runs (results are
panel-only) and that the give-the-chip-back block did not restore it.
2026-10-30 15:45:30 +02:00
arti 8ff16b5b73 NETWORK_REPORT: machine-generated coverage -- 26 unmapped entries found and mapped
Replaces the 30-address spot check with a mechanical pass: every report address
is classified against the MMTM prologue table, so interior labels are shown to
be interior rather than asserted. 225 addresses, 82 mapped, 143 unmapped, of
which 102 fall inside a mapped function and 41 sit in 26 unmapped entries.

Maps those 26: arpresolve (if_ether.c:261) for 0x3F60FB60, ifa_ifwithnet
(if.c:189) for 0x3F623180, tcp_respond (tcp_subr.c:112) for 0x3F6BD9F0,
leerror (if_le.c:972) for 0x3F9DAFF0, the leintr TINT half for 0x3F9D8A50,
leinit/lereset for 0x3F9D27D0, the tcp timer pair for 0x3F604C30, and marks the
rest firmware-specific with the reason.

Also corrects the RX entry: the function starts at 0x3F602DB0, not 0x3F602E00.
2026-10-30 11:25:54 +02:00
arti c02d475678 NETWORK_REPORT: map the remaining function entries; explain the label-vs-entry gap
Maps tcp_slowtimo/tcp_fasttimo (tcp_timer.c:76/51), ether_output
(if_ethersubr.c:69) for the device transmit method 0x3F607C90, m_pullup
(uipc_mbuf.c:440) for 0x3F6B0160, arpresolve for 0x3F6105E0, leinit/lereset
for 0x3FDDCBE0, leintr for the diagnostics handler 0x3FDD8E80 and leprobe for
the address-ROM reader 0x3FDD74B0. Marks the MAC formatter, IP-output helpers,
OS primitives (locks/deferred work) and buffer helpers firmware-specific.

Also documents why a raw address grep shows 225 addresses but only 66 inside
the mapping sections: the rest are internal labels and call sites inside
mapped functions, and mapping is per function entry.
2026-10-30 01:43:59 +02:00
arti 3a685b2775 NETWORK_REPORT: map the TCP helpers, socket/queue helpers and utilities
tcp_output (tcp_output.c:46), tcp_template (tcp_subr.c:65), tcp_close
(tcp_subr.c:244), tcp_timers/TCPT_2MSL (tcp_timer.c:144/158) and timeout
(kern_clock.c:232) for the TCP side; ifa_ifwithaddr (if.c:141) for 0x3F61AB30;
in_cksum (in_cksum.c:24, ADDCARRY at :20) for 0x3FB2C290; bcmp (bcmp.c:18) for
0x3F942230; the ntohl macro for 0x3FB2C000. Marked firmware-specific:
0x3F61B000 (lock wrapper), 0x3F61BFC0 (packet accessor), 0x3F60BD40 (queue
post), 0x3FB2C140 (compare helper).

Also: the 120 written at TIME_WAIT is 2 * TCPTV_MSL with PR_SLOWHZ=2
(tcp_timer.h:60, tcp_input.c:1068), so TCB+0x80 is t_timer[TCPT_2MSL].
2026-10-30 01:43:21 +02:00
arti 8c8e0c4e24 NETWORK_REPORT: map the last addressed functions (codec table, dispatchers, re-arm)
Adds rows for the field-codec dispatcher 0x3F660380 / table 0x1E3D80 (8-byte
entries, fn0 at +0, fn1 at byte 4) and its entry-2 pair 0x3F615E00 (convert,
a ntohl-style swap) / 0x3F616090 (compare, the in_pcblookup address-test
idiom) -- the operations are ordinary BSD, the table-driven codec is firmware
specific (no lib/libc/rpc in this tree, no kernel fn-pair table).

Adds 0x3F65A570 and 0x3F6A86D0 as firmware-specific (cmpi 0x72 character-coded
id; not reachable from any EtherType), and the receive re-arm 0x3F9D17E0
(referenced at 0x3F602EC0/0x3F603100) mapped to lereset/ledrinit.

Extends 56.3's coverage statement to match.
2026-10-30 01:42:05 +02:00
arti 8d8f8656df NETWORK_REPORT: correct 54.4's coverage claim in place
It said every addressed function has a BSD counterpart. The protocol layer
does; the deferred receive/submit path and the interrupt dispatcher are
firmware glue with no 4.4BSD equivalent (section 56).
2026-10-30 01:41:06 +02:00
arti 68f3c677df NETWORK_REPORT: cite the tcp_dooptions/tcp_mss call sites instead of an em dash
tcp_dooptions is called from tcp_input at :387 and :567; tcp_mss from
tcp_dooptions at :1343.
2026-10-30 01:40:55 +02:00
arti a390601aa9 NETWORK_REPORT: finish the mapping honestly -- glue vs source, plus quotes
Adds the quoted identifiers that three rows lacked: ip_output.c:258-261
(htons len/off, zero sum, in_cksum -- operation for operation with
0x3F62EA00), udp_input's preamble and in_pcblookup(&udb, ip_src, uh_sport,
ip_dst, uh_dport) at udp_usrreq.c:223, udp_output's in_cksum at :415, and
tcp_dooptions at tcp_input.c:1309 (called from :387 and :567).

Adds the seven addressed functions that were missing: 0x3F602E00, 0x3F9E0B80,
0x3F600200, 0x3F603250, 0x3F9DC890, 0x3F9DCF50 are VT1200 glue (BSD calls
ether_input inline from lerint at if_le.c:761 and if_start directly), and
0x3F7144C0 is the firmware's own interrupt dispatcher.

Corrects 54.4, which claimed every addressed function has a BSD counterpart.
2026-10-30 01:40:42 +02:00
arti 1364b37e83 NETWORK_REPORT: PCB offsets confirmed against struct inpcb
struct inpcb (sys/netinet/in_pcb.h) lays out inp_faddr, inp_fport, inp_laddr,
inp_lport at 12/16/18/22 with 16-bit alignment -- exactly the ROM offsets
0x60/0x80/0x90/0xB0, in the same order in_pcblookup tests them. A 32-bit
aligned build would put them at 12/16/20/24, so the match also pins the
firmware ABI to 16-bit alignment, consistent with struct protosw's 6/10.
2026-10-30 01:39:49 +02:00
arti b8899645d0 NETWORK_REPORT: finish the BSD mapping (rip_input, tcp_input switches, ICMP checksums)
0x3F61EC00 is rip_input() (raw_ip.c:58): PCB-list walk on protocol/laddr/
faddr, sbappendaddr delivery, and the ips_noproto counter we recorded at
0x96500. The dsj gate compares that PCB field against 1 -- the raw socket
bound to ICMP -- so the 5/8/13..18 skip list is the raw-ICMP filter for types
the kernel answers itself.

The two TCP state tables map onto tcp_input's two switch(tp->t_state) sites
(506 and 865); table A's shared handler for CLOSING/LAST_ACK/TIME_WAIT matches
the source's fallthrough group at 840-842. ICMP checksum verify and reflect
match ip_icmp.c:184 and 522-523.
2026-10-30 01:39:37 +02:00
arti 3ad5a691b8 NETWORK_REPORT: map every reversed function to 4.4BSD source
Adds the ROM -> BSD correspondence with the construct that identifies each
pair: ipintr/ip_input's inetsw[ip_protox[]] dispatch (both tables found in RAM
at 0x1E0F70 and 0xD6BE0, struct protosw offsets 6/10 match the dump),
icmp_input/icmp_reflect/icmp_send, tcp_input/tcp_dooptions/tcp_mss,
udp_input/udp_output, in_pcblookup (line-for-line: matchwild=3, lport, laddr,
faddr/fport, best-match), arpresolve/arplookup/in_arpinput/arprequest, and the
pmax LANCE driver's leinit/lereset/ledrinit/lestart/leintr/lerint.

lestart() writes the descriptor length as plain -len (if_le.c:485), which for
24 bytes is 0xFFE8 -- confirming section 50 from a third direction and showing
our 0x0FE8/0x0A0A were simply positive values.
2026-10-30 01:39:09 +02:00
arti b5fe6935e1 NETWORK_REPORT: CSRG tree provenance; IDON clearing shown to be optional
original-bsd spans 1979..4.4BSD-Lite2 and carries the same if_ether.c string as
the ROM. Its pmax (DECstation) if_le.c clears IDON before STRT, Linux does too,
but NetBSD's lance.c does not -- and neither does the VT1200 firmware itself
(0x3FDDCBE0 writes CSR1/2/3 then 0x7F41 and the live path goes INIT -> poll
IDON -> 0x0042 with no IDON clear). Since the firmware works on the real
machine, the IDON hypothesis is dead and the descriptor length encoding
(section 50) is the remaining explanation.
2026-10-30 01:35:58 +02:00
arti b534bab7d7 NETWORK_REPORT: provenance from 386BSD history, corroborating the ICMP case set
The local NetBSD tree reaches back to 1992 with the 386BSD-0.1 import
(a319363d6037). Its sys/netinet/if_ether.c:350 carries the exact ROM string
'arp: ether address is broadcast for IP address', which the modern tree no
longer uses ('link address'), placing the firmware in that lineage.

The 386BSD ip_icmp.c switch contains exactly the case labels that section 46
derived from the ROM's dsj + compare chain (REDIRECT, ECHO, TSTAMP,
TSTAMPREPLY, IREQ, IREQREPLY, MASKREQ, MASKREPLY), confirming that reading
from source rather than inference.
2026-10-30 01:31:48 +02:00
arti 874d1fbbf5 networking RE report + LANCE payload variants
harness/mame/NETWORK_REPORT.md: how the VT1200 firmware drives the LANCE,
from ROM disassembly and MAME traces. 50 sections, each claim tied to a bit
address, a trace count or a quoted source line. Covers the driver device
struct (0x23CBF0), init (0x3F9D4BC0), descriptor/DMA encoding, the live
handler (0x3F9D5B40) with per-status-bit dispatch, the RX poll (0x3F9D62B0),
ring arming (0x3F9D36C0), TX completion (0x3F9D8AF0) and the single transmit
path every protocol uses (0x3F6084E0 -> 0x3F9DC890 -> 0x3F9D4BC0), plus the
IP protocol dispatch (0x3F6270F0), ICMP (343-count chain), TCP state tables
and live TIME_WAIT, the ARP responder (0x3F6119A0) and the socket 4-tuple
demux confirmed by a memory dump.

It also records what was withdrawn: five successive explanations for the
real-silicon CSR0=0x1BB / TMD1=0x8338 result, and the descriptor-length
encoding bug found by comparing Linux 7990.c and NetBSD am7990.c (length must
be 0xF000 | -len; MAME masks the difference in get_buf_length).

harness/udpstream: the LANCE loopback/takeover payload and the variants used
to test the init/kick orderings in MAME (bare TDMD, STOP before INIT, double
INIT, long wait with CSR0 sampling, IDON clear, corrected descriptor length).
build-lance.sh takes LANCE_SRC/LANCE_OUT so the variants build from one script.
2026-10-30 01:25:21 +02:00
arti 41b0979aef c-hello: first C program on the VT1200, running on real hardware
The 1987 TI K&R toolchain already compiled and linked a fib test, but nothing
had ever been executed on a VT1200. This is that program: hello.c draws HELLO
into video RAM (1 bpp, bit 0x04000000, no X11) and is loaded through the COFF
loader with hostload.py, on the real unit and in MAME.

Two runtime traps shaped the design, both recorded in the README:

  * boot.obj's exit() is `for(;;)` and _c_int00 ends in `reti`.  That is a
    standalone-ROM runtime, not a process: the child spun forever, and because
    the VT1200 OS is cooperative it took the whole terminal down (illegal
    opcode on hardware).  entry.asm replaces boot.obj -- set STK/FP, call
    _main, `rets` back to the create_process trampoline, the same return
    xhello uses -- and the image carries no .cinit (tables are built in code).
  * Pointers are bit addresses and pointer arithmetic scales by object size,
    so p + n on a char * is n bytes.  The pre-fix frame in evidence/ is that
    bug: 32 one-byte bars spread across a row.

Framebuffer geometry is not portable: the real unit is 1280 px wide at a
256-byte row pitch, MAME's canvas is 1024 px at 128.  build.sh builds both
(ROW_BYTES), and the README records how the real pitch was measured from a
real-unit frame rather than taken from MAME.

Evidence: real-unit frames in harness/c-hello/evidence/ (the pre-fix stripes
and the working HELLO), and dwt/loader/evidence/real-unit-xhello/ for the X
window that ran on the unit earlier.
2026-10-29 18:25:38 +02:00
SDD be32e8a138 harness: regenerate duart2_default_tm_frame3000.png -- the cited evidence image predated the LANCE and contradicted the README (no MAC on screen) 2026-10-18 01:21:41 +03: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
SDD af7d3565a5 dis34: print long relative JR as jr and surface B-file SP as b15
The 16-bit-displacement jump (low byte 0x00, two words) is JRcc, not the
three-word JAcc (low byte 0x80).  Printing it as ja made linear
disassembly report the wrong control flow and made every such branch grow
by a word when reassembled.

Register 15 is the stack pointer in either file: 0x0F is A15 and 0x1F is
B15.  The disassembler printed both as sp, hiding B-file stack-pointer
code.  Print b15 when the R bit selects the B file.

Validated with the 1987 TI GSPA/GSPLNK/GSPROM round trip: the 32 KB region
at bit 3F208000 still reassembles byte-for-byte (md5
43adb6669571041985f3e26fd9ff7cb9), as do the 4 KB and 16 KB spans and the
0x8000/3F240000 region.  The converter no longer needs its ja->jr or
sp->b15 rewrites (5 fixups -> 3); a whole-ROM diff contains only those two
output categories.  The ROM is unchanged
(b46a3e442ccb86134d2356bd70d22fd2).
2026-10-18 00:54:34 +03:00
SDD 7d299451da reference: TMS34010 SDK investigation; prove GSPA round trip
The 1987 DOS toolchain (GSPA/GSPLNK/GSPROM) runs under DOSBox and
reassembles 32 KB of the v2.2 ROM byte-for-byte after five documented
fixups to smart34010 output.  The 1990 r5.01 tools are OS/2 NE and do
not run in DOS.  Adds the converter, round-trip script, and the
DOSBox/dosemu benchmark.
2026-10-18 00:39:32 +03:00
SDD c21cab05f8 harness: net-setup.sh -- recreate the tap+bridge the harness needs (they do not survive a reboot) 2026-10-17 14:15:17 +03:00
arti 307df67f9d harness/mame: the TFTP PCF loader skips BITMAPS, so the n*4 overflow is not on that path
The TFTP font path loads through 3F96B850 and parses with the metadata
reader 3F97EF00, which handles only PROPERTIES/ACCELERATORS/METRICS/
ENCODINGS and silently skips BITMAPS. A font with num_chars=0x40000001
therefore passes the loader (LOADER-A-SUCCESS) without the field ever
being read, and pcfReadBitmaps (3F984970, multiply 3F985050) is never
entered. pcfReadBitmaps has exactly one caller, 3F98BB70, in the X11
font-request reader 3F98AC20, not the TFTP fetch path.

The 'refused after 1024 bytes' of evil-real.pcf is the METRICS table:
its big-endian compressed count 0x00DF is read little-endian as 57088,
so fosAlloc(57088*16) fails at ~906 bytes; BITMAPS (offset 2024) is
never reached.  Baseline.pcf fails the same way; baseline_le.pcf puts
that one 2-byte field in the order the loader reads it, parses cleanly,
and still ignores the crafted BITMAPS count.

No ROM patch, no check disabled, no serve.py edit.  Baselines unchanged:
no env samples=198, stub samples=53.
2026-10-17 14:14:07 +03:00
arti 3a51b95317 harness/fontwork: reproduction scripts for the TFTP Disk-full investigation
Isolated-netns test setup (a private tap-mess-0-0 so a second MAME can run
without holding the owner's tap), the one-file/one-index TFTP servers, the
trace/probe run wrappers, and the two server logs quoted in
FONT_STORAGE_REPORT.md.
2026-10-16 22:03:49 +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
SDD 264d5330ad harness/serve.py: optional BOOTP responder (--bootp) so the VT1200 gets an IP at boot
The terminal broadcasts an RFC 951 BOOTP request at power-up. Add an opt-in
responder that answers it with yiaddr 192.168.99.2 and siaddr 192.168.99.1,
echoing htype/hlen/xid/chaddr. It offers no boot file: the request's sname and
file are empty, so the reply's are too.

Off by default -- a bare 'serve.py 192.168.99.1' behaves exactly as before
(TFTP on port 69, nothing else). Enable with --bootp.

Delivery note: the host IP stack never receives the source-0.0.0.0 broadcast,
so the responder attaches to the MAME tap's L2 (AF_PACKET) and writes the reply
frame back. See harness/BOOTP_REPORT.md for the evidence and the still-open
'User settings invalid' / 'Disk full' symptoms.
2026-10-16 17:27:00 +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 82ab65bc82 run.sh: document ScrollLock (UI_TOGGLE_UI) as the fix when MAME's menu stops responding, and that closing the window is a clean exit 2026-10-16 17:01:04 +03:00
SDD b65d9ef670 run.sh net: the per-device interface selection must PERSIST (an empty cfg dir produces no tap); -networkprovider alone does not choose one 2026-10-16 16:54:08 +03:00
SDD b356553825 run.sh net: use a private cfg directory -- a saved per-device interface=-1 silently overrides -networkprovider and no tap is ever opened 2026-10-16 16:53:14 +03:00
SDD a0d752062f run.sh: add a net mode (skip + tablet + networkprovider taptun) for the crash-work configuration 2026-10-16 16:48:40 +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 d1be7da1f1 run.sh: replace the now-redundant tablet mode with a faithful mouse mode (tablet is the default) 2026-10-16 16:40:52 +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 7bf195e615 reference: commit skip_frame3000.png (the X11 weave, cited as evidence in the README); ignore worker scratch 2026-10-16 16:23:15 +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
SDD 942a45800b run.sh: tablet mode (skip + VSXXX-AB absolute pointing); lightgun options in COMMON, verified inert by frame-md5 A/B 2026-10-16 16:09:51 +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