Files
vt1200/README.md
T

233 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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** — `<data_space space="ROM">` must be lowercase
`rom` (matching the slaspec), and every `<memory_block>` 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
```