Lesson 44 of 48 advanced

8251 USART and Serial Communication Basics

Talking One Bit at a Time

Open interactive version (quiz + challenge)

Real-world analogy

Parallel communication is like a wide highway with 8 lanes — all 8 bits travel side by side, arriving instantly. Serial communication is like a single-lane road — bits travel one after another in a queue. It is slower per clock, but you only need one wire instead of eight! The 8251 USART is like a traffic controller at the end of this single-lane road: it takes the 8-bit byte from the CPU, lines the bits up single-file (serialize), and sends them down the wire. On the receiving end, it collects arriving bits and reassembles them into a byte (deserialize).

What is it?

The 8251 USART (Universal Synchronous/Asynchronous Receiver/Transmitter) converts parallel data from the CPU into serial bit streams for transmission, and converts incoming serial bit streams back into parallel bytes. It supports both asynchronous mode (with start/stop bits, configurable baud rates) and synchronous mode (continuous clocked data). Programming involves writing a Mode Word (data format) and Command Word (enable Tx/Rx), then polling status bits to send and receive data.

Real-world relevance

Serial communication is everywhere. Your computer's USB ports evolved from the serial port concept. Bluetooth, Wi-Fi, and cellular data are all serial at the physical layer. Arduino boards use UART to communicate with your PC. Modems, GPS modules, and industrial sensors still use classic RS-232 serial (the same standard the 8251 was designed for). Understanding serial framing — start bits, stop bits, parity — is fundamental to debugging any communication protocol.

Key points

Code example

; =============================================
; 8251 USART — Complete Serial Setup
; =============================================
;
; 8251 at base 80h (data) and 81h (control/status)
; Format: 9600 baud, 8N1, 16x clock
;
; === INITIALIZATION ===
;
; After power-up, send 3 dummy bytes + reset
; (ensures 8251 is in known state)
  MOV AL, 00h
  OUT 81h, AL           ; dummy 1
  OUT 81h, AL           ; dummy 2
  OUT 81h, AL           ; dummy 3
  MOV AL, 40h           ; internal reset command
  OUT 81h, AL           ; reset 8251
;
; Mode Word: 8 data, no parity, 1 stop, 16x
;   01 00 11 10 = 4Eh
  MOV AL, 4Eh
  OUT 81h, AL           ; write mode word
;
; Command Word: enable Tx, Rx, DTR, RTS
;   0 0 1 1 0 1 1 1 = 37h
  MOV AL, 37h
  OUT 81h, AL           ; write command word
;
; === SEND STRING ===
;
  LEA SI, MSG           ; point to message
SEND_LOOP:
  LODSB                 ; AL = next char, SI++
  CMP AL, 0             ; null terminator?
  JE DONE
;
TX_POLL:
  IN AL, 81h            ; read status
  TEST AL, 01h          ; TxRDY?
  JZ TX_POLL            ; wait for ready
  LODSB                 ; reload char (LODSB advanced SI)
  DEC SI                ; fix SI
  MOV AL, [SI-1]        ; get the char
  OUT 80h, AL           ; transmit byte
  JMP SEND_LOOP
;
DONE:
  ; Transmission complete
;
MSG DB 'Hello Serial!', 0Dh, 0Ah, 0
;
; === RECEIVE BYTE ===
RX_POLL:
  IN AL, 81h            ; read status
  TEST AL, 02h          ; RxRDY?
  JZ RX_POLL
  IN AL, 80h            ; AL = received byte

