docs: establish causally what raises HOST PORT - 15; resolve the sub-test polarity; rule out the loopback-clock deviation as the cause

Call site (not a register snapshot): 3FA84FB0 calla L3FA6E4B0; 3FA84FE0 stores a8;
3FA85000 move a8,a8 / 3FA85010 jreq; the nonzero path ends at 3FA85360-3FA853A0 with
a11 = 0x400 returned.  Live breakpoint confirms code=0x400 enters the HOST PORT handler
3FDE9D70.  So the quantity to zero is L3FA6E4B0's return value (all five descriptor
fields zero).

Polarity resolved: L3FA71BC0/L3FA753D0 return their 32-bit flag word, which is 0 iff all
their checks pass, so L3FA6E4B0's bits are failure bits.  My earlier "L3FA753D0 returns
0x408 while its checks pass" was a misread -- the same 32-bit word also carries the
setf-4/setf-2 fields at a13(0xc4/c8/ca/cc).  The checks are therefore the right quantity.

Loopback-clock deviation (SCN2681 p.12 item 2): real but NOT the cause.  CSRA=0x88 gives
TX==RX==2400 baud, so the clock source changes only the sampling phase, not the bit count
or the completion time; and the received bytes are already correct, so MAME samples the
TX bit centres.  Not changed (would be banking a green).  Measured residual: a ~1-bit
write->receive offset (completion at W+1.07 char vs a 1.00-char write interval), sourced
from the ~1 bit between a THRA write and the first transmitted bit; forcing it to zero
corrupted the sampling phase, so the half-bit TX offset is load-bearing.

Model vs datasheet items 3/4/5: matches (loopback fills the FIFO with the receiver
disabled, which is what CRA=5 then CRA=0xA relies on).  Regression holds.
This commit is contained in:
SDD committed 2026-10-16 12:18:30 +03:00
1 parent 70e2486d57
commit 03e6a88b22
2 files changed
+110

No files matched your search

