From 66784de1aace8dc8b24017d7c33d2d267a767dd1 Mon Sep 17 00:00:00 2001 From: SDD Date: Fri, 16 Oct 2026 02:15:13 +0300 Subject: [PATCH] docs: memory-space map from the spike; commit the plan amendment; ignore harness logs --- .gitignore | 3 + .../plans/2026-10-16-vt1200-board-model.md | 8 +- ghidra/vt1200.lock | 9 -- ghidra/vt1200.lock~ | 0 harness/mame/board-4w.log | 56 ------------ reference/memory-space-map.md | 89 +++++++++++++++++++ 6 files changed, 99 insertions(+), 66 deletions(-) delete mode 100644 ghidra/vt1200.lock delete mode 100644 ghidra/vt1200.lock~ delete mode 100644 harness/mame/board-4w.log create mode 100644 reference/memory-space-map.md diff --git a/.gitignore b/.gitignore index 08158dc..28d6eaf 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ lat/code/latd/ harness/mame/vt1200-reset.trace reference/x11/ tools/ghidra-headless-mcp/ + +# Generated harness logs +harness/mame/board-*.log diff --git a/docs/superpowers/plans/2026-10-16-vt1200-board-model.md b/docs/superpowers/plans/2026-10-16-vt1200-board-model.md index 78f4d1a..d206b2b 100644 --- a/docs/superpowers/plans/2026-10-16-vt1200-board-model.md +++ b/docs/superpowers/plans/2026-10-16-vt1200-board-model.md @@ -256,7 +256,13 @@ Expected: `all board tests passed` - [ ] **Step 6: Wire the driver to the model and verify no regression** -In `harness/mame/vt1200.cpp`: add `#include "vt1200_board.h"`, give `vt1200_state` a `vt1200_board m_board;` member, and in `board_read`/`board_write` delegate to `m_board.read(addr)` / `m_board.write(addr, data)` instead of the current inline logic. In `machine_start`, translate the existing env vars into the model's setters (`VT1200_BOARD_DEFAULT`, `VT1200_BOARD_MAP`, `VT1200_BOARD_MODEL`, `VT1200_BOARD_BUSY_TICKS`, `VT1200_BOARD_SWITCH`). Call `m_board.tick()` once per emulated frame — the simplest correct place is the existing `scanline` callback guarded so it only fires once per frame, or `screen_update` when `cliprect.min_y == 0`. +In `harness/mame/vt1200.cpp`: add `#include "vt1200_board.h"`, give `vt1200_state` a `vt1200_board m_board;` member, and in `board_read`/`board_write` delegate to `m_board.read(addr)` / `m_board.write(addr, data)` instead of the current inline logic. In `machine_start`, translate the existing env vars into the model's setters (`VT1200_BOARD_DEFAULT`, `VT1200_BOARD_MAP`, `VT1200_BOARD_MODEL`, `VT1200_BOARD_BUSY_TICKS`, `VT1200_BOARD_SWITCH`). Call `m_board.tick()` once per emulated frame. Put it in the **scanline callback**, guarded so it fires exactly once per frame — the callback is invoked once per scanline (verified: lines 168, 169, 170 …), and `scanline == screen.visible_area().max_y` is the last line of the frame. Do **not** use `screen_update`: it is called with small clip regions and a guard there fired only once in an 80-frame run. + +```cpp +// in scanline(), at the end: +if (scanline == screen.visible_area().max_y) + m_board.tick(); +``` In `harness/mame/build.sh`: add the new source to the symlink list and to `SOURCES=` (comma-separated), and add `g++ -c vt1200_board.cpp` to the unit-test recipe. diff --git a/ghidra/vt1200.lock b/ghidra/vt1200.lock deleted file mode 100644 index 0ae2975..0000000 --- a/ghidra/vt1200.lock +++ /dev/null @@ -1,9 +0,0 @@ -#Ghidra Lock File -#Fri Oct 16 01:36:23 EEST 2026 -\ Supports\ File\ Channel\ Locking=Channel Lock -Hostname=sid -OS\ Architecture=amd64 -OS\ Name=Linux -OS\ Version=7.2.4-arch1-2 -Timestamp=10/16/26, 1\:36\u202FAM -Username=arti diff --git a/ghidra/vt1200.lock~ b/ghidra/vt1200.lock~ deleted file mode 100644 index e69de29..0000000 diff --git a/harness/mame/board-4w.log b/harness/mame/board-4w.log deleted file mode 100644 index 1d889af..0000000 --- a/harness/mame/board-4w.log +++ /dev/null @@ -1,56 +0,0 @@ -# kind addr pc value (bit addresses; distinct (addr,pc) only) -W 0640000D pc=3F2000B0 val=0000 -R 0640000F pc=3F200100 val=0000 -R 0640000E pc=3F200150 val=0000 -W 0620000D pc=3F200190 val=0000 -W 0620000F pc=3F2001E0 val=0004 -R 0640000F pc=3F200250 val=00FF -R 0640000D pc=3F200380 val=0070 -R 0640000D pc=3F200A20 val=0070 -R 0640000D pc=3F202650 val=0070 -W 05900000 pc=3FDDCAE0 val=0000 -W 05800000 pc=3FDDCB20 val=0004 -R 0640000D pc=3F2045A0 val=0070 -W 0620000D pc=3F2052C0 val=0000 -W 0620000E pc=3F205310 val=00FF -W 0620000F pc=3F205370 val=0004 -R 0640000E pc=3F2042D0 val=000F -R 0640000D pc=3F204300 val=0070 -W 0640000D pc=3FDF1150 val=0000 -W 0640000F pc=3FDF1190 val=0001 -R 06200001 pc=3FDF3900 val=0001 -W 06200003 pc=3FDF3AD0 val=0011 -W 06200003 pc=3FDF3BE0 val=008C -R 0640000D pc=3FDF7DE0 val=0070 -W 06200002 pc=3FA91260 val=00CA -W 06200002 pc=3FA91300 val=002A -W 06200002 pc=3FA913B0 val=003A -W 06200002 pc=3FA91460 val=004A -W 06200002 pc=3FA91500 val=005A -W 06200002 pc=3FA915A0 val=001A -W 0620000A pc=3FA91650 val=00CA -W 0620000A pc=3FA916F0 val=002A -W 0620000A pc=3FA917A0 val=003A -W 0620000A pc=3FA91850 val=004A -W 0620000A pc=3FA91900 val=005A -W 0620000A pc=3FA919A0 val=001A -W 06200000 pc=3FA91A30 val=0013 -W 06200000 pc=3FA91AC0 val=0007 -W 06200001 pc=3FA91B70 val=0099 -W 06200002 pc=3FA91C70 val=0005 -W 06200008 pc=3FA91D10 val=0007 -W 06200008 pc=3FA91DB0 val=0007 -W 06200009 pc=3FA91E60 val=0099 -W 0620000A pc=3FA91F40 val=0005 -W 06200005 pc=3FA91FE0 val=0000 -R 06200001 pc=3FDCE670 val=0099 -R 06200003 pc=3FDCE700 val=008C -R 06200001 pc=3FDCE820 val=0099 -W 06200003 pc=3FDCE8D0 val=00E1 -R 06200001 pc=3FDCE910 val=0099 -R 06200003 pc=3FDCE970 val=00E1 -R 06200001 pc=3FDCEA90 val=0099 -W 0620000D pc=3FDF1B40 val=0000 -W 0620000E pc=3FDF1B80 val=000C -R 06200001 pc=3FDCF420 val=0099 -R 06200003 pc=3FDCF560 val=00E1 diff --git a/reference/memory-space-map.md b/reference/memory-space-map.md new file mode 100644 index 0000000..6429886 --- /dev/null +++ b/reference/memory-space-map.md @@ -0,0 +1,89 @@ +# 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.