Serial I/O — MOS Interface

Reference for MOS-level serial port use. For the chip-level details, see 6850-acia and serial-ula.

The 5-pin DIN socket

PinSignalDirection
1Signal ground
2CTS — clear to sendinput
3TD — transmit dataoutput
4RD — receive datainput
5RTS — request to sendoutput

Notch on metal shield → left side of computer (correct orientation). The plug inserts physically rotated 180° too — won’t damage but won’t work.

BBC-to-BBC null modem: cross-wire TD↔RD and RTS↔CTS on each end, plus ground.

RTS/CTS semantics on the BBC

Not standard EIA RS232. The BBC’s RTS is asserted unless the receive buffer is full, and the BBC stops transmitting when its CTS input is unasserted. This is closer to standard DTR/DSR than to RS232 RTS/CTS — useful caveat when connecting a BBC to a PC.

For PC interop, you typically need a hardware cable that:

  • Cross-wires the data lines (TD↔RD).
  • Cross-wires RTS↔CTS and asserts the PC’s DTR/DSR/DCD permanently.

A “null modem with hardware handshake” cable is what works.

OSBYTE summary

OSBYTEFunctionNotes
&02 (2)Select input streamX=0 kbd, X=1 RS423, X=2 both
&03 (3)Select output streamBit-packed (see below)
&07 (7)Set rx baudX = code 0-8
&08 (8)Set tx baudX = code 0-8
&89 (137)Cassette motorX=0 off, X=1 on
&9C (156)R/W 6850 control register + OS shadowTube-safe path
&B0 (176)R/W CFS timeout counterDecremented per vsync (50 Hz)
&B1 (177)R/W input source flagUsed by &02
&B5 (181)R/W RS423 modeSee below
&BF (191)R/W RS423 use flag (bit 7)Bit 7 set = free
&C0 (192)Read shadow of 6850 control regDon’t write — would desync
&CB (203)R/W RS423 handshake thresholdDefault 9 bytes
&CC (204)R/W RS423 input suppressionNon-zero = ignore input
&CD (205)R/W RS423/cassette select&00 serial, &40 cassette
&E8 (232)R/W 6850 IRQ bit maskSee interrupts
&EC (236)R/W output stream destination flagUsed by &03
&F2 (242)Read shadow of serial ULA registerRead-only access

Baud rates

OSBYTE &07 (rx) and OSBYTE &08 (tx) take X = code:

XBaud
09600
175
2150
3300
41200
52400
64800
79600
819200

X=0 and X=7 both give 9600 baud (legacy code aliasing). Use X=7 for clarity.

Input stream selection (OSBYTE &02)

LDA #&02 : LDX #x_value : JSR &FFF4
XEffect
0keyboard only (default)
1RS423 only (keyboard disabled)
2both keyboard + RS423

Returns previous source in X.

Output stream selection (OSBYTE &03)

X is a bit field:

BitSet means
0Enable RS232/423 output
1Disable VDU
2Disable printer driver
3Enable printer independent of VDU 2/VDU 3
4Disable spooled output
6Disable printer unless preceded by VDU 1

*FX 3,0 = defaults: RS423 disabled, VDU enabled, printer follows VDU 2/VDU 3, spool follows *SPOOL.

*FX 3,1 adds RS423 output (so OSWRCH bytes go to both screen and serial).

RS423 mode (OSBYTE &B5)

Controls how received RS423 bytes are processed:

FlagBehaviour
0ESCAPE bytes generate ESCAPE events; soft-key expansion applies; input-buffer event fires per byte; cursor editing keys work
1 (default)Bytes go straight into buffer; no events; no escape recognition

Mode 1 is correct for terminal emulators and file transfer (don’t want stray &1B to abort). Mode 0 makes RS423 input behave exactly like keyboard input — useful for remote console.

Handshake threshold (OSBYTE &CB)

When the RS423 input buffer has fewer than X bytes free, the OS deasserts RTS to halt the sender. Default 9. Increase for slow-responding senders, decrease if you really need every byte of buffer (and trust the sender to stop on time).

6850 control word — basic configuration

For standard RS423 8-N-1 at the selected baud:

; 6850 control: clk÷64, 8 bits no parity 1 stop, RTS low, TX IRQ disabled, RX IRQ enabled
LDA #&95         ; 1001 0101 — bit 7=1 (RX IRQ), bits 6-5=00 (RTS low TX off), bits 4-2=101 (8-N-1), bits 1-0=10 (÷64)
LDX #&95
LDA #&9C : JSR &FFF4

For raw 7-E-1 (common for ASCII terminals):

LDA #&91         ; bits 4-2=010 (7 bits, even, 1 stop)
LDX #&91
LDA #&9C : JSR &FFF4

Bulk transfer pattern

For sending a buffer of bytes:

.send_loop
    LDA &FE08       ; status
    AND #&02        ; TDRE
    BEQ send_loop   ; wait until TX register empty
    LDA (src),Y
    STA &FE09       ; transmit
    INY
    BNE send_loop
    ; ... next page handling ...

For 9600 baud this is fine; ~960 bytes/sec means the CPU spends most of its time waiting. At 19200 it’s ~1.9 KB/sec — still loosely bounded by the serial line, not the CPU.

For higher throughput consider:

  • IRQ-driven transmit — let the TDRE IRQ wake up your buffer-drain code; foreground does other work.
  • Direct ULA write for baud-rate switches (skips the OSBYTE overhead).

CFS (cassette) tangent

The same 6850 + Serial ULA drive the cassette interface. Switching:

LDA #&CD : LDX #&40 : LDY #0 : JSR &FFF4   ; cassette mode flag
LDA #&8C : LDX #12 : JSR &FFF4             ; *TAPE 12 (1200 baud)

Back to RS423:

LDA #&CD : LDX #0 : LDY #0 : JSR &FFF4     ; serial mode flag
LDA #&07 : LDX #7 : JSR &FFF4              ; rx 9600
LDA #&08 : LDX #7 : JSR &FFF4              ; tx 9600

CFS timeout via OSBYTE &B0 is a vsync-counted (50 Hz) timer for tape block gaps.

Bypass-MOS strategies

  • Direct STA &FE09 for transmit — saves the OSWRCH dispatch but you must check status yourself.
  • Direct LDA &FE09 for receive — pulls the byte and clears RX flag in one read.
  • Custom IRQ2V handler — claim 6850 IRQs via OSBYTE &E8 mask, handle yourself for custom protocols (e.g. packet framing).
  • Bit-bang via User VIA — for arbitrary baud rates or non-standard framing (SPI etc.). Costs CPU cycles but unconstrained by the 6850’s framing rules.

See also


This wiki is curated by Claude following the LLM-Wiki methodology — a human curates source documents, the LLM compiles structured cross-linked markdown. Content may contain errors, omissions, or stale claims. For authoritative information refer to the original source documents in the bbc-documents GitHub archive.