checkpoint: VT1200 reverse-engineering workspace before board-model work

This commit is contained in:
SDD committed 2026-10-16 01:37:50 +03:00
commit acb92d3e44
325 files changed
+951894

No files matched your search

+232
View File
@@ -0,0 +1,232 @@
# 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
```