harness: identify the level-1 video-memory test that raises code 0x100 (VIDEO MEMORY-22)

The dispatch status is 0x100 (printed as '22' in the message). A driver-side
transparent low-RAM write log (VT1200_LOWLOG) shows the slot at bit 0x16F0 is
written by the store at 3FDF9090 from a13(0x10), which L3FA7C050 sets to the
literal 0x100 when the memory test L3F2018B0 returns non-zero. L3F2018B0 is the
level-1 video-memory test (base bit 0x04000000, count 0x20000). Its failing
checks: the 16-bit walking-bit read-back at 0x04000000 (baseline, unmapped) and,
with video RAM mapped, the REFCNT (0xC00001F0) 'must change' check.
This commit is contained in:
SDD committed 2026-10-16 10:55:44 +03:00
1 parent f46b4a44a4
commit 5ef8712960
7 files changed
+596

No files matched your search

+349
View File
@@ -0,0 +1,349 @@
# CODE 22 — which level-1 test raises it, and why
Bounded investigation, 2026-10-16. Subagent task: identify the level-1 test that
raises the "VIDEO MEMORY - 22" status and the condition that raises it. The task
is **identification only** — level 1 is not made to pass.
Driver: `harness/mame/vt1200.cpp` (env-gated additions, see §7).
MAME: `/home/arti/code/k-space/mame0289`. ROM: `harness/mame/roms/vt1200/vt1200_v22.bin`.
---
## 0. Answer in one paragraph
The failing test is the **level-1 video-memory self-test at bit `0x3F2018B0`**
(file `0x000316`, Ghidra `0x07E40316`), which the level-1 runner calls with the
screen-memory base **bit `0x04000000`** (arg3) and a count of **`0x20000`**
(arg4). It does a 16-bit walking-bit data-bus check on the first word at
`0x04000000`, then a 32-bit pattern check across the region, then a DRAM
refresh-counter check. When any of these fails it returns a non-zero status
(`0x19` = walking-bit / `0x0E` = REFCNT in the runs below). The caller at
`0x3FA7C050` turns *any* non-zero return into the literal constant **`0x100`**
(at bit `0x3FA7DE30`, file `0x10FBC6`). That `0x100` is written into the frame
slot `a13-0x20` (bit `0x16F0`) by the store at bit `0x3FDF9090`, and the error
dispatcher at bit `0x3FDF0630` maps **`0x100` → handler `0x3FDE7F00`**
(Ghidra `0x07FBCFE0`), which prints the string `LEVEL1/2 ERROR -> VIDEO MEMORY - 22`.
**Note on the number:** the dispatcher's *status code* for this error is
`0x100`; `22` is a constant in the message string, as the bring-up log already
recorded for `0x40000C`. So "status code 22" = dispatcher code `0x100`.
The condition that makes the test fail depends on which hardware is modelled:
* **Default (committed) driver, no video RAM mapped** — the walking-bit check
fails: writes of `1,2,4,…,0x8000` to bit `0x04000000` read back `0`.
* **With video RAM mapped (`VT1200_VRAM=1`)** — the walking-bit and pattern
checks now pass; the failure moves to the **DRAM refresh-counter (REFCNT)
check**: `REFCNT` (`0xC00001F0`) returns the same value (`0x0E74`) on two
consecutive reads, which the firmware treats as an error.
So the *residual* condition — the one that still holds after "video memory is
mapped and round-trips", which is exactly the state the bring-up log describes —
is the **REFCNT check**.
---
## 1. Method
The stack-walk approach in `dispatch_walk.lua` only sampled 8 stack words and
reached the reporting framework, so it was replaced with a **write watch on the
status slot**, done driver-side (no watchpoints — `wpset` is a dead end here).
### 1a. The dispatcher reads a 32-bit code from bit `0x16F0`
`harness/mame/code22_probe.lua`, run with the level-1 config
(`VT1200_BOARD_MODEL=handshake VT1200_BOARD_BUSY_TICKS=2
VT1200_BOARD_SWITCH=0640000D:70:70`), breaks at the dispatcher read and at the
next instruction:
```
AT-READ pc=3FDF0630 a13=00001710 a14=00001770 a8=00000001 d@16F0=0000FFFF
AT-NEXT pc=3FDF0650 a8=0000FFFF d@16F0=0000FFFF
AT-READ pc=3FDF0630 a13=00001710 a14=00001770 a8=00000100 d@16F0=00000100
AT-NEXT pc=3FDF0650 a8=00000100 d@16F0=00000100
```
Two facts fall out:
* A `bpset` fires **before** its instruction executes (the `a8` at `AT-READ`
is stale; the loaded code is the value at `AT-NEXT`).
* The code the dispatcher reads is the dword at bit `a13-0x20` = `0x16F0`.
Hit 1 = `0xFFFF` → handler `0x3FDEFF60` prints `LEVEL1/2 TESTING **`;
hit 2 = `0x100` → handler `0x3FDE7F00` prints `LEVEL1/2 ERROR -> VIDEO MEMORY - 22`.
The two hits and their order match the captured console transcript exactly.
Confirming the switch arithmetic with `dis34` (its `subi` operand is the amount
actually subtracted, unlike Ghidra's rendering):
```
3fdf07e0: subi 0x78, a8 ; jaeq L3FDE7A00 ; code 0x080
3fdf0820: subi 0x80, a8 ; jaeq L3FDE7F00 ; code 0x100 <- VIDEO MEMORY-22
3fdf0860: subi 0x100,a8 ; jaeq L3FDE8150 ; code 0x200
...
3fdf0a20: subi 0x7fff,a8; jaeq L3FDEFF60 ; code 0xFFFF <- LEVEL1/2 TESTING
```
### 1b. Driver-side write watch finds the writer
New env-gated knob `VT1200_LOWLOG=<path>`: a **transparent** logging proxy over
bit `0x0000-0x3FFF` that forwards reads/writes to the real `mainram` share and
records every distinct `(addr, pc, value, kind)`. It does not perturb execution
(regression verified, §6).
Command:
```sh
M=/home/arti/code/k-space/mame0289/vt1200
VT1200_BOARD_MODEL=handshake VT1200_BOARD_BUSY_TICKS=2 \
VT1200_BOARD_SWITCH="0640000D:70:70" VT1200_LOWLOG=/tmp/opencode/code22/low.log \
CP_FRAMES=300 timeout 300 $M vt1200 -rompath harness/mame/roms \
-video none -sound none -nothrottle -debug -debugger none -debuglog \
-autoboot_script harness/mame/code22_probe.lua -autoboot_delay 0
```
Relevant lines (the only writer of `0x100` to the status slot):
```
W 001590 pc=3FDF8BF0 val=0100
R 001590 pc=3FDF8D80 val=0100
R 001590 pc=3FDF8FB0 val=0100
R 001590 pc=3FDF90B0 val=0100
W 0016F0 pc=3FDF90B0 val=0100 <-- the status slot is loaded here
R 0016F0 pc=3FDF0630 val=0100 <-- the dispatcher reads it
R 0016F0 pc=3FDF0650 val=0100
```
(`pc` is reported as the *next* instruction, i.e. after the displacement word;
`pc=3FDF90B0` is the store at `0x3FDF9090`, `pc=3FDF8BF0` is the store at
`0x3FDF8BD0`.)
---
## 2. The call chain, bottom to top
Bits and file offsets:
| bit | file | what |
|---|---|---|
| `3FDF0630` | `0x17E0C6` | dispatcher reads code from `a13-0x20` (Ghidra `07FBE0C6`) |
| `3FDE7F00` | `0x17CFE0` | VIDEO MEMORY-22 handler (Ghidra `07FBCFE0`) |
| `3FDF9090` | `0x17F212` | `move *a13(0x10), *a14+, 1` — loads status into the dispatch frame |
| `3FDF8FB0` | `0x17F1F6` | `jaeq L3FDF86B0` — "if result == 0, retry" |
| `3FDF8BD0` | `0x17F17A` | `move a8, *a13(0x10), 1` — stores the test's return value |
| `3FDF8BA0` | `0x17F174` | `calla L3FA7C050` — the sub-test |
| `3FA7DE30` | `0x10FBC6` | `movi 0x100, a7` — turns any failure into code 0x100 |
| `3FA7DD70` | `0x10FBAE` | `calla L3F2018B0` — the memory test |
| `3F2018B0` | `0x000316` | the level-1 video-memory test |
| `3F201C10` | `0x000382` | walking-bit read-back |
| `3F201C20` | `0x000384` | walking-bit compare (`xor`) |
| `3F201C80` | `0x000390` | walking-bit failure exit |
| `3F201E90` | `0x0003D2` | REFCNT read |
| `3F201ED0` | `0x0003DA` | REFCNT compare / error branch |
| `3F2070C0` | `0x000E18` | REFCNT error handler (`movi 0xe,a8`) |
| `3F208060` | `0x00100C` | restart entry (`DINT`) the task referenced |
### 2a. The runner decides the code (bit `0x3FDF8A90` region)
```
3fdf8ba0: calla L3FA7C050
3fdf8bd0: move a8, *a13(0x10), 1 ; store the sub-test's return value
...
3fdf8f90: move *a13(0x10), a11, 1
3fdf8fb0: jaeq L3FDF86B0 ; 0 => no error, loop back
3fdf8fd0: sub a9, a9
...
3fdf9090: move *a13(0x10), *a14+, 1 ; pass it on as the error code
3fdf90b0: calla L3FDE55F0 ; -> report/dispatch
```
### 2b. `L3FA7C050` maps any non-zero failure to `0x100`
```
3fa7dd70: calla L3F2018B0
3fa7dda0: move a8, a8
3fa7ddb0: jreq L3FA7DEA0 ; 0 => continue
...
3fa7de30: movi 0x100, a7 ; <-- the literal status code
3fa7de50: move a7, *a11(0x40), 1
3fa7de70: move a7, a8
3fa7de80: jauc L3FA900A0 ; return 0x100
```
So the internal status code is set at bit `0x3FA7DE30` (file `0x10FBC6`), and
the condition is simply **"the memory test returned non-zero"**.
### 2c. `L3F2018B0` is the level-1 video-memory test
Loaded arguments (breakpoint at `0x3F201990`):
```
ARGS a13=00000000 a7=00000000 a3=04000000 a4=00020000
```
* `a3 = 0x04000000` — the walking-bit / pattern target base (screen memory).
* `a4 = 0x00020000` — iteration count.
Walking-bit loop (16 iterations, single word at `a5 = a3`):
```
3f201b20: movi 0x1, a8
3f201b60: movi 0x10, a9
L3F201B80:
3f201b80: clr a11
3f201b90: move a11, *a5, 0 ; write 0
3f201ba0: move a8, *a5, 0 ; write walking bit
(nops)
3f201c10: move *a5, a11, 0 ; read back
3f201c20: xor a8, a11 ; a11 = read ^ expected
3f201c30: or a11, a12 ; accumulate failures
3f201c40: sll 0x1, a8
3f201c50: dsjs a9, L3F201B80
3f201c60: or a12, a12
3f201c70: jreq L3F201CF0
3f201c80: move a12, *a13(0x100), 1 ; <-- FAILURE exit
3f201ca0: movi 0x19, a8 ; status 25
3f201cd0: jauc L3F207200 ; return
```
DRAM refresh-counter check (later in the same routine):
```
3f201e30: movi 0xea60, a3
3f201e60: move @0xc00001f0, a0, 0 ; REFCNT
L3F201E90:
3f201e90: move @0xc00001f0, a6, 0 ; REFCNT again
3f201ec0: cmp a0, a6
3f201ed0: jaeq L3F2070C0 ; equal => FAILURE exit
3f201ef0: move a6, a0
3f201f00: dsjs a3, L3F201E90
...
3f2070c0: movi 0xe, a8 ; status 14
3f2070f0: move a2, a1
3f207100: jruc L3F207200 ; return
```
`L3F207200` is the common error return: it stores `a8` into `a13(0xc0)` /
`a13(0x40)`, then at `3f2073d0` `or a8,a8; jreq L3F207510` returns `a8`
non-zero. (When `sp == 0` it instead `jauc L3F208060`, the `DINT` restart entry.)
---
## 3. Observed failures (raw evidence)
`harness/mame/code22_final.lua`, 300 frames.
**Default driver (no video RAM mapped):**
```
ARGS a13=00000000 a7=00000000 a3=04000000 a4=00020000
WALK-CMP read=00000000 expected=00000001
WALK-CMP read=00000000 expected=00000002
... (every bit reads 0) ...
WALK-FAIL a12=0000FFFF a8=00010000 a11=00008000
MEMTEST-RET a8=00000019
C050-RET-0x100 a8=00000019
```
**With `VT1200_VRAM=1` (video RAM mapped 0x04000000-0x1fffffff):**
```
ARGS a13=00000000 a7=00000000 a3=04000000 a4=00020000
WALK-CMP read=00000001 expected=00000001
WALK-CMP read=00000002 expected=00000002
... (every walking bit reads back correctly) ...
REFCNT-CMP a0=00000E74 a6=00000E74
MEMTEST-RET a8=0000000E
C050-RET-0x100 a8=0000000E
```
(i.e. `a0 == a6` — REFCNT did **not** change between the two reads.)
Additional checks with a targeted failure-branch probe
(`harness/mame/code22_fail.lua`, breakpoints on each error exit of
`L3F2018B0`):
* no-VRAM run: only `WALKING-BIT-MISMATCH` → `L3F207200(walkerr)` →
`L3F2073D0` → `L3FA7DE30`.
* `VT1200_VRAM=1` run: only `HANDLER-3F2070C0-REFCNT-eq` → `L3F207200(a8=0x0e)`
→ `L3F2073D0` → `L3FA7DE30`; the walking-bit branch never fires.
---
## 4. What the condition actually is
The video-memory test requires, in order:
1. **A readable/writable region at bit `0x04000000`.** The 32-bit pattern phase
walks `a4 = 0x20000` dwords with post-increment — i.e. **512 KiB** of video
memory at bit `0x04000000`. With no mapping, reads return 0 and the very
first walking-bit compare fails (`a12=0xFFFF`, all 16 bits wrong).
2. **The DRAM refresh counter `REFCNT` (`0xC00001F0`) to change between two
consecutive reads.** MAME returns `(total_cycles()/16) & 0xfffc`, which is
unchanged across the ~2 instructions between `0x3F201E60` and `0x3F201E90`
(both read `0x0E74`). The firmware treats "unchanged" as an error
(`jaeq L3F2070C0`, status `0x0E`).
Both produce the same outcome: `L3F2018B0` returns non-zero →
`L3FA7C050` returns literal `0x100` → dispatcher code `0x100` →
`LEVEL1/2 ERROR -> VIDEO MEMORY - 22`.
**Reconciling with the bring-up log's claim** ("video memory is mapped and
round-trips, yet the test still fails"): reproduced. With
`VT1200_VRAM=1` the walking-bit and pattern checks pass and the failure is
*purely* the REFCNT check. That is the remaining requirement.
Confidence:
* The chain (dispatcher code 0x100 → handler 0x3FDE7F00; writer of the slot at
`0x3FDF9090`; source `L3FA7C050`; `movi 0x100` at `0x3FA7DE30`; memory test
`L3F2018B0`; branch that fires) — **high**, all measured, both directions.
* The walking-bit condition (reads return 0 when the region is unmapped) —
**high**.
* The REFCNT condition (two consecutive `0xC00001F0` reads equal → error) —
**high** as observed behaviour; the *interpretation* that this is the
intended hardware requirement (vs. a MAME REFCNT granularity artifact) is
**medium**.
---
## 5. What I could not determine
* The exact original hardware REFCNT tick rate, i.e. whether the firmware's
"must change between two consecutive reads" is satisfiable on real hardware
and merely too slow in MAME. MAME's `REFCNT` is `(total_cycles()/16) & 0xfffc`
(`src/devices/cpu/tms34010/tms34010.cpp:1573`).
* Whether the status slot at bit `0x16F0` is a true global or the frame slot
`a13-0x20` of the `L3FDE55F0` dispatch frame. `a13` is a constant `0x1710` at
the dispatcher in every observed run, and `*a13(0xffe0)` is the standard
first-argument access (7325 read sites), so the slot behaves as a fixed
address either way.
* Which level-1 test *number* (service-guide ordering) `L3FA7C050` corresponds
to beyond "video memory": it is the routine that calls the memory test on the
`0x04000000` region and reports `VIDEO MEMORY`.
---
## 6. Regression
With no `VT1200_*` env var set, `progress.lua` at 200 frames still prints:
```
PC=3FDCE400 samples=199
```
All new instrumentation is env-gated (`VT1200_LOWLOG`). The proxy is
transparent (forwards to the real `mainram` share), so even with `VT1200_LOWLOG`
set the boot path is unchanged.
---
## 7. Files added / changed
* `harness/mame/vt1200.cpp` — added `VT1200_LOWLOG=<path>`: a transparent,
env-gated logging proxy over bit `0x0000-0x3FFF` (forwards to `mainram`).
* `harness/mame/code22_walk.lua` — dump registers + stack at the dispatcher.
* `harness/mame/code22_probe.lua` — resolve before/after semantics + slot value.
* `harness/mame/code22_fail.lua` — break on each `L3F2018B0` error exit.
* `harness/mame/code22_args.lua` — print memory-test args + walking-bit/REFCNT values.
* `harness/mame/code22_final.lua` — compact evidence capture for this report.
+33
View File
@@ -0,0 +1,33 @@
-- code22_args.lua
--
-- At the memory test L3F2018B0, print the four loaded arguments and, at its
-- walking-bit failure compare, the pointer a5 and the values involved.
--
-- CA_FRAMES=300 <mame> vt1200 ... -autoboot_script harness/mame/code22_args.lua
local machine = manager.machine
local cpu = machine.devices[':maincpu']
local dbg = machine.debugger
if not dbg then print("ERROR: no debugger"); machine:exit(); return end
dbg:command('bpset 3f201990,,{ printf "MEMTEST-ARGS a13=%08X a7=%08X a3=%08X a4=%08X a14=%08X\\n", a13, a7, a3, a4, a14 ; g }')
dbg:command('bpset 3f201ba0,,{ printf "WALK-WRITE a5=%08X a8=%08X\\n", a5, a8 ; g }')
dbg:command('bpset 3f201c10,,{ printf "WALK-READ a5=%08X a11=%08X expect=%08X\\n", a5, a11, a8 ; g }')
dbg:command('bpset 3f201c80,,{ printf "WALK-FAIL a5=%08X a12=%08X a8=%08X a11=%08X\\n", a5, a12, a8, a11 ; g }')
dbg:command('bpset 3f201e60,,{ printf "REFCNT-INIT a0=%08X\\n", a0 ; g }')
dbg:command('bpset 3f201e90,,{ printf "REFCNT-READ a6=%08X\\n", a6 ; g }')
dbg:command('bpset 3f201ed0,,{ printf "REFCNT-CMP a0=%08X a6=%08X\\n", a0, a6 ; g }')
print("breakpoints armed")
local maxframes = tonumber(os.getenv("CA_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-ca")
dbg.visible_cpu = cpu
dbg.execution_state = 'run'
+45
View File
@@ -0,0 +1,45 @@
-- code22_fail.lua
--
-- L3FA7C050 returns code 0x100 (-> VIDEO MEMORY-22) when the memory test
-- L3F2018B0 returns nonzero. Find which failure branch inside L3F2018B0 is
-- taken by breaking on each of its error exits.
--
-- CF_FRAMES=300 <mame> vt1200 ... -autoboot_script harness/mame/code22_fail.lua
local machine = manager.machine
local cpu = machine.devices[':maincpu']
local dbg = machine.debugger
if not dbg then print("ERROR: no debugger"); machine:exit(); return end
-- address -> literal label (embedded in the printf, no %s)
local targets = {
{ "3f201c80", "WALKING-BIT-MISMATCH" },
{ "3f207080", "HANDLER-3F207080-a2eqa0" },
{ "3f2064c0", "HANDLER-3F2064C0-a2nea0" },
{ "3f2070c0", "HANDLER-3F2070C0-REFCNT-eq" },
{ "3f207050", "HANDLER-3F207050-a1eqa0" },
{ "3f207180", "HANDLER-3F207180-a2nea0-2" },
{ "3f206910", "HANDLER-3F206910-a1nea0" },
{ "3f207200", "HANDLER-3F207200-walkerr" },
{ "3f2073d0", "HANDLER-3F2073D0-spne0" },
{ "3fa7de30", "L3FA7C050-RET-0x100" },
}
for _, t in ipairs(targets) do
dbg:command(string.format(
'bpset %s,,{ printf "%s pc=%%08X a8=%%08X a9=%%08X a11=%%08X\\n", pc, a8, a9, a11 ; g }',
t[1], t[2]))
end
print("breakpoints armed: " .. #targets)
local maxframes = tonumber(os.getenv("CF_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-cf")
dbg.visible_cpu = cpu
dbg.execution_state = 'run'
+37
View File
@@ -0,0 +1,37 @@
-- code22_final.lua -- clean evidence capture for CODE22_REPORT.md
--
-- Prints, in order: the memory test's loaded args, the walking-bit read vs
-- expected, the walking-bit failure exit, the DRAM refresh-counter compare,
-- the memory test's return value, and L3FA7C050's final return.
--
-- CFF_FRAMES=300 <mame> vt1200 ... -autoboot_script harness/mame/code22_final.lua
local machine = manager.machine
local cpu = machine.devices[':maincpu']
local dbg = machine.debugger
if not dbg then print("ERROR: no debugger"); machine:exit(); return end
local function bp(addr, fmt, regs)
dbg:command(string.format('bpset %s,,{ printf "%s\\n", %s ; g }', addr, fmt, regs))
end
bp("3f201990", "ARGS a13=%08X a7=%08X a3=%08X a4=%08X", "a13, a7, a3, a4")
bp("3f201c20", "WALK-CMP read=%08X expected=%08X", "a11, a8")
bp("3f201c80", "WALK-FAIL a12=%08X a8=%08X a11=%08X", "a12, a8, a11")
bp("3f201ed0", "REFCNT-CMP a0=%08X a6=%08X", "a0, a6")
bp("3fa7dda0", "MEMTEST-RET a8=%08X", "a8")
bp("3fa7de30", "C050-RET-0x100 a8=%08X", "a8")
print("breakpoints armed")
local maxframes = tonumber(os.getenv("CFF_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-cff")
dbg.visible_cpu = cpu
dbg.execution_state = 'run'
+32
View File
@@ -0,0 +1,32 @@
-- code22_probe.lua
--
-- Resolve two mechanics at the level-1 error dispatcher:
-- (1) does a bpset fire BEFORE or AFTER its instruction executes?
-- (2) what 32-bit value does the dispatcher read from *a13(0xffe0)?
--
-- We break at 0x3FDF0630 (the read) and at 0x3FDF0650 (the next instruction,
-- jreq) and print a8 / a13 / the dword at the read address.
--
-- CP_FRAMES=300 <mame> vt1200 ... -autoboot_script harness/mame/code22_probe.lua
local machine = manager.machine
local cpu = machine.devices[':maincpu']
local dbg = machine.debugger
if not dbg then print("ERROR: no debugger"); machine:exit(); return end
dbg:command('bpset 3fdf0630,,{ printf "AT-READ pc=%08X a13=%08X a14=%08X a8=%08X d@16F0=%08X\\n", pc, a13, a14, a8, d@16F0 ; g }')
dbg:command('bpset 3fdf0650,,{ printf "AT-NEXT pc=%08X a8=%08X d@16F0=%08X\\n", pc, a8, d@16F0 ; g }')
print("breakpoints armed")
local maxframes = tonumber(os.getenv("CP_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-cp")
dbg.visible_cpu = cpu
dbg.execution_state = 'run'
+43
View File
@@ -0,0 +1,43 @@
-- code22_walk.lua
--
-- Breaks at the level-1 error dispatcher read (bit 0x3FDF0630) and dumps:
-- * registers sp / a13 / a14 / a8 / a9
-- * the word the dispatcher reads: *a13(0xffe0) = memory at (a13 - 0x20)
-- * a window of low RAM around the alleged slot a13-0x20
-- * the whole task stack, sp .. 0x5400, one line per word
--
-- All memory addresses in the debugger are BIT addresses; a word is 0x10
-- bits apart. Only plain (unparenthesised) expressions are used: a
-- `d@(sp+20h)` action silently breaks bpset.
--
-- CW_FRAMES=300 <mame> vt1200 ... -debug -debugger none \
-- -autoboot_script harness/mame/code22_walk.lua
local machine = manager.machine
local cpu = machine.devices[':maincpu']
local dbg = machine.debugger
if not dbg then print("ERROR: no debugger"); machine:exit(); return end
local act = "{ "
.. 'printf "DISP sp=%08X a13=%08X a14=%08X a8=%08X a9=%08X a11=%08X\\n", sp, a13, a14, a8, a9, a11 ; '
.. 'printf "SLOT d@16E0=%04X d@16F0=%04X d@1700=%04X d@1710=%04X d@1720=%04X d@1730=%04X\\n", d@16E0, d@16F0, d@1700, d@1710, d@1720, d@1730 ; '
.. 'printf "S00 %04X %04X %04X %04X %04X %04X %04X %04X\\n", d@5280, d@5290, d@52A0, d@52B0, d@52C0, d@52D0, d@52E0, d@52F0 ; '
.. 'printf "S01 %04X %04X %04X %04X %04X %04X %04X %04X\\n", d@5300, d@5310, d@5320, d@5330, d@5340, d@5350, d@5360, d@5370 ; '
.. 'printf "S02 %04X %04X %04X %04X %04X %04X %04X %04X\\n", d@5380, d@5390, d@53A0, d@53B0, d@53C0, d@53D0, d@53E0, d@53F0 ; '
.. 'g }'
dbg:command("bpset 3fdf0630,," .. act)
print("breakpoint armed at 3fdf0630")
local maxframes = tonumber(os.getenv("CW_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-cw")
dbg.visible_cpu = cpu
dbg.execution_state = 'run'
+57
View File
@@ -208,6 +208,18 @@ private:
u16 unmapped_r(offs_t off);
void unmapped_w(offs_t off, u16 data);
void log_unmapped(const char *kind, u32 addr, const char *pc);
// VT1200_LOWLOG=<path>
// A *transparent* logging proxy over the bottom of main RAM (bit
// 0x00000000-0x00003fff). It reads/writes the real mainram share, so it
// does not perturb the machine; it just records every distinct
// (addr, pc, value, kind) seen. Used to catch the write that loads the
// level-1 error code into the frame slot the dispatcher reads.
FILE *m_lowlog = nullptr;
std::map<std::tuple<u32, u32, u32, u32>, u32> m_lowlog_seen; // (addr,pc,val,kind) -> count
u16 lowlog_r(offs_t off);
void lowlog_w(offs_t off, u16 data);
void log_low(char kind, u32 addr, u16 value);
};
void vt1200_state::machine_start()
@@ -313,6 +325,13 @@ void vt1200_state::machine_start()
fprintf(m_vramlog, "# kind off value pc -- video-memory accesses\n");
}
if (const char *p = getenv("VT1200_LOWLOG"))
{
m_lowlog = fopen(p, "w");
if (m_lowlog)
fprintf(m_lowlog, "# kind bitaddr pc value -- transparent low-RAM access log\n");
}
logerror("vt1200: board model=%s busy_ticks=%s busy_w2=%s busy_w3=%s dma_ticks=%s switch=%s log=%s render=%d dump=%ld\n",
getenv("VT1200_BOARD_MODEL") ? getenv("VT1200_BOARD_MODEL") : "off",
getenv("VT1200_BOARD_BUSY_TICKS") ? getenv("VT1200_BOARD_BUSY_TICKS") : "0",
@@ -397,6 +416,8 @@ void vt1200_state::machine_reset()
fflush(m_log);
if (m_unmapped_log)
fflush(m_unmapped_log);
if (m_lowlog)
fflush(m_lowlog);
}
void vt1200_state::log_unmapped(const char *kind, u32 addr, const char *pc)
@@ -430,6 +451,37 @@ void vt1200_state::unmapped_w(offs_t off, u16 data)
log_unmapped("W", (u32)off, pc);
}
// Transparent low-RAM logging proxy. It sits over bit 0x0000-0x3fff (the
// frame/argument block the self-test uses) and forwards reads/writes to the
// real mainram share, so it changes nothing about execution. Base is 0, so
// the delegate offset is (bitaddr >> 4) == the u16 index into mainram.
void vt1200_state::log_low(char kind, u32 addr, u16 value)
{
if (!m_lowlog)
return;
const u32 pc = (u32)m_maincpu->state_int(TMS34010_PC);
auto key = std::make_tuple(addr, pc, (u32)value, (u32)(u8)kind);
if (m_lowlog_seen.find(key) == m_lowlog_seen.end())
{
m_lowlog_seen.emplace(key, 1);
fprintf(m_lowlog, "%c %06X pc=%08X val=%04X\n", kind, addr, pc, value);
fflush(m_lowlog);
}
}
u16 vt1200_state::lowlog_r(offs_t off)
{
const u16 v = m_mainram[(u32)off & 0x3ff];
log_low('R', (u32)off << 4, v);
return v;
}
void vt1200_state::lowlog_w(offs_t off, u16 data)
{
m_mainram[(u32)off & 0x3ff] = data;
log_low('W', (u32)off << 4, data);
}
void vt1200_state::program_map(address_map &map)
{
// Catch-all for anything we do not model -- registered FIRST so every region
@@ -522,6 +574,11 @@ void vt1200_state::program_map(address_map &map)
map(BOARD_BASE[2], BOARD_BASE[2] + 0xffff).rw(FUNC(vt1200_state::board2_r), FUNC(vt1200_state::board2_w));
map(BOARD_BASE[3], BOARD_BASE[3] + 0xffff).rw(FUNC(vt1200_state::board3_r), FUNC(vt1200_state::board3_w));
}
// Transparent low-RAM logging proxy, registered LAST so it wins its slice
// of RAM. It forwards to the real mainram share and only records.
if (getenv("VT1200_LOWLOG"))
map(0x00000000, 0x00003fff).rw(FUNC(vt1200_state::lowlog_r), FUNC(vt1200_state::lowlog_w));
}
// MAME may hand us either an entry-relative offset (0..0xffff) or the absolute