forked from opencode.energia.arti.ee/vt1200
checkpoint: VT1200 reverse-engineering workspace before board-model work
This commit is contained in:
commit
acb92d3e44
325 files changed
+951894
No files matched your search
@@ -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
|
||||
```
|
||||
Reference in new issue
Block a user