; ;================================================================================================== ; UART DRIVER (SERIAL PORT) ;================================================================================================== ; ; SETUP PARAMETER WORD: ; +-------+---+-------------------+ +---+---+-----------+---+-------+ ; | |RTS| ENCODED BAUD RATE | |DTR|XON| PARITY |STP| 8/7/6 | ; +-------+---+---+---------------+ ----+---+-----------+---+-------+ ; F E D C B A 9 8 7 6 5 4 3 2 1 0 ; -- MSB (D REGISTER) -- -- LSB (E REGISTER) -- ; ; UART CONFIGURATION REGISTERS: ; +-------+---+-------------------+ +---+---+-----------+---+-------+ ; | 0 0 |AFE|LP OT2 OT1 RTS DTR| |DLB|BRK|STK EPS PEN|STB| WLS | ; +-------+---+-------------------+ +---+---+-----------+---+-------+ ; F E D C B A 9 8 7 6 5 4 3 2 1 0 ; -- MCR -- -- LCR -- ; ; UART_DEBUG .EQU FALSE ; UART_BUFSZ .EQU 32 ; RECEIVE RING BUFFER SIZE ; UART_NONE .EQU 0 ; UNKNOWN OR NOT PRESENT UART_8250 .EQU 1 UART_16450 .EQU 2 UART_16550 .EQU 3 UART_16550A .EQU 4 UART_16550C .EQU 5 UART_16650 .EQU 6 UART_16750 .EQU 7 UART_16850 .EQU 8 ; UART_RBR .EQU 0 ; DLAB=0: RCVR BUFFER REG (READ) UART_THR .EQU 0 ; DLAB=0: XMIT HOLDING REG (WRITE) UART_IER .EQU 1 ; DLAB=0: INT ENABLE REG (READ) UART_IIR .EQU 2 ; INT IDENT REGISTER (READ) UART_FCR .EQU 2 ; FIFO CONTROL REG (WRITE) UART_LCR .EQU 3 ; LINE CONTROL REG (READ/WRITE) UART_MCR .EQU 4 ; MODEM CONTROL REG (READ/WRITE) UART_LSR .EQU 5 ; LINE STATUS REG (READ) UART_MSR .EQU 6 ; MODEM STATUS REG (READ) UART_SCR .EQU 7 ; SCRATCH REGISTER (READ/WRITE) UART_DLL .EQU 0 ; DLAB=1: DIVISOR LATCH (LS) (READ/WRITE) UART_DLM .EQU 1 ; DLAB=1: DIVISOR LATCH (MS) (READ/WRITE) UART_EFR .EQU 2 ; LCR=$BF: ENHANCED FEATURE REG (READ/WRITE) ; ; THESE BITS ARE SET IN THE UART TYPE BYTE TO FURTHER ; IDENTIFY THE FEATURES OF THE CHIP ; UART_INTACT .EQU 7 ; INT RCV ACTIVE BIT UART_FIFOACT .EQU 6 ; FIFO ACTIVE BIT UART_AFCACT .EQU 5 ; AUTO FLOW CONTROL ACTIVE BIT UART_CTSBAD .EQU 4 ; CTS STALL DETECTED ; #IF (PLATFORM == PLT_DUO) UARTSBASE .EQU $58 #ELSE UARTSBASE .EQU $68 #ENDIF UARTABASE .EQU $A8 UARTCBASE .EQU $80 UARTMBASE .EQU $18 UART4BASE .EQU $C0 UARTRBASE .EQU $A0 #IF (PLATFORM == PLT_DUO) UARTDBASE .EQU $70 #ELSE UARTDBASE .EQU $80 #ENDIF ; #IF (UARTINTS) ; #IF ((INTMODE == 2) | (INTMODE == 3)) ; UART0_IVT .EQU IVT(INT_UART0) UART1_IVT .EQU IVT(INT_UART1) ; #ENDIF ; #ENDIF ; #DEFINE UART_INP(RID) CALL UART_INP_IMP \ .DB RID #DEFINE UART_OUTP(RID) CALL UART_OUTP_IMP \ .DB RID ; ; ; UART_PREINIT: #IF (UART4) ; ; INIT UART4 BOARD CONFIG REGISTER (NO HARM IF IT IS NOT THERE) ; LD A,$80 ; SELECT 7.3728MHZ OSC & LOCK CONFIG REGISTER OUT (UART4BASE+$0F),A ; DO IT #ENDIF ; ; SETUP THE DISPATCH TABLE ENTRIES ; LD B,UART_CNT ; LOOP CONTROL LD C,0 ; PHYSICAL UNIT INDEX XOR A ; ZERO TO ACCUM LD (UART_DEV),A ; CURRENT DEVICE NUMBER UART_PREINIT0: PUSH BC ; SAVE LOOP CONTROL LD A,C ; PHYSICAL UNIT TO A RLCA ; MULTIPLY BY CFG TABLE ENTRY SIZE (8 BYTES) RLCA ; ... RLCA ; ... TO GET OFFSET INTO CFG TABLE LD HL,UART_CFG ; POINT TO START OF CFG TABLE CALL ADDHLA ; HL := ENTRY ADDRESS PUSH HL ; SAVE IT PUSH HL ; COPY CFG DATA PTR POP IY ; ... TO IY CALL UART_INITUNIT ; HAND OFF TO GENERIC INIT CODE POP DE ; GET ENTRY ADDRESS BACK, BUT PUT IN DE POP BC ; RESTORE LOOP CONTROL ; LD A,(IY+1) ; GET THE UART TYPE DETECTED OR A ; SET FLAGS JR Z,UART_PREINIT2 ; SKIP IT IF NOTHING FOUND ; PUSH BC ; SAVE LOOP CONTROL LD BC,UART_FNTBL ; BC := FUNCTION TABLE ADDRESS CALL NZ,CIO_ADDENT ; ADD ENTRY IF UART FOUND, BC:DE POP BC ; RESTORE LOOP CONTROL ; UART_PREINIT2: INC C ; NEXT PHYSICAL UNIT DJNZ UART_PREINIT0 ; LOOP UNTIL DONE ; #IF ((UARTINTS) & (INTMODE > 0)) ; *** FIXME *** WE SHOULD CHECK TO SEE IF ANY UNITS ARE ACTUALLY ; USING INT RCV. IF NOT, WE SHOULD NOT HOOK IM1!!! ; ; SETUP INT VECTORS AS APPROPRIATE LD A,(UART_DEV) ; GET DEVICE COUNT OR A ; SET FLAGS JR Z,UART_PREINIT3 ; IF ZERO, NO UART DEVICES, ABORT ; #IF (INTMODE == 1) ; ADD IM1 INT CALL LIST ENTRY LD HL,UART_INT ; GET INT VECTOR CALL HB_ADDIM1 ; ADD TO IM1 CALL LIST #ENDIF ; #IF ((INTMODE == 2) | (INTMODE == 3)) ; SETUP IM2/3 VECTORS #IF (UARTSBC) LD HL,UART_INTSBC LD (UART0_IVT),HL ; IVT INDEX #ENDIF ; #IF (UARTCAS) LD HL,UART_INTCAS LD (UART1_IVT),HL ; IVT INDEX #ENDIF ; #ENDIF ; #ENDIF ; UART_PREINIT3: XOR A ; SIGNAL SUCCESS RET ; AND RETURN ; ; UART INITIALIZATION ROUTINE ; UART_INITUNIT: ; DETECT THE UART TYPE CALL UART_DETECT ; DETERMINE UART TYPE LD (IY+1),A ; AND SAVE IN CONFIG TABLE OR A ; SET FLAGS RET Z ; ABORT IF NOTHING THERE ; ; UPDATE WORKING UART DEVICE NUM LD HL,UART_DEV ; POINT TO CURRENT UART DEVICE NUM LD A,(HL) ; PUT IN ACCUM INC (HL) ; INCREMENT IT (FOR NEXT LOOP) LD (IY),A ; UDPATE UNIT NUM ; ; CHECK FOR CTS STALL (CTS SHOULD BE ASSERTED HERE) BIT 5,(IY+5) ; IS RTS REQUESTED? JR Z,UART_INITUNIT1 ; IF NOT, SKIP CTS CHECK UART_INP(UART_MSR) ; LOAD MODEM STATUS REG BIT 4,A ; CTS JR NZ,UART_INITUNIT1 ; IF CTS HIGH (GOOD), SKIP AHEAD ; ; CTS LOOKS BORKED, SHUT OFF RTS/CTS FLOW CONTROL RES 5,(IY+5) ; CLEAR RTS BIT OF CONFIG MSB SET UART_CTSBAD,(IY+1) ; RECORD BAD CTS ; UART_INITUNIT1: ; SET DEFAULT CONFIG LD DE,-1 ; LEAVE CONFIG ALONE JP UART_INITDEVX ; IMPLEMENT IT AND RETURN ; ; ; UART_INIT: LD B,UART_CNT ; COUNT OF POSSIBLE UART UNITS LD C,0 ; INDEX INTO UART CONFIG TABLE UART_INIT1: PUSH BC ; SAVE LOOP CONTROL LD A,C ; PHYSICAL UNIT TO A RLCA ; MULTIPLY BY CFG TABLE ENTRY SIZE (8 BYTES) RLCA ; ... RLCA ; ... TO GET OFFSET INTO CFG TABLE LD HL,UART_CFG ; POINT TO START OF CFG TABLE CALL ADDHLA ; HL := ENTRY ADDRESS PUSH HL ; COPY CFG DATA PTR POP IY ; ... TO IY LD A,(IY+1) ; GET UART TYPE OR A ; SET FLAGS JR Z,UART_INIT2 ; SKIP IF ZERO (NOT DETECTED) PUSH AF ; SAVE TYPE VALUE CALL UART_PRTCFG ; PRINT IF NOT ZERO POP AF ; RESTORE TYPE VALUE BIT UART_CTSBAD,A ; CTS STALL? JR Z,UART_INIT2 ; IF NOT, SKIP AHEAD CALL NEWLINE ; FORMATTING PRTS("UART$") ; FORMATTING LD A,(IY) ; DEVICE NUM CALL PRTDECB ; PRINT DEVICE NUM PRTS(": $") ; FORMATTING LD DE,UART_STR_BADCTS ; LOAD WARNING MESSAGE CALL WRITESTR ; ... AND PRINT IT ; UART_INIT2: POP BC ; RESTORE LOOP CONTROL INC C ; NEXT UNIT DJNZ UART_INIT1 ; LOOP TILL DONE ; XOR A ; SIGNAL SUCCESS RET ; DONE ; ; RECEIVE INTERRUPT HANDLER ; #IF ((UARTINTS) & (INTMODE > 0)) ; ; IM1 ENTRY POINT ; ; POLL ALL DEVICES THAT MIGHT ENABLE INTERRUPT DRIVEN ; RECEIVE. HANDLE FIRST INTERRUPT ENCOUNTERED (IF ANY). ; MOST BOARDS REQUIRE UARTS THAT WILL HAVE AFC. THE ; ONLY BOARDS THAT MAY NOT ARE THE SBC AND THE CAS. ; ; THIS COULD BE IMPROVED BY DYNAMICALLY SETTING UP THE ; POLLING CHAIN WHEN DEVICES ARE INITIALIZED SUCH THAT ; ONLY DEVICES ACTUALLY USING INTS ARE POLLED HERE. ; #IF (INTMODE == 1) UART_INT: ; #IF (UARTSBC) LD IY,UART_CFG_SBC CALL UART_INTRCV RET NZ #ENDIF ; #IF (UARTCAS) LD IY,UART_CFG_CAS CALL UART_INTRCV RET NZ #ENDIF ; #ENDIF ; #IF ((INTMODE == 2) | (INTMODE == 3)) ; #IF (UARTSBC) UART_INTSBC: LD IY,UART_CFG_SBC JR UART_INTRCV #ENDIF ; #IF (UARTCAS) UART_INTCAS: LD IY,UART_CFG_CAS JR UART_INTRCV #ENDIF ; #ENDIF ; XOR A ; CLEAR ACCUM (INT NOT HANDLED) RET ; DONE ; ; IM2 ENTRY POINTS ; ; ; HANDLE INT FOR A SPECIFIC CHANNEL ; BASED ON UNIT CFG POINTED TO BY IY ; UART_INTRCV: ; ARE INTERRUPTS IN USE ON THIS DEVICE? LD A,(IY+1) ; GET UART TYPE AND %10000000 ; ISOLATE INT RCV BIT RET Z ; INTS NOT SUPPORTED ; CHECK TO SEE IF SOMETHING IS ACTUALLY THERE LD C,(IY+3) ; STATUS PORT TO C IN A,(C) ; GET LSR AND $01 ; ISOLATE RECEIVE READY BIT RET Z ; NOTHING AVAILABLE ON CURRENT CHANNEL ; UART_INTRCV1: ; RECEIVE CHARACTER INTO BUFFER LD C,(IY+2) ; DATA PORT TO C IN A,(C) ; READ PORT LD B,A ; SAVE BYTE READ LD L,(IY+6) ; SET HL TO LD H,(IY+7) ; ... START OF BUFFER STRUCT LD A,(HL) ; GET COUNT CP UART_BUFSZ ; COMPARE TO BUFFER SIZE JR Z,UART_INTRCV4 ; BAIL OUT IF BUFFER FULL, RCV BYTE DISCARDED INC A ; INCREMENT THE COUNT LD (HL),A ; AND SAVE IT ; *** FIXME *** THE FOLLOWING SHOULD ONLY BE DONE IF RTS FLOW CONTROL IS ON!!! ; SOMETHING LIKE THIS MAY WORK... ;BIT 5,(IY+5) ;JR Z,UART_INTRCV2 CP UART_BUFSZ / 2 ; BUFFER GETTING FULL? JR NZ,UART_INTRCV2 ; IF NOT, BYPASS CLEARING RTS LD C,(IY+3) ; LSR PORT TO C DEC C ; POINT TO MCR PORT IN A,(C) ; GET MCR VALUE AND ~%00000010 ; CLEAR RTS OUT (C),A ; AND SAVE IT ; UART_INTRCV2: INC HL ; HL NOW HAS ADR OF HEAD PTR PUSH HL ; SAVE ADR OF HEAD PTR LD A,(HL) ; DEREFERENCE HL INC HL LD H,(HL) LD L,A ; HL IS NOW ACTUAL HEAD PTR LD (HL),B ; SAVE CHARACTER RECEIVED IN BUFFER AT HEAD INC HL ; BUMP HEAD POINTER POP DE ; RECOVER ADR OF HEAD PTR LD A,L ; GET LOW BYTE OF HEAD PTR SUB UART_BUFSZ+4 ; SUBTRACT SIZE OF BUFFER AND POINTER CP E ; IF EQUAL TO START, HEAD PTR IS PAST BUF END JR NZ,UART_INTRCV3 ; IF NOT, BYPASS LD H,D ; SET HL TO LD L,E ; ... HEAD PTR ADR INC HL ; BUMP PAST HEAD PTR INC HL INC HL INC HL ; ... SO HL NOW HAS ADR OF ACTUAL BUFFER START UART_INTRCV3: EX DE,HL ; DE := HEAD PTR VAL, HL := ADR OF HEAD PTR LD (HL),E ; SAVE UPDATED HEAD PTR INC HL LD (HL),D ; CHECK FOR MORE PENDING... LD C,(IY+3) ; STATUS PORT TO C IN A,(C) ; GET LSR AND $01 ; ISOLATE RECEIVE READY BIT JR NZ,UART_INTRCV1 ; IF SET, DO SOME MORE UART_INTRCV4: OR $FF ; NZ SET TO INDICATE INT HANDLED RET ; #ENDIF ; ; DRIVER FUNCTION TABLE ; UART_FNTBL: .DW UART_IN .DW UART_OUT .DW UART_IST .DW UART_OST .DW UART_INITDEV .DW UART_QUERY .DW UART_DEVICE #IF (($ - UART_FNTBL) != (CIO_FNCNT * 2)) .ECHO "*** INVALID UART FUNCTION TABLE ***\n" #ENDIF ; ; ; UART_IN: CALL UART_IST ; RECEIVED CHAR READY? JR Z,UART_IN ; LOOP IF NOT #IF ((UARTINTS) & (INTMODE > 0)) BIT UART_INTACT,(IY+1) ; INT RCV BIT JR Z,UART_IN1 ; NORMAL INPUT IF NOT SET JR UART_INTIN ; INT RCV INPUT #ENDIF ; UART_IN1: LD C,(IY+2) ; C := BASE UART PORT (WHICH IS ALSO RBR REG) IN E,(C) ; CHAR READ TO E XOR A ; SIGNAL SUCCESS RET ; AND DONE ; #IF ((UARTINTS) & (INTMODE > 0)) ; UART_INTIN: HB_DI ; AVOID COLLISION WITH INT HANDLER LD L,(IY+6) ; SET HL TO LD H,(IY+7) ; ... START OF BUFFER STRUCT LD A,(HL) ; GET COUNT DEC A ; DECREMENT COUNT LD (HL),A ; SAVE UPDATED COUNT ; *** FIXME *** THE FOLLOWING SHOULD ONLY BE DONE IF RTS FLOW CONTROL IS ON!!! ; SOMETHING LIKE THIS MAY WORK... ;BIT 5,(IY+5) ;JR Z,UART_INTIN1 CP UART_BUFSZ / 4 ; BUFFER LOW THRESHOLD JR NZ,UART_INTIN1 ; IF NOT, BYPASS SETTING RTS LD C,(IY+3) ; LSR PORT TO C DEC C ; POINT TO MCR PORT IN A,(C) ; GET MCR VALUE OR %00000010 ; SET RTS OUT (C),A ; AND SAVE IT ; UART_INTIN1: INC HL INC HL INC HL ; HL NOW HAS ADR OF TAIL PTR PUSH HL ; SAVE ADR OF TAIL PTR LD A,(HL) ; DEREFERENCE HL INC HL LD H,(HL) LD L,A ; HL IS NOW ACTUAL TAIL PTR LD C,(HL) ; C := CHAR TO BE RETURNED INC HL ; BUMP TAIL PTR POP DE ; RECOVER ADR OF TAIL PTR LD A,L ; GET LOW BYTE OF TAIL PTR SUB UART_BUFSZ+2 ; SUBTRACT SIZE OF BUFFER AND POINTER CP E ; IF EQUAL TO START, TAIL PTR IS PAST BUF END JR NZ,UART_INTIN2 ; IF NOT, BYPASS LD H,D ; SET HL TO LD L,E ; ... TAIL PTR ADR INC HL ; BUMP PAST TAIL PTR INC HL ; ... SO HL NOW HAS ADR OF ACTUAL BUFFER START UART_INTIN2: EX DE,HL ; DE := TAIL PTR VAL, HL := ADR OF TAIL PTR LD (HL),E ; SAVE UPDATED TAIL PTR INC HL LD (HL),D LD E,C ; MOVE CHAR TO RETURN TO E HB_EI ; INTERRUPTS OK AGAIN XOR A ; SIGNAL SUCCESS RET ; AND DONE ; #ENDIF ; ; ; UART_OUT: CALL UART_OST ; READY FOR CHAR? JR Z,UART_OUT ; LOOP IF NOT LD C,(IY+2) ; C := BASE UART PORT (WHICH IS ALSO THR REG) OUT (C),E ; SEND CHAR FROM E XOR A ; SIGNAL SUCCESS RET ; ; ; UART_IST: #IF ((UARTINTS) & (INTMODE > 0)) BIT UART_INTACT,(IY+1) ; INT RCV BIT JR Z,UART_IST1 ; NORMAL INPUT IF NOT SET JR UART_INTIST ; ELSE INT RCV #ENDIF ; UART_IST1: LD C,(IY+3) ; C := LINE STATUS REG (LSR) IN A,(C) ; GET STATUS AND $01 ; ISOLATE BIT 0 (RECEIVE DATA READY) JP Z,CIO_IDLE ; NOT READY, RETURN VIA IDLE PROCESSING XOR A ; ZERO ACCUM INC A ; ACCUM := 1 TO SIGNAL 1 CHAR WAITING RET ; DONE ; #IF ((UARTINTS) & (INTMODE > 0)) ; UART_INTIST: LD L,(IY+6) ; GET ADDRESS LD H,(IY+7) ; ... OF RECEIVE BUFFER LD A,(HL) ; BUFFER UTILIZATION COUNT OR A ; SET FLAGS JP Z,CIO_IDLE ; NOT READY, RETURN VIA IDLE PROCESSING RET ; #ENDIF ; ; ; UART_OST: LD C,(IY+3) ; C := LINE STATUS REG (LSR) IN A,(C) ; GET STATUS AND $20 ; ISOLATE BIT 5 () JP Z,CIO_IDLE ; NOT READY, RETURN VIA IDLE PROCESSING XOR A ; ZERO ACCUM INC A ; ACCUM := 1 TO SIGNAL 1 BUFFER POSITION RET ; DONE ; ; ; ; ; NOTE THAT THERE ARE TWO ENTRY POINTS. INITDEV WILL DISABLE/ENABLE INTS ; AND INITDEVX WILL NOT. THIS IS DONE SO THAT THE PREINIT ROUTINE ABOVE ; CAN AVOID ENABLING/DISABLING INTS. ; UART_INITDEV: HB_DI ; DISABLE INTS CALL UART_INITDEVX ; DO THE WORK HB_EI ; INTS BACK ON RET ; DONE ; UART_INITDEVX: ; TEST FOR -1 WHICH MEANS USE CURRENT CONFIG (JUST REINIT) LD A,D ; TEST DE FOR AND E ; ... VALUE OF -1 INC A ; ... SO Z SET IF -1 JR NZ,UART_INITDEV1 ; IF DE == -1, REINIT CURRENT CONFIG ; ; LOAD EXISTING CONFIG TO REINIT LD E,(IY+4) ; LOW BYTE LD D,(IY+5) ; HIGH BYTE ; UART_INITDEV1: ; DETERMINE DIVISOR PUSH DE ; SAVE CONFIG CALL UART_COMPDIV ; COMPUTE DIVISOR TO BC POP DE ; RESTORE CONFIG RET NZ ; ABORT IF COMPDIV FAILS! ; ; GOT A DIVISOR, COMMIT NEW CONFIG LD (IY+4),E ; SAVE LOW WORD LD (IY+5),D ; SAVE HI WORD ; ; START OF ACTUAL UART CONFIGURATION LD A,80H ; DLAB IS BIT 7 OF LCR UART_OUTP(UART_LCR) ; DLAB ON LD A,B UART_OUTP(UART_DLM) ; SET DIVISOR (MS) LD A,C UART_OUTP(UART_DLL) ; SET DIVISOR (LS) ; ; FOR 750+, WE ENABLE THE 64-BYTE FIFO ; DLAB MUST STILL BE ON FOR ACCESS TO BIT 5 ; WE DO *NOT* ENABLE ANY OTHER FCR BITS HERE ; BECAUSE IT WILL SCREW UP THE 2552!!! LD A,%00100000 UART_OUTP(UART_FCR) ; DO IT ; XOR A ; DLAB OFF NOW UART_OUTP(UART_LCR) ; DO IT ; XOR A ; IER VALUE FOR NO INTS ; #IF ((UARTINTS) & (INTMODE > 0)) ; BIT UART_INTACT,(IY+1) ; CHECK INT RCV BIT JR Z,UART_INITDEV1A ; SKIP IF NOT SET INC A ; DATA RCVD INT BIT OF IER ; UART_INITDEV1A: ; #ENDIF ; UART_OUTP(UART_IER) ; SETUP IER REGISTER ; ; SETUP FCR, BIT 5 IS KEPT ON EVEN THOUGH IT IS PROBABLY ; IRRELEVANT BECAUSE IT ONLY APPLIES TO 750 AND DLAB IS ; NOW OFF, BUT DOESN'T HURT. ; BITS 7-6 DEFINE THE FIFO RECEIVE INTERRUPT THRESHOLD. WE ; USE A VALUE 0F %01 FOR THESE BITS WHICH REDUCES THE ; FREQUENCY OF INTERRUPTS DURING HEAVY RECEIVE OPERATIONS. LD A,%01100111 ; FIFO ENABLE & RESET UART_OUTP(UART_FCR) ; DO IT ; ; SETUP LCR FROM SECOND CONFIG BYTE LD A,(IY+4) ; GET CONFIG BYTE AND ~$C0 ; ISOLATE PARITY, STOP/DATA BITS UART_OUTP(UART_LCR) ; SAVE IT ; ; SETUP MCR FROM FIRST CONFIG BYTE LD A,(IY+5) ; GET CONFIG BYTE AND ~$1F ; REMOVE ENCODED BAUD RATE BITS OR $03 ; FORCE RTS & DTR ; ; SOME NEWER UARTS USE MCR:3 TO ACTIVATE THE INTERRUPT LINE. ; ALTHOUGH OTHER UARTS USE MCR:3 TO CONTROL A GPIO LINE CALLED ; OUT2, NO ROMWBW HARDWARE USES THIS GPIO LINE. SO, HERE, WE ; JUST SET MCR:3 TO ACTIVATE THE INTERRUPT LINE. NOTE THAT ; EVEN IF WE ARE NOT USING INTERRUPTS FOR THIS UART, THE ; INTERRUPT LINE MUST STILL BE ACTIVATED SO THAT IT WILL ; PRESENT A DEASSERTED CONDITION TO THE CPU. OTHERWISE, THE ; INTERRUPT LINE MAY BE LEFT FLOATING WHICH IS DEFINITELY BAD. OR $08 ; ACTIVATE INT LINE ; ; THE MCR REGISTER AFE BIT WILL NORMALLY BE SET/RESET BY THE ; VALUE OF THE CONFIG BYTE. HOWEVER, IF THE CHIP IS NOT AFC CAPABLE ; WE ARE PROBABLY USING INT RCV FOR FLOW CONTROL. ALTHOUGH THE ; CHIP PROBABLY IGNORES THE AFE BIT, WE FORCE CLEAR IT ANYWAY. IT WOULD ; BE BAD IF AFC AND INT RCV ARE ACTIVE AT THE SAME TIME. BIT UART_AFCACT,(IY+1) ; IS AFC SUPPOSED TO BE ON? JR NZ,UART_INITDEV1B ; IF SO, AFE BIT IS OK BASED ON CONFIG BYTE RES 5,A ; ELSE FORCE IT OFF ; UART_INITDEV1B: UART_OUTP(UART_MCR) ; SAVE MCR VALUE ; ; TEST FOR EFR CAPABLE CHIPS LD A,(IY+1) ; GET UART TYPE AND $0F ; ISOLATE LOW NIBBLE CP UART_16650 ; 16650? JR Z,UART_INITDEV2 ; USE EFR REGISTER CP UART_16850 ; 16850? JR Z,UART_INITDEV2 ; USE EFR REGISTER JR UART_INITDEV4 ; NO EFR, SKIP AHEAD ; UART_INITDEV2: ; WE HAVE AN EFR CAPABLE CHIP, SET EFR REGISTER ; NOTE THAT AN EFR CAPABLE CHIP IMPLIES IT IS CAPABLE OF AFC! UART_INP(UART_LCR) ; GET CURRENT LCR VALUE PUSH AF ; SAVE IT LD A,$BF ; VALUE TO ACCESS EFR UART_OUTP(UART_LCR) ; SET VALUE IN LCR LD A,(IY+5) ; GET CONFIG BYTE BIT 5,A ; AFC REQUESTED? LD A,$C0 ; ASSUME AFC ON JR NZ,UART_INITDEV3 ; YES, IMPLEMENT IT XOR A ; NO AFC REQEUST, EFR := 0 ; UART_INITDEV3: UART_OUTP(UART_EFR) ; SAVE IT POP AF ; RECOVER ORIGINAL LCR VALUE UART_OUTP(UART_LCR) ; AND PUT IT BACK ; UART_INITDEV4: ; #IF ((UARTINTS) & (INTMODE > 0)) ; LD A,(IY+7) ; MSB OF BUFFER OR A ; SET FLAGS JR Z,UART_INITDEV5 ; BYPASS IF NO BUFFER ; RESET THE RECEIVE BUFFER LD E,(IY+6) LD D,(IY+7) ; DE := _CNT XOR A ; A := 0 LD (DE),A ; _CNT = 0 INC DE ; DE := ADR OF _HD PUSH DE ; SAVE IT INC DE INC DE INC DE INC DE ; DE := ADR OF _BUF POP HL ; HL := ADR OF _HD LD (HL),E INC HL LD (HL),D ; _HD := _BUF INC HL LD (HL),E INC HL LD (HL),D ; _TL := _BUF ; UART_INITDEV5: ; #ENDIF ; ; #IF (UART_DEBUG) PRTS(" [$") ; DEBUG: DUMP UART TYPE LD A,(IY+1) CALL PRTHEXBYTE ; DEBUG: DUMP IIR UART_INP(UART_IIR) CALL PC_SPACE CALL PRTHEXBYTE ; DEBUG: DUMP LCR UART_INP(UART_LCR) CALL PC_SPACE CALL PRTHEXBYTE ; DEBUG: DUMP MCR UART_INP(UART_MCR) CALL PC_SPACE CALL PRTHEXBYTE ; DEBUG: DUMP EFR UART_INP(UART_LCR) PUSH AF LD A,$BF UART_OUTP(UART_LCR) UART_INP(UART_EFR) LD H,A EX (SP),HL LD A,H UART_OUTP(UART_LCR) POP AF CALL PC_SPACE CALL PRTHEXBYTE PRTC(']') #ENDIF ; XOR A ; SIGNAL SUCCESS RET ; ; ; UART_QUERY: LD E,(IY+4) ; FIRST CONFIG BYTE TO E LD D,(IY+5) ; SECOND CONFIG BYTE TO D XOR A ; SIGNAL SUCCESS RET ; DONE ; ; ; UART_DEVICE: LD D,CIODEV_UART ; D := DEVICE TYPE LD E,(IY) ; E := PHYSICAL UNIT LD C,$00 ; C := DEVICE TYPE, 0x00 IS RS-232 LD H,(IY+1) ; H := UART TYPE BYTE LD L,(IY+2) ; L := BASE I/O ADDRESS XOR A ; SIGNAL SUCCESS RET ; ; UART DETECTION ROUTINE ; UART_DETECT: CALL UART_CHIP ; DETECT CHIP VARIANT ;LD A,UART_16550A ; *DEBUG* OR A RET Z ; DONE IF NO CHIP LD C,A ; PUT CHIP VARIANT IN C ; #IF ((UARTINTS) & (INTMODE > 0)) ; ; CHECK TO SEE IF INT RCV WANTED ON THIS DEVICE PUSH AF ; SAVE CHIP ID CP UART_16550C ; 16550C OR LATER? JR NC,UART_DETECT1 ; NO INTS, USE AFC INSTEAD LD A,(IY+7) ; MSB OF RING BUFFER OR A ; SET FLAGS JR Z,UART_DETECT1 ; NO BUFFER, NO INTS ALLOWED SET UART_INTACT,C ; SET INT RCV BIT ; UART_DETECT1: POP AF ; RESTORE CHIP ID ; #ENDIF ; CP UART_16550 ; 16550 OR GREATER? JR C,UART_DETECT2 ; NO MORE FEATURES SET UART_FIFOACT,C ; RECORD FIFO FEATURE CP UART_16550C ; 16550C OR GREATER? JR C,UART_DETECT2 ; NO MORE FEATURES SET UART_AFCACT,C ; RECORD AFC FEATURE ; UART_DETECT2: LD A,C ; RETURN RESULT IN A RET ; ; DETERMINE THE UART CHIP VARIANT AND RETURN IN A ; UART_CHIP: ; ; SEE IF UART IS THERE BY CHECKING DLAB FUNCTIONALITY XOR A ; ZERO ACCUM UART_OUTP(UART_IER) ; IER := 0 LD A,$80 ; DLAB BIT ON UART_OUTP(UART_LCR) ; OUTPUT TO LCR (DLAB REGS NOW ACTIVE) LD A,$5A ; LOAD TEST VALUE UART_OUTP(UART_DLM) ; OUTPUT TO DLM UART_INP(UART_DLM) ; READ IT BACK CP $5A ; CHECK FOR TEST VALUE JP NZ,UART_CHIP_NONE ; NOPE, UNKNOWN UART OR NOT PRESENT XOR A ; DLAB BIT OFF UART_OUTP(UART_LCR) ; OUTPUT TO LCR (DLAB REGS NOW INACTIVE) UART_INP(UART_IER) ; READ IER CP $5A ; CHECK FOR TEST VALUE JP Z,UART_CHIP_NONE ; IF STILL $5A, UNKNOWN OR NOT PRESENT ; ; TEST FOR FUNCTIONAL SCRATCH REG, IF NOT, WE HAVE AN 8250 LD A,$5A ; LOAD TEST VALUE UART_OUTP(UART_SCR) ; PUT IT IN SCRATCH REGISTER UART_INP(UART_SCR) ; READ IT BACK CP $5A ; CHECK IT JR NZ,UART_CHIP_8250 ; STUPID 8250 ; ; TEST FOR EFR REGISTER WHICH IMPLIES 16650/850 LD A,$BF ; VALUE TO ENABLE EFR UART_OUTP(UART_LCR) ; WRITE IT TO LCR UART_INP(UART_SCR) ; READ SCRATCH REGISTER CP $5A ; SPR STILL THERE? JR NZ,UART_CHIP1 ; NOPE, HIDDEN, MUST BE 16650/850 ; ; RESET LCR TO DEFAULT (DLAB OFF) ;LD A,$80 ; DLAB BIT ON XOR A ; DLAB BIT OFF UART_OUTP(UART_LCR) ; RESET LCR ; ; TEST FCR TO ISOLATE 16450/550/550A LD A,$E7 ; TEST VALUE UART_OUTP(UART_FCR) ; PUT IT IN FCR UART_INP(UART_IIR) ; READ BACK FROM IIR BIT 6,A ; BIT 6 IS FIFO ENABLE, LO BIT JR Z,UART_CHIP_16450 ; IF NOT SET, MUST BE 16450 BIT 7,A ; BIT 7 IS FIFO ENABLE, HI BIT JR Z,UART_CHIP_16550 ; IF NOT SET, MUST BE 16550 BIT 5,A ; BIT 5 IS 64 BYTE FIFO JR Z,UART_CHIP2 ; IF NOT SET, MUST BE 16550A/C JR UART_CHIP_16750 ; ONLY THING LEFT IS 16750 ; UART_CHIP1: ; PICK BETWEEN 16650/850 ; RESET LCR TO DEFAULT (DLAB OFF) XOR A ; DLAB BIT OFF UART_OUTP(UART_LCR) ; RESET LCR ; NOT SURE HOW TO DIFFERENTIATE 16650 FROM 16850 YET JR UART_CHIP_16650 ; ASSUME 16650 ; UART_CHIP2: ; PICK BETWEEN 16550A/C ; SET AFC BIT IN FCR LD A,$20 ; SET AFC BIT, MCR:5 UART_OUTP(UART_MCR) ; WRITE NEW FCR VALUE ; ; READ IT BACK, IF SET, WE HAVE 16550C UART_INP(UART_MCR) ; READ BACK MCR BIT 5,A ; CHECK AFC BIT JR Z,UART_CHIP_16550A ; NOT SET, SO 16550A JR UART_CHIP_16550C ; IS SET, SO 16550C ; UART_CHIP_NONE: ; #IF (UARTSBCFORCE) ; SIMH DOES NOT EMULATE A UART WELL ENOUGH TO BE DETECTED, SO ; THIS BIT OF CODE CAN BE ENABLED TO FORCE THE PRIMARY SBC ; UART TO BE HANDLED AS AN 8250. LD A,(IY+2) ; BASE IO PORT CP UARTSBASE ; IS THIS PRIMARY SBC PORT? JR Z,UART_CHIP_8250 ; SPECIAL CASE FOR PRIMARY UART! #ENDIF ; LD A,UART_NONE ; NO UART DETECTED AT THIS PORT RET ; UART_CHIP_8250: LD A,UART_8250 RET ; UART_CHIP_16450: LD A,UART_16450 RET ; UART_CHIP_16550: LD A,UART_16550 RET ; UART_CHIP_16550A: LD A,UART_16550A RET ; UART_CHIP_16550C: LD A,UART_16550C RET ; UART_CHIP_16650: LD A,UART_16650 RET ; UART_CHIP_16750: LD A,UART_16750 RET ; UART_CHIP_16850: LD A,UART_16850 RET ; ; COMPUTE DIVISOR TO BC ; UART_COMPDIV: ; WE WANT TO DETERMINE A DIVISOR FOR THE UART CLOCK ; THAT RESULTS IN THE DESIRED BAUD RATE. ; BAUD RATE = UART CLK / DIVISOR, OR TO SOLVE FOR DIVISOR ; DIVISOR = UART CLK / BAUDRATE. ; THE UART CLOCK IS THE UART OSC PRESCALED BY 16. ALSO, WE CAN ; TAKE ADVANTAGE OF ENCODED BAUD RATES ALWAYS BEING A FACTOR OF 75. ; SO, WE CAN USE (UART OSC / 16 / 75) / (BAUDRATE / 75) ; ; FIRST WE DECODE THE BAUDRATE, BUT WE USE A CONSTANT OF 1 INSTEAD ; OF THE NORMAL 75. THIS PRODUCES (BAUDRATE / 75). ; LD A,D ; GET CONFIG MSB AND $1F ; ISOLATE ENCODED BAUD RATE LD L,A ; PUT IN L LD H,0 ; H IS ALWAYS ZERO LD DE,1 ; USE 1 FOR ENCODING CONSTANT CALL DECODE ; DE:HL := BAUD RATE, ERRORS IGNORED EX DE,HL ; DE := (BAUDRATE / 75), DISCARD HL LD HL,UARTOSC / 16 / 75 ; HL := (UART OSC / 16 / 75) JP DIV16 ; BC := HL/DE == DIVISOR AND RETURN ; ; ; UART_PRTCFG: ; ANNOUNCE PORT CALL NEWLINE ; FORMATTING PRTS("UART$") ; FORMATTING LD A,(IY) ; DEVICE NUM CALL PRTDECB ; PRINT DEVICE NUM PRTS(": IO=0x$") ; FORMATTING LD A,(IY+2) ; GET BASE PORT CALL PRTHEXBYTE ; PRINT BASE PORT ; PRINT THE UART TYPE CALL PC_SPACE ; FORMATTING LD A,(IY+1) ; GET UART TYPE BYTE AND $0F ; LOW BITS ONLY RLCA ; MAKE IT A WORD OFFSET LD HL,UART_TYPE_MAP ; POINT HL TO TYPE MAP TABLE CALL ADDHLA ; HL := ENTRY LD E,(HL) ; DEREFERENCE INC HL ; ... LD D,(HL) ; ... TO GET STRING POINTER CALL WRITESTR ; PRINT IT ; ; ALL DONE IF NO UART WAS DETECTED LD A,(IY+1) ; GET UART TYPE BYTE OR A ; SET FLAGS RET Z ; IF ZERO, NOT PRESENT ; PRTS(" MODE=$") ; FORMATTING LD E,(IY+4) ; LOAD CONFIG LD D,(IY+5) ; ... WORD TO DE CALL PS_PRTSC0 ; PRINT CONFIG ; ; PRINT FEATURES ENABLED BIT UART_INTACT,(IY+1) ; GET INT RCV BIT JR Z,UART_PRTCFG1 PRTS(" INT$") ; UART_PRTCFG1: BIT UART_FIFOACT,(IY+1) ; GET FIFO BIT JR Z,UART_PRTCFG2 PRTS(" FIFO$") ; UART_PRTCFG2: BIT UART_AFCACT,(IY+1) ; GET AFC BIT JR Z,UART_PRTCFG3 PRTS(" AFC$") ; UART_PRTCFG3: ; XOR A RET ; ; ROUTINES TO READ/WRITE PORTS INDIRECTLY ; ; READ VALUE OF UART PORT ON TOS INTO REGISTER A ; UART_INP_IMP: EX (SP),HL ; SWAP HL AND TOS PUSH BC ; PRESERVE BC LD A,(IY+2) ; GET UART IO BASE PORT OR (HL) ; OR IN REGISTER ID BITS LD C,A ; C := PORT IN A,(C) ; READ PORT INTO A POP BC ; RESTORE BC INC HL ; BUMP HL PAST REG ID PARM EX (SP),HL ; SWAP BACK HL AND TOS RET ; ; WRITE VALUE IN REGISTER A TO UART PORT ON TOS ; UART_OUTP_IMP: EX (SP),HL ; SWAP HL AND TOS PUSH BC ; PRESERVE BC LD B,A ; PUT VALUE TO WRITE IN B LD A,(IY+2) ; GET UART IO BASE PORT OR (HL) ; OR IN REGISTER ID BITS LD C,A ; C := PORT OUT (C),B ; WRITE VALUE TO PORT POP BC ; RESTORE BC INC HL ; BUMP HL PAST REG ID PARM EX (SP),HL ; SWAP BACK HL AND TOS RET ; ; ; UART_TYPE_MAP: .DW UART_STR_NONE .DW UART_STR_8250 .DW UART_STR_16450 .DW UART_STR_16550 .DW UART_STR_16550A .DW UART_STR_16550C .DW UART_STR_16650 .DW UART_STR_16750 .DW UART_STR_16850 UART_STR_NONE .DB "$" UART_STR_8250 .DB "8250$" UART_STR_16450 .DB "16450$" UART_STR_16550 .DB "16550$" UART_STR_16550A .DB "16550A$" UART_STR_16550C .DB "16550C$" UART_STR_16650 .DB "16650$" UART_STR_16750 .DB "16750$" UART_STR_16850 .DB "16850$" ; UART_PAR_MAP .DB "NONENMNS" ; UART_STR_BADCTS .DB "CTS STALL, HARDWARE FLOW CONTROL SUSPENDED$" ; ; WORKING VARIABLES ; UART_DEV .DB 0 ; DEVICE NUM USED DURING INIT ; ; UART PORT TABLE ; UART_CFG: #IF (UARTSBC) UART_CFG_SBC: ; SBC/ZETA ONBOARD SERIAL PORT .DB 0 ; DEVICE NUMBER (UPDATED DURING INIT) .DB 0 ; UART TYPE .DB UARTSBASE ; IO PORT BASE (RBR, THR) .DB UARTSBASE + UART_LSR ; LINE STATUS PORT (LSR) .DW UARTCFG ; LINE CONFIGURATION .DW UARTSBC_RCVBUF ; POINTER TO RCV BUFFER STRUCT ; .ECHO "UART: MODE=SBC, IO=" .ECHO UARTSBASE #IF ((UARTINTS) & (INTMODE > 0)) .ECHO ", INTERRUPTS ENABLED" #ENDIF .ECHO "\n" #ENDIF #IF (UARTAUX) UART_CFG_AUX: ; AUX SERIAL PORT .DB 0 ; DEVICE NUMBER (UPDATED DURING INIT) .DB 0 ; UART TYPE .DB UARTABASE ; IO PORT BASE (RBR, THR) .DB UARTABASE + UART_LSR ; LINE STATUS PORT (LSR) .DW UARTCFG ; LINE CONFIGURATION .DW 0 ; NO INT HANDLER ; .ECHO "UART: MODE=AUX, IO=" .ECHO UARTABASE .ECHO "\n" #ENDIF #IF (UARTCAS) UART_CFG_CAS: ; CASSETTE INTERFACE SERIAL PORT .DB 0 ; DEVICE NUMBER (UPDATED DURING INIT) .DB 0 ; UART TYPE .DB UARTCBASE ; IO PORT BASE (RBR, THR) .DB UARTCBASE + UART_LSR ; LINE STATUS PORT (LSR) .DW UARTCASSPD ; LINE CONFIGURATION .DW UARTCAS_RCVBUF ; POINTER TO RCV BUFFER STRUCT ; .ECHO "UART: MODE=CAS, IO=" .ECHO UARTCBASE #IF ((UARTINTS) & (INTMODE > 0)) .ECHO ", INTERRUPTS ENABLED" #ENDIF .ECHO "\n" #ENDIF #IF (UARTMFP) UART_CFG_MFP: ; MF/PIC SERIAL PORT .DB 0 ; DEVICE NUMBER (UPDATED DURING INIT) .DB 0 ; UART TYPE .DB UARTMBASE ; IO PORT BASE (RBR, THR) .DB UARTMBASE + UART_LSR ; LINE STATUS PORT (LSR) .DW UARTCFG ; LINE CONFIGURATION .DW 0 ; SHOULD NEVER NEED INT HANDLER ; .ECHO "UART: MODE=MFP, IO=" .ECHO UARTSBASE .ECHO "\n" #ENDIF #IF (UART4) ; 4UART SERIAL PORT A .DB 0 ; DEVICE NUMBER (UPDATED DURING INIT) .DB 0 ; UART TYPE .DB UART4BASE+0 ; IO PORT BASE (RBR, THR) .DB UART4BASE+0 + UART_LSR ; LINE STATUS PORT (LSR) .DW UARTCFG ; LINE CONFIGURATION .DW 0 ; SHOULD NEVER NEED INT HANDLER ; .ECHO "UART: MODE=4UART, IO=" .ECHO UART4BASE+0 .ECHO "\n" ; ; 4UART SERIAL PORT B .DB 0 ; DEVICE NUMBER (UPDATED DURING INIT) .DB 0 ; UART TYPE .DB UART4BASE+8 ; IO PORT BASE (RBR, THR) .DB UART4BASE+8 + UART_LSR ; LINE STATUS PORT (LSR) .DW UARTCFG ; LINE CONFIGURATION .DW 0 ; SHOULD NEVER NEED INT HANDLER ; .ECHO "UART: MODE=4UART, IO=" .ECHO UART4BASE+8 .ECHO "\n" ; ; 4UART SERIAL PORT C .DB 0 ; DEVICE NUMBER (UPDATED DURING INIT) .DB 0 ; UART TYPE .DB UART4BASE+16 ; IO PORT BASE (RBR, THR) .DB UART4BASE+16 + UART_LSR ; LINE STATUS PORT (LSR) .DW UARTCFG ; LINE CONFIGURATION .DW 0 ; SHOULD NEVER NEED INT HANDLER ; .ECHO "UART: MODE=4UART, IO=" .ECHO UART4BASE+16 .ECHO "\n" ; ; 4UART SERIAL PORT D .DB 0 ; DEVICE NUMBER (UPDATED DURING INIT) .DB 0 ; UART TYPE .DB UART4BASE+24 ; IO PORT BASE (RBR, THR) .DB UART4BASE+24 + UART_LSR ; LINE STATUS PORT (LSR) .DW UARTCFG ; LINE CONFIGURATION .DW 0 ; SHOULD NEVER NEED INT HANDLER ; .ECHO "UART: MODE=4UART, IO=" .ECHO UART4BASE+24 .ECHO "\n" #ENDIF #IF (UARTRC) ; UARTRC SERIAL PORT A .DB 0 ; DEVICE NUMBER (UPDATED DURING INIT) .DB 0 ; UART TYPE .DB UARTRBASE ; IO PORT BASE (RBR, THR) .DB UARTRBASE + UART_LSR ; LINE STATUS PORT (LSR) .DW UARTCFG ; LINE CONFIGURATION .DW 0 ; SHOULD NEVER NEED INT HANDLER ; .ECHO "UART: MODE=RC, IO=" .ECHO UARTRBASE+0 .ECHO "\n" ; ; UARTRC SERIAL PORT B .DB 0 ; DEVICE NUMBER (UPDATED DURING INIT) .DB 0 ; UART TYPE .DB UARTRBASE+8 ; IO PORT BASE (RBR, THR) .DB UARTRBASE+8 + UART_LSR ; LINE STATUS PORT (LSR) .DW UARTCFG ; LINE CONFIGURATION .DW 0 ; SHOULD NEVER NEED INT HANDLER ; .ECHO "UART: MODE=RC, IO=" .ECHO UARTRBASE+8 .ECHO "\n" ; #ENDIF #IF (UARTDUAL) ; DUAL UART CHANNEL A .DB 0 ; DEVICE NUMBER (UPDATED DURING INIT) .DB 0 ; UART TYPE .DB UARTDBASE ; IO PORT BASE (RBR, THR) .DB UARTDBASE + UART_LSR ; LINE STATUS PORT (LSR) .DW UARTCFG ; LINE CONFIGURATION .DW 0 ; SHOULD NEVER NEED INT HANDLER ; .ECHO "UART: MODE=DUAL, IO=" .ECHO UARTDBASE+0 .ECHO "\n" ; ; DUAL UART CHANNEL B .DB 0 ; DEVICE NUMBER (UPDATED DURING INIT) .DB 0 ; UART TYPE .DB UARTDBASE+8 ; IO PORT BASE (RBR, THR) .DB UARTDBASE+8 + UART_LSR ; LINE STATUS PORT (LSR) .DW UARTCFG ; LINE CONFIGURATION .DW 0 ; SHOULD NEVER NEED INT HANDLER ; .ECHO "UART: MODE=DUAL, IO=" .ECHO UARTDBASE+8 .ECHO "\n" ; #ENDIF ; UART_CNT .EQU ($ - UART_CFG) / 8 ; #IF ((!UARTINTS) | (INTMODE == 0)) ; UARTSBC_RCVBUF .EQU 0 UARTCAS_RCVBUF .EQU 0 ; #ELSE ; ; UART SBC RECEIVE BUFFER ; #IF (UARTSBC) ; UARTSBC_RCVBUF: UARTSBC_CNT .DB 0 ; CHARACTERS IN RING BUFFER UARTSBC_HD .DW UARTSBC_BUF ; BUFFER HEAD POINTER UARTSBC_TL .DW UARTSBC_BUF ; BUFFER TAIL POINTER UARTSBC_BUF .FILL UART_BUFSZ,0 ; RECEIVE RING BUFFER ; #ENDIF ; ; UART CASSETTE RECEIVE BUFFER ; #IF (UARTCAS) ; UARTCAS_RCVBUF: UARTCAS_CNT .DB 0 ; CHARACTERS IN RING BUFFER UARTCAS_HD .DW UARTCAS_BUF ; BUFFER HEAD POINTER UARTCAS_TL .DW UARTCAS_BUF ; BUFFER TAIL POINTER UARTCAS_BUF .FILL UART_BUFSZ,0 ; RECEIVE RING BUFFER ; #ENDIF ; #ENDIF