Direct writer evidence (VT1200_STRUCTLOG): test +0x3c0 = ISR struct[idx4].0x3b0 and
+0x3d0 = the ISR index5 SRA snapshot (TxRDYA path, 3FA79690/3FA7A390); test
struct[i].0x60 = the ISR's received-status field (RxRDYA path 3FA79690). So both
sub-tests' failures are the ISR's SRA snapshots and nothing else.
L3FA71BC0 (18 flag sites probed, exactly one fires): c6, cmp 3FA73970 btst5 on
struct[i].0x60, set when bit5 = SRA PARITY ERROR is set. Observed 0x0001 (clean) in most
combinations and 0x0021 (RxRDY|parity error) in 3 -> its remaining failure is a parity
error in the received status, not a timing one. c5 (framing error) does not fire.
L3FA753D0's failure is the index4/index5 snapshots lacking FFULL/OVR (receive-count lag).
One mechanism can produce both (a receive-side sub-bit mis-sampling/offset) but the parity
error should be checked directly before attributing.
Owner's L3FA753D0 reading verified with two corrections: their PCs are the loads (the
compares are 3FA76050/E0, 3FA761E0, 3FA76390); and the flag encoding is bit N = flag
+cN (proof: the +c6 store logs as val=0040 at the word a13(0xc0)), with the caveat that
the 32-bit return also spans a13+0xd0.
Enumeration (measurement only): of the 18 flag-set sites in L3FA71BC0 and 4 in L3FA753D0,
exactly three fire -- L3FA71BC0 c6 (cmp 3FA73970 btst5 on struct[index].0x60; actual
0x0001/0x0009, bit5 clear) and L3FA753D0 c1/c0 (cmp 3FA760E0 btst1 / 3FA76050 btst4 on
the index4/index5 SRA snapshots; actual 0x05/0x07, FFULL and OVR clear). All other
checks pass; cE is a summary flag, not a check.
Task 2 citation (for independent reading): SCN2681 p.7 "Data is transferred from the
holding register to transmit shift register when it is idle..." and p.22 "TxRDY sets at
the end of the 'start bit' time. It is during the start bit that the data in the transmit
holding register is transferred...". Neither states a bit-count latency; MAME measurably
inserts ~1 bit (RX sync = THRA write + 2605 cycles, bit = 2604). Candidate deviation,
not written.
Full picture (established / ruled out / remaining discrepancy / open deviations) recorded
in DUART_REPORT.md. Acceptance still fails; regression holds.
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.
Independent verification (reference/mc68681-datasheet-verification.md) read the SCN2681
spec: p.22 "Transmitter Disable Note" -- "TxRDY sets at the end of the 'start bit' time.
It is during the start bit that the data in the transmit holding register is transferred
to the transmit shift register." That is exactly upstream's tra_callback() assertion, so
upstream was faithful and our edit contradicted the datasheet. Reverted; the revert note
records the refutation and says not to re-apply. Commit 9902722165fc's "fix two DUART
timing deviations" is therefore half false -- only the overrun edit stands.
Reverting it does NOT relocate the failure: cfg2 (edit1 off, edit2 on) gives
L3FA71BC0=0x00900040, L3FA753D0=3, L3FA767E0=0, L3FA6E4B0=6, console unchanged.
"ISR one byte ahead" explained: TxRDY re-asserts at the end of the start bit (faithful),
so the ISR writes the next byte ~1 bit into the current character, while MAME clocks the
loopback receiver from the independent receive clock -- the datasheet (p.12, local
loopback item 2) says "The transmit clock is used for the receiver". Edit 1 was masking
that deviation. Next fix identified but not written (needs the quoted sentence read
independently first, per process).
Step-5 leads: crystal 3.6864 MHz is the datasheet nominal but the board crystal is still
unconfirmed; CSRA=0x88 gives TX==RX==2400 baud (the rx arg is unused in calc_baud);
MR1A=0x10 -> 5-bit frame matches the 0x1F mask; COUNTER_READY cleared (firmware's own
Start-Counter read, not in IMR, not handled); INT1/INT2 immaterial (L3FA78D90 ignores the
mask arg; OPCR/OPR cleared, so the single INTRN is the path).
Regression holds.
Four-config A/B (IRQ wiring kept in all four; both mc68681 edits reverted in cfg 4):
the console line and L3FA6E4B0's return are identical (HOST PORT - 15 / 6) in cfg 1-4.
Verdict: PRE-EXISTING. Neither timing edit causes or relocates the failure.
Methodology correction: hp_ret.lua's labels were on the wrong rets addresses. The real
returns are L3FA6E4B0 -> 6, L3FA71BC0 -> 0x00900040, L3FA753D0 -> 0x00000408,
L3FA767E0 -> 0. hp_ret2.lua fixes the addresses.
What 6 is: not a device code. L3FA6E4B0 reduces five descriptor fields (struct+0x60/
0x80/0xa0/0xc0/0xe0, written from the four sub-test calls) to a 5-bit value; 6 = 0b00110
= "L3FA71BC0 nonzero, L3FA753D0 nonzero". The caller turns any nonzero into 0x400,
which the dispatcher maps to HOST PORT - 15. Flagged: L3FA753D0 returns 0x408 while all
its own checks pass, so "nonzero" != "failed its checks"; polarity unresolved.
Leads: COUNTER_READY is set by the firmware's own Start-Counter-Command read (reg 0x0E),
is not in IMR, and is not handled by the ISR -- not the cause. The DUART clock
(3.6864 MHz) could not be confirmed from the service guide.
Variant MAME files kept in harness/mame/mame_edits/. Regression holds.
Download the SCN2681 (Philips 1995), SCC2681, SCN68681 and MC68HC681 UM
datasheets into reference/duart-datasheet/ with pdftotext extractions, and
audit each claimed justification against them:
Claim 1 (TxRDY asserted in tra_complete, not tra_callback): REFUTED.
The quoted p.7 sentence is real, but SCN2681 p.22 says explicitly
'TxRDY sets at the end of the start bit time' -- which is exactly the
removed upstream behaviour. Upstream was right.
Claim 2 (overrun latched on the start bit, not completion): CONFIRMED.
'the overrun error status bit (SR[4]) will be set upon receipt of the
start bit of the new (overrunning) character' (p.8, also SRA[4] p.14).
Caveats: p.8 also says errors are 'strobed ... at the received
character boundary' (internal tension); and a valid start bit is
validated ~half a bit after the falling edge, not at it.
Claim 3 (3-deep FIFO, 4th held, overrun on 5th; MAME faithful): CONFIRMED.
'capacity of three characters' + 4th held in the shift register (p.8);
MAME's SIZE+1 guard overruns exactly on the 5th.
Also snapshot the worker's uncommitted MAME diff and upstream 0.289 code.
Root cause of the missing THRA write (HOST PORT test): the level-1 host-port
test's transmit is ISR-driven, and the DUART interrupt was never wired to the
CPU. The firmware installs RAM trampolines in the fixed vector table (INT1
vector 0xffffffc0 -> stub at bit 0x0f60 = JA 0x3FA92F40, which calls the DUART
ISR L3FA78D90 with (2, 0x270860, 0x6400000)). Counters: INTENB reaches 0x0006
(INT1|INT2) at the test, so interrupts are enabled.
vt1200.cpp: wire m_duart1/m_duart2 irq_cb() to CPU external interrupt 1/2
(duart1_irq_w/duart2_irq_w), gated on VT1200_DUART. Result: THRA writes go
from 0 to 20 per run; the test's SRA-overrun and RHRA-pattern checks now pass.
MAME tree (mc68681.cpp/.h), two datasheet-backed corrections with revert notes:
1. TxRDY was asserted mid-character ("not until the end of start bit time");
the datasheet asserts it at the THR->TSR transfer. Corrected.
2. The overrun error was latched when the overrunning character *completed*
(rx_fifo_push); the datasheet latches it "upon receipt of the start bit of
the new (overrunning) character". Corrected via duart_channel::rx_w().
This clears every check in the RXCHECK sub-test (flags 1 -> 0).
Acceptance is NOT met: cap.lua still prints 'LEVEL1/2 ERROR -> HOST PORT - 15'.
The next failure is the preceding L3FA71BC0 (DUART baud/timer) sub-test
returning 6; its flag-set sites were breakpointed and never fired. Regression
holds (no VT1200_* env -> PC=3FDCE400 samples=199). Details, numbers and the
datasheet audit: harness/mame/DUART_REPORT.md sections FINAL FINDINGS / UPDATE.
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.
The dispatch status is 0x100 (printed as '22' in the message). A driver-side
transparent low-RAM write log (VT1200_LOWLOG) shows the slot at bit 0x16F0 is
written by the store at 3FDF9090 from a13(0x10), which L3FA7C050 sets to the
literal 0x100 when the memory test L3F2018B0 returns non-zero. L3F2018B0 is the
level-1 video-memory test (base bit 0x04000000, count 0x20000). Its failing
checks: the 16-bit walking-bit read-back at 0x04000000 (baseline, unmapped) and,
with video RAM mapped, the REFCNT (0xC00001F0) 'must change' check.
Closed cross-check (single run): the catch-all and VT1200_CAL proxies are
directly comparable (both base 0) and resolve identically; last-wins holds
for same- and mismatched-width overlaps. The apparent conflict was a units
error: a .rw(FUNC(offs_t)) handler receives (bitaddr-base)>>4 (tms34010
Width=1, AddrShift=3), so the catch-all logs bitaddr>>4. Its 0x400000..0x40D7FF
is bit 0x04000000..0x040D7FF0 (bytes 0x800000..0x81AFFE), outside the declared
4 MiB RAM -- genuinely unmapped. The old 0x00400000 proxy was 16x too low.
Catch-all reproduces the committed log byte-for-byte and does not perturb
coverage (56 / PC=3F208950 with and without). Regression holds: PC=3FDCE400
samples=199. Report: harness/mame/XCROSS_REPORT.md