+87
View File
@@ -731,3 +731,90 @@ independently before it is written.
* Console: `'LEVEL1/2 TESTING ** '` then `'LEVEL1/2 ERROR -> HOST PORT - 15 '` — **still
present**; no next gate.
* Regression (no `VT1200_*`): `PC=3FDCE400 samples=199` — **holds**.
---
# WHAT MAKES THE CALLER RAISE 0x400 — resolved empirically
The owner's gating worry (if the caller does not actually turn a nonzero `L3FA6E4B0`
return into `0x400`, then fixing the sub-tests' checks cannot clear `HOST PORT - 15`)
is resolved. Breakpoints at the call site and at the handler, one run
(`harness/mame/hp_disp.lua`):
```
DISPATCH-READ code=0000FFFF <- 'LEVEL1/2 TESTING **'
CALL-L3FA6E4B0 <- call site 3FA84FB0
AFTER-CALL a8=00000006 <- call site 3FA84FE0 stores a8
CALLER-ERR-RET a8=00000006 a11=00000400 <- error path sets a11=0x400, returns it
DISPATCH-READ code=00000400
HOSTPORT-HANDLER code=00000400 <- handler 3FDE9D70 prints HOST PORT - 15
```
So the chain is confirmed **at the call site**, not from a register snapshot:
* `3FA84FB0 calla L3FA6E4B0`; `3FA84FE0 move a8, *a13(0x160)` (the return);
`3FA85000 move a8,a8 ; 3FA85010 jreq L3FA853D0` — **any nonzero takes the error path**;
that path ends at `3FA85360 movi 0x400,a11 ; 3FA85380 *a7(0x40)=a11 ; 3FA853A0 a8=a11`.
* `0x400` **is** the HOST PORT dispatch code (measured live at `3FDE9D70`). My earlier
static reading of the dispatcher's `subi` chain suggested `0x3F8`; dis34's `subi`
rendering is misleading here and the live value is authoritative.
**The quantity to zero is `L3FA6E4B0`'s return value**: it must be 0, i.e. *all five*
descriptor fields must be 0.
## Polarity — resolved, and my earlier caveat was wrong
`L3FA71BC0` returns `*a13(0xc0)` (its 32-bit flag word) and `L3FA753D0` returns the same
word (`3FA76550`) or `a9`, which is set to that word at `3FA76440` and is untouched by the
success loop — so on success both return **0**. Therefore `L3FA6E4B0`'s bits are
**failure** bits, and "nonzero" *does* mean "this sub-test failed its checks".
The earlier "`L3FA753D0` returns `0x408` while all its checks pass" was a **misread**: the
32-bit flag word is not the four loop flags alone — `3FA76480`–`3FA76520` also write a
4-bit field at `a13(0xc4)` and 2-bit fields at `a13(0xc8/ca/cc)`, which are inside the
same 32-bit read. So `0x408` is a genuine failure, not a false positive. This also
removes the concern that the checks are the wrong quantity: they are the right one.
## Is the local-loopback clock deviation the cause? No.
Mechanism, stated explicitly (the owner's requirement): `CSRA = 0x88` selects the same
`calc_baud` value for TX and RX (the `rx` argument is unused in `duart_base_device`), so
the two clocks are the **same rate** (2400 baud). Clocking the loopback receiver from the
transmit clock therefore changes only the **sampling phase/edges**, not the rate — and it
does **not** change how many bit-times the receiver takes (6.5) nor when it finishes.
Measured, it is not even observable: the received bytes are **correct**
(`0x0A,0x1F,0x0A` = the pattern masked by 0x1F), so MAME already samples the TX bit
centres; and the completion is `sync + 6.5` bits either way. **Verdict: a real deviation
(SCN2681 p.12 item 2) but NOT the cause of the residual**, and it cannot move the symptom
— so I did **not** change it rather than bank a green.
The measured residual is a **~1-bit write->receive offset**: `RX completion = W + 1.07
char` while the write interval is `1.00 char`, so the received byte misses the next
snapshot by 0.5 bit. Its source is the **~1 bit between a THRA write and the first
transmitted bit** (`sync = W + 2605`, bit = 2604) — i.e. the transmit clock's first edge
is at `2*m_tra_rate`, not `m_tra_rate`. I tried forcing it to `m_tra_rate` and to zero;
the first did nothing and the second **corrupted the received bytes** (sampling moved to
the bit boundaries), so the half-bit TX offset is load-bearing for the sampling phase.
Not banked; recorded as the open item.
## The datasheet items checked against our model
* item 3 (TxDA held High) and item 4 (RxDA ignored): modelled — `tra_callback()` does not
drive the TX pin in loopback and feeds the bit to `rx_w` instead; the external RxD input
is unused in this mode.
* item 5 (transmitter must be enabled, receiver need not be): **matches** —
`rcv_complete()` pushes when `rx_enabled || loopback`, so the FIFO keeps filling with
the receiver disabled, which is what the firmware's `CRA=0x05` then `CRA=0x0A`
(bit1 = disable Rx, bit3 = disable Tx) sequence relies on. `write_CR(0x0A)` also
disables the transmitter, and MAME lets the in-flight character finish (the tra timer is
already running), so the 5-byte burst completes.
* One minor deviation, not biting here: `write_CR`'s bit1 handler clears `RxRDY`, whereas
the datasheet says a receiver *disable* preserves the FIFO and its status. `read_rx_fifo`
re-derives `RxRDY`, and the test's SRA check looks only at the high nibble (`OVR`), so it
does not affect this test.
## Acceptance / regression (current tree: edit 1 reverted, edit 2 kept)
* Console: `HOST PORT - 15` — **still present**; no next gate.
* Regression: `PC=3FDCE400 samples=199` — **holds**.
+23
View File
@@ -0,0 +1,23 @@
-- hp_disp.lua -- what code value actually reaches the HOST PORT handler?
-- Dispatcher reads the status code from bit 0x16F0 (CODE22_REPORT). HOST PORT
-- handler = 3FDE9D70. Also log the host-port caller's 0x400 return site.
local machine = manager.machine
local cpu = machine.devices[':maincpu']
local dbg = machine.debugger
if not dbg then print("ERROR: no debugger"); machine:exit(); return end
local function bp(addr, body)
dbg:command('bpset ' .. addr .. ',,{ ' .. body .. ' ; g }')
end
bp("3fde9d70", 'printf "HOSTPORT-HANDLER code=%08X\\n", d@16F0')
bp("3fa84fb0", 'printf "CALL-L3FA6E4B0\\n"')
bp("3fa84fe0", 'printf "AFTER-CALL a8=%08X\\n", a8')
bp("3fa853a0", 'printf "CALLER-ERR-RET a8=%08X a11=%08X\\n", a8, a11')
bp("3fa853d0", 'printf "CALLER-OK-PATH code=%08X\\n", d@16F0')
bp("3fdf0630", 'printf "DISPATCH-READ code=%08X\\n", d@16F0')
local n = 0
emu.register_frame_done(function()
n = n + 1
if n >= tonumber(os.getenv("HP_FRAMES") or "700") then print("done "..n); machine:exit() end
end, "vt1200-hp-disp")
dbg.visible_cpu = cpu
dbg.execution_state = 'run'