|
Z80 Build From Scratch |
|
| PAGE 13: UART Init Program | |
|
Examine a Sketch: We'll start by looking at the code found in UART_Echo-test.asm The UART-specific EQUates for the UART registers and functions are defined in lines 6 to 15. By setting the starting value for the address registers block to $00 for the UART, we can refer to any of the registers by the equated-to address. Example: the LCR is UART_R3. Lines 25 to 40 show us the bit-level breakdown of the 8 UART registers. Line 35 shows the LCR, the Line Control Register UART_R3. LCR is an 8-bit register that we reference as an address, and its bit 7 has a special name and purpose: it's called the DLAB bit or Divisor Latch Access Bit. The DLAB itself, a 16-bit register, is composed of the DLM and DLL. Initially during configuration the DLAB bit is set to a 1 so that the DLL (least significant byte) and DLM (most significant byte) can be used to set the baud rate. Once the baud rate config is completed during UART_Init, the DLAB is set to 0 so the Tx and Rx buffers can be used to move an internal byte to external bits, or the reverse action of assembling external bits to an internal byte. At that time the DLAB is no longer needed for configuration.
(Z80 Assembly: command, destination, source Example: LD A, $80 Meaning: Load $80 into register A)
In line 45+ of the code you can see $80 (source)being loaded into the LCR (UART_R3, destination) via the OUT instruction: address $03 (UART_R3) is put on the Address bus with IORQ asserted and $80 is sent on the Data bus. This will cause the UART to go into configuration mode and the DLAB will be temporarily enabled. In lines 49 to 52, $60 is sent to address $00 (UARTbase + 0, DLAB DLL) and $00 is sent to address $01 (UARTbase + 1, DLAB DLM), thus the DLAB (DLM + DLL) contains $0060. (Remember it's Little Endian byte storage.) As the comments in line 55 indicate, we have set the baud rate to 1200bps. If we had used a smaller value like $000C in the Device Latch Registers table, the baud rate would have been higher but we want you to see the characters you type being echoed back to the terminal screen, hence the reason for the slower speed. The final part of the UART initialization is to set the line protocol. Doing so will also reset the DLAB flag, concluding initialization. The value $03 from the LCR table means 8N1 or 8 bits Data, no Parity, one Stop bit. Lines 45+ were used to initialize the UART. Line 58 simply clears the Z80 accumulator, register A.
UART Get a Character: The Line Status Register, UART_R5, tells us if a character has been received. It does so by testing bit number zero (Data Available). In line 65 in function GET_CHAR_UART, we read the LSR into Z80 CPU register A (the accumulator) and then test bit 0. If it's a 0, then no character has been received and we jump back to the start of the function. If it's a 1, then the bits received from the wire have been assembled into a byte in UART_R0. (UART_R0 was used as the DLAB DLL during Init but now it's the Receive buffer.) Line 68 shows the received/assembled character in UART_R0 being moved as I/O into the accumulator then being copied into CPU register B in line 69.
UART Put a Character: Now that we have a character from the user stored in Z80 register B we're going to output it to the terminal screen, but first we have to check if the UART is ready to send. To do so we read in UART_R5 LSR again but this time we test bit 5 instead of bit 0. If bit 5 is a 1, then the Data Ready indicator is set indicating we can send the byte. The character we received and stored in Z80 register B is now output to the UART's Transmitter Holding Register (THRE) by copying B into A and then sending it out UART_R0 in line 78.
The program repeats itself in line 82 by jumping back to line 57 where we clear register A. Thus every character you type should be echoed to your terminal screen. Try holding down a key to see how "fast" 1200bps is.
In summary: - We set the LCR to initialize the UART - UART_R0 and UART_R1 will set the DLAB for baud rate configuration - UART_R3 will set the line protocol configuration of 8N1. Init is now complete - We check the DR Indicator of UART_R5 to see if a user has sent a character, then store it in Z80 register B - We check the THRe indicator of UART_R5 to see if the UART is ready to echo the character to the screen. If so the stored character in Z80 register B is sent out UART_R0 - UART_R0 is used during Init to set the baud rate and is both the incoming and outgoing buffer once Init is complete
|
![]()
|
Updated 2025-12-08