Commit Graph
178 Commits
Author SHA1 Message Date
arti fb095eb778 udpstream: alternating-frame flash (30.7 fps sustained) + DRAM store benchmark + ROM cross-check
cmd 8 alternates 0xFFFF/0x0000 per frame so the rate is visible: 300 frames
in 9.775 s on the unit, 32.6 ms each, 30.69 fps sustained.

cmd 9 is the same byte-store loop aimed at a DRAM buffer, to measure the CPU's
store rate without the VRAM arbiter and size a frame-expansion stage.
vtcmd.py --dram selects it.

Report 74.3 records the independent Ghidra pass over the ROM: the firmware
sets CONVSP/CONVDP=0x14 (pitch 0x800 bits = 256 B), PSIZE=1, PMASK=0 and
OFFSET=0x04000000 for XY addressing -- the same values vram.asm uses -- and
fills with FILL_L chunked at 0x7FC0 pixels because DYDX is 15 bits per axis.
There is no VBLT and no wait-state/arbitration register in the ROM, so the
12 us/byte CPU store cost is inherent to this board, not a setting.
2026-10-30 17:34:47 +02:00
arti d99a9cdb6c udpstream: hardware FILL L makes full-screen writes 70x faster (30.6 fps on the unit)
Byte stores to VRAM measure 2284 ms per 1280x1024 frame on the real unit
(0.44 fps) -- about a scanline period per byte, i.e. almost no access through
the arbiter.  vram.asm now drives the GSP's array engine instead: FILL L with
DADDR/DPTCH/DYDX/COLOR1 (B2/B3/B7/B9), PSIZE=1, PMASK=0 and PPOP=0, per the
TMS34010 User's Guide's implied-operand table, with CONTROL read-modify-written
so CD/RM/RR survive.

Measured: 32.7 ms/frame, 30.6 fps on the unit; 27.1 ms in MAME (74x over byte
stores there, 70x on hardware).  2,508,224 destination words/s = 0.40 us/word.

Consequence for video: a full uncompressed frame is wire-limited at 8 fps
(163840 B at 1.27 MB/s); a small source expanded on the device is limited by
the ~33 ms transfer, i.e. ~30 fps full screen if the expansion uses PIXBLT.

build-lance.sh assembles vram.asm and links VRAM.obj; MARK moved to 0x01A00000
because .text had come within 146 bytes of it.
2026-10-30 17:32:59 +02:00
arti 1571328a6b NETWORK_REPORT: osboot 3 of 3 on the unit; scripted hand-back is 7 s load + 40 s restart 2026-10-30 17:20:28 +02:00
arti ed185279ca udpstream: the payload announces itself; test-osboot.sh has no sleep
Loading the COFF over rdebug pokes thousands of words one at a time, so how
long the payload takes to appear varies by tens of seconds -- a fixed sleep is
either wasteful or too short.  The payload now broadcasts three frames the
moment its ring is armed: ethertype 0x88B6, 'K' 'S', opcode 0xFF, magic
"LANC".  `vtcmd.py wait` listens for it; measured at 8.7 s in MAME.

Two details found by measurement:

  * The announce must use TX slot 0, because the chip transmits only from its
    ring position -- arming slot 7 while the position is 0 never sends
    anything (the first attempt did exactly that and was silently lost).
  * After the announce the reply cursor advances to slot 1 to match the chip's
    new position, so replies do not collide with the announce descriptor.
    txfull then stays 0 across consecutive replies; before this it read 1.

test-osboot.sh ties the hand-back together with no sleep: listen, load, wait
for the announce, stats, osboot, wait for :771.  Report sections 73.3/73.4.
2026-10-30 17:19:28 +02:00
arti f74bd7ae05 NETWORK_REPORT: osboot works on the unit -- 40 s, Terminal Manager back
osboot at 17:11:21, rdebug back at 17:12:01 (40 s), ping OK, and the VRAM
screenshot shows the Terminal Manager with a live display, the right MAC/IP/
LAT name and the clean-boot banner.

Same machine, same payload as the two failed reset-vector attempts: reboot
0/2 (POST, never returns), osboot 1/1 (OS cold start, 40 s).  The display
returns before the network -- the operator could see the screen at 32 s while
port 771 was still closed.

