harness: make board switch knob unambiguous and non-silent

This commit is contained in:
SDD committed 2026-10-16 01:50:03 +03:00
1 parent 43f69738f4
commit db2caa5558
5 files changed
+62 -9

No files matched your search

+15 -5
View File
@@ -16,7 +16,8 @@ handshake the firmware waits on and modelling it correctly.
| `VT1200_BOARD_MAP=<a=v;...>` | per-address fixed return values, e.g. `0x06200001=1` |
| `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=<addr>` / `_TICKS=<n>` / `_BIT=<mask>` | tune the handshake |
| `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_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 |
| `VT1200_DUMP_FRAME=<n>` / `VT1200_DUMP_DIR=<d>` | write that frame as a PPM |
@@ -331,6 +332,11 @@ a constant for the whole byte breaks the output bits, so the boot stays stuck.
New harness knob for exactly this: **`VT1200_BOARD_SWITCH=<addr>:<mask>:<value>`** —
bits in `<mask>` read as `<value>`, all other bits stay write-back.
*(Updated 2026-10-16: the model now applies this mix only to the monitor switch port
`0x0640000D` with mask `0x70`, and `<value>` is the raw bits **or** a position 0-7. A
different `<addr>`/`<mask>` is warned about and ignored — see the extraction section at
the end.)*
```
VT1200_BOARD_MODEL=handshake VT1200_BOARD_BUSY_TICKS=2 \
VT1200_BOARD_SWITCH="0640000D:70:70"
@@ -548,7 +554,8 @@ unrelated blocks — and it explains why the same shapes keep appearing.
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** (`VT1200_BOARD_SWITCH=<addr>:<mask>:<value>`).
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
@@ -562,7 +569,8 @@ unrelated blocks — and it explains why the same shapes keep appearing.
`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" (use `VT1200_BOARD_SWITCH=<addr>:<mask>:<value>`).
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.
@@ -603,8 +611,10 @@ The busy bit now has the semantics the design spec intended: a write to `+0x20`
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`); the arbitrary `<addr>:<mask>:<value>` form is no longer
honoured.
(bits 4-6 of `0x64000D0`): `<value>` 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 `<addr>` or
`<mask>` 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
+22
View File
@@ -49,12 +49,34 @@ static void test_busy_bit_arms_on_write_and_clears(void)
CHECK((b.read(status) & 0x0001) == 0x0000); // done
}
static void test_monitor_switch_accepts_raw_bits_or_position(void)
{
vt1200_board raw;
raw.set_monitor_switch(0x70); // raw register bits
raw.write(VT1200_WIN3 + VT1200_OFF_SWITCH, 0x0001);
CHECK((raw.read(VT1200_WIN3 + VT1200_OFF_SWITCH) & 0x0070) == 0x0070);
vt1200_board pos;
pos.set_monitor_switch(7); // position 7
pos.write(VT1200_WIN3 + VT1200_OFF_SWITCH, 0x0001);
CHECK((pos.read(VT1200_WIN3 + VT1200_OFF_SWITCH) & 0x0070) == 0x0070);
}
static void test_switch_mix_only_when_configured(void)
{
vt1200_board b; // no set_monitor_switch call
b.write(VT1200_WIN3 + VT1200_OFF_SWITCH, 0x0070);
CHECK(b.read(VT1200_WIN3 + VT1200_OFF_SWITCH) == 0x0070); // returned verbatim
}
int main(void)
{
test_default_read();
test_override_read();
test_monitor_switch_is_mixed_with_latches();
test_busy_bit_arms_on_write_and_clears();
test_monitor_switch_accepts_raw_bits_or_position();
test_switch_mix_only_when_configured();
std::printf(failures ? "%d FAILURE(S)\n" : "all board tests passed\n", failures);
return failures ? 1 : 0;
}
+11 -2
View File
@@ -179,12 +179,21 @@ void vt1200_state::machine_start()
m_board.set_busy_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.
// 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
// other port or mask is not modelled, so say so rather than ignoring it.
if (const char *sw = getenv("VT1200_BOARD_SWITCH"))
{
unsigned a = 0, m = 0, v = 0;
if (sscanf(sw, "%x:%x:%x", &a, &m, &v) == 3)
m_board.set_monitor_switch(u8((v & 0x70) >> 4));
{
if (a != VT1200_WIN3 + VT1200_OFF_SWITCH)
osd_printf_info("vt1200: VT1200_BOARD_SWITCH addr %07X is not the monitor switch port %07X; addr ignored\n",
a, VT1200_WIN3 + VT1200_OFF_SWITCH);
if (m != 0x70)
osd_printf_info("vt1200: VT1200_BOARD_SWITCH mask %02X is not the monitor switch mask 70; mask ignored\n", m);
m_board.set_monitor_switch(u8(v));
}
}
// Video: render by default; optionally dump a frame as PPM.
+1 -1
View File
@@ -18,7 +18,7 @@ uint16_t vt1200_board::read(uint32_t addr)
if (st != m_store.end())
value = st->second;
if (is_switch_port(addr))
if (m_switch_set && is_switch_port(addr))
return (uint16_t)((value & 0xff8f) | (m_switch_bits & 0x0070));
if (m_handshake && (addr & 0xffff) == VT1200_OFF_STATUS)
+13 -1
View File
@@ -41,7 +41,18 @@ public:
void set_override(uint32_t addr, uint16_t v) { m_override[addr] = v; }
void set_model_handshake(bool on) { m_handshake = on; }
void set_busy_ticks(int n) { m_busy_ticks = n; }
void set_monitor_switch(uint8_t position) { m_switch_bits = (uint16_t)((position & 7) << 4); }
// Monitor switch (bits 4-6 of the switch port). The argument is accepted
// unambiguously as EITHER the raw register bits (e.g. 0x70) OR a small
// position 0-7: a value above 0x07 is taken as the raw bit pattern, any
// value <= 0x07 is a position and is shifted into bits 4-6. The input mix
// is only applied to reads after this has been called.
void set_monitor_switch(uint8_t bits_or_position)
{
m_switch_bits = (uint16_t)(bits_or_position > 0x07
? bits_or_position : ((bits_or_position & 0x07) << 4));
m_switch_set = true;
}
// Bus interface.
uint16_t read(uint32_t addr);
@@ -57,5 +68,6 @@ private:
bool m_handshake = false;
int m_busy_ticks = 0;
int m_busy_left = 0;
bool m_switch_set = false; // set_monitor_switch was called
uint16_t m_switch_bits = 0;
};