;The following set of routines uses the COP888CG UART and several I/O pins
;to simulate an RS232 port interface. The code handles hardware control
;signals, echo back of received characters, and error checking.  A single
;routine called INIT initializes the UART and hardware control signals.
;The transmitting and receiving of characters is handled in several
;interrupt routines. The UART is disabled by calling the DISABLE routine.
;The user must select values for several constants before compiling
;this code.
;
;NOTES:
;       * The COP tranmitter is enabled only when the transmit/receive
;         buffer is not empty and the appropriate RS232 control signals
;         from the remote receiver are present.
;       * The COP receiver is always enabled.
;       * The remote transmitter is disabled whenever the transmit/
;         receive data buffer is full.
;
; EDITTED MAY 11, 1994 - REVISED FOR NEW ASSEMBLER (ASMCOP v4.2) 
;

	.TITLE	UART
        .INCLD  COP888.INC
	.SECT	MAIN,ROM,ABS=0

;Definition of Constants
        ENUCMD  =  081      ;Value to put in the ENU register
                            ;Selects bits per char and parity option
                            ;DEFAULT = 081  (7 bits/char and odd parity)

        ENUICMD =  023      ;Value to put in the ENUI register
                            ;Selects number of stop bits, uart clock option,
                            ;sync/async option, xmit/rcv interrupt enable,
                            ;and TDX pin enable
                            ;DEFAULT = 023 ( 1 stop bit, internal BRG,
                            ;async operation, no interrupt, and TDX enabled)

        BAUDVAL = 04        ;Baud rate divisor equals N - 1
        PSRVAL  = 0C8       ;Baud rate prescalar
                            ; BR = FC/(16 * N*P) where
                            ;       FC = CKI frequency
                            ;       N  = Baud Divisor
                            ;       P  = Prescalar
                            ;GIVEN:         CALCULATE:   BAUDVAL:  PSRVAL:
                            ;CKI = 10MHz     N = 5
                            ;BR  = 9600      P = 13        04       0C8
                            ;
                            ;CKI = 10MHz     N = 10
                            ;BR  = 4800      P = 13        09       0C8
                            ;
                            ;See tables in users manual for translation
                            ;of N and P to BAUDVAL and PSRVAL

        START   =  010      ;Beginning address of the xmit/rcv buffer
        END     =  01D      ;End address of the xmit/rcv buffer
        HEAD    =  01E      ;RAM address where current head of buffer stored
        TAIL    =  01F      ;RAM address where current tail of buffer stored
        SIZE    =  0E       ;Size of transmit/receive data buffer

        DCD     =  00       ;Bit position of DCD signal on L port pins
        CTS     =  05       ;Bit position of CTS signal on L port pins
        DTR     =  07       ;Bit position of DTR signal on L port pins
        RTS     =  04       ;Bit position of RTS signal on L port pins
        DSR     =  06       ;Bit position of DSR signal on L port pins
        ETDX    =  05       ;Bit position of TDX enable pin in ENUI
        TIE     =  00       ;Bit position of TX interrupt enable bit
        RIE     =  01       ;Bit position of RX interrupt enable bit
;        PE      =  05       ;Bit position of parity error in ENUR  ; Declared in include
;        FE      =  06       ;Bit position of framing error in ENUR ; Declared in include
        DOE     =  07       ;Bit position of data overrun error in ENUR





MAIN:   JSR     INIT                ;INITIALIZE UART
        JP      .                   ;DO OTHER TASKS
        JSR     DISABLE             ;DISABLE UART
        JP      .                   ;DO OTHER TASKS

