# DUART datasheet verification — SCN2681 / MC68681 Subject: the two "datasheet-backed" MAME `mc68681.cpp/.h` edits (commit `9902722165fc`) that make the VT1200 level-1 HOST PORT self-test progress, plus the incidental FIFO-depth claim. **This verification is done against the datasheet, not against the code.** The MAME tree (`/home/arti/code/k-space/mame0289`) was reverted and re-modified by another worker several times during this audit; a snapshot of the modified diff and of upstream 0.289 is kept under `reference/duart-datasheet/`. ## Sources used | File | What it is | |---|---| | `reference/duart-datasheet/SCN2681-transputer.pdf` (+ `.txt`) | **Primary.** Philips Semiconductors **SCN2681** DUART product specification, dated **1995 May 1**. The part actually fitted to the VT1200 is the Signetics/Philips SCN2681. | | `reference/duart-datasheet/SCC2681-nxp.pdf` (+ `.txt`) | Philips **SCC2681** datasheet, 2004 Apr 06 (same family, later rev) — corroboration. | | `reference/duart-datasheet/SCN68681-xdevs.pdf` (+ `.txt`) | Signetics **SCN68681** datasheet — corroboration. | | `reference/duart-datasheet/MC68681UM-nxp.pdf` (+ `.txt`) | Motorola **MC68HC681 User's Manual** (MC68681 / MC2681 family) — the Motorola-flavoured text, for the cross-family comparison. | | `reference/duart-datasheet/MC2681-motorola-retroweb.pdf`, `SCN2681-signetics-tcd.pdf` | Additional copies (scanned / image-only; kept for provenance). | | `reference/duart-datasheet/mc68681-VT1200-worker-diff.patch` | Snapshot of the worker's uncommitted MAME diff. | | `reference/duart-datasheet/mc68681-upstream-0.289-transmit.txt` | Pristine upstream (MAME 0.289 clone commit `f34f02505e32`) transmit/RX code. | Page numbers below are the **printed page numbers** in the Philips SCN2681 document (each page footer reads `1995 May 1 `). ## Summary | # | Claim | Verdict | |---|---|---| | 1 | TxRDY must be asserted in `tra_complete()` (at the THR→TSR transfer / character boundary), because "Data is transferred … when it is idle or has completed transmission of the previous character. The TxRDY conditions are then asserted again", so upstream's `tra_callback()` assertion ("not until the end of start bit time") was wrong. | **REFUTED** | | 2 | The overrun error is latched on the **start bit** of the overrunning character, not when that character completes; MAME's `rx_fifo_push()` latch is a whole character late. | **CONFIRMED** (one caveat) | | 3 | MAME's receive FIFO is faithful: 3-deep, a 4th character held, overrun on the 5th. | **CONFIRMED** | --- ## Claim 1 — TxRDY assertion point — **REFUTED** **The cited sentence is real.** SCN2681, printed page **7**, section **"TRANSMITTER OPERATION"** (also page 3-2 of the MC68HC681 User's Manual): > "The SCN2681 indicates to the CPU that it is ready to accept a character by setting > the TxRDY bit in the status register. … When a character is loaded into the Transmit > Holding Register (THR), the above conditions are negated. **Data is transferred from > the holding register to transmit shift register when it is idle or has completed > transmission of the previous character. The TxRDY conditions are then asserted > again** which means one full character time of buffering is provided." **But that sentence does not establish *when*, and the datasheet does establish it — in the opposite direction.** SCN2681, printed page **22**, section **"The CTS, RTS, CTS Enable Tx signals" → "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.**" The same sentence appears verbatim in the 2004 Philips SCC2681 (p. 22) and the Signetics SCN68681. It is the datasheet's only statement of TxRDY timing **relative to the start bit**, and it agrees with the code the worker removed. **Upstream was right.** The removed block was: ```c++ // TxRDY is not set until the end of start bit time if (++m_bits_transmitted > 1 && !m_tx_data_in_buffer && m_tx_enabled) { SR |= STATUS_TRANSMITTER_READY; update_interrupts(); } ``` `m_bits_transmitted` is reset to 0 when a character is loaded into the transmit shift register (`write_TX` idle case, and `tra_complete` transfer case). `tra_callback()` is called once per transmit-clock edge and the first call emits the **start bit** (`device_serial_interface::transmit_register_get_data_bit()` returns the frame MSB first). So the condition `++m_bits_transmitted > 1` first fires on the **second** edge — i.e. at the **end of the start-bit interval**. Upstream's comment is a near-verbatim restatement of the datasheet sentence on page 22, and upstream implemented it correctly. **What the citation actually describes** is the THR→TSR transfer — the same event upstream's `tra_complete()` **buffered** branch already handled ("Data is transferred … when … it has completed transmission of the previous character"). It says nothing about deferring the assertion to full character completion, which is what the worker's change does (it removes the page-22 assertion and relies on `tra_complete()` alone). Asserting TxRDY at `tra_complete()` moves it **~1 character earlier** than the datasheet's "end of the start bit time". **Cross-family note (as requested):** the Motorola **MC68HC681 User's Manual** does *not* contain the "end of the start bit time" sentence — it only says (§4.3.9.6, SRA[2]) "Transmitter ready is set when the character is transferred to the transmit shift register." On this point the Motorola text is the weaker/less specific of the two. The VT1200 part is the **SCN2681 (Philips/Signetics)**, so page 22 governs, and it refutes the claim. **Assessment:** the quoted sentence exists and is accurately transcribed, but it does not justify the change; the datasheet's explicit TxRDY-timing sentence supports the behaviour that was removed. --- ## Claim 2 — Overrun latch point — **CONFIRMED** (with one caveat) **The cited wording is real and explicit.** SCN2681, printed page **8**, section **"Receiver FIFO"**: > "The RHR consists of a First-In-First-Out (FIFO) stack with a capacity of three > characters. … If the FIFO is full when a new character is received, that character is > held in the receive shift register until a FIFO position is available. If an > additional character is received while this state exits[sic], the contents of the FIFO > are not affected; the character previously in the shift register is lost and the > **overrun error status bit (SR[4] will be set-upon receipt of the start bit of the new > (overrunning) character)**." Corroborated by the SRA[4] definition, SCN2681 printed page **14**: > "SRA[4] – Channel A Overrun Error — This bit, when set indicates that one or more > characters in the received data stream have been lost. **It is set upon receipt of a > new character when the FIFO is full and a character is already in the receive shift > register waiting for an empty FIFO position.**" and by the MC68HC681 User's Manual (§3.2, p. 3-3/3-4): > "… the character previously in the shift register is lost and the **overrun-error > status bit will be set upon receipt of the start bit of the new overrunning > character**." So the datasheet **does** say "start bit", not "when the character has been received". MAME's original `rx_fifo_push()` latches `STATUS_OVERRUN_ERROR` only when the overrunning character has been **fully received** (it is called from `rcv_complete()`), i.e. ~one character late. The worker's direction of correction is datasheet-backed. **Caveat 1 (datasheet is internally in tension).** SCN2681 page 8 also says: > "The parity error, framing error, overrun error and received break state (if any) are > **strobed into the SR at the received character boundary**, before the RxRDY status > bit is set." Taken alone this reads like completion timing. It is a general statement about all four error/break flags; the overrun-specific sentence ("upon receipt of the start bit") is the more specific and repeated one (it appears in all four datasheets), and SRA[4] repeats "upon receipt of a new character". So the start-bit reading is the better-supported one — but the datasheet is not perfectly self-consistent on this point, and a reader could be forgiven for the completion reading. **Caveat 2 (which "start bit"?).** The datasheet defines a start bit as *received* only after it is validated: "the state of the RxD pin is sampled each 16X clock for 7 1/2 clocks … If RxD is still Low, a valid start bit is assumed" (p. 8). "Upon receipt of the start bit" therefore means ~½ bit time **after** the falling edge, not at the falling edge itself. The worker's `duart_channel::rx_w()` latches on the falling edge (`state == 0`), i.e. about half a bit **earlier** than the datasheet. For the overrun condition this is inconsequential, but it is not a literal implementation of the text. **Assessment:** confirmed. The datasheet explicitly latches overrun at the start bit of the overrunning character; MAME's completion-time latch was late. (Minor caveats above.) --- ## Claim 3 — FIFO depth — **CONFIRMED** **Datasheet.** SCN2681, printed page **8**, section **"Receiver FIFO"**: > "The RHR consists of a First-In-First-Out (FIFO) stack with a **capacity of three > characters**." and, immediately after (same page): > "If the FIFO is full when a new character is received, **that character is held in the > receive shift register** until a FIFO position is available. If an additional > character is received while this state exits[sic] … the **overrun** error status bit > (SR[4]) will be set …" So: three characters in the FIFO, a fourth held in the receive shift register, and overrun on the **fifth** — exactly "3-deep, 4th held, overrun on 5th". The MC68HC681 User's Manual agrees ("consists of three receive holding registers. … data flowing from the receiver to the CPU is quadruply buffered", p. 3-3). **MAME.** `MC68681_RX_FIFO_SIZE == 3`, FIFO array `rx_fifo[MC68681_RX_FIFO_SIZE + 1]` (4 slots). `rx_fifo_num` is incremented only while `<= 3` (so it reaches 4 on the 4th character) and `rx_fifo_push()` sets `STATUS_OVERRUN_ERROR` when it is called with `rx_fifo_num == MC68681_RX_FIFO_SIZE + 1 == 4` — i.e. when the **5th** character is pushed. `STATUS_FIFO_FULL` is set when `rx_fifo_num == 3`. This matches the datasheet's threshold: 3 in the FIFO, 4th accepted, overrun on the 5th. **Assessment:** confirmed. The `+1` array slot and the `SIZE+1` overrun guard model the datasheet's "FIFO of three + one held in the shift register" correctly. This part of the worker's reasoning is not the source of the test failure. --- ## Incidental findings * **Local loopback clock.** SCN2681, printed page **12** (MR2A[7:6] = 10, local loopback mode): "2. **The transmit clock is used for the receiver.**" The report's §5.4 note (MAME clocks the loopback receiver from the independent receive clock) is directionally correct against the datasheet. Not one of the three claims under review. * **"Receipt of a start bit" vs falling edge** applies to Claim 2 (above). * **The datasheet's own inconsistency** ("strobed … at the received character boundary" vs "set upon receipt of the start bit") is the only place any of these three points fails to be unambiguous; on Claim 1 there is no ambiguity — page 22 is explicit. ## Bottom line Only **one** of the three cited justifications holds up as written. Claim 2 and Claim 3 are accurately quoted and correctly applied. **Claim 1 is the misleading one**: its quotation is genuine, but the datasheet contains a more specific sentence — "TxRDY sets at the end of the 'start bit' time" (SCN2681 p. 22) — that says the opposite of what the edit assumes, and which the removed upstream code implemented. Removing the `tra_callback()` assertion is **not** datasheet-backed.