From 03e6a88b22c760377f4870ad2cc8490027859190 Mon Sep 17 00:00:00 2001 From: SDD Date: Fri, 16 Oct 2026 12:18:30 +0300 Subject: [PATCH] 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. --- harness/mame/DUART_REPORT.md | 87 ++++++++++++++++++++++++++++++++++++ harness/mame/hp_disp.lua | 23 ++++++++++ 2 files changed, 110 insertions(+) create mode 100644 harness/mame/hp_disp.lua diff --git a/harness/mame/DUART_REPORT.md b/harness/mame/DUART_REPORT.md index b14ef4b..68cd845 100644 --- a/harness/mame/DUART_REPORT.md +++ b/harness/mame/DUART_REPORT.md @@ -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**. diff --git a/harness/mame/hp_disp.lua b/harness/mame/hp_disp.lua new file mode 100644 index 0000000..3209796 --- /dev/null +++ b/harness/mame/hp_disp.lua @@ -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'