INIT:
        LD      B,#PSW
        RBIT    GIE,[B]             ;DISABLE ALL INTERRUPTS
        LD      PSR,#00             ;UART OFF (POWERDOWN)
        LD      PORTLC,#065         ;SET I/O
        LD      B,#PORTLD           ;NOT READY TO RECEIVE
        SBIT    DSR,[B]             ;   TURN OFF DATA SET READY
        SBIT    CTS,[B]             ;   TURN OFF CLEAR TO SEND
        RBIT    DCD,[B]             ;   TURN ON DATA CARRIER DETECT
        LD      HEAD,#START         ;INIT HEAD POINTER
        LD      TAIL,#START         ;INIT TAIL POINTER
        LD      B,#ICNTRL           ;CONFIGURE PORTL INTERRUPTS
        RBIT    LPEN,[B]            ;  DISABLE PORTL INTERRUPTS
        LD      WKEDG,#090          ;  SELECT FALLING EDGE FOR RTS AND DTR
        LD      WKEN,#090           ;  ENABLE RTS AND DTR INTERRUPT
        LD      WKPND,#00           ;  CLEAR PORTL INTERRUPT PENDING FLAGS
        SBIT    LPEN,[B]            ;  ENABLE PORT L INTERRUPTS
        LD      ENU,#ENUCMD         ;SELECT BITS/CHAR AND PARITY OPTION
        LD      ENUR,#00            ;CLEAR ERROR BITS
        LD      ENUI,#ENUICMD       ;SELECT CLOCK,INTERRUPTS,STOPBITS
        LD      BAUD,#BAUDVAL       ;SETUP BRG
        LD      B,#ENUI
        SBIT    TIE,[B]             ;ENABLE TRANSMITTER INTERRUPT
        SBIT    RIE,[B]             ;ENABLE RECEIVER INTERRUPT
        LD      PSR,#PSRVAL         ;UART ON
        LD      B,#PORTLD           ;READY TO RECEIVE
        RBIT    DSR,[B]             ;  TURN ON DATA SET READY
        RBIT    CTS,[B]             ;  TURN ON CLEAR TO SEND
        LD      B,#PSW
        SBIT    GIE,[B]             ;ENABLE ALL INTERRUPTS
        RET

DISABLE:
        RBIT    GIE,PSW             ;DISABLE INTERRUPTS
        LD      PORTLD,#061         ;TURN OFF HANDSHAKING SIGNALS
        LD      PSR,#00             ;UART POWERDOWN
        LD      ENU,#00             ;CLEAR UART CONTROL REGISTERS
        LD      ENUI,#00
        LD      ENUR,#00
        LD      B,#WKEN             ;DISABLE RTS AND DTR INTERRUPTS
        RBIT    RTS,[B]
        RBIT    DTR,[B]
        SBIT    GIE,PSW             ;ENABLE INTERRUPTS
        RET


;INTERRUPT ROUTINES

        . = 0FF                     ;INTERRUPT START ADDRESS
        PUSH    A                   ;CONTEXT SAVE
        LD      A,B
        PUSH    A
        LD      A,PSW
        PUSH    A
        VIS
REST:   POP     A                   ;CONTEXT RESTORE
        X       A,PSW
        POP     A
        X       A,B
        POP     A
        RETI


;PORT L INTERRUPTS
;  The port L interrupts are used to indicate a return to active
; state of the DTR and RTS signals from the remote receiver.
; If both DTR and RTS are active, the remote receiver is ready
; to accept data and the COP transmitter is enabled.

LINT:                               ;PORT L INTERRUPT
        LD      WKPND,#00           ;RESET PENDING BITS
        LD      A,PORTLP            ;READ PORT L PINS
        IFBIT   #RTS,A              ;IF RTS (ACTIVE LOW) NOT PRESENT
        JP      NOTRDY              ;THEN REMOTE NOT READY TO RECEIVE
        IFBIT   #DTR,A              ;IF DTR (ACTIVE LOW) NOT PRESENT
        JP      NOTRDY              ;THEN REMOTE NOT READY TO RECEIVE
READY:  LD      B,#ENUI
        SBIT    TIE,[B]             ;RE-ENABLE TRANSMITTER INTERRUPT
NOTRDY: JP      REST                ;EXIT INTERRUPT


;UART RECEIVE INTERRUPT
;  The UART receive interrupt does the following:
;       1. Reads the received data
;       2. Checks for receiver errors
;       3. If no errors detected, places the received data in
;          the transmit/receive buffer and enables the transmitter (for echo
;          back of characters).
;       4. If errors detected, the transmit/receive buffer is cleared
;          of ALL data and an error message is placed in the data buffer.
RCVINT:                             ;RECEIVER INTERRUPT
        LD      A,TAIL
        X       A,B                 ;GET TAIL POINTER
        LD      A,RBUF              ;READ RECEIVED DATA
        X       A,[B+]              ;STORE RECEIVED DATA
        LD      A,ENUR              ;READ ERROR REGISTER
        SBIT    TIE,ENUI            ;ENABLE TRANSMITTER INTERRUPT
        ANDSZ   A,#0E0              ;CHECK FOR PE,DOE,FE
        JP      ERROR               ;THROW DATA AWAY IN BUFFER
        LD      A,B                 ;LOAD ACC WITH NEW TAIL PTR
        IFEQ    A,#END+1            ;IF END OF DATA BUFFER
        LD      A,#START            ; SET TAIL PTR TO START OF BUFFER
        X       A,TAIL              ;SAVE TAIL PTR
        LD      A,HEAD              ;IS DATA BUFFER FULL?
        SC
        SUBC    A,TAIL              ;A = HEAD - TAIL
        IFNC                        ;IF BORROWED (TAIL > HEAD)
        ADD     A,#SIZE             ;THEN ADD BUFFER SIZE TO RESULT
        IFGT    A,#03               ;IF DATA BUFFER NOT FULL
        JMP     REST                ;  THEN EXIT INTERRUPT
