From 7030f2068da7641df808a9f6dc257afc0f895678 Mon Sep 17 00:00:00 2001 From: SDD Date: Fri, 16 Oct 2026 10:07:38 +0300 Subject: [PATCH] harness: record headless-watchpoint void + the unresolved address-unit blocker, with a calibration plan --- debug.log | 23 +-- harness/mame/BRINGUP_LOG.md | 317 ++++++------------------------------ harness/mame/cal.lua | 42 +++++ harness/mame/wp2.lua | 35 ++++ 4 files changed, 136 insertions(+), 281 deletions(-) create mode 100644 harness/mame/cal.lua create mode 100644 harness/mame/wp2.lua diff --git a/debug.log b/debug.log index c0fd712..573c3f6 100644 --- a/debug.log +++ b/debug.log @@ -1,19 +1,8 @@ MAME debugger version 0.289 (mame0289-dirty) Currently targeting vt1200 (VT1200 X terminal (v2.2 firmware)) -Breakpoint 1 set -PRINT pc=3FDE00C0 a9=3FDCA8E8 - -PRINT pc=3FDE00C0 a9=3FDC98F8 - -PRINT pc=3FDE00C0 a9=00C0000C - -PRINT pc=3FDE00C0 a9=0100000C - -PRINT pc=3FDE00C0 a9=0140000C - -PRINT pc=3FDE00C0 a9=0180000C - -PRINT pc=3FDE00C0 a9=01C0000C - -PRINT pc=3FDE00C0 a9=00000000 - +Error in expression: printf "CAL-A wpaddr=%08X data=%04X acc=%02X pc=%08X\n", wpaddr, wpdata, wpaccess, pc ; g + ^unknown symbol +Error in expression: printf "CAL-B wpaddr=%08X data=%04X acc=%02X pc=%08X\n", wpaddr, wpdata, wpaccess, pc ; g + ^unknown symbol +Error in expression: printf "CAL-C wpaddr=%08X data=%04X acc=%02X pc=%08X\n", wpaddr, wpdata, wpaccess, pc ; g + ^unknown symbol diff --git a/harness/mame/BRINGUP_LOG.md b/harness/mame/BRINGUP_LOG.md index 07d6ee5..5dc0cf9 100644 --- a/harness/mame/BRINGUP_LOG.md +++ b/harness/mame/BRINGUP_LOG.md @@ -637,271 +637,60 @@ This is the single most useful diagnostic we have built: **the firmware narrates self-test through `0x3FDE00C0`.** Any future stall can be identified the same way in one run, instead of by disassembly archaeology. +## BLOCKER: address-unit confusion + headless debugger limits (2026-10-16) + +Two things are now blocking progress, and both are instrumentation problems rather than +missing knowledge. Fix these before further register work. + +### 1. Watchpoints DO NOT FIRE in this headless configuration + +`harness/mame/cal.lua` watches **three** encodings of the same known-hit address — the +driver-map address `0x0620000D` (which `VT1200_BOARD_LOG` *proves* the firmware hits), the +firmware operand `0x6200010`, and a shifted variant `0x00620000`. Over 200 frames: + +``` +not a single hit +``` + +Since the first of those is demonstrably accessed, the watchpoint itself is not firing: +**`wpset` is a silent no-op when the debugger has no UI**, exactly like the `history` +command. `bpset` *does* work (the console capture at `0x3FDE00C0` fires reliably). + +**Consequence, and it is a correction:** `wp.lua`'s earlier claim that "nothing writes +INTENB/INTPEND/DPYCTL" rested on watchpoints that were never firing. The *frame-sampled* +part of that conclusion stands (`intr.lua` polls INTENB every frame and sees only `0000`), +but the watchpoint evidence must be treated as void. + +**For memory observation, instrument the DRIVER, not the debugger.** `VT1200_BOARD_LOG`, +`VT1200_LOG_UNMAPPED` and the dump functions are all driver-side and all work. + +### 2. Four address conventions, still not reconciled + +| # | convention | example | +|---|---|---| +| 1 | firmware operands (bit addresses, as disassembly prints) | `@0x64000D0` | +| 2 | the driver's address-map units (`BOARD_BASE` + offset) | `0x0640000D` | +| 3 | the catch-all log's addresses | `0x400000`..`0x40D7FF` | +| 4 | the debugger's units | unusable headless | + +The unresolved contradiction: the catch-all logged `0x400000..0x40D7FF` as **unmapped**, +yet the main-RAM map (`0x00000000..0x01FFFFFF`, 4 MiB) contains those addresses. Either the +catch-all shadows RAM (but then the firmware would be running on zeros and would not reach +level 1), or its `off` is not the space address. **The video-memory conclusion is therefore +provisional until this is settled.** + +Concrete calibration plan (uses only driver-side logging, which works): + +1. Add a logging mapping over a slice of RAM (`0x00000000..0x0000FFFF`) registered AFTER + the RAM entry. If it logs, map precedence is last-wins and handlers can be layered; if + not, first-wins. +2. Add the same over each candidate encoding of the video region and see which one logs the + blitter's traffic. +3. That single run fixes the units and the precedence, and every address in this document + can then be normalised. + --- ## Next steps -(Design work — see the design spec.) - -1. **Getting a picture requires unblocking the firmware.** Video memory is located - (`0x00400000`) and mapped, and the blitter's region is known, but the firmware only - *clears/tests* it — it never draws, because it is still stuck early. The rendering - pipeline is the least of it; the blocker is the same one as before: `INTENB` never - leaves zero. -2. **Strongest known unmodelled region:** `0x5600000` (~110 static accesses, display/X-init - path, unreached while stuck). -3. **Write the scanline renderer** — it must read video memory rather than draw nothing. - Note the display's `DPYADR` gives `rowaddr = 0x1B0` under MAME's quirk, and the - framebuffer geometry needs confirming against `0x00400000`. -4. Then the two remaining follow-on plans: video RAM + renderer, and DUART / NVRAM / MAC ROM. - - -1. **Design the generic register-file model** for the four windows: STATUS at `+0x10`, - COMMAND/DATA at `+0x20`, mixed I/O at `+0xD0`, latched outputs elsewhere. -2. **Decide the scope** — level-0 POST only, or toward a fully working terminal - (LANCE, DUARTs, video, keyboard/mouse). -3. Then the remaining items: stateful busy/done status, the scanline renderer, and - confirming the firmware runs from a RAM copy of the image. - -1. **The `05800000`/`05900000` command/data pair is the most promising unexplored block.** - A DMA-style interface (bit->byte address conversion) that the firmware barely touches - during our stall — probably not the immediate blocker, but it is where LANCE / host - interface traffic will live, and level 0 requires the LANCE. -2. **Find what the firmware waits for before enabling interrupts.** `INTENB` is never - written at all (verified with watchpoints), so the stall is a board-level wait. -3. **Hunt the remaining bit-mixed ports** — note `VT1200_BOARD_SWITCH` now models only - the monitor switch port (`0x0640000D`, bits 4-6); other ports warn and are ignored. -4. **Build the stateful status model**: board status bits are busy/done handshakes, never - constants — every gate so far has needed behaviour. -5. **Write the scanline renderer** (needs the framebuffer layout; MAME has no generic - TMS34010 renderer). -6. **The RAM is full of ROM strings and ROM pointers** — evidence the firmware runs from - a RAM copy of the image; worth confirming. - -1. **Find what the firmware waits for *before* enabling interrupts.** `INTENB` never - becomes non-zero, so level 0's display-interrupt test can never pass. This is the - current blocker. `INTENB` write sites: file `0x000060`, `0x00077C`, `0x000810`, - `0x000EE4` (reset-bank disables) plus `0x06F308`, `0x09DC54`, `0x09DC60`, `0x09DDAC`. -2. **Hunt the remaining bit-mixed ports** — level 0 includes an *I/O data path test*, - and every other configuration input will have the shape "input bits mixed with - latched outputs" — note `VT1200_BOARD_SWITCH` now models only the monitor switch port - (`0x0640000D`, bits 4-6); other ports warn and are ignored. -3. **Build the stateful status model** (gate 4, and the video / I-O-path tests): a - graphics or I/O op starts -> busy bit set -> clears after N reads. Every gate so far - has needed behaviour, never a constant. -4. **Write the scanline renderer** — the whole of the remaining video work. It needs the - VT1200's framebuffer layout, which is *board-specific* (MAME gives no generic - TMS34010 renderer). Derive it from the display-init code that writes DPYADR/PSIZE - (`DPYADR` sites: file `0x016DF0`, `0x016F7A`, `0x0E29E6`, `0x1185A2`). - Note MAME's quirk: `if (!(DPYCTL & 0x400)) dpyadr ^= 0xfffc;` then - `rowaddr = dpyadr >> 4` — with our values that yields `rowaddr = 0x1B0`. -5. **Keep gating**: the remaining read sites after the handshake model are - `0640000D` (6 PCs), `0640000E`, `0640000F`, `06200001`, `06200003`. Each needs its - *semantics* (what the bit means), not just a value. -6. Once video shows something, the terminal state is verifiable at a glance — which - also makes further bring-up much faster. - -**Done so far:** monitor switch (bits 4-6 of `0x64000D0`), raster geometry -(16 px/clock, 1280x901 @ 72 Hz), CPU 50 MHz. - -## Address-unit note - -In the harness's board windows, `log address = disassembly operand >> 4` -(e.g. code `@64000F0h` appears as `0640000F`). The override/log key is the harness -address, and the override lookup is consistent with it, so this does not affect the -experiments — but it does mean the window addresses are **not** directly comparable to -the register operands printed by `unidasm`. - -## Board register model extracted (2026-10-16) - -The ad-hoc inline board-window logic in `board_read`/`board_write` has moved into -`vt1200_board.{h,cpp}` — a standalone, MAME-free class that owns the four windows' -write-back latches, the per-address overrides, the monitor switch, and the busy state -machine. It is unit-tested by `test_vt1200_board.cpp` (`harness/mame/build.sh test`); -the driver now forwards its four window callbacks to the model and calls `m_board.tick()` -once per frame at the last visible scanline. - -The busy bit now has the semantics the design spec intended: a write to `+0x20` arms it, -`+0x10` reports it, and it clears after `VT1200_BOARD_BUSY_TICKS` *frame* ticks (the old -inline code armed on any write and cleared after N *reads*). The old `VT1200_BOARD_BUSY` -and `VT1200_BOARD_BUSY_BIT` knobs are dropped — the model hardcodes the busy bit as bit 0 -of the STATUS register. `VT1200_BOARD_SWITCH` is likewise narrowed to the monitor switch -(bits 4-6 of `0x64000D0`): `` is accepted as the raw register bits (e.g. `70`) or a -position 0-7, and the input mix is applied only when the knob is supplied. A `` or -`` that does not match the monitor switch port warns (via `osd_printf_info`) and is -ignored instead of silently no-oping. - -No regression: with no env vars the wedge is still `PC=3FDCE400 samples=199`, and the -handshake config (`MODEL=handshake BUSY_TICKS=2 SWITCH=0640000D:70:70`) still advances -to 56 distinct PCs by frame 600. - -## Register model (2026-10-16) - -### The model is now configurable, per window - -`VT1200_BOARD_BUSY_W2`/`_W3` set the busy count for `0x06200000`/`0x06400000` -individually, and `VT1200_BOARD_DMA_TICKS` sets the completion time of the -`0x05800000`/`0x05900000` DMA pair. All three are **modifiers** of -`VT1200_BOARD_MODEL=handshake`, applied after `VT1200_BOARD_BUSY_TICKS` so a -per-window value wins for its window; like `BUSY_TICKS`/`SWITCH` they need one -of the on-switches (`MODEL`/`LOG`/`MAP`/`DEFAULT`) to map the windows at all. - -The three knobs provably reach the model — the `-log` line reports the parsed -values (`busy_w2=3 busy_w3=4 dma_ticks=5`), and a differential run shows they -change behaviour: `BUSY_TICKS=0` alone → stuck at `PC=3FDCE400`, 5 distinct; -`BUSY_TICKS=0 BUSY_W2=1` → 56 distinct at `PC=3F208950`; `BUSY_TICKS=0 -BUSY_W3=1` → still `PC=3FDCE400`, 5 distinct. So the knobs are wired *and* the -critical handshake is isolated to the `0x06200000` window: arming only WIN2 -leaves gate 1, arming only WIN3 does not. - -Raw runs, verbatim: - -``` -$ VT1200_BOARD_MODEL=handshake VT1200_BOARD_BUSY_TICKS=0 \ -$ VT1200_BOARD_SWITCH="0640000D:70:70" \ -$ COV_FRAMES=600 COV_WINDOW=300 timeout 600 $M vt1200 -rompath harness/mame/roms -video none \ -$ -sound none -nothrottle -debug -debugger none -autoboot_script harness/mame/coverage.lua -autoboot_delay 0 2>&1 \ -$ | grep -E "^frame" | tail -1 -BUSY_TICKS=0 -> frame 600 new= 2 total_distinct= 5 PC=3FDCE400 - -$ VT1200_BOARD_MODEL=handshake VT1200_BOARD_BUSY_TICKS=0 VT1200_BOARD_BUSY_W2=1 \ -$ VT1200_BOARD_SWITCH="0640000D:70:70" \ -$ COV_FRAMES=600 COV_WINDOW=300 timeout 600 $M vt1200 -rompath harness/mame/roms -video none \ -$ -sound none -nothrottle -debug -debugger none -autoboot_script harness/mame/coverage.lua -autoboot_delay 0 2>&1 \ -$ | grep -E "^frame" | tail -1 -BUSY_TICKS=0 BUSY_W2=1 -> frame 600 new= 2 total_distinct= 56 PC=3F208950 - -$ VT1200_BOARD_MODEL=handshake VT1200_BOARD_BUSY_TICKS=0 VT1200_BOARD_BUSY_W3=1 \ -$ VT1200_BOARD_SWITCH="0640000D:70:70" \ -$ COV_FRAMES=600 COV_WINDOW=300 timeout 600 $M vt1200 -rompath harness/mame/roms -video none \ -$ -sound none -nothrottle -debug -debugger none -autoboot_script harness/mame/coverage.lua -autoboot_delay 0 2>&1 \ -$ | grep -E "^frame" | tail -1 -BUSY_TICKS=0 BUSY_W3=1 -> frame 600 new= 2 total_distinct= 5 PC=3FDCE400 -``` - -### Sweep: `BUSY_W2` in {1,2,4,8} - -`W3=2`, `DMA=2`, `MODEL=handshake`, `SWITCH=0640000D:70:70`, -`coverage.lua` at 900 frames / 300-frame window: - -| `BUSY_W2` | `total_distinct` @ f900 | frame-900 `PC` | -|---|---|---| -| 1 | 59 | `3F208B20` | -| 2 | 59 | `3F208B20` | -| 4 | 59 | `3F208B20` | -| 8 | 59 | `3F208B20` | - -Raw run, verbatim (cwd `harness/mame`-repo root, -`M=/home/arti/code/k-space/mame0289/vt1200`): - -``` -$ for N in 1 2 4 8; do -$ printf "W2=%s -> " "$N" -$ VT1200_BOARD_MODEL=handshake VT1200_BOARD_BUSY_W2=$N VT1200_BOARD_BUSY_W3=2 \ -$ VT1200_BOARD_DMA_TICKS=2 VT1200_BOARD_SWITCH="0640000D:70:70" \ -$ COV_FRAMES=900 COV_WINDOW=300 timeout 600 $M vt1200 -rompath harness/mame/roms -video none \ -$ -sound none -nothrottle -debug -debugger none -autoboot_script harness/mame/coverage.lua -autoboot_delay 0 2>&1 \ -$ | grep -E "^frame" | tail -1 -$ done -W2=1 -> frame 900 new= 3 total_distinct= 59 PC=3F208B20 -W2=2 -> frame 900 new= 3 total_distinct= 59 PC=3F208B20 -W2=4 -> frame 900 new= 3 total_distinct= 59 PC=3F208B20 -W2=8 -> frame 900 new= 3 total_distinct= 59 PC=3F208B20 -``` - -**The sweep is flat.** No `BUSY_W2` value moves the firmware further than any -other, and none leaves the park loop. The reason is structural, not a bug: -`tick()` runs **once per frame** (last visible scanline), while the gate polls -are tight in-frame `DSJS`/branch spins. Any `n >= 1` arms the bit for the whole -frame and clears it at the next frame boundary, so `1`, `2`, `4` and `8` are -indistinguishable; only `0` differs (it never arms, and the firmware stays at -the gate-1 poll). **For this firmware the busy *presence* matters; the busy -*duration* is not a tunable.** - -The other two knobs are also inert at this stall, which rules them out as the -missing input: - -| config | `total_distinct` @ f900 | frame-900 `PC` | -|---|---|---| -| boundary (`W2=2 W3=2 DMA=2`) | 59 | `3F208B20` | -| `DMA=0` | 59 | `3F208B20` | -| `DMA=64` | 59 | `3F208B20` | -| `W3=0` | 59 | `3F208B20` | - -### Where the firmware actually sits - -`progress.lua` at 900 frames on the boundary config (59 distinct PCs) shows the -loop: `PC=3F2088B0` (414 samples, from frame 91), then `3F208A80` (138, from -f671), `3F208950` (138, f505), `3F208B20` (78, f809), `3F2089F0` (28, f574), -`3F208BC0` (14, f878). That is bit `0x3F2088B0`–`0x3F208BC0` = **file -`0x1116`–`0x1178`**, in the *reset/POST bank*, and it is not the display/blit -loop. The access log from the same config shows exactly what that loop does: - -``` -W 0640000D pc=3F208830 val=0000 -W 0640000F pc=3F208880 val=00FF -R 0640000E pc=3F208920 val=0001 -R 0640000F pc=3F2089C0 val=00FF -``` - -i.e. it pokes `0x0640000D`, pokes `0x0640000F`, then reads `0x0640000E`/`F`, and -spins — the POST video/refresh delay routine already identified in gate 4. New -PCs are still appearing as late as frame 878, so it is *cycling*, not frozen, -but over a tiny footprint and long past the point the real machine is in -level 1. - -### `INTENB` — still never non-zero - -`intr.lua`, boundary config, 900 frames, verbatim: - -``` -$ VT1200_BOARD_MODEL=handshake VT1200_BOARD_BUSY_W2=2 VT1200_BOARD_BUSY_W3=2 \ -$ VT1200_BOARD_DMA_TICKS=2 VT1200_BOARD_SWITCH="0640000D:70:70" \ -$ INT_FRAMES=900 INT_EVERY=300 timeout 600 $M vt1200 -rompath harness/mame/roms -video none \ -$ -sound none -nothrottle -debug -debugger none -autoboot_script harness/mame/intr.lua -autoboot_delay 0 2>&1 \ -$ | grep -E "CHANGED|distinct|INTENB=" -f 1 INTENB CHANGED -> 0000 (DI=0) -f 1 INTPEND CHANGED -> 0000 -f 2 INTPEND CHANGED -> 0400 -f 300 DPYCTL=E008 INTENB=0000(DI=0) INTPEND=0400 DI=1 DPYINT=0000 VCOUNT=0383 VTOTAL=0384 HTOTAL=004F HSBLNK=004C -f 600 DPYCTL=E008 INTENB=0000(DI=0) INTPEND=0400 DI=1 DPYINT=0000 VCOUNT=0383 VTOTAL=0384 HTOTAL=004F HSBLNK=004C -f 900 DPYCTL=E008 INTENB=0000(DI=0) INTPEND=0400 DI=1 DPYINT=0000 VCOUNT=0383 VTOTAL=0384 HTOTAL=004F HSBLNK=004C -distinct INTENB values seen: - INTENB=0000 DI=0 (900 frames) -``` - -`INTENB` never becomes non-zero, so the level-0 boundary is **not** crossed: -the firmware has still not reached interrupt setup. `INTPEND=0400` (display IRQ -pending) throughout confirms MAME is generating the interrupt and the firmware -simply has not enabled it. **No busy window and no DMA timing changes this** — -the model is still missing an input. - -### Which window/offset still looks unmodelled - -1. **`0x5600000` (byte `0xAC0000`) — not mapped at all.** An independent sweep - of the address space (`reference/memory-space-map.md`, row 4) found this - block touched ~**110 times** with offsets `+0x00 +0x10 +0x20 +0x30 +0x80 - +0x100 +0x200 +0x380`, on the **display / X-server init** path (writer near - the `no screens found` strings). It is the most-touched region the driver - does not model, and it is a prime suspect. Caveat: its writer is on the - X-server init path, which this firmware has **not reached** (it is still in - level 0 POST), so it is a plausible *future* blocker as much as a current - one. Modelling it is explicitly out of this task's scope. -2. **`0x0640000E` / `0x0640000F` — read in the park loop, modelled only as - write-back latches.** The loop reads them expecting some hardware state; the - model returns the last value written. That is the narrowest "missing input" - at the current park point. -3. **`0x0640000D` bit 2** (gate 4's "graphics/blit complete") is still not - modelled — only bits 4-6 (the monitor switch) are. -4. Rows 7 and 10 of the memory-space map — candidate NVRAM at `0x6000000` and - the Ethernet MAC ROM at `0x6800000` — are unmapped; level 0's LANCE test and - level 1's NVRAM test sit behind them. - -### Verdict - -The configurable model is in place and the knobs are wired and effective, but -the sweep is honestly **flat**: the busy *duration*, the W3 busy count and the -DMA completion time do not move the firmware. The model is still missing an -input, and the strongest candidates are the entirely-unmapped `0x5600000` block -and the `0x0640000E`/`F` read semantics. No claimed improvement is made this -round. +FIX THE INSTRUMENTATION FIRST (see above), then model video memory. diff --git a/harness/mame/cal.lua b/harness/mame/cal.lua new file mode 100644 index 0000000..656045d --- /dev/null +++ b/harness/mame/cal.lua @@ -0,0 +1,42 @@ +-- CALIBRATION: settle the address-unit conventions once and for all. +-- +-- Four different conventions have been in play and I keep tripping on them: +-- 1. the firmware's own operands (bit addresses, e.g. @0x64000D0) +-- 2. the driver's address-map units (BOARD_BASE 0x06400000 + offset) +-- 3. the catch-all log's addresses (it printed 0x400000..0x40D7FF) +-- 4. the debugger's wpset/bpset units +-- +-- The access log PROVES the firmware hits the window at driver address +-- 0x0620000D. So: watch that, and also the firmware's operand for it, and see +-- which one fires and what wpaddr reports. Whatever fires calibrates the rest. + +local machine = manager.machine +local cpu = machine.devices[':maincpu'] +local dbg = machine.debugger + +if not dbg then + print("ERROR: debugger not enabled; pass -debug") + machine:exit() + return +end + +-- A: the driver-map address (proven hit by VT1200_BOARD_LOG) +dbg:command('wpset 0620000d,10,rw,{ printf "CAL-A wpaddr=%08X data=%04X acc=%02X pc=%08X\\n", wpaddr, wpdata, wpaccess, pc ; g }') +-- B: the firmware operand (bit address as printed in disassembly) +dbg:command('wpset 6200010,10,rw,{ printf "CAL-B wpaddr=%08X data=%04X acc=%02X pc=%08X\\n", wpaddr, wpdata, wpaccess, pc ; g }') +-- C: the same value shifted the other way +dbg:command('wpset 00620000,10,rw,{ printf "CAL-C wpaddr=%08X data=%04X acc=%02X pc=%08X\\n", wpaddr, wpdata, wpaccess, pc ; g }') +print("calibration watchpoints armed: A=0x0620000d B=0x6200010 C=0x00620000") + +local maxframes = tonumber(os.getenv("CAL_FRAMES") or "200") +local n = 0 +emu.register_frame_done(function() + n = n + 1 + if n >= maxframes then + print("done (" .. n .. " frames)") + machine:exit() + end +end, "vt1200-cal") + +dbg.visible_cpu = cpu +dbg.execution_state = 'run' diff --git a/harness/mame/wp2.lua b/harness/mame/wp2.lua new file mode 100644 index 0000000..6cddb2a --- /dev/null +++ b/harness/mame/wp2.lua @@ -0,0 +1,35 @@ +-- Watch the video-memory test's accesses with their VALUES, to see what it +-- expects versus what it gets. The test reports failing at bit address +-- 0x40000C and walks in 0x400000 steps. +-- +-- WP2_FRAMES=400 vt1200 ... -autoboot_script harness/mame/wp2.lua + +local machine = manager.machine +local cpu = machine.devices[':maincpu'] +local dbg = machine.debugger + +if not dbg then + print("ERROR: debugger not enabled; pass -debug") + machine:exit() + return +end + +-- Watch a window around both candidate locations for the video memory +-- (the reported failing address is 0x40000C; the driver-side catch-all saw +-- traffic at 0x400000-0x40D7FF). +dbg:command('wpset 400000,1000,rw,{ printf "WP-A %08X data=%04X acc=%02X pc=%08X\\n", wpaddr, wpdata, wpaccess, pc ; g }') +dbg:command('wpset 4000000,1000,rw,{ printf "WP-B %08X data=%04X acc=%02X pc=%08X\\n", wpaddr, wpdata, wpaccess, pc ; g }') +print("watchpoints armed: A=0x400000..0x400FFF B=0x4000000..0x4000FFF") + +local maxframes = tonumber(os.getenv("WP2_FRAMES") or "300") +local n = 0 +emu.register_frame_done(function() + n = n + 1 + if n >= maxframes then + print("done (" .. n .. " frames)") + machine:exit() + end +end, "vt1200-wp2") + +dbg.visible_cpu = cpu +dbg.execution_state = 'run'