mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
146 lines
6.2 KiB
Markdown
146 lines
6.2 KiB
Markdown
# VT1200 memory map
|
||
|
||
Derived statically from the firmware ROM images (v2.1 and v2.2) and
|
||
independently re-verified. Confidence is called out per section.
|
||
|
||
The CPU is a Texas Instruments **TMS34010**. It is a *bit-addressed* machine:
|
||
a 32-bit logical address selects a bit; the top two logical address bits are
|
||
not emitted, so the external physical space is 128 MiB and
|
||
`physical_bit == logical_bit & 0x3FFFFFFF`. **The firmware stores absolute
|
||
bit addresses**, so a stored pointer `P` refers to physical byte `P >> 3`.
|
||
|
||
## ROM — confidence: HIGH
|
||
|
||
| Property | Value |
|
||
|---|---|
|
||
| Size | 1,835,008 bytes (`0x1C0000`, 1.75 MiB) |
|
||
| Physical byte range | `0x07E40000` – `0x07FFFFFF` |
|
||
| Bit range | `0x3F200000` – `0x3FFFFFFF` |
|
||
| Placement | **top-aligned** in the 128 MiB space |
|
||
|
||
Evidence: 1000+ absolute string pointers in the image satisfy
|
||
`(P >> 3) - string_offset == 0x07E40000`; e.g. `"Messages re-xmt'ed"` at
|
||
offset `0xC2DDB` is referenced by the 32-bit value `0x3F816ED8`
|
||
(`0x3F816ED8 >> 3 == 0x07F02DDB == 0x07E40000 + 0xC2DDB`).
|
||
|
||
## Bank order — confidence: HIGH
|
||
|
||
The seven 256 KiB banks are laid out in **reverse part-number order**: the
|
||
highest-numbered EPROM pair sits at the lowest address. In the address-ordered
|
||
image (`reconstruct.py`, default) `file offset == physical byte - 0x07E40000`.
|
||
|
||
| `k` | v2.1 pair | v2.2 pair | file offset | physical byte | bit base |
|
||
|---|---|---|---|---|---|
|
||
| 0 | 23-222E9 / 223E9 | 23-286E9 / 287E9 | `0x000000` | `0x07E40000` | `0x3F200000` |
|
||
| 1 | 23-220E9 / 221E9 | 23-284E9 / 285E9 | `0x040000` | `0x07E80000` | `0x3F400000` |
|
||
| 2 | 23-218E9 / 219E9 | 23-282E9 / 283E9 | `0x080000` | `0x07EC0000` | `0x3F600000` |
|
||
| 3 | 23-216E9 / 217E9 | 23-280E9 / 281E9 | `0x0C0000` | `0x07F00000` | `0x3F800000` |
|
||
| 4 | 23-214E9 / 215E9 | 23-278E9 / 279E9 | `0x100000` | `0x07F40000` | `0x3FA00000` |
|
||
| 5 | 23-212E9 / 213E9 | 23-276E9 / 277E9 | `0x140000` | `0x07F80000` | `0x3FC00000` |
|
||
| 6 | 23-210E9 / 211E9 | 23-274E9 / 275E9 | `0x180000` | `0x07FC0000` | `0x3FE00000` |
|
||
|
||
Conversion, for a part-number-ordered image (e.g. a raw `reconstruct.py
|
||
--part-order` file):
|
||
|
||
```
|
||
physical_byte(F) = 0x07E40000 + (6 - (F >> 18)) * 0x40000 + (F & 0x3FFFF)
|
||
```
|
||
|
||
## Reset / interrupt vector page — confidence: HIGH
|
||
|
||
The vector page occupies the last 128 bytes of the ROM.
|
||
|
||
* Physical byte `0x07FFFF80`–`0x07FFFFFF` (file `0x1BFF80`–`0x1C0000`)
|
||
* Logical bit `0xFFFFFC00`–`0xFFFFFFFF`
|
||
* 32 entries, one 32-bit bit-address each; **entry 31 (`0xFFFFFFE0`) is reset**
|
||
|
||
| entry | value | target |
|
||
|---|---|---|
|
||
| 31 (RESET) | `0x3F200040` | `0x07E40008` — power-on code (`60 03 50 05 40 07 00 56 …`) |
|
||
| 1 | `0x3F207C40` | `0x07E40F88` |
|
||
| 22, 23 | `0x3F207CC0`, `0x3F207D00` | `0x07E40F98`, `0x07E40FA0` |
|
||
| 0, 2–21, 24–30 | `0x000009C0` … `0x00000F60` | unresolved (see Open items) |
|
||
|
||
The reset vector is **identical in v2.1 and v2.2**, and points at byte-identical
|
||
POST code.
|
||
|
||
## Bank header — confidence: HIGH (checksum), LOW (`h1`)
|
||
|
||
Every 256 KiB bank begins with an 8-byte header of four big-endian-friendly
|
||
16-bit little-endian words:
|
||
|
||
```
|
||
[ h0 ][ h1 ][ 0xAAAA ][ 0x5555 ]
|
||
```
|
||
|
||
* `h0` is a **16-bit additive checksum**: the sum of all 16-bit words from the
|
||
`0xAAAA` magic word to the end of the bank, mod 2^16. Verified for **all 14
|
||
banks in both revisions** (`tools/vt1200_map.py`).
|
||
* `0xAAAA5555` is a fixed format marker.
|
||
* `h1` is **unidentified** (not CRC32-low16, CRC-16/CCITT, XOR or byte sum). It
|
||
is per-bank and differs between revisions — possibly a build/part id.
|
||
|
||
The header is *not* a load address or length field.
|
||
|
||
## CPU on-chip registers — confidence: HIGH (datasheet)
|
||
|
||
The TMS34010's own I/O registers live in logical bit space
|
||
`0xC0000000`–`0xC00001FF`, register *n* at `0xC0000000 + 0x10*n`. Observed in
|
||
the firmware:
|
||
|
||
```
|
||
CONTROL 0xC00000B0 DPYCTL 0xC0000080
|
||
HSTCTLH 0xC0000100 INTENB 0xC0000110
|
||
INTPEND 0xC0000120 CONVSP 0xC0000130
|
||
CONVDP 0xC0000140 HSBLNK 0xC0000150
|
||
```
|
||
|
||
## RAM and board peripherals — confidence: MEDIUM (RAM) / LOW (peripherals)
|
||
|
||
* **RAM** begins at `0x0000000`. 1 MB on the system-logic board plus up to
|
||
3× 1 MB SIMMs (service guide). POST clears words from address 0; interrupt
|
||
handlers save registers at byte `0x40`–`0x100`; hundreds of `@0x1xxxx`…
|
||
`@0x4xxxx` globals are referenced, so at least the first ~`0x50000` bytes are
|
||
statically used.
|
||
* **Board peripherals** appear around byte `0x00B00000`, `0x00C40000`,
|
||
`0x00C80000` (bit `0x5800000`, `0x6200000`, `0x6400000`). POST writes
|
||
`@0x62000d0/f0` and `@0x64000d0/e0/f0`; the two identical windows at
|
||
`0xC40000`/`0xC80000` are presumed to be the two DUARTs. **Device decoding
|
||
not confirmed.**
|
||
|
||
Interrupt sources (service guide): Display, Host Interface, Interrupt 1,
|
||
Interrupt 2, NMI, Trap, Window Violation; plus LANCE Ethernet, DUART (2 ch +
|
||
2 timers), video clock.
|
||
|
||
## v2.1 vs v2.2 — confidence: HIGH
|
||
|
||
Same load base, same bank order, same vector page, same header format, and the
|
||
boot bank (286/287) is **byte-identical** (reset target `0x07E40008`). The
|
||
main X-server banks were relinked: ~90 % of bytes differ overall (bank 0 ≈50 %,
|
||
banks 1–6 ≈93–97 %). So v2.2 is not a small patch to v2.1.
|
||
|
||
## Reproduce
|
||
|
||
```sh
|
||
./reconstruct.py v2.2 # address-ordered image (default)
|
||
python3 tools/vt1200_map.py vt1200_v22.bin # vectors + header checksums
|
||
|
||
# disassemble the power-on / exception bank (address 0, bit base 0x3F200000):
|
||
unidasm vt1200_v22.bin -arch tms34010 -basepc 0 -count 40
|
||
python3 -m unittest test_reconstruct -v # layout regression tests
|
||
```
|
||
|
||
Load the image in an emulator/disassembler at byte base `0x07E40000`.
|
||
|
||
## Open items
|
||
|
||
1. `h1` header field meaning.
|
||
2. The 28 non-reset vector entries (`0x000009C0`…): look like low-RAM handlers
|
||
installed at boot, or unrelocated offsets — unresolved.
|
||
3. Exact RAM extent and the peripheral device decoding.
|
||
4. **Address model mismatch:** the firmware stores *bit* addresses, but the
|
||
Ghidra TMS34010 module models *byte* addresses (instructions step by 2). A
|
||
pointer constant must be divided by 8 to resolve in Ghidra. `tools/ptr_constants.py`
|
||
finds and resolves those constants; the remaining step is a Ghidra script to
|
||
rewrite them in place.
|