mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
README: rewrite as the project front page -- status, findings, tidbits
The old README still described the repo as ROM images plus a Ghidra setup. The project now spans the MAME harness, the real terminal, the COFF/SDK toolchain and the networking reverse engineering, so rewrite it as the entry point. New top-level content: - a dated status table (ROM, Ghidra, MAME, real unit, our code, networking, LAT, font path), with the open items named as open; - what the firmware actually contains (MIT X11 R4, BSD TCP/IP via 386BSD, BIND, telnet, TI RTS, fonts); - the address model table, corrected against harness/mame/vt1200.cpp (RAM 0..0x1FFFFFF; DUART bit 0x06200000/0x06400000); - how the faithful MAME run stops at level 0, why no documented device can pass it, and what the stub does and does not prove; - the real-unit debug channel (TCP 771), the challenge, and a table of what is proven on hardware; - the three routes for running our own code, and the heap model; - the LANCE + BSD stack mapping, the RMD1_FRAM blocker and the claim deferred for real silicon; - LAT and the idle-crash investigation, and the two font results; - a curated "odd findings" list and a documentation index. Factual corrections made while checking sources: the rdebug gate is a SETF 2 field (the port was never closed), CSR0=0x1BB has ERR clear, the pcfReadBitmaps overflow is not reachable on the TFTP path, and the real-unit framebuffer is 1024x2048 line-interleaved (the "line doubled" look was a partial read). The XCROSS 123k figure is the unmapped log, not a VT1200_CAL proxy hit. ROM md5 unchanged.
This commit is contained in:
1 parent
8ff16b5b73
commit
a4ed68c0e4
1 file changed
+548
-156
@@ -1,41 +1,96 @@
|
||||
# DEC VT1200 X Terminal — ROM images
|
||||
# DEC VT1200 X Terminal — ROM images, emulation, and what we found
|
||||
|
||||
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.
|
||||
The DEC VT1200 is a monochrome X Window System terminal (circa 1990). It runs
|
||||
the X server, the terminal manager, the window manager and a 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)**.
|
||||
This repository started as the firmware ROM dumps and has grown into a full
|
||||
reverse-engineering and execution project: a MAME machine that runs the
|
||||
firmware, a Ghidra database with the bit-address problems solved, a 1987 TI
|
||||
toolchain that still builds working code, and a debug channel into a **real
|
||||
terminal** over the LAN.
|
||||
|
||||
## Contents
|
||||
**The ROM stays byte-identical throughout.** Every experiment patches at
|
||||
runtime, if at all; the reference image is `vt1200_v22.bin`
|
||||
(`md5 b46a3e442ccb86134d2356bd70d22fd2`).
|
||||
|
||||
---
|
||||
|
||||
## Status at a glance
|
||||
|
||||
_As of 2026-10-30._
|
||||
|
||||
| Area | State |
|
||||
|---|---|
|
||||
| **ROM images** | **Done.** v2.1 and v2.2 reconstructed from 14 EPROMs each, bank order and byte interleave proven, checksums verified. |
|
||||
| **Disassembly / Ghidra** | **Done.** TMS34010 module rebuilt for Ghidra 12, bit-address pointer constants materialised as references (~40k in v2.2). |
|
||||
| **MAME harness** | **Running.** Boots POST, fails level 0 honestly, parks in the diagnostics menu. With `VT1200_SKIP_LEVEL1=1` it boots the X server and reaches the **Terminal Manager**, with keyboard, mouse/tablet and LANCE. |
|
||||
| **Real terminal** | **Opened.** The firmware's hidden debug TCP service on port **771** is reversed, its challenge is solved offline, and we can read, write, dump the ROM, load COFF images and run them. |
|
||||
| **Our own code** | **Runs.** TI SDK assembly, a C program (`HELLO` in video RAM), and an X11 window — on the emulator *and* on the real unit. |
|
||||
| **Networking RE** | **Mapped.** The LANCE driver and the BSD-derived TCP/IP stack are mapped function by function, with explicit firmware-specific entries where no counterpart exists. One claim still needs real silicon. |
|
||||
| **LAT / the idle crash** | **Open.** The receive path is audited, the crash is narrowed to service announcements (not the data stream), and a soak procedure is ready for when the unit is back. |
|
||||
| **Font path** | **Characterised, not exploitable as hoped.** The known `pcfReadBitmaps` overflow is real but is **not reachable** through the TFTP font loader. |
|
||||
|
||||
---
|
||||
|
||||
## What the machine actually runs
|
||||
|
||||
The image is not just DEC code. `reference/third-party-code.md` maps what is
|
||||
inside `vt1200_v22.bin`:
|
||||
|
||||
| component | evidence |
|
||||
|---|---|
|
||||
| **MIT X11 R4** — server (`dix`, `mfb`/`cfb`) **and Xlib** | `mfb*`, `pcf*`, `snf*`, `bdf*`, `Xlib` strings; exact match to X11R4 `pcfrbitmaps.c` |
|
||||
| **BSD 4.3/4.4 TCP/IP** | `$Header`/`@(#)` IDs for the socket/TCP/IP/UDP/mbuf set, imported at DEC by `craig`, 1989-10-05/06 |
|
||||
| **BSD BIND resolver, telnet + TN3270, inetd, lpd lineage, libc pieces** | file IDs and code match |
|
||||
| **TI C runtime V2.00** | `strlen`/`strcpy`/`strcmp` match `RTS.SRC` instruction for instruction |
|
||||
| **Bitstream / Bigelow & Holmes / DEC Japan fonts** | copyright strings in the font data |
|
||||
|
||||
Source-ID strings (RCS `$Header:`, SCCS `@(#)`) name the original files and
|
||||
dates. They are strong evidence of provenance, but they are file names, not
|
||||
licence statements.
|
||||
|
||||
---
|
||||
|
||||
## Quick start
|
||||
|
||||
```sh
|
||||
md5sum vt1200_v22.bin # b46a3e442ccb86134d2356bd70d22fd2
|
||||
|
||||
./reconstruct.py v2.1 # -> vt1200_v21.bin (address order)
|
||||
./reconstruct.py v2.2 # -> vt1200_v22.bin
|
||||
python3 -m unittest test_reconstruct test_ptr_constants -v
|
||||
|
||||
harness/mame/build.sh # build the MAME single-driver binary
|
||||
harness/mame/run.sh # faithful: stops in the diagnostics menu
|
||||
harness/mame/run.sh skip # VT1200_SKIP_LEVEL1=1: boot the OS to the GUI
|
||||
harness/mame/run.sh stub # + VT1200_IO_PATH_STUB=1: get past level 0
|
||||
harness/mame/run.sh pie # runtime-patch the failing checks; level 1 runs
|
||||
harness/mame/run.sh net # skip + tablet + LANCE on a host tap
|
||||
```
|
||||
|
||||
The confirmed memory map is in **[MEMORY_MAP.md](MEMORY_MAP.md)**; the harness
|
||||
detail — devices, env knobs, regression baseline, working conventions — is in
|
||||
**[harness/mame/README.md](harness/mame/README.md)**.
|
||||
|
||||
---
|
||||
|
||||
## ROM images
|
||||
|
||||
| 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 |
|
||||
| `v2.1/` | V2.1 | 14 × 128 KiB | 23-210E9 .. 23-223E9 |
|
||||
| `v2.2/` | V2.2 | 14 × 128 KiB | 23-274E9 .. 23-287E9 |
|
||||
|
||||
Each file is 131072 bytes (128 KiB) and is named by its DEC part number.
|
||||
Each file is 131072 bytes and is named by its DEC part number. Dumped and
|
||||
published by **9track.net** (Dave/9track collection):
|
||||
<https://9track.net/roms/> (section "DEC VT1200", category *Terminal*),
|
||||
files at <https://9track.net/roms/files/dec/vt1200/>. Fetched 2026-10-15.
|
||||
The bitsavers terminal archive does not host VT1200 images (checked `/bits/`
|
||||
and `/pdf/dec/terminal/`), so 9track.net is the canonical source.
|
||||
|
||||
## 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)
|
||||
### 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
|
||||
@@ -48,185 +103,522 @@ 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
|
||||
`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.
|
||||
Evidence the 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`).
|
||||
wrong shifts every string by one byte (`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]`.
|
||||
## The address model (read this before any address)
|
||||
|
||||
MAME's bundled disassembler, which ships a `tms34010` backend:
|
||||
The TMS34010 is **bit-addressed**. A register, PC or stored pointer `P` selects
|
||||
a *bit*; the referred physical byte is `P >> 3`. The top two logical address
|
||||
bits are not emitted, so `physical_bit == logical_bit & 0x3FFFFFFF`.
|
||||
|
||||
| region | physical byte | bit address |
|
||||
|---|---|---|
|
||||
| RAM (4 MiB) | `0x000000` | `0x00000000` |
|
||||
| Video RAM | — | `0x04000000` |
|
||||
| DUART2 (keyboard/mouse) | `0x00C40000` | `0x06200000` |
|
||||
| DUART1 (host/printer) | `0x00C80000` | `0x06400000` |
|
||||
| NVR (battery-backed settings) | — | `0x06000000` |
|
||||
| Ethernet address ROM (8 bytes) | — | `0x06800000` |
|
||||
| LANCE RDP / RAP | — | `0x05800000` / `0x05900000` |
|
||||
| ROM (1.75 MiB) | `0x07E40000` | `0x3F200000` |
|
||||
| ROM aliases (`0x7f…`, `0xbf…`, `0xff200000`–`0xffffffff`) | same ROM | top 2 bits undecoded |
|
||||
| Reset vector | file `0x1BFFFC` | logical `0xFFFFFFE0` → `0x3F200040` |
|
||||
|
||||
Consequences that cost real time if forgotten:
|
||||
|
||||
* `read_word(0x3f200000)` returns ROM bytes 0..1 (physical `0x07e40000`).
|
||||
* MAME models the program space as `memory_access<32, 1, 3>` — the address-map
|
||||
unit is **one bit**, so a 16-bit word spans 16 address units. The TMS34020
|
||||
uses `memory_access<32, 2, 3>` and is **not** interchangeable.
|
||||
* Ghidra loads the image at byte base `0x07E40000`; **firmware bit address =
|
||||
Ghidra address × 8**. `unidasm`/`dis34` print bit addresses; Ghidra prints
|
||||
byte addresses.
|
||||
* `currentProgram.getImageBase()` is not the ROM base — use the loaded block.
|
||||
|
||||
---
|
||||
|
||||
## Disassembly and Ghidra
|
||||
|
||||
MAME's bundled `unidasm` has 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):
|
||||
`smart34010`'s `dis34` (note `-b` is a *bit*-address base; two encodings were
|
||||
corrected and validated by round-trip through the 1987 assembler — see
|
||||
`harness/sdk-roundtrip/`):
|
||||
|
||||
```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:
|
||||
`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
|
||||
`tools/ptr_constants.py` finds bit-address pointer constants and resolves their
|
||||
targets (`--strings` for string targets, `--tsv` for a table). 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 …'`.
|
||||
|
||||
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
|
||||
- `tools/install-ghidra-plugin.sh` — installs and compiles it (needs sudo)
|
||||
|
||||
```sh
|
||||
tools/install-ghidra-plugin.sh # needs sudo; installs into /opt/ghidra
|
||||
The patch fixes two things: the **Ghidra 12 pspec schema** (`space="rom"`
|
||||
lowercase, `initialized="false"` on memory blocks) and the upstream **Midway
|
||||
arcade board map**, whose `GFX`/`ROM` blocks collided with the loaded VT1200
|
||||
image.
|
||||
|
||||
`ghidra/` is a ready-to-open project (`ghidra/vt1200.gpr`) with both images
|
||||
imported at byte base `0x07E40000`. `ghidra_scripts/FixPtrConstants.java`
|
||||
creates the bit-constant references (37,187 in v2.1, 40,290 in v2.2);
|
||||
`AssertPtrFix.java` is the headless regression check. Caveat: the module's
|
||||
pcode semantics are incomplete, so flow-following cannot expand far — the boot
|
||||
bank is disassembled linearly as well.
|
||||
|
||||
---
|
||||
|
||||
## Running the firmware under MAME
|
||||
|
||||
A minimal machine (`harness/mame/vt1200.cpp`) loads the firmware into a
|
||||
TMS34010 so it can be run under MAME's debugger: trace, breakpoints,
|
||||
single-stepping, memory watch. It is a *visibility* tool, not an accurate
|
||||
VT1200 emulation.
|
||||
|
||||
**A bare `harness/mame/run.sh` with no environment is the faithful configuration.** It
|
||||
models video RAM, the board-window handshake, both DUARTs (real `SCN2681`
|
||||
devices), the LK401 keyboard and a pointing device, the LANCE, the NVR and the
|
||||
Ethernet address ROM. Two of those were not conveniences:
|
||||
|
||||
* the level-1 host-port transmit is **ISR-driven**, so a fake DUART cannot work;
|
||||
* without a real DUART2, the board write-back latch answered a read of the
|
||||
**write-only** IMR with its own value, so the OS serviced a phantom interrupt
|
||||
that could never clear (and the GUI never appeared).
|
||||
|
||||
### Where a faithful run stops
|
||||
|
||||
A bare run passes POST and then **fails level 0's I/O data path test**, parking
|
||||
in the interactive diagnostics menu:
|
||||
|
||||
```
|
||||
VERSION 2.08 - SELECTED TEST TYPE:
|
||||
```
|
||||
|
||||
The patch does two things:
|
||||
That test wants **10 bytes** on DUART2 channel A, and nothing is attached. It
|
||||
is not satisfiable by any documented device: the routine writes one byte
|
||||
(`0xE1`, "disable auto-repeat", which has no reply), sets the channel to
|
||||
**normal mode, not loopback**, and checks **only the count** — the received
|
||||
byte is stored and never compared. The keyboard's reply is 4 bytes, the mouse's
|
||||
4, the tablet's 5. A faithful LK401 would not help: the init sequence's
|
||||
receiver reset **flushes the FIFO**. See `reference/level0-10byte-puzzle.md`.
|
||||
|
||||
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.
|
||||
It used to "pass" by accident, because the write-back latch returned `0x0099`
|
||||
from the status register and bit 0 is `RXRDY`, feeding the drain loop ten
|
||||
phantom bytes. Making DUART2 real moved the regression baseline — that looks
|
||||
like a regression and is the **removal of a false pass**.
|
||||
|
||||
### Project
|
||||
### The one deliberate test-fixture stand-in
|
||||
|
||||
`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.
|
||||
`VT1200_IO_PATH_STUB=1` is **not a keyboard model**. It stands in for an
|
||||
unknown fixture on the keyboard port: after the one `0xE1` write it feeds
|
||||
**10 arbitrary (`0x00`) bytes** back on the same receive path, only while the
|
||||
channel is polled. Nothing compares them; only the count matters. It is off by
|
||||
default, and the faithful run still fails level 0 exactly as before.
|
||||
|
||||
| Program | Functions | Instructions |
|
||||
|---------|-----------|--------------|
|
||||
| `vt1200_v21.bin` | 8 | 61,401 |
|
||||
| `vt1200_v22.bin` | 6 | 62,444 |
|
||||
With it on, level 0 clears and level 1 reports `HOST PORT - 15` and
|
||||
`PRINTER PORT - 16`, prints `TYPE 'GO' TO CONTINUE`, and **continues** — then
|
||||
wedges in a DUART1 counter-ready **interrupt storm** from frame ~2839. It does
|
||||
not boot the OS, and it does not park at `HOST PORT - 15`.
|
||||
|
||||
Helper scripts live in `ghidra_scripts/`:
|
||||
### How far the OS gets
|
||||
|
||||
- `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.
|
||||
`VT1200_SKIP_LEVEL1=1` drives DUART1 **IP3** high, which the firmware takes as
|
||||
a signal to jump to the OS cold-start and skip all 15 level-1 tests. This is a
|
||||
*modelled board input*; the real machine reads IP3 low. A skipped run is
|
||||
evidence about the **OS, graphics and crash behaviour**, and is **not** evidence
|
||||
about the real terminal's POST.
|
||||
|
||||
Caveats:
|
||||
With the skip, the OS boots a **running X11 server** and reaches the
|
||||
**Terminal Manager**: the root-window weave renders, the cursor is handed to
|
||||
the terminal's own mouse cursor, and the V2.2 box appears — title bar,
|
||||
Messages, Ethernet/IP/LAT, a Memory bar, and "All Diagnostics Passed". The
|
||||
keyboard and mouse drivers initialise and speak the real protocol; the LANCE is
|
||||
initialised and the TM shows the real hardware address
|
||||
`08-00-2B-16-4B-5A`. Evidence frames: `harness/mame/skip_frame3000.png`,
|
||||
`duart2_default_tm_frame3000.png`, `tablet_cursor_*.png`.
|
||||
|
||||
- **`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.
|
||||
### Regression baseline
|
||||
|
||||
## Documentation
|
||||
`progress.lua` at 200 frames, **no env**:
|
||||
|
||||
`docs/` holds the free DEC/TI documents gathered during this work. Each PDF
|
||||
has a `.txt` sidecar with the extracted text (handy for `grep`).
|
||||
```
|
||||
PC=3FDCE400 samples=198 (3 distinct PCs)
|
||||
```
|
||||
|
||||
| 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) |
|
||||
It is a change detector, not a specification. It has moved twice, both
|
||||
deliberately; `harness/mame/README.md` records why, including the
|
||||
`VT1200_IO_PATH_STUB=1` value (`samples=53`, 75 PCs) as a separate baseline.
|
||||
|
||||
`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).
|
||||
### Deliberate divergences
|
||||
|
||||
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.
|
||||
| divergence | default | what it does | restore the real machine |
|
||||
|---|---|---|---|
|
||||
| **VSXXX-AB tablet** | **on** | tablet on DUART2-B; absolute host-pointer reports | `VT1200_MOUSE=1` |
|
||||
| `VT1200_SKIP_LEVEL1` | off | DUART1 IP3 high → skip all level-1 tests | leave unset |
|
||||
| `VT1200_IO_PATH_STUB` | off | 10 bytes after the level-0 `0xE1` write | leave unset |
|
||||
|
||||
The full env-knob table (21 knobs, down from 33) is in
|
||||
`harness/mame/README.md`.
|
||||
|
||||
---
|
||||
|
||||
## The real terminal
|
||||
|
||||
The unit is a stock v2.2 machine with MAC `08:00:2b:16:4b:5a`, static IP
|
||||
`100.103.100.103`, reached from host `100.103.255.254`.
|
||||
|
||||
The big find is that the firmware runs a **remote debugger** it calls
|
||||
`DEBUG TCP Port`, listening on **TCP 771** (`dwt/notes/rdebug-static.md`). Over
|
||||
one connection a client can read and write the entire address space, call any
|
||||
address as a function, capture console output, draw text, and make the terminal
|
||||
open an outbound telnet connection.
|
||||
|
||||
The path is gated, and the static note's first gate turned out not to be one:
|
||||
the settings word around `0xAECD0` reads `0x8070`, but the compare is a
|
||||
`SETF 2` field at `0xAECD2` — **two bits**, and bits 2..3 are zero, so the
|
||||
connection is **accepted**. The real gate is a 16-byte echo challenge at bit
|
||||
`0x3F59FDC0` (`dwt_prf`). It is **solved offline**: `dwt/challenge.py` computes
|
||||
the reply from the challenge alone, and two live sessions passed the `memcmp`.
|
||||
That gave us, on real hardware:
|
||||
|
||||
| proven on the real unit | evidence |
|
||||
|---|---|
|
||||
| full ROM dump **byte-identical** to `vt1200_v22.bin` | `dwt/evidence/real-unit-rom.bin` |
|
||||
| the read primitive (`peek`), screenshots assembled over it | `dwt/evidence/real-unit-canvas.png` |
|
||||
| the write path: `malloc` → poke → COFF loader → `create_process` | `dwt/loader/evidence/hostload/` |
|
||||
| the **first C program** on the terminal (`HELLO` into VRAM) | `harness/c-hello/` |
|
||||
| a **real X11 window** created by an injected client | `dwt/gui/`, `dwt/notes/GUI_APPS.md` |
|
||||
| a LAN TFTP fetch and load | `dwt/notes/TCPIP.md` |
|
||||
| NVRAM and the LANCE address ROM read back and matching the model | `dwt/nvram_dump.py`, `dwt/rom_dump.py` |
|
||||
|
||||
The terminal's own MOP remote console also works (`dwt/mop_console.py`); `SHOW`
|
||||
reports `Monitor Type VR319` and `Firmware Revision V2.2`.
|
||||
|
||||
### Real-unit display geometry (it is not what the emulator shows)
|
||||
|
||||
* The framebuffer is **1024×2048, line-interleaved, 256 KiB**: the image sits on
|
||||
**even rows at 1:1**, odd rows carry a mask. The visible raster is
|
||||
**1280×1024**, with the 1024-wide canvas at x=192.
|
||||
* A 128 KiB read holds only the **top 512 image lines**; doubling that is what
|
||||
produced the early "line doubled" and "missing bottom" pictures. The full
|
||||
256 KiB read plus `dwt/screenshot.py --field even --stretch 1280x1024` matches
|
||||
the glass.
|
||||
* The Terminal Manager's `0x55`/`0xff` row pattern is a **non-displayed mask**,
|
||||
not a background setting: setting the root background to 100% white changes
|
||||
the root and leaves the TM pattern intact, while the screen looks solid white.
|
||||
* The bottom `Ethernet Port Error - 24` box is the separate **AUI/transceiver
|
||||
termination** test, not the address ROM test — that one passes.
|
||||
|
||||
MAME models a linear 1bpp framebuffer and therefore never shows the interleave;
|
||||
that is a display-fetch difference, not a CPU difference.
|
||||
|
||||
---
|
||||
|
||||
## Running our own code
|
||||
|
||||
Three independent routes exist, in increasing order of realism:
|
||||
|
||||
1. **MAME debugger entry.** `harness/hello-world/` is an 82-byte GSPA payload
|
||||
built by the 1987 TI SDK (`GSPA`/`GSPLNK` under DOSBox). `hello.lua` loads it
|
||||
at bit `0x01800000`, arms a breakpoint on the diagnostics idle loop, and jumps
|
||||
to it. It writes RAM markers and paints a VRAM block. This proves the
|
||||
toolchain, **not** a delivery mechanism.
|
||||
2. **COFF loader + `create_process`.** `dwt/loader/loader.asm` takes a linked
|
||||
GSPLNK COFF already in RAM, places sections at their header addresses, zeroes
|
||||
`.bss`, and runs the image as a first-class process via
|
||||
`create_process(entry, "LOADER", 0x6000, 1, 0)`. The loader also **refuses a
|
||||
target outside a heap free-list block** (status 8), which is a real check, not
|
||||
a reservation.
|
||||
3. **Real hardware.** `dwt/loader/hostload.py` drives the whole chain over
|
||||
rdebug: `malloc` → poke the COFF → poke the loader → descriptor →
|
||||
`create_process` → markers/PNG. `harness/c-hello/build.sh` builds a K&R C
|
||||
program for the real unit (`ROW_BYTES 256`) and `build.sh emu` for MAME
|
||||
(`128`). `dwt/gui/xhello.asm` does `XOpenDisplay` → `XCreateSimpleWindow` →
|
||||
`XMapWindow` → `XSync` from a firmware-created process and leaves a real
|
||||
window on the screen.
|
||||
|
||||
Address convention in the payload work: **Ghidra = `0x7E40000` + file offset**,
|
||||
and **firmware bit address = Ghidra × 8**.
|
||||
|
||||
### The heap, and why payload addresses matter
|
||||
|
||||
Payloads are poked to fixed scratch addresses at bit `0x01800000` (byte
|
||||
`0x00300000`) and above, at the top of the 4 MiB RAM. On a default boot that
|
||||
region is **not allocated** — it lies inside the largest free block
|
||||
(`byte 0x12B772..0x400000`, ~2.97 MB), so the OS may hand it out. There *is* a
|
||||
real allocator and it is callable: `malloc` at bit `0x3F701230`, `free` at
|
||||
`0x3F702440`, a bit-addressed first-fit over three arenas. The global free-byte
|
||||
counter is itself **in bits** (bit `0x1E8BA0`); on the real unit it reported
|
||||
**3,251,638 bytes** free. `dwt/heapmap.py` maps the free list read-only.
|
||||
|
||||
---
|
||||
|
||||
## Networking: the LANCE and the BSD stack
|
||||
|
||||
`harness/mame/NETWORK_REPORT.md` is the reference (4,000+ lines, all
|
||||
measurement and disassembly).
|
||||
|
||||
**The driver.** The LANCE is an AM79C90 at two register windows (RAP bit
|
||||
`0x05900000`, RDP bit `0x05800000`), with bus-master DMA where LANCE byte `B`
|
||||
lands in main-RAM word `B >> 1`, and its interrupt on **INT1**. The driver does
|
||||
not hard-code the registers in its normal paths; it keeps a device struct —
|
||||
found in a live RAM dump at bit `0x23CBF0`. The **Ethernet address ROM** is
|
||||
eight bytes, `08 00 2b 16 4b 5a c1 86`: the six-byte MAC plus two checksum bytes
|
||||
the firmware validates.
|
||||
|
||||
**The stack.** It is 4.3/4.4BSD, imported at DEC in October 1989 and descended
|
||||
from the **386BSD** line. Every function entry the report mentions now has a
|
||||
mapping row: a quoted 4.4BSD counterpart where one exists (`leinit`, `leintr`,
|
||||
`ipintr`, `ip_output`, `icmp_input`, `icmp_reflect`, `icmp_send`, `tcp_input`,
|
||||
`tcp_dooptions`, `tcp_mss`, `udp_input`, `udp_output`, `rip_input`,
|
||||
`in_arpinput`, `arpresolve`, `in_pcblookup`), otherwise an explicit
|
||||
*firmware-specific* row saying why. The coverage check is mechanical, not a spot
|
||||
check: 225 addressed sites, 143 initially absent from a mapping row, of which 41
|
||||
sat in 26 distinct entry points — all 26 are now mapped. The honest caveat:
|
||||
fourteen entries are marked firmware-specific because no counterpart was
|
||||
**found**, which is not a proof that none exists.
|
||||
|
||||
**What a faithful model still owes.** `C — LANCE TEST` (the firmware's internal
|
||||
loopback) fails on one bit: the test expects `RMD1 = 0x6B00`, MAME reports
|
||||
`0x4B00` and never sets `RMD1_FRAM`. It is **not** an FCS loss — the passing
|
||||
`DTCR=1` case carries a correct FCS, and the failing case is a *negative* test
|
||||
that deliberately sends none. `M — ETHERNET ROM TEST` passes.
|
||||
|
||||
**One claim needs real silicon.** On the real unit, `INIT → IDON → STRT` works
|
||||
(`CSR0 = 0x1B3`, then a stable `0x33`), but with a bare-`TDMD` kick the
|
||||
transmitter never services the demand (`TDMD` stays set, the TX descriptor stays
|
||||
owned at `TMD1 = 0x8338`). MAME cannot tell the two kicks apart, so this is
|
||||
deferred until the hardware is back. Note the corrected decode: `CSR0 = 0x1BB`
|
||||
has **`ERR` clear**; the anomaly is `INIT` still set while `IDON` is set.
|
||||
|
||||
### BOOTP
|
||||
|
||||
`harness/serve.py --bootp` answers the request the terminal broadcasts at
|
||||
power-up, so its address no longer has to be typed in. Two details worth
|
||||
keeping: `chaddr` carries **eight** bytes (MAC plus the two checksum bytes), and
|
||||
the request is only visible at **L2** — the host IP stack never receives it from
|
||||
the MAME tap, so the responder is an `AF_PACKET` socket, while the packet itself
|
||||
stays an RFC 951 reply. It is off by default.
|
||||
|
||||
---
|
||||
|
||||
## LAT, and the crash we are chasing
|
||||
|
||||
`lat/` holds the normative DEC LAT specification (`AA-NL26A-TE`, June 1989),
|
||||
the `latd` implementation and the Wireshark dissector, so the firmware's LAT
|
||||
subsystem can be read against a spec and against working code.
|
||||
|
||||
`lat/notes/lat-receive-audit.md` audits the receive path. The demultiplexer
|
||||
walks `nbr_slots` (wire byte 1) with **no length bound**, and slots are *acted
|
||||
on*: Data_a is delivered, Start/Stop/Reject mutate session state. On a real
|
||||
unit this produced an idle crash:
|
||||
|
||||
```
|
||||
LEVEL0 ERROR -> Illegal OPCODE Occurrence - 28
|
||||
007AA3F0 00200008 00000260 3F9B8EA0
|
||||
```
|
||||
|
||||
Decoded as bit addresses, only `3F9B8EA0` is in ROM; the rest are RAM. The CPU
|
||||
was executing out of RAM — the signature of a hijack via memory corruption.
|
||||
|
||||
Two candidate triggers were ranked: the LAT receive path, and the font parser.
|
||||
Replaying the exact 27,529-byte session stream **did not reproduce it** (twice
|
||||
over, ~2.4×). The remaining protocol difference is `latd`'s periodic **LAT
|
||||
service announcements** (message type `0xA`, multicast), 91 of them in the
|
||||
crashing session, none in the replay. The soak procedure in `NEXT_SESSION.md`
|
||||
(20 s heartbeat, watch both screen and wire keepalives) is written up and
|
||||
waiting on the unit.
|
||||
|
||||
---
|
||||
|
||||
## Fonts, and two results that correct earlier hopes
|
||||
|
||||
* The **TFTP "Disk full" abort is not storage**. A capture shows the terminal
|
||||
itself sends TFTP error 3; it is the firmware's `tftp_read` buffer check
|
||||
(`bufsize (%d) too small for %d bytes`) reporting an incomplete read. The font
|
||||
buffers are in correctly-mapped RAM, so there is no second map overlap.
|
||||
* The **`pcfReadBitmaps` overflow is real but unreachable on the TFTP path**.
|
||||
The arithmetic is confirmed (`n` is a full 32-bit `pcfReadInt32`, no bound
|
||||
check; `n=0x10000001` → `fosAlloc(0x40000004)`, `n=0x40000000` → alloc of 0),
|
||||
but the TFTP font loader recognises the PCF magic and calls a **metadata
|
||||
reader** that handles PROPERTIES/ACCELERATORS/METRICS/ENCODINGS and silently
|
||||
**skips the BITMAPS table**. A crafted `num_chars` is never read there. See
|
||||
`harness/mame/PCF_VALIDATION_REPORT.md`.
|
||||
|
||||
The earlier "disk full" symptom was traced to a client-side condition, which is
|
||||
also why a server-side change cannot cause or cure it.
|
||||
|
||||
---
|
||||
|
||||
## Odd findings and other tidbits
|
||||
|
||||
* **Every 256 KiB ROM bank starts with an 8-byte header** `[checksum16][?][AAAA][5555]`.
|
||||
`tools/vt1200_map.py` verifies them.
|
||||
* **The reset vector is identical in v2.1 and v2.2**, and points at byte-identical
|
||||
POST code (`0x3F200040` → `0x07E40008`).
|
||||
* **The bank order is reversed**: the highest part numbers sit at the lowest
|
||||
addresses. A natural-looking ascending order silently produces garbage.
|
||||
* **The TM's stipple is a mask the display never shows.** The raw framebuffer is
|
||||
striped; the glass is white because only the even field is fetched.
|
||||
* **The debug service is documented in the firmware itself** — the string is
|
||||
literally `DEBUG TCP Port`, next to the X11 port (`0x1770` = 6000) in the same
|
||||
descriptor table.
|
||||
* **The "closed" rdebug gate was a field-size mistake.** `0x8070` looked
|
||||
non-zero, so the port looked shut; the compare is `SETF 2`, a **two-bit**
|
||||
field, and bits 2..3 are zero. The port was always open.
|
||||
* **The challenge has an ordering rule.** Send the opcode alone, read and answer
|
||||
the challenge, *then* send the handler's arguments. Sent together, `dwt_prf`
|
||||
eats the first 16 argument bytes as the answer and the handler reads shifted
|
||||
fields.
|
||||
* **The challenge is per-8-byte independent.** Changing bytes 0–7 never changes
|
||||
output bytes 8–15 (0/64 single-bit trials), so the transform is blockwise.
|
||||
* **The debug connection writes a message into the Terminal Manager**: each
|
||||
connection adds a `Debug connection from IP host …` line to Messages. Five
|
||||
lines in the first real-unit screenshot are five connections, not an error.
|
||||
* **The firmware contains a full X11 R4 server *and* Xlib**, so our payloads are
|
||||
X clients. `XOpenDisplay` from the rdebug dispatcher never completes; the same
|
||||
code works from a process created by `create_process`, with the `Display *`
|
||||
kept in a callee-saved register.
|
||||
* **MAME's address-map precedence is last-registered-wins**, not first — which is
|
||||
why one `VT1200_CAL` proxy over `0x00400000` saw zero hits while another over
|
||||
`0x00000000` fired, and why the "unmapped" log's 123k accesses were not
|
||||
unmapped at all (`XCROSS_REPORT.md`). The labels were right; the mental model
|
||||
was wrong.
|
||||
* **The level-1 VIDEO MEMORY failure is test code 22**, a 16-bit walking-bit
|
||||
check plus a 32-bit pattern over `0x20000` units starting at bit
|
||||
`0x04000000` (`CODE22_REPORT.md`).
|
||||
* **The NVR was invisible because its address was swallowed** by the video-memory
|
||||
mapping; it is at bit `0x6000000` and the font *path* lives there, not the
|
||||
font data.
|
||||
* **`scn2681.cpp` does not exist** in the MAME tree — the SCN2681 lives in
|
||||
`mc68681.*`. Searching for the former walks the whole disk before failing.
|
||||
* **A blank LAT window is not a crash.** `sleep 3600` printed nothing and an idle
|
||||
session is legitimately blank; the connect handshake was identical to the
|
||||
healthy session. Use an output-producing soak.
|
||||
* **A capture filter can hide the suspect**: `ether host <mac>` does not capture
|
||||
multicast. Use `ether proto 0x6004`.
|
||||
* **The 1987 TI toolchain still works** under DOSBox, and `as34`/`ld34` were
|
||||
ported to 64-bit Linux for assembler-level work. `dis34` had two encoding
|
||||
bugs, both fixed and round-tripped through GSPA/GSPLNK
|
||||
(`harness/sdk-roundtrip/`).
|
||||
|
||||
---
|
||||
|
||||
## Documentation index
|
||||
|
||||
| file | what it records |
|
||||
|---|---|
|
||||
| `MEMORY_MAP.md` | the confirmed ROM/RAM/vector map, with per-section confidence |
|
||||
| `harness/mame/README.md` | harness devices, env knobs, divergences, regression baseline |
|
||||
| `harness/mame/NETWORK_REPORT.md` | LANCE driver + BSD stack mapping, function by function |
|
||||
| `harness/mame/GATE_CLEANUP_REPORT.md` | how the working config became the default |
|
||||
| `harness/mame/DUART_REPORT.md` | DUART wiring, ISR-driven transmit, datasheet-verified edits |
|
||||
| `harness/mame/RENDER_REPORT.md` | the active-pixel scanline renderer and its geometry |
|
||||
| `harness/mame/INPUT_REPORT.md` | LK401 + VSXXX-AA, and the cursor evidence |
|
||||
| `harness/mame/TABLET_REPORT.md` | the VSXXX-AB tablet and 5-byte absolute reports |
|
||||
| `harness/mame/LANCE_REPORT.md` | the LANCE model, address ROM, byte↔bit DMA |
|
||||
| `harness/mame/LANCE_LOOPBACK_REPORT.md` | why `C — LANCE TEST` fails (one bit) |
|
||||
| `harness/mame/NVRAM_REPORT.md` | the battery-backed settings RAM |
|
||||
| `harness/mame/LEVEL1_CONTINUE_REPORT.md` | what the stub run *actually* does |
|
||||
| `harness/mame/CODE22_REPORT.md` | which test raises VIDEO MEMORY - 22 |
|
||||
| `harness/mame/XCROSS_REPORT.md` | MAME address-map precedence, one instrument at a time |
|
||||
| `harness/mame/FONT_STORAGE_REPORT.md` | the TFTP "Disk full" is a client-side read check |
|
||||
| `harness/mame/PCF_VALIDATION_REPORT.md` | the overflow exists but the TFTP path never reaches it |
|
||||
| `harness/mame/HELLO_WORLD_REPORT.md` | the SDK payload, debugger entry only |
|
||||
| `harness/mame/FINDINGS.md` | font-parser and ROM-mapping analysis |
|
||||
| `harness/mame/BRINGUP_LOG.md` | the bring-up narrative, **including withdrawn claims** |
|
||||
| `harness/BOOTP_REPORT.md` | the BOOTP responder and why it is L2 |
|
||||
| `dwt/notes/rdebug-static.md` | the debug TCP service, statically |
|
||||
| `dwt/notes/CHALLENGE_REVERSE.md` | the 16-byte challenge, solved |
|
||||
| `dwt/notes/REAL_UNIT.md` | first contact, geometry, corrections |
|
||||
| `dwt/notes/GUI_APPS.md` | the X11 architecture and what a payload can do |
|
||||
| `dwt/notes/LOADER.md` | the COFF loader and `create_process` |
|
||||
| `dwt/notes/TCPIP.md` | the socket API and the TFTP fetch |
|
||||
| `dwt/notes/HEAP.md` | the allocator and where payloads live |
|
||||
| `dwt/notes/MESSAGEBOX.md` | the bottom diagnostics box and its status mask |
|
||||
| `lat/notes/lat-receive-audit.md` | the LAT receive path, finding by finding |
|
||||
| `reference/level0-10byte-puzzle.md` | why level 0 cannot be satisfied faithfully |
|
||||
| `reference/level1-test-sequence.md` | the 15 level-1 tests and their codes |
|
||||
| `reference/third-party-code.md` | what open-source code is in the ROM |
|
||||
| `reference/tms34010-sdk.md` | the 1987 toolchain, buildable today |
|
||||
| `EXPLOIT_NOTES.md` | the original code-execution audit and the crash decode |
|
||||
| `NEXT_SESSION.md` | the LAT soak handoff (older; see the reports for later work) |
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
├── reconstruct.py # de-interleave -> address-ordered image
|
||||
├── 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
|
||||
├── MEMORY_MAP.md # the confirmed memory map
|
||||
├── ghidra/ ghidra_scripts/ # Ghidra project + Verify/Stats/FixPtr/Assert
|
||||
├── 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
|
||||
│ ├── smart34010/ # dis34 (two encoding fixes)
|
||||
│ ├── as34/ # TMS34010 binutils, ported to 64-bit Linux
|
||||
│ ├── tms34010-sdk/ # the 1987 TI SDK (under DOSBox)
|
||||
│ └── ghidra-tms34010/ # patched processor module + install script
|
||||
├── harness/
|
||||
│ ├── mame/ # the MAME driver, Lua probes, reports
|
||||
│ ├── hello-world/ # 82-byte SDK payload (debugger entry)
|
||||
│ ├── c-hello/ # first C program, runs on real hardware
|
||||
│ ├── udpstream/ # UDP/TCP receive-throughput payloads
|
||||
│ ├── sdk-roundtrip/ # dis34 fixes vs. the real assembler
|
||||
│ └── serve.py # TFTP (+ optional BOOTP) server
|
||||
├── dwt/ # real-unit debug client and notes
|
||||
│ ├── challenge.py exploit.py # the challenge and the rdebug client
|
||||
│ ├── loader/ # COFF loader + hostload over rdebug
|
||||
│ ├── gui/ # injected X11 window
|
||||
│ ├── tcpip/ # socket API payloads
|
||||
│ └── notes/ # the real-unit investigation
|
||||
├── lat/ # LAT spec, implementations, audit, probes
|
||||
├── reference/ # datasheets, manuals, disassembly, boot video
|
||||
├── docs/ # DEC/TI manuals (PDF + .txt), wiki drafts
|
||||
└── images/ # real-unit photographs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ground rules
|
||||
|
||||
The project has five hard-won rules, written down in
|
||||
`harness/mame/README.md`: search with an explicit path (never from `/`), stage
|
||||
only your own paths (never `git add -A`), one sample is not evidence, any change
|
||||
to MAME needs a quote and a second reader, and **do not fake it** — model what
|
||||
the documentation supports, report the gap, and make any stand-in opt-in and
|
||||
named for what it is.
|
||||
|
||||
That last rule is why the faithful configuration is the default, why
|
||||
`VT1200_IO_PATH_STUB` is off and described as a test fixture rather than a
|
||||
keyboard, and why `harness/mame/BRINGUP_LOG.md` keeps its withdrawn claims.
|
||||
Reference in new issue
Block a user