RXOFF:  SBIT    CTS,PORTLD          ;  ELSE TURN OFF REMOTE TRANSMITTER
        JMP     REST                ;EXIT INTERRUPT
ERROR:
        LD      HEAD,#START         ;CLEAR BUFFER
        LD      B,#START            ;POINT TO START OF BUFFER
        IFBIT   PE,A
        LD      [B+],#'P'           ;P = PARITY
        IFBIT   FE,A
        LD      [B+],#'F'           ;F = FRAMING
        IFBIT   DOE,A
        LD      [B+],#'D'           ;D = DATA OVERRUN
        LD      [B+],#020           ;BLANK SPACE
        LD      [B+],#'E'
        LD      [B+],#'R'
        LD      [B+],#'R'
        LD      [B+],#'O'
        LD      [B+],#'R'
        LD      [B+],#0A            ;LINE FEED
        LD      [B+],#0D            ;CARRIAGE RETURN
OUTERR: LD      A,B                 ;SAVE NEW TAIL PTR
        X       A,TAIL
        JMP     REST


;UART TRANSMIT INTERRUPT
;  The UART transmit interrupt does the following:
;       1. Checks for RTS and DTR signals (OK to transmit?)
;       3. If OK to transmit and buffer not empty, transmits data.
;       4. If not OK to transmit or buffer empty, disables transmitter.

XMITINT:                            ;TRANSMITTER INTERRUPT
        LD      A,PORTLP
        ANDSZ   A,#090              ;IS IT OK TO TRANSMITT?
        JMP     IDLE                ;NO:  GO TURN OFF TRANSMITTER
        LD      A,HEAD              ;YES: GET PTR TO DATA
        IFEQ    A,TAIL              ;IF DATA BUFFER EMPTY
        JMP     IDLE                ;THEN TURN OFF TRANSMITTER
        X       A,B                 ;ELSE
        LD      A,[B+]              ;GET TRANSMIT DATA
        X       A,TBUF              ;SEND TRANSMIT DATA
        LD      A,B                 ;LOAD ACC WITH NEW HEAD PTR
        IFEQ    A,#END+1            ;IF END OF DATA BUFFER
        LD      A,#START            ;  SET HEAD PTR TO START OF BUFFER
        X       A,HEAD              ;SAVE HEAD PTR
        LD      A,HEAD              ;IS DATA BUFFER FULL?
        IFEQ    A,TAIL              ;IF BUFFER EMPTY
        JP      NFULL               ;   THEN NOT FULL
        SC                          ;   ELSE CHECK HOW FULL
        SUBC    A,TAIL              ;A = HEAD - TAIL
        IFNC                        ;IF BORROWED (TAIL > HEAD)
        ADD     A,#SIZE             ;THEN ADD BUFFER SIZE TO RESULT
        IFGT    A,#03               ;IF DATA BUFFER NOT FULL
NFULL:  RBIT    CTS,PORTLD          ;   THEN TURN ON REMOTE TRANSMITTER
        JMP     REST                ;   ELSE EXIT INTERRUPT
IDLE:   LD      B,#ENUI
        RBIT    TIE,[B]             ;DISABLE TRANSMITTER INTERRUPT
        JMP     REST                ;EXIT INTERRUPT

;Software Trap
;
SFTINT: RPND
        JMP     00                  ;RESTART

;VECTOR INTERRUPT TABLE

        .=01E2
        .ADDRW  LINT                ;L PORT INTERRUPT
        .=01EC
        .ADDRW  XMITINT             ;TRANSMITTER INTERRUPT
        .ADDRW  RCVINT              ;RECEIVER INTERRUPT
        .=01FE
        .ADDRW  SFTINT              ;SOFTWARE INTERRUPT/TRAP
        .END
