mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
harness: configurable board model busy timings + findings
This commit is contained in:
1 parent
a2f19b2d29
commit
2d7747a20d
3 files changed
+172
-1
No files matched your search
@@ -17,6 +17,9 @@ handshake the firmware waits on and modelling it correctly.
|
||||
| `VT1200_BOARD_DEFAULT=<v>` | value for addresses with no override |
|
||||
| `VT1200_BOARD_MODEL=handshake` | write-back registers **plus a self-clearing busy bit** |
|
||||
| `VT1200_BOARD_BUSY_TICKS=<n>` | busy-bit tick count for `VT1200_BOARD_MODEL=handshake` (the busy bit is fixed to bit 0 of the `+0x10` STATUS register) |
|
||||
| `VT1200_BOARD_BUSY_W2=<n>` | busy ticks for window `0x06200000` only; applied after `BUSY_TICKS`, so it overrides it for that window |
|
||||
| `VT1200_BOARD_BUSY_W3=<n>` | busy ticks for window `0x06400000` only; likewise |
|
||||
| `VT1200_BOARD_DMA_TICKS=<n>` | completion time (ticks) of a command written to the `0x05900000` DMA command port; 0 leaves the pair as a plain latch |
|
||||
| `VT1200_BOARD_SWITCH=<addr>:<mask>:<value>` | monitor switch: bits 4-6 of `0x0640000D` read as `<value>` (raw bits, or a position 0-7) |
|
||||
| `VT1200_BOARD_RAM=1` | windows behave as plain read/write RAM |
|
||||
| `VT1200_NO_RENDER=1` | restore the old blank-everything video behaviour |
|
||||
@@ -619,3 +622,123 @@ 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.
|
||||
|
||||
### 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` |
|
||||
|
||||
**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:
|
||||
|
||||
```
|
||||
f 1 INTENB CHANGED -> 0000 (DI=0)
|
||||
f 2 INTPEND CHANGED -> 0400
|
||||
f 300 DPYCTL=E008 INTENB=0000(DI=0) INTPEND=0400 DI=1
|
||||
f 900 DPYCTL=E008 INTENB=0000(DI=0) INTPEND=0400 DI=1
|
||||
distinct INTENB values seen: 0000 (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.
|
||||
@@ -100,6 +100,28 @@ static void test_dma_pair_command_completes(void)
|
||||
CHECK(b.read(VT1200_WIN1) == 0x0000); // completed
|
||||
}
|
||||
|
||||
// The two timers the sweep tunes (window busy and DMA completion) must advance
|
||||
// on the same tick(), or a later slower timer would silently stretch the other.
|
||||
static void test_one_tick_advances_window_and_dma_timers(void)
|
||||
{
|
||||
vt1200_board b;
|
||||
b.set_model_handshake(true);
|
||||
b.set_window_busy_ticks(VT1200_WIN2, 2);
|
||||
b.set_dma_ticks(2);
|
||||
|
||||
b.write(VT1200_WIN2 + VT1200_OFF_CMD, 0x0001); // arms WIN2's busy bit
|
||||
b.write(VT1200_WIN0, 0x0000); // data port first
|
||||
b.write(VT1200_WIN1, 0x0042); // then a DMA command
|
||||
|
||||
b.tick(); // one frame: both advance
|
||||
CHECK((b.read(VT1200_WIN2 + VT1200_OFF_STATUS) & 1) == 1);
|
||||
CHECK(b.read(VT1200_WIN1) == 0x0042);
|
||||
|
||||
b.tick();
|
||||
CHECK((b.read(VT1200_WIN2 + VT1200_OFF_STATUS) & 1) == 0);
|
||||
CHECK(b.read(VT1200_WIN1) == 0x0000);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
test_default_read();
|
||||
@@ -110,6 +132,7 @@ int main(void)
|
||||
test_monitor_switch_accepts_raw_bits_or_position();
|
||||
test_switch_mix_only_when_configured();
|
||||
test_dma_pair_command_completes();
|
||||
test_one_tick_advances_window_and_dma_timers();
|
||||
std::printf(failures ? "%d FAILURE(S)\n" : "all board tests passed\n", failures);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
+26
-1
@@ -43,6 +43,18 @@
|
||||
VT1200_BOARD_RAM=1 map the windows as plain read/write RAM
|
||||
instead (writes persist and read back).
|
||||
|
||||
The remaining board knobs refine VT1200_BOARD_MODEL=handshake. They are
|
||||
modifiers, not on-switches: one of the knobs above must also be set, or
|
||||
the windows stay unmapped and they do nothing.
|
||||
|
||||
VT1200_BOARD_BUSY_TICKS=<n> busy ticks for every window (default 0).
|
||||
VT1200_BOARD_BUSY_W2=<n> busy ticks for 0x06200000 only; applied
|
||||
after BUSY_TICKS, so it overrides it.
|
||||
VT1200_BOARD_BUSY_W3=<n> busy ticks for 0x06400000 only; likewise.
|
||||
VT1200_BOARD_DMA_TICKS=<n> completion time of a command written to
|
||||
the 0x05900000 DMA command port; 0 (the
|
||||
default) leaves the pair as a plain latch.
|
||||
|
||||
See FULL_BOOT_ESTIMATE.md section 4b for measured results.
|
||||
|
||||
***************************************************************************/
|
||||
@@ -178,6 +190,16 @@ void vt1200_state::machine_start()
|
||||
if (const char *t = getenv("VT1200_BOARD_BUSY_TICKS"))
|
||||
m_board.set_busy_ticks((int)strtol(t, nullptr, 0));
|
||||
|
||||
// Per-window refinements of the busy count: W2/W3 target the two big
|
||||
// windows individually. Applied after BUSY_TICKS so they win for their
|
||||
// window, and the DMA pair's completion time is its own knob.
|
||||
if (const char *t = getenv("VT1200_BOARD_BUSY_W2"))
|
||||
m_board.set_window_busy_ticks(VT1200_WIN2, (int)strtol(t, nullptr, 0));
|
||||
if (const char *t = getenv("VT1200_BOARD_BUSY_W3"))
|
||||
m_board.set_window_busy_ticks(VT1200_WIN3, (int)strtol(t, nullptr, 0));
|
||||
if (const char *t = getenv("VT1200_BOARD_DMA_TICKS"))
|
||||
m_board.set_dma_ticks((int)strtol(t, nullptr, 0));
|
||||
|
||||
// The monitor-selection switch lives in bits 4-6 of 0x64000D0. The model
|
||||
// mixes those input bits with the port's write-back output latches; the
|
||||
// value may be the raw register bits (e.g. 70) or a position 0-7. Any
|
||||
@@ -206,9 +228,12 @@ void vt1200_state::machine_start()
|
||||
if (const char *d = getenv("VT1200_DUMP_DIR"))
|
||||
m_dump_dir = d;
|
||||
|
||||
logerror("vt1200: board model=%s busy_ticks=%s switch=%s log=%s render=%d dump=%ld\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",
|
||||
getenv("VT1200_BOARD_BUSY_W2") ? getenv("VT1200_BOARD_BUSY_W2") : "-",
|
||||
getenv("VT1200_BOARD_BUSY_W3") ? getenv("VT1200_BOARD_BUSY_W3") : "-",
|
||||
getenv("VT1200_BOARD_DMA_TICKS") ? getenv("VT1200_BOARD_DMA_TICKS") : "0",
|
||||
getenv("VT1200_BOARD_SWITCH") ? getenv("VT1200_BOARD_SWITCH") : "off",
|
||||
m_log ? "on" : "off", m_render ? 1 : 0, m_dump_frame);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user