Line-by-line walkthrough

  1. 1. Title comment for 8251 USART setup
  2. 2. Separator line
  3. 3. Blank line
  4. 4. 8251 ports: 80h for data read/write, 81h for control/status
  5. 5. Configuration: 9600 baud, 8 data bits, no parity, 1 stop bit, 16x clock
  6. 6. Blank line
  7. 7. === INITIALIZATION SECTION ===
  8. 8. Blank line
  9. 9. After power-up, the 8251 might be in an unknown state
  10. 10. Write three dummy bytes to flush any pending mode/command bytes
  11. 11. First dummy write to control port
  12. 12. Second dummy write
  13. 13. Third dummy write
  14. 14. Load internal reset command (bit 6 = 1) into AL
  15. 15. Send reset command — 8251 is now waiting for a mode word
  16. 16. Blank line
  17. 17. Mode word for 8 data, no parity, 1 stop, 16x oversampling
  18. 18. Binary breakdown: 01 (1 stop) 00 (no parity) 11 (8 bits) 10 (16x) = 4Eh
  19. 19. Write mode word to control register — 8251 format is now set
  20. 20. Blank line
  21. 21. Command word: enable transmitter, receiver, DTR, and RTS lines
  22. 22. Binary: 00110111 = 37h — TxEN, DTR, RxEN, RTS all set to 1
  23. 23. Write command — 8251 is now active and ready to communicate
  24. 24. Blank line
  25. 25. === SEND STRING SECTION ===
  26. 26. Blank line
  27. 27. Load address of the null-terminated message string into SI
  28. 28. SEND_LOOP label — iterate through each character
  29. 29. LODSB loads byte at [SI] into AL and increments SI
  30. 30. Check if AL is zero (null terminator marks end of string)
  31. 31. If null, jump to DONE — message fully transmitted
  32. 32. Blank line
  33. 33. TX_POLL label — wait for transmitter to be ready
  34. 34. Read status register from port 81h
  35. 35. Test bit 0 (TxRDY) — is the transmitter ready for a new byte?
  36. 36. If TxRDY is 0, loop back and poll again
  37. 37. Reload the character (SI was advanced by LODSB)
  38. 38. Adjust SI back to correct position
  39. 39. Get the character byte
  40. 40. Write byte to data register at port 80h — 8251 serializes and transmits it
  41. 41. Jump back to send the next character
  42. 42. Blank line
  43. 43. DONE label — all characters sent
  44. 44. Blank line
  45. 45. MSG: the message bytes in memory including carriage return and line feed
  46. 46. Blank line
  47. 47. === RECEIVE SECTION ===
  48. 48. RX_POLL: poll for incoming data
  49. 49. Read status register
  50. 50. Test bit 1 (RxRDY) — has a complete byte been received?
  51. 51. If not, keep polling
  52. 52. Read received byte from data register — AL contains the deserialized byte

Spot the bug

; Initialize 8251: 8 data, even parity, 1 stop, 16x
;
; Mode word:
;   B1B0 = 10 (16x)
;   L1L0 = 11 (8 bits)
;   PEN  = 1  (parity on)
;   EP   = 1  (even)
;   S1S0 = 01 (1 stop)
;
  MOV AL, 01 11 1 1 11 10  ; = 7Eh
  OUT 81h, AL
;
; Command word: enable Tx and Rx
  MOV AL, 05h              ; TxEN + RxEN
  OUT 80h, AL              ; write to data port
Need a hint?
Where should the command word be written — data port or control port? Also check the binary-to-hex conversion.
Show answer
Two bugs: (1) The command word is written to port 80h (data register) instead of port 81h (control/status register). Commands must go to the control port. Fix: OUT 81h, AL. (2) The binary value written as the mode word comment doesn't assemble correctly. The actual mode word should be: S1S0=01, EP=1, PEN=1, L1L0=11, B1B0=10 → 01111110 = 7Eh — the hex value is correct but the MOV instruction syntax is wrong. Use: MOV AL, 7Eh.

Explain like I'm 5

Imagine you have a long narrow tube that can only fit one marble at a time. You have 8 different-colored marbles (your data byte) but you can only send them one by one through the tube. Before you start, you send a special black marble (start bit) to tell your friend on the other end 'get ready, marbles are coming!' Then you send all 8 color marbles, and finish with a white marble (stop bit) meaning 'that is one complete message.' Your friend catches each marble and lines them up to see the original 8-color pattern. That is serial communication!

Fun fact

The RS-232 serial standard that the 8251 implements was first published in 1960 — over 60 years ago! It used voltage levels of +12V and -12V to represent 0 and 1. The 9600 baud rate that became the most common default was chosen because it was the maximum speed that worked reliably over telephone-quality copper wires. Incredibly, RS-232 serial ports are still used today in industrial equipment, network switches, and scientific instruments.

Hands-on challenge

Configure the 8251 for 7 data bits, even parity, 2 stop bits, with a 64x baud rate factor. Calculate the mode word bit by bit. Then write a complete 'echo' program that receives a byte from the serial port and immediately transmits it back — the simplest possible serial communication test.

More resources

Open interactive version (quiz + challenge) ← Back to course: Microprocessor A–Z