# VT1200 firmware — address-space map (spike, 2026-10-16) What external memory/register space the v2.2 firmware actually touches, derived by sweeping every scalar operand in the disassembled image plus the raw ROM images, and cross-checked against the MAME harness. ## Conventions (read this first) * The TMS34010 is **bit-addressed**. The firmware stores **bit addresses**. A physical **byte** address is `bit >> 3`; **Ghidra** byte address = `0x07E40000 + file_offset`. * This document gives **bit addresses** (the value the firmware actually stores) and, in parentheses, the **byte** address (`bit>>3`). * "harness unit" (the MAME driver's `BOARD_BASE` and the access-log address) = `bit >> 4` = 2 bytes per unit. The four known windows are written `0x5800000` (bit) here, which is the driver's `0x5800000` too (leading-zero identical) — but the **log** prints them as `0x0580000D` style (`bit>>4`). Do not mix the two. * Evidence codes: **CONST** = scalar-operand count from the Ghidra sweep (96,801 instructions / 1,177 distinct `@`-targets); **XREF** = string/reference cross-check; **LOG** = `harness/mame/board-4w.log` / `board-access.log`; **SG** = service guide. * "Driver?" = does `harness/mame/vt1200.cpp` currently map it. ## Region table | # | bit range | byte range | what | evidence | driver? | conf. | |---|---|---|---|---|---|---| | 1 | `0x0000000`–`0x2000000` | `0x000000`–`0x400000` | **Main RAM** 4 MiB | mem-test at `0x07EDFF22` walks SIMM regions at bit `0x800000`/`0x1000000`… (byte 1 MiB/2 MiB); ~5 000 absolute globals; POST clears RAM; SG: 1 MiB onboard + 3×1 MiB SIMM | yes (bit `0`–`0x1FFFFFF`) | **HIGH** | | 2 | `0xC0000000`–`0xC00001FF` | (on-chip) | **CPU on-chip registers** (DPYCTL/DPYSTRT/DPYADR/CONTROL/…) | datasheet + `MEMORY_MAP.md`; 72 absolute operands | internal | **HIGH** | | 3 | `0x5000000` | `0xA00000` | **Memory-config / SIMM-presence input port.** bits 0,1,2 = "SIMM 1/2/3 installed" | CONST 3 `@` reads; XREF: `0x07E58FB2`/`0x07E58FCE`/`0x07E58FEA` read bits 0/1/2 then print `Not Present`/`SIMM n installed` (strings @ file `0x0199BD`…); also read at `0x07EDFF22` | **no** | **HIGH** | | 4 | `0x5600000` | `0xAC0000` | **Unidentified board register block** — most-touched unmodelled region. Offsets `+0x00 +0x10 +0x20 +0x30 +0x80 +0x100 +0x200 +0x380` | CONST **110** `@` accesses (2nd only to RAM/the known windows); writer at `0x07E6CB62`, adjacent to X-server strings (`no screens found`) | **no** | **HIGH** region / **LOW** identity | | 5 | `0x5800000` | `0xB00000` | **DMA/board data port** | CONST 10 `@`; LOG; paired with #6; firmware `>>3`s a bit address into it (byte-addressed device) | yes | **HIGH** | | 6 | `0x5900000` | `0xB20000` | **DMA/board command port** | CONST 6 `@`; LOG (commands 0,1,2,4,0x42) | yes | **HIGH** | | 7 | `0x6000000` | `0xC00000` | **Candidate NVRAM.** ~2 KiB user-settings copy writes here; sits in a 256 KiB device slot between #6 and #8 | CONST 2 (`0x07E957F4`, `0x07E95974` push it as the destination of a settings copy/metadata routine `0x07E966D0`); XREF: adjacent `Old user settings layout.` / `User settings invalid.` | **no** | **MEDIUM-LOW** | | 8 | `0x6200000` | `0xC40000` | **Board window** (STATUS `+0x10`, CMD `+0x20`, config `+0xD0`) | CONST 26 `@`; LOG; `BRINGUP_LOG.md` | yes | **HIGH** | | 9 | `0x6400000` | `0xC80000` | **Board window** incl. monitor-selection switch (bits 4–6 of `+0xD0`) | CONST 24 `@`; LOG; `BRINGUP_LOG.md` | yes | **HIGH** | | 10 | `0x6800000` | `0xD00000` | **Ethernet MAC-address ROM** | CONST 2 (`0x07E46112`); the 6-iteration byte-read loop `0x07E4611E`–`0x07E4613A` copies 6 bytes (a MAC) from this base; SG level-1 "Ethernet address ROM test" | **no** | **HIGH** | | 11 | `0x4000000` | `0x800000` | **VRAM / framebuffer — candidate only** | `MOVI 0x4000000,B4` at `0x07E40AB0` in the reset-bank video init (`video_regs_init`); the `B` file holds bitmap bases | **no** | **LOW** | ### Notes on rows 3–7 (the peripheral map is regular) The byte addresses `0xA00000`, `0xAC0000`, `0xB00000`, `0xB20000`, `0xC00000`, `0xC40000`, `0xC80000`, `0xD00000` look like a decoded device map with a ~256 KiB slot at the top end (`0xC00000`, `0xC40000`, `0xC80000`, `0xD00000` are exactly `0x40000` apart). That regularity is *circumstantial* evidence for rows 7 and 10. ## What was ruled out (candidates that are NOT regions) These appear constantly as large 32-bit constants but are **not** addresses: | value (bit) | file offsets | why it is not an address | |---|---|---| | `0x112A880`, `0x2DC6C0`, `0x5B8D80`, `0x927C0`, `0x281F90`, `0x3600400`, `0x4000500` | reset/POST bank | `DSJS` delay-loop counters (gate 2 in `BRINGUP_LOG.md`) and per-monitor display-format constants selected by the monitor switch (`0x07E4089E`–`0x07E4090A`) | | `0x800000`, `0x1000000` | `0x07EDFF2C`… | **RAM region boundaries** used by the SIMM memory test (byte 1 MiB / 2 MiB), not devices | | `0x4000000` (all but one) | 49 aligned hits | bit-26 mask OR/AND, not an address | | byte `0x600000`–`0x7E40000` "buckets" | whole image | code bytes / data tables. A naive raw scan of every 32-bit word is ~25 % false positives. The `0x5000000`/`0x6000000` "cluster" in bank 6 is **unaligned byte noise** inside sparse tables — re-scanning 16-bit-aligned collapses it. | ## What remains unknown (stated plainly) 1. **Exact VRAM base and size.** The framebuffer is *not* accessed by any absolute operand (the `@`-sweep finds it nowhere), so it is addressed indirectly — via the `B`-register file and/or the `DPYADR` (`0xC00001E0`) register, both set at runtime from a RAM screen descriptor. Row 11 is the only static hint and is **weak**. It is also possible the video DRAM sits *behind* the `0x5800000`/`0x5900000` DMA pair rather than being CPU-visible. 2. **Identity of row 4 (`0x5600000`).** Heavy, display-adjacent, but I could not name the device. 3. **NVRAM extent / exact base** (row 7), and whether the MAC ROM (row 10) is 16-bit-wide (the read loop advances 2 bytes per MAC byte). 4. **Extent of each I/O block** — only point accesses are observed, so sizes are guessed from slot spacing. ## How to reproduce ``` # operand sweep (Ghidra, session required): # iterate listing.getInstructions(True); for each Scalar operand >= 0x10000 and < 0x40000000, # bucket by (value>>3)>>16. Filter to "@0x" operands for true memory accesses. python3 tools/ptr_constants.py vt1200_v22.bin # ROM-pointer subset # raw aligned scan: 32-bit LE words whose (value>>3) lands in the region of interest ``` ## Durable Ghidra annotations added (project `ghidra/vt1200`, program `vt1200_v22.bin`) * Memory blocks (with comments) created at each region byte base: `VRAM_800000_CAND`, `IO_50_SIMM_MEMCFG`, `IO_56_BOARD_REG`, `IO_58_DMA_DATA`, `IO_59_DMA_CMD`, `IO_60_NVRAM_CAND`, `IO_62_WIN2`, `IO_64_WIN3`, `IO_68_MACROM`. * Functions created/named: `video_regs_init`, `io5600000_screen_init`, `nvr_user_settings_validate`, `io_settings_copy_expand`, `io_settings_copy_linear`, `io_settings_restore`, `io5800_5900_init`, `io5800_status_read`, `keyboard_mouse_irq_handler`, `led_control_1/2`. * PRE comments at the key access sites (MAC read, SIMM read, `0x5600000` write, settings copy, SIMM mem-test, video init, `B4=0x4000000`). * Program saved. Function count 8 → 19; memory blocks 1 → 10.