# VT1200 execution harness (MAME) A minimal MAME machine that loads the VT1200 firmware into a TMS34010 CPU so we can **run it under MAME's debugger**: instruction trace, breakpoints, single-stepping, and direct memory watch. It is a *visibility* tool, not an accurate VT1200 emulation. Everything in this directory is source-of-truth in this repo; the MAME source tree is disposable and lives outside the project (`/home/arti/code/k-space/mame0289`). ``` harness/mame/ ├── vt1200.cpp # the MAME driver (symlinked into the MAME tree) ├── build.sh # clone-safe build wrapper ├── trace.lua # Lua: trace execution from the reset vector ├── overflow.lua # Lua: exercise the pcfReadBitmaps n*4 overflow ├── FINDINGS.md # results, evidence, and what was learned └── roms/vt1200/ # MAME ROM dir (symlink to ../../vt1200_v22.bin) ``` ## Address model (the important part) The TMS34010 is **bit-addressed**: a register/PC holds a bit address, and a pointer `P` refers to physical byte `P >> 3`. MAME models this with a program space of `memory_access<32, 16, 3>`, i.e. the **address-map unit is 1 bit** (a 16-bit word spans 16 address units). Therefore: * Address-map entries here are **bit addresses**, not byte addresses. * `read_word(0x3f200000)` returns ROM bytes 0..1 (physical byte 0x07e40000). * The reset vector is read from logical bit `0xffffffe0`, which maps to the last 4 bytes of the image (file offset `0x1bfffc`). The driver maps: | region | bit range | bytes | |---|---|---| | RAM | `0x00000000`–`0x001fffff` | 4 MiB at byte 0 | | ROM | `0x3f200000`–`0x3fffffff` | 1.75 MiB at byte 0x07e40000 | | ROM aliases | `0x7f…`, `0xbf…`, `0xff200000`–`0xffffffff` | same ROM (top 2 logical bits not decoded) | The `0xff…` alias is what makes the reset-vector page resolve. Board peripherals (LANCE Ethernet, DUARTs) are **not** modelled; their reads return `0xffff` and writes are dropped. The terminal will therefore not boot to the X server — that is expected and not the goal. ## Build MAME source is required. A shallow 0.289 checkout is large and slow to fetch. ```sh git clone --depth 1 --branch mame0289 \ https://github.com/mamedev/mame.git /home/arti/code/k-space/mame0289 harness/mame/build.sh # symlinks the driver in and runs make ``` `build.sh` runs `make -j$JOBS SUBTARGET=vt1200 SOURCES=src/mame/dec/vt1200.cpp`, which builds a single-driver binary `/home/arti/code/k-space/mame0289/vt1200`. The MAME tree is large and the build is **memory-hungry**: `build.sh` defaults to **`-j8`**, not all cores. Override with `JOBS=4 harness/mame/build.sh` on a small/low-memory machine. Progress is written to `harness/mame/build.log`. ## Run Headless execution and inspection use MAME's Lua debugger API from `-autoboot_script`. (`-debugscript` only works when a debugger UI drives the console, so it is not usable headless.) ```sh cd /home/arti/code/k-space/vt1200 M=/home/arti/code/k-space/mame0289/vt1200 # trace execution from the reset vector $M vt1200 -rompath harness/mame/roms -video none -sound none -nothrottle \ -debug -debugger none \ -autoboot_script harness/mame/trace.lua -autoboot_delay 0 # demonstrate the pcfReadBitmaps n*4 overflow (OVF_N overrides n, decimal) $M vt1200 -rompath harness/mame/roms -video none -sound none -nothrottle \ -debug -debugger none -debuglog -seconds_to_run 5 \ -autoboot_script harness/mame/overflow.lua -autoboot_delay 0 ``` The Lua API exposes `manager.machine.debugger`, `device.debug` (step, go, bpset, wpset), CPU registers (`cpu.state['A0'].value`), memory (`cpu.spaces['program']:read_u32/write_u32`), and frame hooks. ### Trace from the reset vector (`trace.lua`) Prints the reset PC (expect `3F200040`), disassembles the power-on code to `reset.dasm`, and writes an instruction trace of the first two frames to `vt1200-reset.trace`. ### Exercise the `pcfReadBitmaps` overflow (`overflow.lua`) Instead of booting the parser we jump directly into its arithmetic block with a crafted in-memory `PCF_CharInfo`: ``` a9 = 0x00040000 ; fake pCS; pCS->nChars at a9+0x270 a13 = 0x00060000 ; fake arg blk; n (from pcfReadInt32) at a13+0x20 write_u32(0x00040270, 0) ; nChars == 0 write_u32(0x00060020, n) ; n pc = 3f984e70 break at 3f985060 ; just after 'sll 0x2,a11' ``` This is possible because the font parser's field moves use **field 1**, which `SETF 0,0,1` leaves at 32 bits, so `n` is a full 32-bit `pcfReadInt32` value. See `FINDINGS.md` for results and the rest of the analysis.