Recorded as 1 of 1, not as reliable: section 72.3 already lists three
conclusions drawn from single observations, so a second run is needed before
this is the standard hand-back.
2026-10-30 17:12:41 +02:00
arti baffb8292e udpstream: osboot -- jump to the OS cold start (0x3F5788C0), the path that quietens the LANCE
The reset vector is the wrong entry: it starts POST, which re-programs the
board window from a state a payload has already changed.  The firmware has a
second entry, 0x3F5788C0, where the boot gate at 0x3F2042D0 branches when it
skips level 1.  It begins with DINT, CSR0=STOP on the LANCE, and a board-window
re-program -- exactly what a takeover breaks.

MAME, from the LANCE access log: the payload's last access and the OS's first
are 130 us apart, then pc=3F578940 writes CSR0=0x0004 (STOP) and pc=3FDDCAE0
(the OS driver) re-initialises the chip, after which the OS keeps servicing it.

cmd 6 / vtcmd.py osboot.  Unit run pending; the report section is 73.
2026-10-30 17:10:45 +02:00
arti 504d63a705 udpstream: the soft reboot has never worked (0/2) -- attempt 1 was a manual power cycle
The operator confirms attempt 1's recovery at 88 s was a manual power cycle,
not the payload.  That makes the record 0 confirmed successes out of 2, and
the first reading of attempt 1 -- "black and hung, needs a power cycle" --
was right after all.

Three corrections are now recorded in section 72.3, because they are one
mistake made three times: "hung" from a single sample, then "works" from the
same sample after it recovered, then "works" again when that recovery was
attributed to the payload.  The recovery had an external cause, which one
observation cannot tell apart from the payload working.

The Ghidra finding stands: the firmware has no software reboot, so there is
no supported entry to jump to.  Untested hypothesis for the hang remains
that the reset path leaves the LANCE busy-mastering; CSR0_STOP before the
jump would be the single-variable test.

vtcmd.py reboot requires --force and now says plainly that it has never
worked and that a power cycle should be expected.
2026-10-30 17:07:50 +02:00
arti f1dc5e7f6e udpstream: reboot is 1-in-2 on the real unit, not a working hand-back
Attempt 1 (16:58:45) came back: port 771 reopened at 88 s, the unit pinged,
and a VRAM screenshot showed the Terminal Manager.  Attempt 2 (17:04:15,
after a power cycle) never came back -- no ping, no rdebug at 130 s, monitor
asleep, operator reports it stuck.

The previous commit recorded attempt 1 as "it works, no power cycle needed".
That was a single sample and attempt 2 falsified it.  Section 72.3 now records
both runs as a table, the 1-of-2 rate, and the two corrections made on the way
(first "hung", then "works").

Untested hypothesis: the reset path does not reset the LANCE, and the payload
hands over with bus-master DMA enabled; an in-flight transfer or pending
interrupt may leave the board-window handshake at 0xC00000B0/0xC0000100
waiting forever.  The single-variable test would be CSR0_STOP before the jump.

vtcmd.py reboot requires --force again and states the measured rate.
2026-10-30 17:06:49 +02:00
arti 55236fbf54 NETWORK_REPORT: the firmware has no software reboot (Ghidra check of the test menu) 2026-10-30 17:03:04 +02:00
arti 41e21b6494 udpstream: reboot works on the real unit (88 s), and correct the earlier call
Issued while the payload owned the LANCE: the reply arrived, control went to
0x3F200040, the screen went black -- and the OS was back 88 s later.  Port 771
reopened on its own, the unit answered ping, and a VRAM screenshot shows the
Terminal Manager with the right MAC/IP/LAT name and the clean-boot
"Ethernet Port Error - 24" banner.  A taken-over unit can therefore hand
itself back without a power cycle.

An earlier reading of the same run reported "black and hung, needs a power
cycle".  That was a single observation taken inside the ~90 s POST window.
The black screen is the reset path reprogramming the board/video window
(0xC00000B0, 0xC0000100) and not re-enabling the display until POST ends.

vtcmd.py reboot now says how long it takes and warns not to power cycle
during it, instead of refusing to run.
2026-10-30 17:01:25 +02:00
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 a4ed68c0e4 README: rewrite as the project front page -- status, findings, tidbits
The old README still described the repo as ROM images plus a Ghidra
setup. The project now spans the MAME harness, the real terminal, the
COFF/SDK toolchain and the networking reverse engineering, so rewrite it
as the entry point.

