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.
DEC VT1200 X Terminal — ROM images
The DEC VT1200 is a monochrome X Window System (X11) terminal (circa 1990). It runs the X server, terminal manager, window manager, and VT emulator entirely from ROM on a Texas Instruments TMS34010 CPU, with 2–4 MB of RAM and no virtual memory.
These are the firmware ROM dumps, split by firmware revision. The confirmed memory map is in MEMORY_MAP.md.
Contents
| Folder | Firmware | ROMs | Part numbers |
|---|---|---|---|
v2.1/ |
V2.1 | 14 x 128 KiB | 23-210E9 .. 23-223E9 |
v2.2/ |
V2.2 | 14 x 128 KiB | 23-274E9 .. 23-287E9 |
Each file is 131072 bytes (128 KiB) and is named by its DEC part number.
Source
Dumped and published by 9track.net (Dave/9track collection):
- Index page: https://9track.net/roms/ (section "DEC VT1200", category Terminal)
- Files: https://9track.net/roms/files/dec/vt1200/
Fetched: 2026-10-15.
Notes
- The bitsavers terminal document archive does not currently host VT1200 ROM
images (checked
/bits/and/pdf/dec/terminal/), so 9track.net is the canonical source for these dumps. - The 9track table does not list the component/location for each ROM; only the part-number-labeled filenames are given.
- Verify integrity with
sha256sum v2.1/* v2.2/*.
Firmware layout (reverse-engineered)
The 14 EPROMs are byte-interleaved 16-bit words, not a flat byte stream. Within each consecutive pair of part numbers, the odd part number holds the low byte and the even part number holds the high byte:
v2.1 pairs: (210,211) (212,213) (214,215) (216,217) (218,219) (220,221) (222,223)
v2.2 pairs: (274,275) (276,277) (278,279) (280,281) (282,283) (284,285) (286,287)
The seven 256 KiB banks are laid out so that file offset == physical byte - 0x07E40000: the highest part-number pair sits at the lowest address.
reconstruct.py writes that 1.75 MiB (0x1C0000) image by default; pass
--part-order for the legacy ascending-part-number ordering.
./reconstruct.py v2.1 # -> vt1200_v21.bin (address order)
./reconstruct.py v2.2 # -> vt1200_v22.bin
python3 -m unittest test_reconstruct -v # layout regression tests
Evidence this interleave is correct: only it produces readable text, e.g.
-DEC-Terminal-Medium-R-Normal--14-100-100-100-C-80-ISO8859-1, WM XIO Error,
packet_size = %d exceeds MAXSIZE_RCVPKT, and SCCS headers such as
$Header: ctype.c,v 1.1 89/10/05 19:40:49 craig Exp $. Getting the interleave
wrong shifts every string by one byte (e.g. DEC becomes EDC).
Disassembly
The CPU is a TI TMS34010. The ROM is loaded at physical byte base
0x07E40000 (bit 0x3F200000); the reset vector (0x3F200040) points at the
power-on code at ROM+8 (DINT; SETF 16,0,0; SETF 0,0,1; CLR A0; …). Each
256 KiB bank begins with an 8-byte header [checksum16][?][AAAA][5555].
MAME's bundled disassembler, which ships a tms34010 backend:
unidasm vt1200_v22.bin -arch tms34010 -basepc 0x7E40000 -count 40
Or smart34010's dis34 (note -b is a bit-address base):
dis34 -a -l -b 3F200000 vt1200_v21.bin
unidasm/dis34 print bit addresses (0x10 per 16-bit instruction), whereas
Ghidra uses byte addresses. tools/vt1200_map.py prints the vectors and
verifies the per-bank header checksums:
python3 tools/vt1200_map.py vt1200_v22.bin
Pointer constants (bit-address → byte-address)
The firmware stores bit addresses; a 32-bit value P refers to physical
byte P >> 3. A byte-addressed tool such as Ghidra cannot resolve those
constants directly. tools/ptr_constants.py scans an address-ordered image
for such constants and resolves their targets:
python3 tools/ptr_constants.py vt1200_v22.bin
python3 tools/ptr_constants.py vt1200_v22.bin --strings
python3 tools/ptr_constants.py vt1200_v22.bin --tsv pointers.tsv
python3 -m unittest test_ptr_constants -v
On v2.2 it finds ~44,700 constants, of which ~3,680 distinct targets are
strings (e.g. 0x004B08 <- 0x3F225840 'LAT: could not allocate XCB for …').
The Ghidra side is implemented in ghidra_scripts/FixPtrConstants.java: it
scans the loaded ROM and adds a DATA reference from each constant to the byte
it points at (P >> 3), leaving the bytes untouched. AssertPtrFix.java is a
headless regression check for it. Already applied to the project:
| Program | references created |
|---|---|
vt1200_v21.bin |
37,187 |
vt1200_v22.bin |
40,290 |
/opt/ghidra/support/analyzeHeadless ghidra vt1200 \
-process -noanalysis \
-scriptPath ghidra_scripts \
-postScript FixPtrConstants.java
Ghidra
Ghidra has no built-in TMS34010 language, so we install the community module
ZPaul2Fresh8/Ghidra_TMS34010:
tools/ghidra-tms34010/— vendored, patched module sourcetools/ghidra-tms34010-local.patch— all local changes vs. upstreamtools/install-ghidra-plugin.sh— installs + compiles it into Ghidra
tools/install-ghidra-plugin.sh # needs sudo; installs into /opt/ghidra
The patch does two things:
- Ghidra 12 schema fixes —
<data_space space="ROM">must be lowercaserom(matching the slaspec), and every<memory_block>needsinitialized="false"or a bit/byte-mapped address. - Removes the Midway arcade content — upstream shipped a Midway board map
(
VRAM/RAM/NVRAM/PALETTE/GFX/ROMblocks plusWATCHDOG,DIPSWITCHES,INPUT_n,midtunit_*and arom:0xFFFFCtrap table) that does not describe the VT1200 and whoseGFX/ROMblocks collided with the loaded image. The cleanedpspeckeeps only the CPU definition.
Project
ghidra/ is a ready-to-open Ghidra project (ghidra/vt1200.gpr) containing the
reconstructed v2.1 and v2.2 images, imported with tms34010:LE:32:default
at byte base 0x07E40000 and disassembled from the reset vector plus a
linear pass over the boot bank.
| Program | Functions | Instructions |
|---|---|---|
vt1200_v21.bin |
8 | 61,401 |
vt1200_v22.bin |
6 | 62,444 |
Helper scripts live in ghidra_scripts/:
VerifyTMS34010.java— locates the loaded block, follows flow from the reset vector, then linearly disassembles the boot bank, and prints regions.StatsTMS34010.java— function/instruction counts.FixPtrConstants.java— converts bit-address pointer constants to references.AssertPtrFix.java— headless regression check for the above.
Caveats:
currentProgram.getImageBase()is not the ROM base. Use the loaded block (rom:07e40000–rom:07ffffff); the helper script does this.- The module's pcode semantics are incomplete, so flow-following cannot
expand far and the analyzers emit non-fatal
ArrayIndexOutOfBoundsExceptions. That is why the boot bank is also disassembled linearly. - Ghidra 12 dropped Jython; its bundled Python scripts need PyGhidra, so the helpers here are Java.
- Data cross-references are materialised by
FixPtrConstants.java(the bit vs byte address issue) — see the pointer-constants section above.
Documentation
docs/ holds the free DEC/TI documents gathered during this work. Each PDF
has a .txt sidecar with the extracted text (handy for grep).
| File | Source |
|---|---|
EK-V1000-SV-002_service-guide.pdf |
filibeto.org/dec/hardware/EK-V1000-SV-002.pdf |
EK-V1000-UG-002_installing-VT1000.pdf |
filibeto.org/dec/hardware/EK-V1000-UG-002.pdf |
EK-V1200-UG-001_installing-VT1200.pdf |
filibeto.org/dec/hardware/EK-V1200-UG-001.pdf |
EK-V1000-RN-005_VT1000-v2.0-release-notes.pdf |
filibeto.org/dec/hardware/EK-V1000-RN-005.pdf |
EK-V1200-RN-001_VT1200-release-notes.pdf |
filibeto.org/dec/hardware/EK-V1200-RN-001.pdf |
EK-V1000-RC-003_installing-optional-memory.pdf |
filibeto.org/dec/hardware/EK-V1000-RC-003.pdf |
EK-V120I-IN-001_installing-DECimage-1200.pdf |
filibeto.org/dec/hardware/EK-V120I-IN-001.pdf |
EK-V1000-IP-001_illustrated-parts-breakdown.pdf |
filibeto.org/dec/hardware/EK-V1000-IP-001.pdf |
DTJ-1991-vol3num4_X-Window-Terminals.pdf |
Digital Technical Journal 3(4), Fall 1991, pp. 26–35 |
TMS34010-users-guide.pdf |
TI TMS34010 User's Guide |
ipb_pages/*.png |
page renders of the IPB (that scan has no text layer) |
reference/ holds the VT1000 v1.2 ROM dump (12 × 27C010) published by bitsavers
(bitsavers.org/pdf/dec/terminal/vt1000/VT1000-v1.2.zip) plus an earlier linear
dis34 disassembly of v2.1 (gzipped).
Notably no online document contains the memory map — the missing artifact is
the MP-02989-01 VT1000/VT1200 System Box Maintenance Print Set (fold-out
schematics). See MEMORY_MAP.md.
Repository layout
vt1200/
├── v2.1/ v2.2/ # original 28 EPROM dumps
├── reconstruct.py # de-interleave a revision -> address-ordered image
├── test_reconstruct.py
├── MEMORY_MAP.md # the confirmed memory map
├── README.md
├── vt1200_v21.bin vt1200_v22.bin
├── docs/ # DEC/TI manuals (PDF + extracted .txt) + ipb_pages/
├── reference/ # VT1000 v1.2 ROM dump, earlier dis34 output
├── ghidra/ # Ghidra project (vt1200.gpr + .rep)
├── ghidra_scripts/ # Verify/Stats/FixPtrConstants/AssertPtrFix .java
├── tools/
│ ├── vt1200_map.py # vectors + bank-header checksums
│ ├── ptr_constants.py # find/resolve bit-address constants
│ ├── smart34010/ # dis34 disassembler source
│ ├── ghidra-tms34010/ # patched processor module source
│ ├── ghidra-tms34010-local.patch
│ └── install-ghidra-plugin.sh
└── test_ptr_constants.py