# Plan 1 outcome — VT1200 board register model Plan: `docs/superpowers/plans/2026-10-16-vt1200-board-model.md` Spec: `docs/superpowers/specs/2026-10-16-vt1200-board-model-design.md` Commits: `acb92d3e44ec..e23dadcfd4de` (7 commits) Status: **all 4 tasks complete, final whole-branch review clean after one documentation-only fix wave.** This file is the durable record; the SDD workspace was deleted after review, as the process requires, with git history as the record. ## What was delivered | Commit | What | |---|---| | `43f69738f405` | board register model extracted into a **MAME-free, unit-tested** class (`vt1200_board.h/.cpp`, `test_vt1200_board.cpp`); driver forwards to it | | `db2caa555803` | fix round 1: switch knob made unambiguous (`0x70` raw bits vs position) and non-silent | | `66fe15367b90` | per-window busy state machines | | `a2f19b2d29a4` | the `0x5800000`/`0x5900000` DMA command/data pair | | `2d7747a20d48` | configurable timings + the timing sweep and its honest findings | | `8e18d81cef07` | fix round 1: verbatim sweep/differential/INTENB evidence into the tracked log | | `e23dadcfd4de` | fix wave: documentation corrections (four windows, real callback trap, guessed DMA timing) | Guarantees that hold: **no regression** — with no `VT1200_*` env vars set the driver still reproduces the original wedge `PC=3FDCE400 samples=199`, argued structurally as well as measured; 9 unit tests pass and the model compiles standalone with plain `g++`. ## The experiment's real result (negative, and valuable) * The timing sweep is **flat**: `BUSY_W2` in `{1,2,4,8}` all give `total_distinct=59`. The reason is structural — `tick()` advances **once per emulated frame**, so busy durations of 1/2/4/8 ticks are indistinguishable. Duration is the wrong axis. * **The critical handshake is isolated to `0x06200000` (WIN2):** `BUSY_TICKS=0` -> stuck `PC=3FDCE400` (5 PCs); `BUSY_TICKS=0 BUSY_W2=1` -> 56 PCs at `PC=3F208950`; `BUSY_TICKS=0 BUSY_W3=1` -> still stuck. * **`INTENB` never becomes non-zero** across 900 frames (`INTPEND=0400` throughout), so the level-0 interrupt boundary is still not crossed. ## Rulings I made (all of them, in order) 1. **Initialised a git repository in the project.** The directory was not under version control and the process needs an isolated tree plus per-task commits. A strict `.gitignore` excludes generated artifacts (160 MB AVI, RAM dumps, traces, core files, nested clones). *Cost if wrong:* a `.git` the user does not want; reversible with `rm -rf .git`. 2. **Pinned the `tick()` call site to the scanline callback**, guarded on `scanline == screen.visible_area().max_y`. The plan left it ambiguous and the `screen_update` alternative is wrong (it fires in small clip regions). *Cost if wrong:* busy machines advance at the wrong rate; one call site. 3. **Model policy: DeepSeek V4.1 (`deepseek/deepseek-flash`) preferred over V4**, per user guidance; `high` variant for implementers/reviewers, `max` for the final review. *Cost if wrong:* a task or two on a weaker model, with review coverage. 4. **Ran the memory-space investigation as a spike, not a plan**, with full Ghidra access — output is an answer plus durable artefacts, not product code. *Cost if wrong:* one worker's time; deliverables additive and write-isolated. 5. **Resumed Task 1's implementer on a different model for its fix round** (V4.1 vs its V4 Pro dispatch), keeping session context. *Cost if wrong:* none observed — the re-review found no new breakage. 6. **Treated the spike's VRAM finding as inference, not proof**, so the video plan's first task is an *empirical* determination (watchpoint `DPYADR`), not a static one. *Cost if wrong:* one wasted probe. 7. **Task 4's flat sweep stands as a successful outcome.** No improvement was claimed and none was fabricated; the negative result is the deliverable. ## Reviewer findings triaged as acceptable (not fixed, deliberately) Thirteen minors were raised across the four tasks; the final review triaged them and required only the three documentation corrections (now fixed). The rest were judged acceptable: write-back latches on non-handshake reads; the handshake matching only the low 16 bits; `m_busy_ticks` default 2 -> 0; assorted test-coverage gaps; the unmasked key in `set_window_busy_ticks`; the DMA defaults-off gate being untested; a WIN1 read ignoring the register offset (inert while DMA defaults off); and cosmetic diagnostic wording. **Two worth acting on before the next modelling round** (the reviewer's words): mask the key in `set_window_busy_ticks`, and offset-gate the WIN1 DMA read so a WIN1 status bit is not shadowed. ## Where the goal stands This plan deliberately delivered only its slice. `INTENB` still never leaves zero and no frame has been rendered — the spec's success criteria 2 and 3 remain open, to be met by the two named follow-on plans: **video RAM + scanline renderer**, and **DUART / NVRAM / MAC ROM**. The spike (`reference/memory-space-map.md`) plus this plan's findings tell the video plan where to start: the framebuffer is not in main RAM, it is set at runtime via `DPYADR`, and the most-touched unmodelled region is `0x5600000` (~110 accesses on the display/X-init path).