# 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](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. ```sh ./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: ```sh unidasm vt1200_v22.bin -arch tms34010 -basepc 0x7E40000 -count 40 ``` Or `smart34010`'s `dis34` (note `-b` is a *bit*-address base): ```sh 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: ```sh 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: ```sh 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 | ```sh /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`](https://github.com/ZPaul2Fresh8/Ghidra_TMS34010): - `tools/ghidra-tms34010/` — vendored, patched module source - `tools/ghidra-tms34010-local.patch` — all local changes vs. upstream - `tools/install-ghidra-plugin.sh` — installs + compiles it into Ghidra ```sh tools/install-ghidra-plugin.sh # needs sudo; installs into /opt/ghidra ``` The patch does two things: 1. **Ghidra 12 schema fixes** — `` must be lowercase `rom` (matching the slaspec), and every `` needs `initialized="false"` or a bit/byte-mapped address. 2. **Removes the Midway arcade content** — upstream shipped a Midway board map (`VRAM`/`RAM`/`NVRAM`/`PALETTE`/`GFX`/`ROM` blocks plus `WATCHDOG`, `DIPSWITCHES`, `INPUT_n`, `midtunit_*` and a `rom:0xFFFFC` trap table) that does not describe the VT1200 and whose `GFX`/`ROM` blocks collided with the loaded image. The cleaned `pspec` keeps 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 `ArrayIndexOutOfBoundsException`s. 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 ```