New top-level content:
- a dated status table (ROM, Ghidra, MAME, real unit, our code,
  networking, LAT, font path), with the open items named as open;
- what the firmware actually contains (MIT X11 R4, BSD TCP/IP via
  386BSD, BIND, telnet, TI RTS, fonts);
- the address model table, corrected against harness/mame/vt1200.cpp
  (RAM 0..0x1FFFFFF; DUART bit 0x06200000/0x06400000);
- how the faithful MAME run stops at level 0, why no documented device
  can pass it, and what the stub does and does not prove;
- the real-unit debug channel (TCP 771), the challenge, and a table of
  what is proven on hardware;
- the three routes for running our own code, and the heap model;
- the LANCE + BSD stack mapping, the RMD1_FRAM blocker and the claim
  deferred for real silicon;
- LAT and the idle-crash investigation, and the two font results;
- a curated "odd findings" list and a documentation index.

Factual corrections made while checking sources: the rdebug gate is a
SETF 2 field (the port was never closed), CSR0=0x1BB has ERR clear, the
pcfReadBitmaps overflow is not reachable on the TFTP path, and the
real-unit framebuffer is 1024x2048 line-interleaved (the "line doubled"
look was a partial read). The XCROSS 123k figure is the unmapped log,
not a VT1200_CAL proxy hit. ROM md5 unchanged.
2026-10-30 12:03:01 +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
arti 6338095683 tools: add as34/ld34 TMS34010 binutils, ported to 64-bit Linux
Andrew Morton's Unix-hosted assembler/linker for the TMS34010, fetched from the gsptools archive and patched to build on x86-64. The K&R sources silently truncate pointers there: implicit int parameter declarations and fixed-argument printf wrappers. The patch fixes those and the flex 2.6 yy_init mismatch.

hello.as34.g is harness/hello-world/hello.asm in as34 syntax. as34/ld34 reproduce the GSPA-built payload byte-for-byte (82 bytes, md5 db91788991ebba476b55a11127a36475), and the MAME hello.lua run reports the same markers, the same 1024-word VRAM block and a byte-identical frame.

build.sh downloads the upstream tarball at build time (sha256 pinned); the tree is not vendored.
2026-10-22 14:19:14 +03:00
SDD f3d26ad687 dwt: trace the bottom diagnostics message box to @0xCB640 and the RAM message table 2026-10-18 17:49:13 +03:00
SDD 225394e1ae dwt: xtext draws text in a real X window on the VT1200; TFTP retarget fetches the COFF 2026-10-18 17:28:08 +03:00
SDD 3c16824f5b dwt: record that the owner moved windows on the real unit (extra windows are not a bug) 2026-10-18 17:28:01 +03:00
SDD edc26855fd dwt: full ROM dump over rdebug is byte-identical to vt1200_v22.bin (md5 match) 2026-10-18 17:25:18 +03:00
SDD 09f8607466 dwt/tcpip: LAN TFTP client (100.103.255.254) and a verified self-contained fetch + load on the real unit 2026-10-18 17:12:00 +03:00
SDD efef682b63 dwt/evidence: real-unit Diagnostics dialog and the session-manager hang after the first ROM-test run 2026-10-18 17:07:11 +03:00
SDD e2c18ee70b dwt: NVRAM dump over rdebug (settings block) and LANCE address ROM read; both match the model 2026-10-18 17:06:12 +03:00
SDD 0db6dd2e65 dwt: real-unit menu-driven Ethernet Address ROM test passes (All Diagnostics Passed); 24 is the separate port/termination test 2026-10-18 17:02:55 +03:00
SDD 6195556524 dwt: mailbox_watch.py -- poll 0x27C190/0x16F0 across a boot; results are consumed before rdebug is reachable 2026-10-18 16:57:45 +03:00
SDD 2df00d32e5 dwt: real-unit MOP console works; SHOW reports Monitor Type VR319; TEST refused with DWT IN USE while a window is open 2026-10-18 16:40:19 +03:00
SDD 1c1f804a13 dwt: real X11 app via rdebug -- hostload --real; xhello window on the terminal (status 0, stage 6) 2026-10-18 16:32:58 +03:00
SDD 736e2c84a4 dwt: 50%-pattern real-unit capture at the correct 1280x1024/256-stride geometry 2026-10-18 16:30:30 +03:00