5.2 KiB
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_W2in{1,2,4,8}all givetotal_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-> stuckPC=3FDCE400(5 PCs);BUSY_TICKS=0 BUSY_W2=1-> 56 PCs atPC=3F208950;BUSY_TICKS=0 BUSY_W3=1-> still stuck. INTENBnever becomes non-zero across 900 frames (INTPEND=0400throughout), so the level-0 interrupt boundary is still not crossed.
Rulings I made (all of them, in order)
- 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
.gitignoreexcludes generated artifacts (160 MB AVI, RAM dumps, traces, core files, nested clones). Cost if wrong: a.gitthe user does not want; reversible withrm -rf .git. - Pinned the
tick()call site to the scanline callback, guarded onscanline == screen.visible_area().max_y. The plan left it ambiguous and thescreen_updatealternative is wrong (it fires in small clip regions). Cost if wrong: busy machines advance at the wrong rate; one call site. - Model policy: DeepSeek V4.1 (
deepseek/deepseek-flash) preferred over V4, per user guidance;highvariant for implementers/reviewers,maxfor the final review. Cost if wrong: a task or two on a weaker model, with review coverage. - 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.
- 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.
- 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. - 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).