mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
harness: promote the working VT1200 config to the default; retire settled env gates
A bare 'vt1200 vt1200' now boots exactly as the old six-variable incantation (VRAM, handshake busy=2, DUART1, monitor switch) did, so the no-env run is no longer a silently-broken machine. Promoted (5): VT1200_VRAM, VT1200_BOARD_MODEL, VT1200_BOARD_BUSY_TICKS, VT1200_DUART, VT1200_DUART_SEL (folded to DUART1-only; DUART2 is the new opt-in VT1200_DUART2). Deleted (15): VT1200_CAL/_VID4M/_W8, VT1200_BOARD_RAM/_DEFAULT/_BUSY_W2/ _BUSY_W3/_DMA_TICKS/_SWITCH, VT1200_DUART_LB1/LB2, VT1200_LOWLOG, VT1200_VRAMLOG, VT1200_STRUCTLOG, VT1200_DUART1_INT2. VT1200_BOARD_SWITCH is superseded: 0x0640000D is the DUART1 IP register, and the DUART mapping wins the last-wins slice, so the board model switch was never consulted (verified: identical output with and without it). Renamed: VT1200_DUART_IP3 -> VT1200_SKIP_LEVEL1, which now also selects the OS-phase DUART1 IRQ routing (INT2) instead of POST's INT1, removing the standalone VT1200_DUART1_INT2 knob. The skip gate itself stays opt-in. Evidence (equivalence): frame-160/200/600 md5, progress.lua at 200/1500 frames, and the cap.lua console transcript from the no-env run are byte-identical to the incantation run. Re-baseline: no-env 200-frame hottest PC 3FDCE400 samples 199 (1 PC) -> samples 53 (78 PCs). ROM md5 unchanged b46a3e442ccb86134d2356bd70d22fd2. Header comment block updated; see harness/mame/GATE_CLEANUP_REPORT.md.
This commit is contained in:
1 parent
9d912ae084
commit
5abf4f33be
2 files changed
+257
-480
No files matched your search
@@ -0,0 +1,131 @@
|
||||
# `VT1200_*` env-gate cleanup — outcome
|
||||
|
||||
Executes `docs/superpowers/plans/2026-10-16-env-gate-cleanup.md`. The working
|
||||
configuration is now the **default**: a bare `vt1200 vt1200` (no env at all)
|
||||
behaves exactly like the old incantation
|
||||
|
||||
```sh
|
||||
VT1200_VRAM=1 VT1200_BOARD_MODEL=handshake VT1200_BOARD_BUSY_TICKS=2 \
|
||||
VT1200_BOARD_SWITCH="0640000D:70:70" VT1200_DUART=1 VT1200_DUART_SEL=1
|
||||
```
|
||||
|
||||
Only `harness/mame/vt1200.cpp` was changed (`build.sh` needed no edit). ROM
|
||||
untouched: `md5 b46a3e442ccb86134d2356bd70d22fd2`.
|
||||
|
||||
## Promoted (gate removed, value is now the default) — 5
|
||||
|
||||
| gate | new default (code) |
|
||||
|---|---|
|
||||
| `VT1200_VRAM` | `map(VRAM_BASE_BITS, 0x1fffffff).ram().share("vram")` unconditional |
|
||||
| `VT1200_BOARD_MODEL` | `m_board.set_model_handshake(true)` |
|
||||
| `VT1200_BOARD_BUSY_TICKS` | `m_board.set_busy_ticks(2)` |
|
||||
| `VT1200_DUART` | DUART1 always mapped + `ip4/5/6_w(1)`; `irq_cb` always wired |
|
||||
| `VT1200_DUART_SEL` | folded to **DUART1 only**; DUART2 is a new opt-in `VT1200_DUART2` |
|
||||
|
||||
## Deleted (never on a default path) — 15
|
||||
|
||||
`VT1200_CAL`, `VT1200_CAL_VID4M`, `VT1200_CAL_W8`, `VT1200_BOARD_RAM`,
|
||||
`VT1200_BOARD_DEFAULT`, `VT1200_BOARD_BUSY_W2`, `VT1200_BOARD_BUSY_W3`,
|
||||
`VT1200_BOARD_DMA_TICKS`, `VT1200_BOARD_SWITCH` (superseded — see below),
|
||||
`VT1200_DUART_LB1`, `VT1200_DUART_LB2`, `VT1200_LOWLOG`, `VT1200_VRAMLOG`,
|
||||
`VT1200_STRUCTLOG`, and `VT1200_DUART1_INT2` (folded into `VT1200_SKIP_LEVEL1`).
|
||||
|
||||
## Kept — 15
|
||||
|
||||
Instrumentation: `VT1200_BOARD_LOG`, `VT1200_BOARD_MAP`, `VT1200_DUARTLOG`,
|
||||
`VT1200_IRQLOG`, `VT1200_LOG_UNMAPPED`, `VT1200_DPYLOG` + `VT1200_DPYLOG_FRAMES`,
|
||||
`VT1200_DUMP_FRAME`, `VT1200_DUMP_RAM`, `VT1200_DUMP_VRAM`, `VT1200_DUMP_DIR`,
|
||||
`VT1200_NO_RENDER`.
|
||||
Behaviour switches: `VT1200_SKIP_LEVEL1` (renamed from `VT1200_DUART_IP3`),
|
||||
`VT1200_DUART2` (new opt-in for DUART2).
|
||||
|
||||
---
|
||||
|
||||
## Equivalence proof (the promotion)
|
||||
|
||||
The "with env" capture is the six-variable incantation; the "after" capture is
|
||||
a bare run with **no environment variables at all**. Same binary MAME build
|
||||
(`/home/arti/code/k-space/mame0289/vt1200`), same commands, in the same order.
|
||||
|
||||
| observable | with env (before) | no env (after) | match |
|
||||
|---|---|---|---|
|
||||
| frame 160 md5 | `713c3e559e8e95b85e801901c505bf60` | same | ✅ |
|
||||
| frame 200 md5 | `330a44bfa3263eb54427a8e5320d69b8` | same | ✅ |
|
||||
| frame 600 md5 | `e8c133380ca1d4d063b95a74a39e17e3` | same | ✅ |
|
||||
| `progress.lua` 200 frames | 78 distinct PCs, hot `PC=3FDCE400 samples=53` | byte-identical file | ✅ |
|
||||
| `progress.lua` 1500 frames | 103 distinct PCs, hot `PC=3F2088B0 samples=415` | byte-identical file | ✅ |
|
||||
| `cap.lua` 3000-frame console | 7 `PRINT` lines | byte-identical file | ✅ |
|
||||
|
||||
Console transcript (both runs, identical):
|
||||
|
||||
```
|
||||
PRINT ... a9=3FDCA8E8 "LEVEL1/2 TESTING ** "
|
||||
PRINT ... a9=3FDC9C68 "LEVEL1/2 ERROR -> HOST PORT - 15 "
|
||||
PRINT ... a9=0080000C / 00C0000C / 0100000C / 0200000C (RAM diagnostic pointers)
|
||||
PRINT ... a9=3FDCD2A8 "TYPE 'GO' TO CONTINUE"
|
||||
```
|
||||
|
||||
`diff before/progress-*.txt after/progress-*.txt` and the console diff are empty.
|
||||
A bare `vt1200 vt1200` therefore reaches exactly as far as the incantation.
|
||||
|
||||
The five promoted gates were all set in the "before" capture (the incantation is
|
||||
their conjunction), so the before/after pair covers the promoted set. Each was
|
||||
load-bearing in the old code — its old default was off/0, and the committed
|
||||
reports record the consequence of the off state: `RENDER_REPORT.md` §6 (VRAM off
|
||||
→ level-1 VIDEO MEMORY fails), `BRINGUP_LOG.md` Gate 1/3/4 (no handshake/busy →
|
||||
pole at `PC=3FDCE400`), `DUART_REPORT.md` FINAL FINDINGS (no DUART → host-port
|
||||
transmit never runs). None was a dead promotion.
|
||||
|
||||
## `VT1200_BOARD_SWITCH` is superseded (deletion proof, the one uncertain gate)
|
||||
|
||||
`0x0640000D` is the DUART1 IP register — it is inside the DUART1 mapping
|
||||
(`VT1200_WIN3..+0xff`, `off 0x0D < 0x10`), which is registered **after** the
|
||||
board-window model, so map precedence (last-wins) hands the read to the DUART
|
||||
and the board model's switch is never consulted. Measured:
|
||||
|
||||
* six-var incantation **with** `BOARD_SWITCH` vs **without** it: frame md5s,
|
||||
both progress files and the console file are byte-identical (`before` vs
|
||||
`before_noswitch`).
|
||||
* with no env at all, the DUART1 IP read is `val=00F0` (bit-7 marker + the
|
||||
VR150 `0x70`), the monitor-switch value, from every PC that reads it.
|
||||
|
||||
## Deletion proof (all deleted gates)
|
||||
|
||||
Every deleted gate was read only through `getenv("<VAR>")` and its handler had
|
||||
no other caller. After deletion the file has **0** `getenv("<VAR>")`
|
||||
occurrences for each, the removed handlers (`log_probe`/`cal*`, `vramlog*`,
|
||||
`lowlog*`, `structlog*`) are gone, and the driver links — so no default path can
|
||||
reach removed code. `VT1200_DUART_LB1/2` had no effect unless set (the firmware
|
||||
sets MR2 local loopback itself). The deleted probe handlers were registered
|
||||
only inside their own `if (getenv(...))` block. `git diff`: +126 / −480 lines.
|
||||
|
||||
## Re-baseline (expected, recorded)
|
||||
|
||||
| | value |
|
||||
|---|---|
|
||||
| **old** no-env baseline (200 frames) | `PC=3FDCE400 samples=199` (1 distinct PC — the early hardware-ready wedge) |
|
||||
| **new** no-env baseline (200 frames) | `PC=3FDCE400 samples=53`, **78 distinct PCs**; at 1500 frames hot PCs are the LED loop `3F2088B0/3F208B20/3F208A80/3F208950` |
|
||||
|
||||
Reason: the no-env run is now the working configuration, so it leaves the early
|
||||
wedge and runs POST/level 1 up to the HOST PORT test (then the LED loop). The
|
||||
baseline is a change detector, not a specification; it changed deliberately.
|
||||
|
||||
## Design points
|
||||
|
||||
1. `VT1200_DUART_IP3` **renamed to `VT1200_SKIP_LEVEL1`** and it now also
|
||||
selects the OS-phase interrupt routing: default (level 1 runs) → POST routing
|
||||
(`m_duart1_int2 = false`, INT1); `VT1200_SKIP_LEVEL1` → drives IP3 high **and**
|
||||
sets `m_duart1_int2 = true` (INT2, the OS's DUART1 driver). The standalone
|
||||
`VT1200_DUART1_INT2` knob is gone. Verified live: `SKIP_LEVEL1=1` gives 143
|
||||
distinct PCs, OS-phase hot PCs (`3FAEE280`, `3FB30390`, …) and **no** console
|
||||
output, i.e. it really skips level 1.
|
||||
2. The skip gate itself is **not** promoted: the faithful default runs level 1
|
||||
(real terminal reads IP3 low); `VT1200_SKIP_LEVEL1` is how we diverge.
|
||||
|
||||
## Not changed / follow-ups
|
||||
|
||||
* `harness/mame/README.md` still says the DUARTs are "not modelled" and lists the
|
||||
old mapping table — now stale. Not edited (file ownership), flagged here.
|
||||
* `vt1200_board.h/.cpp` still expose `set_default` / `set_monitor_switch` /
|
||||
`set_dma_ticks` / `set_window_busy_ticks`; now unused by the driver but left
|
||||
untouched (not owned by this task).
|
||||
+126
-480
@@ -8,101 +8,92 @@
|
||||
to *execute* the VT1200 firmware under the TMS34010 interpreter with a
|
||||
debugger (breakpoints, single step, memory watch, trace).
|
||||
|
||||
What is modelled:
|
||||
What is modelled BY DEFAULT. A bare `vt1200 vt1200` boots the way the
|
||||
old six-variable incantation
|
||||
VT1200_VRAM=1 VT1200_BOARD_MODEL=handshake VT1200_BOARD_BUSY_TICKS=2
|
||||
VT1200_BOARD_SWITCH="0640000D:70:70" VT1200_DUART=1 VT1200_DUART_SEL=1
|
||||
did. Five of those knobs are now the default and no longer exist; the
|
||||
sixth, VT1200_BOARD_SWITCH, was superseded once DUART1's input port
|
||||
(IP4-IP6) became the monitor-switch source.
|
||||
|
||||
* TMS34010 CPU (bit-addressed: MAME's tms34010 memory space uses a
|
||||
right shift of 3, so address-map units are BITS).
|
||||
* RAM at logical bit address 0, 4 MiB (byte 0 .. 0x3fffff).
|
||||
* Video RAM at bit 0x04000000 (required for the level-1 VIDEO MEMORY
|
||||
test to pass).
|
||||
* The four undecoded board windows, driving the handshake register model
|
||||
with a two-tick busy bit (required to leave early POST).
|
||||
* MAME's SCN2681 at DUART1 (0x0640000 = host/printer), with its input
|
||||
port IP4-IP6 held high -- which is how the driver reads the VR150
|
||||
monitor switch (value 0x70). The level-1 host-port transmit is
|
||||
ISR-driven, so a real DUART is required.
|
||||
* The 1.75 MiB firmware ROM (physical byte base 0x07e40000 = bit
|
||||
0x3f200000), mirrored into the three upper 1 GiB quadrants so that
|
||||
the reset-vector page at logical bit 0xffffffe0 resolves to the last
|
||||
four bytes of the image (file offset 0x1bfffc).
|
||||
* OPTIONALLY (see below) the four undecoded board windows.
|
||||
|
||||
What is NOT modelled beyond the opt-in register model documented below:
|
||||
LANCE Ethernet, the DUARTs, and the board system logic at bit 0x5800000 /
|
||||
0x5900000 / 0x6200000 / 0x6400000. Because of that the terminal does not
|
||||
boot to the X server; the firmware parks in early POST waiting on a
|
||||
hardware-ready poll.
|
||||
What is NOT modelled: LANCE Ethernet, DUART2 (0x0620000 = keyboard/mouse;
|
||||
opt in with VT1200_DUART2=1), and the board system logic beyond the
|
||||
register model. The firmware therefore still stops at the level-1 HOST
|
||||
PORT test and wedges in the system-board LED loop; that is expected.
|
||||
|
||||
---------------------------------------------------------------------
|
||||
Board-window experiments (all opt-in; with no env vars set the windows
|
||||
stay unmapped and behave exactly as before):
|
||||
Instrumentation knobs (all opt-in; none changes execution):
|
||||
|
||||
VT1200_BOARD_LOG=<path> append a line for every *distinct*
|
||||
(address, PC) board access, flushed
|
||||
immediately. This is the evidence
|
||||
gatherer: it shows every register the
|
||||
firmware polls and the PC doing the
|
||||
polling.
|
||||
|
||||
VT1200_BOARD_DEFAULT=<value> value returned for addresses without an
|
||||
override (default 0).
|
||||
|
||||
immediately. Shows every register the
|
||||
firmware polls and the PC polling it.
|
||||
VT1200_BOARD_MAP=<list> per-address overrides, e.g.
|
||||
"0x06200010=1;0x0640000d0=0"
|
||||
|
||||
VT1200_BOARD_RAM=1 map the windows as plain read/write RAM
|
||||
instead (writes persist and read back).
|
||||
|
||||
VT1200_DUART=1 put MAME's SCN2681 on the two DUART windows
|
||||
(0x0640000 = host/printer, 0x0620000 =
|
||||
keyboard/mouse), with TX wired back to RX for
|
||||
the self-test's loopback connector. Gated so
|
||||
the no-env baseline is unchanged.
|
||||
VT1200_DUART_SEL=<s> which DUART(s): "1" = 0x0640000, "2" =
|
||||
0x0620000; default "12".
|
||||
VT1200_DUART_LB1/LB2=1 external TX->RX loopback on DUART1 / DUART2.
|
||||
VT1200_DUARTLOG=<path> every DUART access (not de-duplicated).
|
||||
VT1200_DUART_IP3=1 drive DUART1 IP3 (input-port bit 3) high.
|
||||
The boot gate at bit 0x3F2042D0 tests IP3
|
||||
(firmware @0x64000D0 bit 3) and, when set
|
||||
and the 0x6200000 status handshake did not
|
||||
return 0x55, branches to the OS cold-start
|
||||
0x3F5788C0 -- skipping ALL of the level-1
|
||||
batch (the sole caller of the level-1
|
||||
runner 0x3FDF72D0 is 0x3F204400, inside the
|
||||
skipped region). This is a modelled board
|
||||
input, not a ROM change: the real terminal
|
||||
reads IP3 low. See
|
||||
reference/level1-bypass-analysis.md.
|
||||
VT1200_DUART1_INT2=1 route DUART1's IRQ to TMS34010 INT2 (input
|
||||
line 1) instead of INT1 (line 0). Default
|
||||
is INT1, the wiring the level-1 self-test
|
||||
wanted. The OS's DUART1 driver attaches to
|
||||
INT2 (its stub 0x0F30 -> 0x3FA93070 handles
|
||||
the 0x6400000 window), so with the default
|
||||
the OS's DUART interrupt is serviced by the
|
||||
INT1 stub's 0x5800000 board-DMA handler and
|
||||
never acknowledged. Kept opt-in so the two
|
||||
wirings can be A/B tested.
|
||||
|
||||
Video: the driver paints the active scanlines itself, reading the
|
||||
framebuffer out of video RAM (see render_active_line). VT1200_NO_RENDER=1
|
||||
blanks instead. Dumps:
|
||||
|
||||
VT1200_DUMP_FRAME=<n> write frame <n> as vt1200-frame-<n>.ppm
|
||||
VT1200_DUMP_DIR=<dir> directory for the dumps (default ".")
|
||||
"0x06200010=1;0x0640000d=0"
|
||||
VT1200_DUARTLOG=<path> every DUART access (not de-duplicated),
|
||||
so a write that happens once per iteration
|
||||
cannot hide.
|
||||
VT1200_IRQLOG=<path> DUART1 IRQ assert: ISR/SRA at the moment of
|
||||
the interrupt.
|
||||
VT1200_LOG_UNMAPPED=<path> catch-all over the whole address space;
|
||||
anything the driver does not model is
|
||||
logged once per distinct (address, PC,
|
||||
direction).
|
||||
VT1200_DPYLOG=<path> dump the TMS34010 display params, one line
|
||||
per scanline, for the first
|
||||
VT1200_DPYLOG_FRAMES frames (default 1)
|
||||
VT1200_DPYLOG_FRAMES frames (default 1).
|
||||
VT1200_DUMP_FRAME=<n> write frame <n> as vt1200-frame-<n>.ppm
|
||||
VT1200_DUMP_RAM=<n> write main RAM at frame <n> as
|
||||
vt1200-ram-<n>.bin
|
||||
VT1200_DUMP_VRAM=<n> write video memory at frame <n> as
|
||||
vram-<n>.bin
|
||||
VT1200_DUMP_DIR=<dir> directory for the dumps (default ".")
|
||||
VT1200_NO_RENDER=1 blank the screen instead of painting it
|
||||
|
||||
VT1200_DUART also wires each DUART's IRQ output to the TMS34010's external
|
||||
interrupt inputs (DUART1 -> INT1, DUART2 -> INT2). This is required: the
|
||||
level-1 host-port test's transmit is entirely ISR-driven (the ISR writes
|
||||
THRA), and without the wiring THRA is never written at all. See
|
||||
DUART_REPORT.md section FINAL FINDINGS.
|
||||
Behaviour switch (deliberately diverges from the real terminal):
|
||||
|
||||
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_SKIP_LEVEL1=1 drive DUART1's input-port bit 3 high. The
|
||||
real terminal reads IP3 low, so with this
|
||||
UNSET the boot runs the whole level-1
|
||||
self-test batch (the faithful default).
|
||||
With it set, the boot gate at bit
|
||||
0x3F2042D0 (`@0x64000D0` bit 3 set and the
|
||||
0x6200000 status handshake not 0x55)
|
||||
branches to the OS cold-start 0x3F5788C0,
|
||||
skipping ALL of the level-1 batch. Because
|
||||
the OS phase is then in charge, the switch
|
||||
ALSO routes DUART1's IRQ to INT2 (line 1):
|
||||
the OS's DUART1 driver attaches there,
|
||||
while POST's DUART dispatcher sits on INT1
|
||||
(line 0), the default. Both lines are
|
||||
enabled (INTENB = 0x0606) and the RAM stub
|
||||
at 0x0F60 is rewritten between the two
|
||||
phases, so the two wirings correspond to
|
||||
the two phases' own routings. See
|
||||
reference/level1-bypass-analysis.md.
|
||||
|
||||
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.
|
||||
Video: the driver paints the active scanlines itself, reading the
|
||||
framebuffer out of video RAM (see render_active_line).
|
||||
|
||||
VT1200_DUART2=1 additionally maps MAME's SCN2681 at 0x0620000
|
||||
(keyboard/mouse). It is off by default because mapping both DUARTs
|
||||
diverts the level-0 keyboard path.
|
||||
|
||||
See FULL_BOOT_ESTIMATE.md section 4b for measured results.
|
||||
|
||||
@@ -119,7 +110,6 @@
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "vt1200_board.h"
|
||||
@@ -189,9 +179,9 @@ public:
|
||||
u16 board2_r(offs_t off); void board2_w(offs_t off, u16 data);
|
||||
u16 board3_r(offs_t off); void board3_w(offs_t off, u16 data);
|
||||
|
||||
// The two DUARTs, gated on VT1200_DUART. DUART1 = host (ch A) +
|
||||
// printer (ch B) at window 0x0640000; DUART2 = keyboard (ch A) + mouse
|
||||
// (ch B) at window 0x0620000.
|
||||
// The two DUARTs. DUART1 = host (ch A) + printer (ch B) at window
|
||||
// 0x0640000 and is always mapped; DUART2 = keyboard (ch A) + mouse (ch B)
|
||||
// at window 0x0620000 is opt-in via VT1200_DUART2.
|
||||
u16 duart1_r(offs_t off); void duart1_w(offs_t off, u16 data);
|
||||
u16 duart2_r(offs_t off); void duart2_w(offs_t off, u16 data);
|
||||
|
||||
@@ -221,7 +211,7 @@ private:
|
||||
// byte 2N, i.e. harness offset N), which MAME's 68681 model expects.
|
||||
required_device<scn2681_device> m_duart1; // 0x0640000: host(A)/printer(B)
|
||||
required_device<scn2681_device> m_duart2; // 0x0620000: keyboard(A)/mouse(B)
|
||||
bool m_duart1_int2 = false; // VT1200_DUART1_INT2
|
||||
bool m_duart1_int2 = false; // set by VT1200_SKIP_LEVEL1
|
||||
|
||||
// The four undecoded board windows, owned by the standalone model in
|
||||
// vt1200_board.h so it can be unit-tested without MAME. The driver only
|
||||
@@ -256,43 +246,6 @@ private:
|
||||
bool m_dumped_vram = false;
|
||||
optional_shared_ptr<u16> m_vram;
|
||||
|
||||
// VT1200_CAL=<path>
|
||||
// Address-unit and map-precedence cross-check. Each probe is registered at
|
||||
// the END of program_map (so, if precedence is last-wins, it wins its
|
||||
// slice) and logs the delegate offset `off` it is handed. It also prints
|
||||
// the space address reconstructed under the two candidate unit rules:
|
||||
// space4 = base + (off << 4) (delegate off = space-bit-address >> 4)
|
||||
// space0 = base + off (delegate off = space-bit-address)
|
||||
// Whichever rule yields a coherent picture for the positive control is the
|
||||
// real one. See XCROSS_REPORT.md.
|
||||
FILE *m_cal_log = nullptr;
|
||||
std::map<std::tuple<u32, u32, u32>, u32> m_cal_seen; // (base, off, pc) -> count
|
||||
std::vector<u16> m_cal_ram;
|
||||
void log_probe(const char *tag, char kind, u32 off, u32 base);
|
||||
|
||||
// One read/write pair per probe so each knows its own base address. These
|
||||
// bases are SPACE (bit) addresses, matching the driver's map entries.
|
||||
u16 calctrl_r(offs_t off); void calctrl_w(offs_t off, u16 data); // 0x00000000 control
|
||||
u16 calvida_r(offs_t off); void calvida_w(offs_t off, u16 data); // 0x00400000 (old VID-A)
|
||||
u16 calvid4m_r(offs_t off); void calvid4m_w(offs_t off, u16 data); // 0x04000000 (0x400000<<4)
|
||||
u16 calram80_r(offs_t off); void calram80_w(offs_t off, u16 data); // 0x00800000
|
||||
u16 calram100_r(offs_t off); void calram100_w(offs_t off, u16 data); // 0x01000000
|
||||
u16 calram180_r(offs_t off); void calram180_w(offs_t off, u16 data); // 0x01800000
|
||||
u16 calram200_r(offs_t off); void calram200_w(offs_t off, u16 data); // 0x02000000 (outside RAM)
|
||||
// Mismatched-width overlap check (gated on VT1200_CAL_W8, needs VT1200_CAL
|
||||
// too): an 8-bit handler over the same bottom slice as CTRL. It is
|
||||
// registered even later, so if the mismatched populate path also follows
|
||||
// last-wins it fires and CTRL does not.
|
||||
u8 calw8_r(offs_t off); void calw8_w(offs_t off, u8 data);
|
||||
|
||||
// VT1200_VRAMLOG=<path>
|
||||
// Transparent logging proxy over the start of video memory, to compare what
|
||||
// the level-1 video RAM test writes against what it reads back.
|
||||
FILE *m_vramlog = nullptr;
|
||||
std::vector<u16> m_vramlog_ram;
|
||||
u16 vramlog_r(offs_t off);
|
||||
void vramlog_w(offs_t off, u16 data);
|
||||
|
||||
// VT1200_LOG_UNMAPPED=<path>
|
||||
// A catch-all mapping over the whole address space, registered FIRST so every
|
||||
// real region added afterwards takes precedence. Anything the firmware
|
||||
@@ -305,28 +258,9 @@ private:
|
||||
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);
|
||||
|
||||
// VT1200_STRUCTLOG=<path>
|
||||
// Transparent logging proxy over the DUART self-test descriptor area
|
||||
// (bit 0x270000-0x270fff), to find who writes the status/count fields the
|
||||
// level-1 port tests check. Forwards to the real mainram share.
|
||||
// VT1200_IRQLOG=<path>: log DUART1's IRQ assertions (ISR/SRA at the
|
||||
// moment of the interrupt).
|
||||
FILE *m_irqlog = nullptr;
|
||||
FILE *m_structlog = nullptr;
|
||||
std::map<std::tuple<u32, u32, u32, u32>, u32> m_structlog_seen; // (addr,pc,val,kind) -> count
|
||||
u16 structlog_r(offs_t off);
|
||||
void structlog_w(offs_t off, u16 data);
|
||||
void log_struct(char kind, u32 addr, u16 value);
|
||||
|
||||
// VT1200_DUARTLOG=<path>
|
||||
// EVERY DUART access (not de-duplicated), so a write that happens once per
|
||||
@@ -338,9 +272,6 @@ private:
|
||||
|
||||
void vt1200_state::machine_start()
|
||||
{
|
||||
if (const char *p = getenv("VT1200_BOARD_DEFAULT"))
|
||||
m_board.set_default(u16(strtoul(p, nullptr, 0)));
|
||||
|
||||
if (const char *list = getenv("VT1200_BOARD_MAP"))
|
||||
{
|
||||
std::string s(list), item;
|
||||
@@ -369,39 +300,10 @@ void vt1200_state::machine_start()
|
||||
}
|
||||
|
||||
// Handshake model: write-back registers + a self-clearing busy bit. The
|
||||
// board model owns the state machine; the env knobs configure it.
|
||||
if (const char *model = getenv("VT1200_BOARD_MODEL"))
|
||||
m_board.set_model_handshake(!strcmp(model, "handshake"));
|
||||
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
|
||||
// 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)
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
// board model owns the state machine; these are the settled defaults (a
|
||||
// two-tick busy bit is what leaves early POST).
|
||||
m_board.set_model_handshake(true);
|
||||
m_board.set_busy_ticks(2);
|
||||
|
||||
// Video: render by default; optionally dump a frame as PPM.
|
||||
if (getenv("VT1200_NO_RENDER"))
|
||||
@@ -432,28 +334,6 @@ void vt1200_state::machine_start()
|
||||
"# address units are the address-map's; firmware operand = addr << 4\n");
|
||||
}
|
||||
|
||||
if (const char *p = getenv("VT1200_CAL"))
|
||||
{
|
||||
m_cal_log = fopen(p, "w");
|
||||
if (m_cal_log)
|
||||
fprintf(m_cal_log, "# tag kind addr pc -- address-unit / precedence calibration\n");
|
||||
}
|
||||
|
||||
if (const char *p = getenv("VT1200_VRAMLOG"))
|
||||
{
|
||||
m_vramlog = fopen(p, "w");
|
||||
m_vramlog_ram.assign(0x10000, 0);
|
||||
if (m_vramlog)
|
||||
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");
|
||||
}
|
||||
|
||||
if (const char *p = getenv("VT1200_IRQLOG"))
|
||||
{
|
||||
m_irqlog = fopen(p, "w");
|
||||
@@ -461,13 +341,6 @@ void vt1200_state::machine_start()
|
||||
fprintf(m_irqlog, "# DUART1 IRQ assert: ISR/SRA at the moment of the interrupt\n");
|
||||
}
|
||||
|
||||
if (const char *p = getenv("VT1200_STRUCTLOG"))
|
||||
{
|
||||
m_structlog = fopen(p, "w");
|
||||
if (m_structlog)
|
||||
fprintf(m_structlog, "# kind bitaddr pc value -- DUART descriptor area (bit 0x270000-0x270fff)\n");
|
||||
}
|
||||
|
||||
if (const char *p = getenv("VT1200_DUARTLOG"))
|
||||
{
|
||||
m_duartlog = fopen(p, "w");
|
||||
@@ -475,110 +348,34 @@ void vt1200_state::machine_start()
|
||||
fprintf(m_duartlog, "# win kind reg val pc -- every DUART access (not de-duplicated)\n");
|
||||
}
|
||||
|
||||
// With the real DUARTs in place, board-level signals that used to be faked
|
||||
// With the real DUART in place, board-level signals that used to be faked
|
||||
// by the window model come from the DUART's input port (read register 0x0D:
|
||||
// IP0-IP6, bit 7 always set). The monitor-selection switch is IP bits 4-6;
|
||||
// driving them high selects the VR150 CRT (0x70), matching the value the
|
||||
// firmware looks for and the value the board model was configured with.
|
||||
// IP2 stays low (the value the board model produced for the blit-status
|
||||
// test at bit 0x3F204AA0).
|
||||
if (getenv("VT1200_DUART"))
|
||||
// firmware looks for. IP2 stays low (the value for the blit-status test at
|
||||
// bit 0x3F204AA0).
|
||||
m_duart1->ip4_w(1);
|
||||
m_duart1->ip5_w(1);
|
||||
m_duart1->ip6_w(1);
|
||||
|
||||
// VT1200_SKIP_LEVEL1=1 drives DUART1 IP3 high, which makes the boot gate at
|
||||
// bit 0x3F2042D0 take the OS cold-start branch (0x3F5788C0) and skip the
|
||||
// whole level-1 self-test batch. Since the OS phase is then in charge, the
|
||||
// same switch routes DUART1's IRQ to INT2 (line 1): the OS's DUART1 driver
|
||||
// attaches there, while POST's dispatcher sits on INT1 (line 0). Both
|
||||
// lines are enabled (INTENB = 0x0606), so this selects which phase's
|
||||
// routing applies.
|
||||
if (getenv("VT1200_SKIP_LEVEL1"))
|
||||
{
|
||||
const char *sel = getenv("VT1200_DUART_SEL");
|
||||
if (!sel || strchr(sel, '1'))
|
||||
{
|
||||
m_duart1->ip4_w(1);
|
||||
m_duart1->ip5_w(1);
|
||||
m_duart1->ip6_w(1);
|
||||
// VT1200_DUART_IP3=1 drives DUART1 IP3 high, which makes the
|
||||
// boot gate at bit 0x3F2042D0 take the OS cold-start branch
|
||||
// (0x3F5788C0) and skip the whole level-1 self-test batch.
|
||||
if (getenv("VT1200_DUART_IP3"))
|
||||
m_duart1->ip3_w(1);
|
||||
// VT1200_DUART1_INT2=1 routes DUART1's IRQ to INT2 (line 1) so
|
||||
// the OS's DUART1 driver (INT2 stub 0x0F30 -> 0x3FA93070) can
|
||||
// acknowledge it, instead of the INT1 board-DMA handler.
|
||||
m_duart1_int2 = (getenv("VT1200_DUART1_INT2") != nullptr);
|
||||
}
|
||||
m_duart1->ip3_w(1);
|
||||
m_duart1_int2 = true;
|
||||
}
|
||||
|
||||
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",
|
||||
logerror("vt1200: board model=handshake busy_ticks=2 switch=duart-ip4/5/6 log=%s render=%d dump=%ld\n",
|
||||
m_log ? "on" : "off", m_render ? 1 : 0, m_dump_frame);
|
||||
logerror("vt1200: duart=%s\n", getenv("VT1200_DUART") ? "on (SCN2681 @0640000 host/printer, @0620000 kbd/mouse, tx->rx loopback)" : "off");
|
||||
}
|
||||
|
||||
void vt1200_state::log_probe(const char *tag, char kind, u32 off, u32 base)
|
||||
{
|
||||
if (!m_cal_log)
|
||||
return;
|
||||
const u32 p = (u32)m_maincpu->state_int(TMS34010_PC);
|
||||
auto key = std::make_tuple(base, off, (p << 8) | (u8)kind);
|
||||
if (m_cal_seen.find(key) == m_cal_seen.end())
|
||||
{
|
||||
m_cal_seen.emplace(key, 1);
|
||||
// space4: the rule "delegate off = space address >> 4". space0: off is
|
||||
// already the space address. Only one of them can be right for a run.
|
||||
fprintf(m_cal_log, "%s %c off=%08X space4=%08X space0=%08X pc=%08X\n",
|
||||
tag, kind, off, base + (off << 4), base + off, p);
|
||||
fflush(m_cal_log);
|
||||
}
|
||||
}
|
||||
|
||||
// The control probe shadows the very bottom of RAM (bit 0..0xffff = byte
|
||||
// 0..0x1fff). It behaves like RAM via a small backing array so it perturbs as
|
||||
// little as possible, and it MUST fire if last-wins precedence holds.
|
||||
u16 vt1200_state::calctrl_r(offs_t off) { log_probe("CTRL", 'R', (u32)off, 0x00000000); return m_cal_ram[(u32)off % m_cal_ram.size()]; }
|
||||
void vt1200_state::calctrl_w(offs_t off, u16 data) { log_probe("CTRL", 'W', (u32)off, 0x00000000); m_cal_ram[(u32)off % m_cal_ram.size()] = data; }
|
||||
|
||||
u16 vt1200_state::calvida_r(offs_t off) { log_probe("VID-A-0x00400000", 'R', (u32)off, 0x00400000); return 0; }
|
||||
void vt1200_state::calvida_w(offs_t off, u16 data) { log_probe("VID-A-0x00400000", 'W', (u32)off, 0x00400000); }
|
||||
|
||||
u16 vt1200_state::calvid4m_r(offs_t off) { log_probe("VID-4M-0x04000000", 'R', (u32)off, 0x04000000); return 0; }
|
||||
void vt1200_state::calvid4m_w(offs_t off, u16 data) { log_probe("VID-4M-0x04000000", 'W', (u32)off, 0x04000000); }
|
||||
|
||||
u16 vt1200_state::calram80_r(offs_t off) { log_probe("RAM-0x00800000", 'R', (u32)off, 0x00800000); return 0; }
|
||||
void vt1200_state::calram80_w(offs_t off, u16 data) { log_probe("RAM-0x00800000", 'W', (u32)off, 0x00800000); }
|
||||
|
||||
u16 vt1200_state::calram100_r(offs_t off) { log_probe("RAM-0x01000000", 'R', (u32)off, 0x01000000); return 0; }
|
||||
void vt1200_state::calram100_w(offs_t off, u16 data) { log_probe("RAM-0x01000000", 'W', (u32)off, 0x01000000); }
|
||||
|
||||
u16 vt1200_state::calram180_r(offs_t off) { log_probe("RAM-0x01800000", 'R', (u32)off, 0x01800000); return 0; }
|
||||
void vt1200_state::calram180_w(offs_t off, u16 data) { log_probe("RAM-0x01800000", 'W', (u32)off, 0x01800000); }
|
||||
|
||||
u16 vt1200_state::calram200_r(offs_t off) { log_probe("RAM-0x02000000", 'R', (u32)off, 0x02000000); return 0; }
|
||||
void vt1200_state::calram200_w(offs_t off, u16 data) { log_probe("RAM-0x02000000", 'W', (u32)off, 0x02000000); }
|
||||
|
||||
u8 vt1200_state::calw8_r(offs_t off) { log_probe("W8-MISMATCH", 'R', (u32)off, 0x00000000); return 0; }
|
||||
void vt1200_state::calw8_w(offs_t off, u8 data) { log_probe("W8-MISMATCH", 'W', (u32)off, 0x00000000); }
|
||||
|
||||
u16 vt1200_state::vramlog_r(offs_t off)
|
||||
{
|
||||
const u32 o = (u32)off & 0xffff;
|
||||
const u16 v = (o < m_vramlog_ram.size()) ? m_vramlog_ram[o] : 0;
|
||||
if (m_vramlog)
|
||||
{
|
||||
fprintf(m_vramlog, "R %04X %04X pc=%08X\n", o, v, (unsigned)m_maincpu->state_int(TMS34010_PC));
|
||||
fflush(m_vramlog);
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void vt1200_state::vramlog_w(offs_t off, u16 data)
|
||||
{
|
||||
const u32 o = (u32)off & 0xffff;
|
||||
if (o < m_vramlog_ram.size())
|
||||
m_vramlog_ram[o] = data;
|
||||
if (m_vramlog)
|
||||
{
|
||||
fprintf(m_vramlog, "W %04X %04X pc=%08X\n", o, data, (unsigned)m_maincpu->state_int(TMS34010_PC));
|
||||
fflush(m_vramlog);
|
||||
}
|
||||
logerror("vt1200: duart1=on (SCN2681 @0640000 host/printer), duart2=%s skip_level1=%s\n",
|
||||
getenv("VT1200_DUART2") ? "on (kbd/mouse)" : "off",
|
||||
getenv("VT1200_SKIP_LEVEL1") ? "on (IP3 high, IRQ->INT2)" : "off (IP3 low, IRQ->INT1)");
|
||||
}
|
||||
|
||||
void vt1200_state::machine_reset()
|
||||
@@ -587,10 +384,6 @@ void vt1200_state::machine_reset()
|
||||
fflush(m_log);
|
||||
if (m_unmapped_log)
|
||||
fflush(m_unmapped_log);
|
||||
if (m_lowlog)
|
||||
fflush(m_lowlog);
|
||||
if (m_structlog)
|
||||
fflush(m_structlog);
|
||||
}
|
||||
|
||||
void vt1200_state::log_unmapped(const char *kind, u32 addr, const char *pc)
|
||||
@@ -624,68 +417,6 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
void vt1200_state::log_struct(char kind, u32 addrof, u16 value)
|
||||
{
|
||||
if (!m_structlog)
|
||||
return;
|
||||
const u32 pc = (u32)m_maincpu->state_int(TMS34010_PC);
|
||||
auto key = std::make_tuple(addrof, pc, (u32)value, (u32)(u8)kind);
|
||||
if (m_structlog_seen.find(key) == m_structlog_seen.end())
|
||||
{
|
||||
m_structlog_seen.emplace(key, 1);
|
||||
fprintf(m_structlog, "%c %06X pc=%08X val=%04X\n", kind, addrof, pc, value);
|
||||
fflush(m_structlog);
|
||||
}
|
||||
}
|
||||
|
||||
// The entry base is bit 0x270000, so the delegate offset is
|
||||
// (bitaddr - 0x270000) >> 4 and the mainram word index is bitaddr >> 4.
|
||||
u16 vt1200_state::structlog_r(offs_t off)
|
||||
{
|
||||
const u32 bitaddr = 0x270000 + ((u32)off << 4);
|
||||
const u16 v = m_mainram[bitaddr >> 4];
|
||||
log_struct('R', bitaddr, v);
|
||||
return v;
|
||||
}
|
||||
|
||||
void vt1200_state::structlog_w(offs_t off, u16 data)
|
||||
{
|
||||
const u32 bitaddr = 0x270000 + ((u32)off << 4);
|
||||
m_mainram[bitaddr >> 4] = data;
|
||||
log_struct('W', bitaddr, data);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -697,31 +428,13 @@ void vt1200_state::program_map(address_map &map)
|
||||
if (getenv("VT1200_LOG_UNMAPPED"))
|
||||
map(0x00000000, 0xffffffff).rw(FUNC(vt1200_state::unmapped_r), FUNC(vt1200_state::unmapped_w));
|
||||
|
||||
// Video memory. Discovered empirically: the firmware's blit paths
|
||||
// (PC 3F204A20 right after FILL XY, and 3FDF97A0 / 3FDF9A00 in the display
|
||||
// code) read and write 0x00400000..0x0040D7FF -- memory the driver never
|
||||
// mapped, which is exactly why the screen renders black: the blits went
|
||||
// nowhere and reads returned 0. Gated on an env var while we characterise
|
||||
// its size and layout, so the established no-env baseline is preserved.
|
||||
// Video memory. Located empirically and then corrected by a closed
|
||||
// cross-check of two instruments: the catch-all's handler receives
|
||||
// (bitaddr - base) >> 4, so its logged 0x400000..0x40D7FF means BIT
|
||||
// Video memory, at bit 0x04000000. Located empirically and then corrected
|
||||
// by a closed cross-check of two instruments: the catch-all's handler
|
||||
// receives (bitaddr - base) >> 4, so its logged 0x400000..0x40D7FF means BIT
|
||||
// 0x04000000..0x040D7FF0 == bytes 0x800000..0x81AFFE (~110 KB, about one
|
||||
// 1280x1024 1bpp framebuffer). The earlier 0x00400000 was 16x too low.
|
||||
//
|
||||
// This is the memory the level-1 VIDEO RAM test walks (it reports failing at
|
||||
// 0x40000C in the same >>4 units, i.e. bit 0x040000C0), which is why the
|
||||
// firmware reports error 22 and restarts before it ever draws anything.
|
||||
if (getenv("VT1200_VRAM"))
|
||||
map(VRAM_BASE_BITS, 0x1fffffff).ram().share("vram");
|
||||
|
||||
// Transparent logging proxy over the first 64 KiB of video memory, registered
|
||||
// last so it wins over the .ram() above. off = (bitaddr - base) >> 4, so this
|
||||
// covers bit 0x04000000..0x040FFFFF, which includes the address the level-1
|
||||
// test reports failing at (bit 0x040000C0).
|
||||
if (getenv("VT1200_VRAMLOG"))
|
||||
map(0x04000000, 0x0400ffff).rw(FUNC(vt1200_state::vramlog_r), FUNC(vt1200_state::vramlog_w));
|
||||
|
||||
// 1280x1024 1bpp framebuffer). Mapping it is what lets the level-1 VIDEO
|
||||
// MEMORY test's walking-bit read-back pass, so it is unconditional now.
|
||||
map(VRAM_BASE_BITS, 0x1fffffff).ram().share("vram");
|
||||
|
||||
// Main RAM: 4 MiB (1 MiB onboard + 3 x 1 MiB SIMMs on the real machine).
|
||||
// The TMS34010 is bit-addressed, so 4 MiB of bytes == 0x2000000 address
|
||||
@@ -737,73 +450,22 @@ void vt1200_state::program_map(address_map &map)
|
||||
map(0xbf200000, 0xbfffffff).rom().region("maincpu", 0);
|
||||
map(0xff200000, 0xffffffff).rom().region("maincpu", 0);
|
||||
|
||||
// Calibration cross-check. Every probe is registered LAST, so under
|
||||
// last-wins it wins its own slice. Bases are SPACE (bit) addresses.
|
||||
if (getenv("VT1200_CAL"))
|
||||
{
|
||||
m_cal_ram.assign(0x10000, 0);
|
||||
// Positive control: proven to fire. Last-wins -> this shadows the
|
||||
// bottom 64K bits of RAM.
|
||||
map(0x00000000, 0x0000ffff).rw(FUNC(vt1200_state::calctrl_r), FUNC(vt1200_state::calctrl_w));
|
||||
// The original disputed "video" range (a space address inside RAM).
|
||||
map(0x00400000, 0x004fffff).rw(FUNC(vt1200_state::calvida_r), FUNC(vt1200_state::calvida_w));
|
||||
// The catch-all's logged 0x400000 read as a space address, times 16:
|
||||
// 0x400000 << 4 = 0x04000000. Gated separately so a plain VT1200_CAL run
|
||||
// leaves the region to the catch-all (the strict A-vs-B cross-check).
|
||||
if (getenv("VT1200_CAL_VID4M"))
|
||||
map(0x04000000, 0x040fffff).rw(FUNC(vt1200_state::calvid4m_r), FUNC(vt1200_state::calvid4m_w));
|
||||
// Where does RAM actually end? Probes at three MiB marks of the
|
||||
// declared 4 MiB range, plus one OUTSIDE it (0x02000000).
|
||||
map(0x00800000, 0x00800fff).rw(FUNC(vt1200_state::calram80_r), FUNC(vt1200_state::calram80_w));
|
||||
map(0x01000000, 0x01000fff).rw(FUNC(vt1200_state::calram100_r), FUNC(vt1200_state::calram100_w));
|
||||
map(0x01800000, 0x01800fff).rw(FUNC(vt1200_state::calram180_r), FUNC(vt1200_state::calram180_w));
|
||||
map(0x02000000, 0x02000fff).rw(FUNC(vt1200_state::calram200_r), FUNC(vt1200_state::calram200_w));
|
||||
// Mismatched-width check: same slice as CTRL, but an 8-bit handler.
|
||||
if (getenv("VT1200_CAL_W8"))
|
||||
map(0x00000000, 0x0000ffff).rw(FUNC(vt1200_state::calw8_r), FUNC(vt1200_state::calw8_w));
|
||||
}
|
||||
// Undecoded board windows, driving the handshake register model. Always
|
||||
// mapped: the busy handshake is what leaves early POST.
|
||||
map(BOARD_BASE[0], BOARD_BASE[0] + 0xffff).rw(FUNC(vt1200_state::board0_r), FUNC(vt1200_state::board0_w));
|
||||
map(BOARD_BASE[1], BOARD_BASE[1] + 0xffff).rw(FUNC(vt1200_state::board1_r), FUNC(vt1200_state::board1_w));
|
||||
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));
|
||||
|
||||
// Undecoded board windows. Left unmapped unless an experiment asks for
|
||||
// them, so the default behaviour is unchanged.
|
||||
if (getenv("VT1200_BOARD_RAM"))
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
map(BOARD_BASE[i], BOARD_BASE[i] + 0xffff).ram();
|
||||
}
|
||||
else if (getenv("VT1200_BOARD_LOG") || getenv("VT1200_BOARD_MAP")
|
||||
|| getenv("VT1200_BOARD_DEFAULT") || getenv("VT1200_BOARD_MODEL"))
|
||||
{
|
||||
map(BOARD_BASE[0], BOARD_BASE[0] + 0xffff).rw(FUNC(vt1200_state::board0_r), FUNC(vt1200_state::board0_w));
|
||||
map(BOARD_BASE[1], BOARD_BASE[1] + 0xffff).rw(FUNC(vt1200_state::board1_r), FUNC(vt1200_state::board1_w));
|
||||
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));
|
||||
}
|
||||
|
||||
// The two SCN2681 DUARTs, gated on VT1200_DUART so the no-env baseline is
|
||||
// untouched. Registered AFTER the board-window model so they win the
|
||||
// register slice (map precedence is last-wins); the model still serves any
|
||||
// other register in the same window. Base in map units is the bit
|
||||
// address; 16 registers at 2 bytes each = 32 bytes = 0x100 bit units.
|
||||
// VT1200_DUART_SEL picks which ("1" = 0x0640000 host/printer, "2" =
|
||||
// 0x0620000 keyboard/mouse; default both).
|
||||
if (getenv("VT1200_DUART"))
|
||||
{
|
||||
const char *sel = getenv("VT1200_DUART_SEL");
|
||||
std::string s(sel ? sel : "12");
|
||||
if (s.find('1') != std::string::npos)
|
||||
map(VT1200_WIN3, VT1200_WIN3 + 0xff).rw(FUNC(vt1200_state::duart1_r), FUNC(vt1200_state::duart1_w));
|
||||
if (s.find('2') != std::string::npos)
|
||||
map(VT1200_WIN2, VT1200_WIN2 + 0xff).rw(FUNC(vt1200_state::duart2_r), FUNC(vt1200_state::duart2_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));
|
||||
|
||||
// Transparent logging proxy over the DUART self-test descriptor area.
|
||||
if (getenv("VT1200_STRUCTLOG"))
|
||||
map(0x270000, 0x270fff).rw(FUNC(vt1200_state::structlog_r), FUNC(vt1200_state::structlog_w));
|
||||
// MAME's SCN2681 at DUART1 (0x0640000 = host/printer), registered AFTER the
|
||||
// board-window model so it wins the register slice (map precedence is
|
||||
// last-wins); the model still serves any other register in the same window.
|
||||
// Base in map units is the bit address; 16 registers at 2 bytes each = 32
|
||||
// bytes = 0x100 bit units. DUART2 (0x0620000 = keyboard/mouse) is opt-in
|
||||
// via VT1200_DUART2: mapping both diverts the level-0 keyboard path.
|
||||
map(VT1200_WIN3, VT1200_WIN3 + 0xff).rw(FUNC(vt1200_state::duart1_r), FUNC(vt1200_state::duart1_w));
|
||||
if (getenv("VT1200_DUART2"))
|
||||
map(VT1200_WIN2, VT1200_WIN2 + 0xff).rw(FUNC(vt1200_state::duart2_r), FUNC(vt1200_state::duart2_w));
|
||||
}
|
||||
// MAME may hand us either an entry-relative offset (0..0xffff) or the absolute
|
||||
// address; BOARD_BASE is 0x10000-aligned, so ORing the low 16 bits is correct
|
||||
@@ -1155,7 +817,7 @@ void vt1200_state::dump_vram()
|
||||
}
|
||||
if (!m_vram)
|
||||
{
|
||||
osd_printf_info("vt1200: VT1200_VRAM not mapped; nothing to dump\n");
|
||||
osd_printf_info("vt1200: video RAM not mapped; nothing to dump\n");
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
@@ -1209,31 +871,15 @@ void vt1200_state::vt1200(machine_config &config)
|
||||
SCN2681(config, m_duart1, XTAL(3'686'400));
|
||||
SCN2681(config, m_duart2, XTAL(3'686'400));
|
||||
|
||||
// External loopback (TX wired back to RX) models the loopback connectors
|
||||
// the service guide's port tests use. Selectable per DUART so the
|
||||
// host/printer connector can be fitted without feeding the keyboard/mouse
|
||||
// DUART looped-back bytes that the firmware would read as key presses.
|
||||
if (getenv("VT1200_DUART_LB1"))
|
||||
{
|
||||
m_duart1->a_tx_cb().set(m_duart1, FUNC(scn2681_device::rx_a_w));
|
||||
m_duart1->b_tx_cb().set(m_duart1, FUNC(scn2681_device::rx_b_w));
|
||||
}
|
||||
if (getenv("VT1200_DUART_LB2"))
|
||||
{
|
||||
m_duart2->a_tx_cb().set(m_duart2, FUNC(scn2681_device::rx_a_w));
|
||||
m_duart2->b_tx_cb().set(m_duart2, FUNC(scn2681_device::rx_b_w));
|
||||
}
|
||||
|
||||
// Connect the DUART interrupt outputs to the CPU. The level-1 host-port
|
||||
// test enables the DUART's TxRDYA/RxRDYA interrupts (IMR = 7) and the
|
||||
// firmware's ISR is what feeds THRA -- but only if the IRQ reaches the CPU.
|
||||
// Gated on VT1200_DUART so the no-env baseline is untouched. DUART1's
|
||||
// firmware trampoline sits on INT1; DUART2's on INT2.
|
||||
if (getenv("VT1200_DUART"))
|
||||
{
|
||||
m_duart1->irq_cb().set(FUNC(vt1200_state::duart1_irq_w));
|
||||
m_duart2->irq_cb().set(FUNC(vt1200_state::duart2_irq_w));
|
||||
}
|
||||
// No external TX->RX loopback is wired: the firmware sets MR2's local
|
||||
// loopback itself and the datasheet holds TxD high in local loopback, so
|
||||
// the external wire is unnecessary. duart1_irq_w picks INT1 (POST) or INT2
|
||||
// (OS) from VT1200_SKIP_LEVEL1; DUART2's trampoline is on INT2.
|
||||
m_duart1->irq_cb().set(FUNC(vt1200_state::duart1_irq_w));
|
||||
m_duart2->irq_cb().set(FUNC(vt1200_state::duart2_irq_w));
|
||||
}
|
||||
|
||||
INPUT_PORTS_START(vt1200)
|
||||
|
||||
Reference in new issue
Block a user