; RS232_String_Echo_2e.asm ; 2025-12-08 prefontDon@gmail.com ;*************************************************************************** ; PROGRAM: RS232_String_Echo_2e.asm ; PURPOSE: Initializes UART, prints a string, then echoes user keypresses. ;*************************************************************************** #define EQU .equ ; UART Register Definitions: UART_R0: EQU $00 ; RBR/THR (00 + 01). UART_R2: EQU $02 ; Interrupts, FCR. UART_R3: EQU $03 ; LCR. UART_R4: EQU $04 ; MCR. UART_R5: EQU $05 ; LSR. UART_R6: EQU $06 ; MSR. UART_R7: EQU $07 ; Scratch register. EOS: EQU $00 ; End of string marker. CR: EQU $0d ; Carriage return. LF: EQU $0a ; Line feed. space: EQU $20 ; Space. tab: EQU $09 ; Tabulator. ROM_START EQU $0000 ROM_END EQU $7FFF ; 32KB ROM. RAM_START EQU $8000 RAM_END EQU $FFFF ; 32KB RAM. .ORG $0000 LD SP, RAM_END ; Stack Pointer at top or RAM. LD HL, 0 LD BC, 0 CALL UART_DELAY_100MS ; UART delay needed to get ready. JP UART_INIT ; Initialize UART. ;*************************************************************************** ;UART DELAY: ;*************************************************************************** ; Z80 Nested Loop Delay Routine (Uses B and C registers) ; CPU Clock: 1 MHz (1 T-State = 1 us) ; Total Delay: Approximately 102 milliseconds (ms) UART_DELAY_100MS: PUSH BC LD B, 25 ; Outer Loop Setup: load B with the outer counter (25 repetitions). OUTER_LOOP: LD C, 255 ; Inner Loop Setup: Load C with the inner counter (255 repetitions). INNER_LOOP: ; --- Inner Loop Execution --- DEC C ; Decrement the inner counter (4 T-states). JR NZ, INNER_LOOP ; Jump back if C is not zero (7/12 T-states). ; --- Outer Loop Execution --- DJNZ OUTER_LOOP ; Decrement B, and jump back to OUTER_LOOP if B is not zero (8/13 T-states). POP BC RET ; Return from the subroutine. ;*************************************************************************** ; 1. INITIALIZATION SEQUENCE: ;*************************************************************************** UART_INIT: LD A, $80 ; Set DLAB=1 for configuration. OUT (UART_R3), A LD A, $0C ; Baud Rate Divisor LSB (9600bps). OUT (UART_R0), A LD A, $00 ; Baud Rate Divisor MSB. OUT (UART_R0 + 1), A ; Note: UART_R1 = UART_R0 + 1. LD A, $07 ; Value 07h: Enable FIFOs, Clear Rx FIFO, Clear Tx FIFO. OUT (UART_R2), A ; Write to FCR (UART_R2). *FCR reset*. LD A, $03 ; Set 8-N-1, DLAB=0 (end of configuration). OUT (UART_R3), A JP STRING_LOOP ;*************************************************************************** ; 2. STRING PRINTING CALL (Changed from 10 string printings to one): ;*************************************************************************** ;STRING_LOOP: ;LD B, 10 ;STRING_REPEAT: ;CALL STRING_START ;DEC B ;JR NZ, STRING_REPEAT ;JP MAIN_ECHO_LOOP ;STRING_START: ;LD HL, RS232_Echo ; Load address of string ;CALL PRINT_STR_ONCE ; Print the string ;RET STRING_LOOP: LD HL, RS232_Echo ; Load address of string ;CALL PRINT_STR_ONCE ; Print the string ;JP STRING_LOOP JP PRINT_STR_ONCE ; Print the string ;*************************************************************************** ; PRINT_STR_ONCE Subroutine: ; This subroutine uses CALL/RET but is only executed ONCE, minimizing ; the risk of the THRE lockup compared to calling it repeatedly. ; Registers used: A, HL. Registers B, C, D, E are preserved. ;*************************************************************************** PRINT_STR_ONCE: PUSH AF ; Save A and Flags ;PUSH BC ;PUSH HL ; Save the return pointer HL (not strictly needed, but good practice) PRNT_LOOP: LD A,(HL) ; Get char from HL CP EOS ; Test for End Of String (00h) JP Z, PRNT_END ; Jump if EOS found LD B, A ; --- TRANSMIT SINGLE CHARACTER --- TX_READY_WAIT: IN A, (UART_R5) ; Check LSR (THRE bit 5) BIT 5, A JP Z, TX_READY_WAIT ; Wait until ready LD A, B OUT (UART_R0), A ; Send character INC HL ; Increment string pointer JP PRNT_LOOP PRNT_END: ;POP HL ; Restore HL ;POP BC POP AF ; Restore A and Flags ;RET JP MAIN_ECHO_LOOP ; Use this for single iteration. ;JP STRING_LOOP ; Use this for continuous testing. ;*************************************************************************** ; 3. MAIN ECHO LOOP: ;*************************************************************************** MAIN_ECHO_LOOP: ; --- GET CHARACTER (RX) --- GET_CHAR_UART: IN A, (UART_R5) ; Check LSR (DR bit 0). BIT 0, A JP Z, GET_CHAR_UART ; Loop until char is ready. IN A, (UART_R0) ; Read character into A (Clears DR flag). LD B, A ; Store character into B Register for safe keeping. ; --- SEND CHARACTER (TX) --- SEND_CHAR_UART: IN A, (UART_R5) ; Check LSR (THRE bit 5). BIT 5, A JP Z, SEND_CHAR_UART ; Loop until Transmitter Holding Register (THRe) is Empty. LD A, B ; Get character from B Register. OUT (UART_R0), A ; Send character. JP MAIN_ECHO_LOOP ; Continue the loop of echoing user keypresses. ;************************************************************************* RS232_Echo: .DB "RS232_String_Echo_2e.bin", $0D,$0A,$00 ;************************************************************************* .END