mirror of
https://git.k-space.ee/arti/vt1200.git
synced 2026-10-30 12:03:54 +02:00
harness: wire MAME SCN2681 at the DUART windows (host-port investigation)
Adds SCN2681 devices at 0x0640000 (DUART1 = host/printer) and 0x0620000 (DUART2 = keyboard/mouse), gated on VT1200_DUART, with the remap verified as duart_register = handler_off (duart_byte = off*2, value in the low byte of the firmware's 16-bit move). Optional external TX->RX loopback (VT1200_DUART_LB1/2) models the loopback connectors; the monitor switch is driven on IP4-IP6. New VT1200_DUARTLOG gives a full non-deduplicated DUART access log. Result: the level-1 HOST PORT test is reached and executed against the real DUART, but still reports 'LEVEL1/2 ERROR -> HOST PORT - 15'. The test reads SRA/RHRA and requires a 5-byte external pattern (0xAA,0xFF,0xAA,0x55) plus SRA overrun, yet the firmware never writes the transmit register (0 writes to reg 3/B on either DUART in 1400 frames), so no internal or external loopback can supply it. See harness/mame/DUART_REPORT.md. Regression holds: no VT1200_* env -> PC=3FDCE400 samples=199.
This commit is contained in:
1 parent
1c0a9b2b64
commit
2fd9286c1a
3 files changed
+482
-1
No files matched your search
@@ -0,0 +1,278 @@
|
||||
# DUART — wiring MAME's SCN2681 at the host-port window
|
||||
|
||||
Bounded task, 2026-10-16. Goal: make the firmware's level-1 **HOST PORT** test pass
|
||||
(DUART1 channel A) by modelling the DUART with MAME's `SCN2681`, so that
|
||||
`'LEVEL1/2 ERROR -> HOST PORT - 15 '` stops appearing in the console capture.
|
||||
|
||||
Driver: `harness/mame/vt1200.cpp`. MAME: `/home/arti/code/k-space/mame0289`.
|
||||
ROM: `harness/mame/roms/vt1200/vt1200_v22.bin`.
|
||||
Acceptance instrument: `harness/mame/cap.lua`.
|
||||
|
||||
---
|
||||
|
||||
## 0. Answer in one paragraph
|
||||
|
||||
MAME's `SCN2681` **is** the right part and *is* wireable: the two windows are
|
||||
byte-register DUARTs with `duart_register = handler_off` (verified below), and
|
||||
they are now mapped to an `SCN2681` with TX wired back to RX. **The acceptance line
|
||||
still appears** — but not because of the DUART model. The level-1 host-port test
|
||||
reads DUART1 status/data (`SRA` at off `0x01`, `RHRA` at off `0x03`) and requires
|
||||
the receiver to hold a **5-byte external pattern** (`0xAA,0xFF,0xAA,0x55,…`) with
|
||||
**`SRA` overrun set** (`(SRA & 0xF0) == 0x10`). **The firmware never writes the DUART
|
||||
transmit register anywhere on that path** (no write to off `0x03`, driver-side
|
||||
logged), so no loopback — internal (MR2 loopback) or external (TX→RX) — can produce
|
||||
that data. The same is true of the level-0 "I/O data path test", which writes `0xE1`
|
||||
to DUART2's THRA and requires a **10-byte** reply. These are **host-protocol tests,
|
||||
not passive loopback tests**: they need an external responder (the host, or a loopback
|
||||
connector that is more than a passive TX→RX strap). That is the specific blocker.
|
||||
|
||||
---
|
||||
|
||||
## 1. What I wired, and where
|
||||
|
||||
Device: **MAME `SCN2681`** (Signetics 2681; same register model as the Motorola
|
||||
`MC68681`, which is the "same family"). Reasons for `SCN2681` over `MC68681`:
|
||||
|
||||
* The firmware uses only the **RD/WR-strobe** register interface; it never touches the
|
||||
68681-specific **Interrupt Vector Register** behaviour or IACK. The 2681 is the
|
||||
strobe part, so it is the closer of the two; the 68681 would work identically here.
|
||||
* Both share `duart_base_device`; the 68681 adds only IVR/DTACK/IACK. Nothing is lost.
|
||||
|
||||
Windows and role (chosen from ROM strings and from which test the level-1 runner
|
||||
drives — see §3):
|
||||
|
||||
| window (map units) | device | channels | evidence |
|
||||
|---|---|---|---|
|
||||
| `0x0640000` | `SCN2681` `duart1` | A = **host**, B = **printer** | the level-1 host-port test writes this window's CRA/MR/CSRA (`06400002`, `06400000`, `06400001`) |
|
||||
| `0x0620000` | `SCN2681` `duart2` | A = **keyboard**, B = **mouse** | ROM strings `E - DUART1, CHANNEL A (HOST)`, `H - DUART2, CHANNEL A (KEYBOARD)`, `I - DUART2, CHANNEL B (MOUSE)`; the level-0 I/O test drives this window |
|
||||
|
||||
Both are gated on `VT1200_DUART` so the no-env baseline is byte-identical. The entries
|
||||
cover only the DUART's 16 registers (32 bytes = `0x100` map units) and are registered
|
||||
**after** the board-window model, so the rest of a window still falls through to the
|
||||
model (a window can mix a DUART with other board registers, as the brief warned).
|
||||
|
||||
The monitor-selection switch is modelled the way the hardware does it: it is
|
||||
`IP4-IP6` of the DUART at `0x0640000D`, driven high (`0x70` = "VR150") with
|
||||
`ip4_w/ip5_w/ip6_w`. Confirmed in the log: `R 0640000D … val=00F0`, and the firmware's
|
||||
`andi 0x70` yields `0x70` (a legal monitor) rather than "Undefined monitor switch
|
||||
setting".
|
||||
|
||||
Loopback wiring: `a_tx_cb → rx_a_w` and `b_tx_cb → rx_b_w`, selectable per DUART
|
||||
(`VT1200_DUART_LB1` for the host/printer DUART, `VT1200_DUART_LB2` for keyboard/mouse)
|
||||
to model the service guide's loopback connectors. When MR2 bit 7:6 = `10` the chip
|
||||
does its own **internal** loopback and does not call the TX callback, so this wiring is
|
||||
harmless there and supplies **external** loopback for the normal-mode tests.
|
||||
|
||||
New env knobs (all off by default; documented in the driver header):
|
||||
|
||||
```
|
||||
VT1200_DUART=1 map the SCN2681s (both windows unless SEL says otherwise)
|
||||
VT1200_DUART_SEL=1|2|12 which DUART ("1" = 0x0640000, "2" = 0x0620000; default 12)
|
||||
VT1200_DUART_LB1=1 external TX→RX on DUART1 A/B (host/printer connector)
|
||||
VT1200_DUART_LB2=1 external TX→RX on DUART2 A/B (keyboard/mouse connector)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. The remap, and the evidence for it
|
||||
|
||||
I did **not** assume it; I verified it, and it comes out as `duart_byte = handler_off * 2`,
|
||||
i.e. `duart_register = handler_off`.
|
||||
|
||||
Facts:
|
||||
|
||||
* Map units are **bits** (`tms34010.h`: program space `data_width = 1`), and a handler
|
||||
on an entry at `base` receives `(bitaddr − base) >> 4`. So byte offset within the
|
||||
window = `handler_off * 2`.
|
||||
* The datasheet register map has register *N* at byte *N* (for the registers in use:
|
||||
`0x00` MR1A/MR2A, `0x01` SRA/CSRA, `0x02` CRA, `0x03` RHRA/THRA, `0x05` ISR/IMR,
|
||||
`0x08` MR1B/MR2B, `0x09` SRB/CSRB, `0x0A` CRB, `0x0D` IP/OPCR, `0x0F` Stop/Reset OP).
|
||||
* **Therefore register index = `dup_byte / 2` = `handler_off`.**
|
||||
|
||||
The observed traffic fits only under that rule:
|
||||
|
||||
* The firmware's generic DUART driver (`L3FA90960`, bit `0x3FA90960`) writes the command
|
||||
sequence `0xCA,0x2A,0x3A,0x4A,0x5A,0x1A` to operand `+0x20` (→ off `0x02`) and the same
|
||||
sequence to operand `+0xA0` (→ off `0x0A`). `0x02` = **CRA**, `0x0A` = **CRB**; the
|
||||
values are coherent 68681 command nibbles (`0xC` enable Rx, `0x2` reset Rx, `0x3` reset
|
||||
Tx, `0x4` reset errors, `0x5` reset break-change, `0x1` reset MR pointer). No other
|
||||
`off` assignment makes those coherent.
|
||||
* It then writes `MR1A = 0x13` and `MR2A = 0x07` to off `0x00` (MR pointer toggles),
|
||||
`CSRA = 0x99` to off `0x01`, `MR1B/MR2B = 0x07` to off `0x08`, `CSRB = 0x99` to off
|
||||
`0x09`, and the IMR value to off `0x05`. All match the MAME map.
|
||||
* The host-port test writes `MR1A = 0x12`, `MR2A = 0x80` to off `0x00` (i.e. **local
|
||||
loopback**: MR2 bit 7:6 = `10`), `CSRA = 0x88` to off `0x01`, and CRA commands
|
||||
(`0x05` = enable Tx+Rx, `0x0A` = disable) to off `0x02` — then reads off `0x01` (SRA)
|
||||
and off `0x03` (RHRA). The monitor switch at `0x0640000D` reads as `IP` (off `0x0D`),
|
||||
bits 4-6, exactly as the service guide describes.
|
||||
* The register value travels in the **low byte** of the firmware's 16-bit move (the
|
||||
board log shows `val=0088` for CSRA), which is why the driver passes `data & 0xff`.
|
||||
|
||||
Cross-check against the brief's own numbers: `byte = off*2` maps the brief's hot
|
||||
`06400002` → byte `0x04` and `0640000A` → byte `0x14`, which the brief labels CRA and
|
||||
CRB. `0x04` = register `0x02` = CRA, `0x14` = register `0x0A` = CRB. Consistent.
|
||||
|
||||
(VT240, DEC's own terminal, wires the same part as `m_duart->read(offset >> 1)` on a
|
||||
16-bit bus with `.umask16(0x00ff)` at `0172000` — the same "register = byte/2, value in
|
||||
the low byte" convention. Independent corroboration.)
|
||||
|
||||
---
|
||||
|
||||
## 3. Acceptance test — before / after
|
||||
|
||||
**Baseline (board model only), 900 frames** (`cap.lua`), confirmed this session:
|
||||
|
||||
```
|
||||
'LEVEL1/2 TESTING ** '
|
||||
'LEVEL1/2 ERROR -> HOST PORT - 15 '
|
||||
```
|
||||
|
||||
**With the DUART wired at the host window** — the config that actually reaches the
|
||||
host-port test (DUART2 left to the board model so level 0 still passes):
|
||||
|
||||
```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 \
|
||||
$MAME vt1200 -rompath harness/mame/roms -video none -sound none -nothrottle \
|
||||
-debug -debugger none -debuglog -autoboot_script harness/mame/cap.lua -autoboot_delay 0
|
||||
```
|
||||
|
||||
```
|
||||
'LEVEL1/2 TESTING ** '
|
||||
'LEVEL1/2 ERROR -> HOST PORT - 15 ' <-- STILL PRESENT
|
||||
```
|
||||
|
||||
So the acceptance criterion is **not met**, and I can say exactly why (§4).
|
||||
|
||||
For completeness, mapping **both** DUARTs (`VT1200_DUART_SEL` unset) changes the
|
||||
transcript to the **interactive diagnostics menu** (`VERSION 2.08 - SELECTED TEST
|
||||
TYPE:` + the A–Z test list). As the owner noted, that menu is what the firmware falls
|
||||
into on a failure, and I confirm the specific failure: the **level-0 I/O data-path
|
||||
test** (`L3FDCE470`) writes `0xE1` to DUART2's THRA and then requires **10 received
|
||||
bytes**; a real DUART with no responder has none, so it diverts to the menu. That is
|
||||
the same underlying requirement as §4, one level earlier.
|
||||
|
||||
---
|
||||
|
||||
## 4. Why HOST PORT still fails — the specific, measured condition
|
||||
|
||||
The host-port test is reached and executed against the real DUART. Driver-side
|
||||
(`VT1200_BOARD_LOG`) shows the test's whole transaction against `0x0640000`, at PCs
|
||||
`3FA71F00…3FA76390`:
|
||||
|
||||
```
|
||||
W 06400002 val=003A / 002A / 004A / 001A ; CRA commands (reset Tx/Rx/err/MR ptr)
|
||||
W 06400001 val=0088 ; CSRA
|
||||
W 06400000 val=0012 ; MR1A
|
||||
W 06400000 val=0080 ; MR2A = 0x80 -> local loopback
|
||||
W 06400002 val=0005 ; CRA: enable Tx + Rx
|
||||
W 06400002 val=000A ; CRA: disable Tx + Rx
|
||||
R 06400001 val=0000 ; SRA
|
||||
R 06400003 val=0000 ; RHRA
|
||||
```
|
||||
|
||||
**There is no write to off `0x03` (THRA/RHRA) anywhere on this path** — the only
|
||||
`06400003` line in the whole log is the read above. So the transmitter is never fed.
|
||||
|
||||
I made that claim airtight with a **full, non-deduplicated** DUART access log
|
||||
(`VT1200_DUARTLOG`), 1400 frames, both DUARTs:
|
||||
|
||||
```
|
||||
total DUART1 accesses: 134
|
||||
writes to reg 3 (THRA): 0
|
||||
writes to reg B (THRB): 0
|
||||
reg-write histogram (DUART1): reg2=45 regA=24 regF=10 regE=7
|
||||
reg5=6 regD=4 reg0=4 reg1=2 reg4=1
|
||||
```
|
||||
|
||||
Every write is a mode/command/status register (MR, CSRA/CRA, ACR, IMR, CRB, OPCR,
|
||||
OP bits). **No transmit register is ever written, on either channel of either DUART.**
|
||||
|
||||
Breakpointing the test's own comparisons (`harness/mame/duart_probe.lua`) gives the
|
||||
expected vs actual:
|
||||
|
||||
```
|
||||
3FA761E0 SRA & 0xF0 -> 0x00 ; test requires 0x10
|
||||
3FA76390 RHRA -> 0x00 ; test requires 0x0A, then 0x1F, then 0x0A
|
||||
```
|
||||
|
||||
The expected values come from a RAM table at bit `0x270220` (dumped from main RAM):
|
||||
`0xAA, 0xFF, 0xAA, 0x55, …`, masked by the table at `0x270470` (`0x1F,…`), i.e.
|
||||
`0xAA & 0x1F = 0x0A`, `0xFF & 0x1F = 0x1F`. Two details then fit together exactly:
|
||||
|
||||
* the test first waits for a receive count to reach **5** (`*struct+0x320 ≥ 5`), and
|
||||
* it requires the **overrun** bit (`SRA & 0xF0 == 0x10`) — which is what a 5-byte burst
|
||||
into the 4-deep 68681 Rx FIFO produces.
|
||||
|
||||
In other words the test expects a **5-byte external pattern** to have arrived on the
|
||||
receiver, overflowing the FIFO, and then reads the status and the first bytes back.
|
||||
Nothing in the firmware transmits that pattern, so the data can only come from off-chip.
|
||||
|
||||
This is why **MAME's `SCN2681` is necessary but not sufficient**: the model is faithful,
|
||||
but the test's data source is external. The owner's fallback of "wire TX back to RX"
|
||||
cannot help, because there is no TX activity to loop. (Both were tried: internal
|
||||
loopback is already set by the firmware via MR2=`0x80`; external TX→RX was wired with
|
||||
`VT1200_DUART_LB1` — the transcript is identical.)
|
||||
|
||||
### What would be needed (an honest statement, not done)
|
||||
|
||||
To make the line disappear, something must play the **host / active loopback**:
|
||||
e.g. a driver-side callback that drives `duart1->rx_a_w` with the expected serial
|
||||
pattern (`0xAA,0xFF,0xAA,0x55,…`) in response to the receiver being enabled. That is a
|
||||
model of the **host protocol**, not of the DUART, and it is exactly the kind of
|
||||
hand-rolled stand-in the owner asked me to avoid unless the real device is
|
||||
impractical. I did **not** add it. I am flagging it as the concrete remaining piece.
|
||||
|
||||
---
|
||||
|
||||
## 5. Regression
|
||||
|
||||
With **no** `VT1200_*` env vars set, `progress.lua` at 200 frames still reports:
|
||||
|
||||
```
|
||||
PC=3FDCE400 samples=199
|
||||
```
|
||||
|
||||
All new behaviour is behind `VT1200_DUART` / `VT1200_DUART_SEL` /
|
||||
`VT1200_DUART_LB1` / `VT1200_DUART_LB2`; the device entries are only registered when
|
||||
`VT1200_DUART` is set, and the SCN2681s are instantiated but idle (no timer running, no
|
||||
interrupt wired) otherwise.
|
||||
|
||||
## 6. MAME-tree changes
|
||||
|
||||
**None this task.** The only local MAME change remains the earlier, already-recorded
|
||||
`REFCNT` edit in `src/devices/cpu/tms34010/tms34010.cpp` (with its revert note inline).
|
||||
MAME's `mc68681.cpp` already implements the 68681's local loopback (MR2 bit 7:6 = `10`)
|
||||
and the break-in-loopback behaviour; that is **stock upstream code**, not a local
|
||||
modification (verified: `git status` in the MAME tree shows only `tms34010.cpp` and
|
||||
`mame.lst` modified).
|
||||
|
||||
## 7. What I could not determine
|
||||
|
||||
* The **source** of the host-port test's expected pattern. Every transmit path I could
|
||||
find in the ROM (`*aX(0x30)` writes at bits `3FA79C00`, `3FA7B8E0`, `3FA7BAA0`) is in
|
||||
a *different* routine that is **not reached** by the level-1 host-port test. I could
|
||||
not find a THRA write on the host-port path, and the driver-side log agrees. I do not
|
||||
know whether on real hardware the pattern comes from the connected host or from a
|
||||
loopback connector with active logic.
|
||||
* Whether the VT1200's level-1 host-port test is *expected to pass* with nothing but a
|
||||
passive loopback plug. The service guide says level-1 "completes within 25 seconds"
|
||||
and mentions loopback connectors, but the firmware's own comparison (5-byte pattern +
|
||||
overrun) implies an active responder.
|
||||
* The DUART interrupt path. There is a DUART ISR in the ROM (`L3FA78D90`), and the
|
||||
second host-port sub-test waits on a flag that an ISR would set; I did not wire
|
||||
`irq_cb()` to the CPU, and did not establish whether the host-port test needs it. The
|
||||
failing sub-test (`L3FA753D0`) does **not** wait on that flag — it reads the chip
|
||||
directly — so the IRQ is not what fails it.
|
||||
|
||||
## 8. Files added / changed
|
||||
|
||||
* `harness/mame/vt1200.cpp` — `SCN2681` at `0x0640000` (host/printer) and `0x0620000`
|
||||
(keyboard/mouse); `duart1_r/w`, `duart2_r/w` with `register = off`, `data & 0xff`;
|
||||
optional external TX→RX loopback; monitor switch on `IP4-IP6`; all gated on
|
||||
`VT1200_DUART*`. Doc block updated. Also `VT1200_DUARTLOG` (full, non-deduplicated
|
||||
DUART access log — the proof in §4).
|
||||
* `harness/mame/duart_probe.lua` — breakpoints on the host-port test's comparisons and
|
||||
register writes (the evidence in §4).
|
||||
* `harness/mame/DUART_REPORT.md` — this report.
|
||||
@@ -0,0 +1,24 @@
|
||||
local machine = manager.machine
|
||||
local cpu = machine.devices[':maincpu']
|
||||
local dbg = machine.debugger
|
||||
if not dbg then print("ERROR: need -debug"); machine:exit() return end
|
||||
local function bp(addr, msg)
|
||||
dbg:command(string.format('bpset %s,,{ printf "%s\\n", pc, a7, a9, a11, a13 ; g }', addr, msg))
|
||||
end
|
||||
bp("3fa761e0", "SRA-highnibble pc=%08X a7=%08X a9=%08X a11=%08X a13=%08X")
|
||||
bp("3fa76f30", "CSRA-WRITE pc=%08X a7=%08X a9=%08X a11=%08X a13=%08X")
|
||||
bp("3fa76f50", "CSRA-WRITTEN pc=%08X a7=%08X a9=%08X a11=%08X a13=%08X")
|
||||
bp("3fa76340", "RHRA-READ pc=%08X a7=%08X a9=%08X a11=%08X a13=%08X")
|
||||
bp("3fa76390", "RHRA-CMP pc=%08X a7=%08X a9=%08X a11=%08X a13=%08X")
|
||||
bp("3fa76010", "CRA-0A-pre pc=%08X a7=%08X a9=%08X a11=%08X a13=%08X")
|
||||
-- also the registry writes by the wrapper
|
||||
bp("3fa71f00", "CRA-3A pc=%08X a7=%08X a9=%08X a11=%08X a13=%08X")
|
||||
bp("3fa721f0", "CSRA-tbl-write pc=%08X a7=%08X a9=%08X a11=%08X a13=%08X")
|
||||
print("probe armed")
|
||||
local n=0
|
||||
emu.register_frame_done(function()
|
||||
n=n+1
|
||||
if n >= tonumber(os.getenv("CAP_MAX") or "1400") then print("done "..n); machine:exit() end
|
||||
end, "duart-probe2")
|
||||
dbg.visible_cpu = cpu
|
||||
dbg.execution_state = 'run'
|
||||
+180
-1
@@ -44,6 +44,16 @@
|
||||
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).
|
||||
|
||||
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.
|
||||
@@ -63,6 +73,7 @@
|
||||
#include "emu.h"
|
||||
#include "cpu/tms34010/tms34010.h"
|
||||
#include "emupal.h"
|
||||
#include "machine/mc68681.h"
|
||||
#include "screen.h"
|
||||
|
||||
#include <cstdio>
|
||||
@@ -94,6 +105,8 @@ public:
|
||||
, m_screen(*this, "screen")
|
||||
, m_palette(*this, "palette")
|
||||
, m_mainram(*this, "mainram")
|
||||
, m_duart1(*this, "duart1")
|
||||
, m_duart2(*this, "duart2")
|
||||
, m_vram(*this, "vram")
|
||||
{
|
||||
}
|
||||
@@ -122,6 +135,12 @@ 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.
|
||||
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);
|
||||
|
||||
private:
|
||||
void program_map(address_map &map) ATTR_COLD;
|
||||
|
||||
@@ -134,6 +153,12 @@ private:
|
||||
required_device<palette_device> m_palette;
|
||||
required_shared_ptr<u16> m_mainram;
|
||||
|
||||
// The two DUARTs (Signetics SCN2681 / Motorola MC68681 family). The
|
||||
// firmware's level-1 I/O tests drive them byte-per-register (register N at
|
||||
// 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)
|
||||
|
||||
// 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
|
||||
// forwards its window callbacks and configures the model from the env knobs.
|
||||
@@ -220,6 +245,13 @@ private:
|
||||
u16 lowlog_r(offs_t off);
|
||||
void lowlog_w(offs_t off, u16 data);
|
||||
void log_low(char kind, u32 addr, u16 value);
|
||||
|
||||
// VT1200_DUARTLOG=<path>
|
||||
// EVERY DUART access (not de-duplicated), so a write that happens once per
|
||||
// iteration cannot hide. Used to prove whether the transmit register is
|
||||
// ever written on the host-port test path.
|
||||
FILE *m_duartlog = nullptr;
|
||||
void log_duart(int win, char kind, u32 reg, u16 value);
|
||||
};
|
||||
|
||||
void vt1200_state::machine_start()
|
||||
@@ -332,6 +364,31 @@ void vt1200_state::machine_start()
|
||||
fprintf(m_lowlog, "# kind bitaddr pc value -- transparent low-RAM access log\n");
|
||||
}
|
||||
|
||||
if (const char *p = getenv("VT1200_DUARTLOG"))
|
||||
{
|
||||
m_duartlog = fopen(p, "w");
|
||||
if (m_duartlog)
|
||||
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
|
||||
// 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"))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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",
|
||||
@@ -340,6 +397,7 @@ void vt1200_state::machine_start()
|
||||
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);
|
||||
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)
|
||||
@@ -575,12 +633,28 @@ void vt1200_state::program_map(address_map &map)
|
||||
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));
|
||||
}
|
||||
|
||||
// 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
|
||||
// in both cases.
|
||||
@@ -627,6 +701,88 @@ void vt1200_state::board1_w(offs_t off, u16 data) { board_write(1, off, data); }
|
||||
void vt1200_state::board2_w(offs_t off, u16 data) { board_write(2, off, data); }
|
||||
void vt1200_state::board3_w(offs_t off, u16 data) { board_write(3, off, data); }
|
||||
|
||||
// --- DUARTs ---------------------------------------------------------------
|
||||
//
|
||||
// Remap. The map entry base is a BIT address and the handler receives
|
||||
// (bitaddr - base) >> 4, so byte offset within the window = off * 2. The
|
||||
// observed access pattern is a canonical 68681 setup (CRA writes of
|
||||
// 0x3A/0x2A/0x4A/0x1A, CSRA=0x88/0x99, MR1A/MR2A at off 0) and those offsets
|
||||
// line up with the datasheet register map only when register index = off:
|
||||
// byte 2N (harness off N) with the register in the low byte of the 16-bit
|
||||
// access. So:
|
||||
//
|
||||
// firmware operand 0x064000D0 -> bit addr -> harness off 0x0D
|
||||
// -> byte offset 0x1A = register 0x0D (IP)
|
||||
//
|
||||
// i.e. duart_byte = handler_off * 2 and duart_register = handler_off.
|
||||
//
|
||||
// Each window is 16 registers (32 bytes = 0x100 map units); the entry only
|
||||
// covers that, so anything else in the window still falls through to the
|
||||
// board model below it (a window may mix a DUART with other board registers).
|
||||
// The firmware presents the register value in the LOW byte of its 16-bit
|
||||
// move (the board log shows value 0x0088 for CSRA), hence `data & 0xff`.
|
||||
|
||||
u16 vt1200_state::duart1_r(offs_t off)
|
||||
{
|
||||
const u32 a = VT1200_WIN3 | (u32(off) & 0xffff);
|
||||
if ((off & 0xffff) < 0x10)
|
||||
{
|
||||
const u16 v = m_duart1->read(u32(off) & 0xf);
|
||||
log_pc_access('R', a, v);
|
||||
log_duart(1, 'R', u32(off) & 0xf, v);
|
||||
return v;
|
||||
}
|
||||
return board_read(3, off);
|
||||
}
|
||||
|
||||
void vt1200_state::duart1_w(offs_t off, u16 data)
|
||||
{
|
||||
const u32 a = VT1200_WIN3 | (u32(off) & 0xffff);
|
||||
if ((off & 0xffff) < 0x10)
|
||||
{
|
||||
m_duart1->write(u32(off) & 0xf, u8(data & 0xff));
|
||||
log_pc_access('W', a, data);
|
||||
log_duart(1, 'W', u32(off) & 0xf, data);
|
||||
return;
|
||||
}
|
||||
board_write(3, off, data);
|
||||
}
|
||||
|
||||
u16 vt1200_state::duart2_r(offs_t off)
|
||||
{
|
||||
const u32 a = VT1200_WIN2 | (u32(off) & 0xffff);
|
||||
if ((off & 0xffff) < 0x10)
|
||||
{
|
||||
const u16 v = m_duart2->read(u32(off) & 0xf);
|
||||
log_pc_access('R', a, v);
|
||||
log_duart(2, 'R', u32(off) & 0xf, v);
|
||||
return v;
|
||||
}
|
||||
return board_read(2, off);
|
||||
}
|
||||
|
||||
void vt1200_state::duart2_w(offs_t off, u16 data)
|
||||
{
|
||||
const u32 a = VT1200_WIN2 | (u32(off) & 0xffff);
|
||||
if ((off & 0xffff) < 0x10)
|
||||
{
|
||||
m_duart2->write(u32(off) & 0xf, u8(data & 0xff));
|
||||
log_pc_access('W', a, data);
|
||||
log_duart(2, 'W', u32(off) & 0xf, data);
|
||||
return;
|
||||
}
|
||||
board_write(2, off, data);
|
||||
}
|
||||
|
||||
void vt1200_state::log_duart(int win, char kind, u32 reg, u16 value)
|
||||
{
|
||||
if (!m_duartlog)
|
||||
return;
|
||||
fprintf(m_duartlog, "%d %c reg=%X val=%04X pc=%08X\n",
|
||||
win, kind, reg, value, (unsigned)m_maincpu->state_int(TMS34010_PC));
|
||||
fflush(m_duartlog);
|
||||
}
|
||||
|
||||
void vt1200_state::scanline(screen_device &screen, bitmap_ind16 &bitmap, int scanline,
|
||||
const tms340x0_device::display_params *params)
|
||||
{
|
||||
@@ -801,6 +957,29 @@ void vt1200_state::vt1200(machine_config &config)
|
||||
// 256 entries so the TMS34010 renderer can encode multi-bit pixels; the
|
||||
// terminal's real output stage is a later question.
|
||||
PALETTE(config, m_palette).set_entries(256);
|
||||
|
||||
// The two Signetics SCN2681 DUARTs (same family as the Motorola MC68681:
|
||||
// 2681 = RD/WR strobes, 68681 = R/W + DTACK + interrupt vector). The
|
||||
// VT1200 firmware drives them with the generic 68681 register map, and the
|
||||
// level-1 host-port test uses the chip's own local loopback (MR2 bits 7:6 =
|
||||
// 10), so the SCN2681 fits and needs no vector-register behaviour.
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
INPUT_PORTS_START(vt1200)
|
||||
|
||||
Reference in new issue
Block a user