; ;================================================================================================== ; ACIA 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) -- ; ; CURRENTLY ONLY SUPPORTS A SINGLE CHIP IN SYSTEM ; ACIA_DEBUG .EQU FALSE ; ACIA_NONE .EQU 0 ACIA_ACIA .EQU 1 ; ; POSSIBLE BASE I/O ADDRESSES ; NOTE THAT THE ACIA ONLY QUALIFIES ADDRESS BITS 7 & 6, SO ; THE ACIA'S TWO PORTS APPEAR REPEATEDLY OVER AN ADDRESS RANGE ; OF $40 STARTING FROM THE REAL BASE PORT. ; WE TAKE ADVANTAGE OF THIS TO AVOID CONFLICTING WITH SIO ; AND COMPACT FLASH MODULES DURING DETECTION PROBES. ; ACIAA_BASE .EQU $80 + $20 ; MODULE A ACIAB_BASE .EQU $40 + $20 ; MODULE B ; ACIA_RTSON .EQU %10010110 ; RCV INT, RTS ASSERTED, 8N1, CLK/64 BAUD ACIA_RTSOFF .EQU %11010110 ; RCV INT, RTS DEASSERTED, 8N1, CLK/64 BAUD ; ; ; ACIA_PREINIT: ; ; SETUP THE DISPATCH TABLE ENTRIES ; LD B,ACIA_CNT ; LOOP CONTROL LD C,0 ; PHYSICAL UNIT INDEX XOR A ; ZERO TO ACCUM LD (ACIA_DEV),A ; CURRENT DEVICE NUMBER ACIA_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,ACIA_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 ACIA_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 ACIA TYPE DETECTED OR A ; SET FLAGS JR Z,ACIA_PREINIT2 ; SKIP IT IF NOTHING FOUND ; PUSH BC ; SAVE LOOP CONTROL LD BC,ACIA_FNTBL ; BC := FUNCTION TABLE ADDRESS CALL NZ,CIO_ADDENT ; ADD ENTRY IF ACIA FOUND, BC:DE POP BC ; RESTORE LOOP CONTROL ; ACIA_PREINIT2: INC C ; NEXT PHYSICAL UNIT DJNZ ACIA_PREINIT0 ; LOOP UNTIL DONE ; XOR A ; SIGNAL SUCCESS RET ; AND RETURN ; ; ACIA INITIALIZATION ROUTINE ; ACIA_INITUNIT: CALL ACIA_DETECT ; DETERMINE ACIA TYPE LD (IY + 1),A ; SAVE IN CONFIG TABLE OR A ; SET FLAGS RET Z ; ABORT IF NOTHING THERE ; UPDATE WORKING ACIA DEVICE NUM LD HL,ACIA_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 ; ADD IM1 INT CALL LIST ENTRY LD L,(IY+6) ; GET RCVBUF PTR LD H,(IY+7) ; ... INTO HL LD A,5 ; OFFSET OF INT HANDLER PTR CALL ADDHLA ; ADD TO HL LD A,(HL) ; DEREFERENCE INC HL ; ... LD H,(HL) ; ... LD L,A ; ... CALL HB_ADDIM1 ; ADD TO IM1 CALL LIST ; SET DEFAULT CONFIG LD DE,-1 ; LEAVE CONFIG ALONE JP ACIA_INITDEV ; IMPLEMENT IT AND RETURN ; ; ; ACIA_INIT: LD B,ACIA_CNT ; COUNT OF POSSIBLE ACIA UNITS LD C,0 ; INDEX INTO ACIA CONFIG TABLE ACIA_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,ACIA_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 ACIA TYPE OR A ; SET FLAGS CALL NZ,ACIA_PRTCFG ; PRINT IF NOT ZERO POP BC ; RESTORE LOOP CONTROL INC C ; NEXT UNIT DJNZ ACIA_INIT1 ; LOOP TILL DONE ; XOR A ; SIGNAL SUCCESS RET ; DONE ; ; INTERRUPT HANDLERS ; ACIAA_INT: LD IY,ACIAA_CFG ; POINT TO CONFIG ; ; CHECK FOR RECEIVE PENDING LD C,(IY+3) ; STATUS PORT IN A,(C) ; GET STATUS AND $01 ; ISOLATE RECEIVE READY BIT RET Z ; IF NOT, RETURN WITH Z SET ; ACIAA_INT00: ; HANDLE PENDING RECEIVE INC C ; DATA PORT IN A,(C) ; READ PORT DEC C ; BACK TO CONTROL PORT LD E,A ; SAVE BYTE READ LD A,(ACIAA_BUFCNT) ; GET CURRENT BUFFER USED COUNT CP ACIAA_BUFSZ ; COMPARE TO BUFFER SIZE RET Z ; BAIL OUT IF BUFFER FULL, RCV BYTE DISCARDED INC A ; INCREMENT THE COUNT LD (ACIAA_BUFCNT),A ; AND SAVE IT CP ACIAA_BUFSZ - 5 ; BUFFER GETTING FULL? JR NZ,ACIAA_INT0 ; IF NOT, BYPASS DEASSERTING RTS LD A,ACIA_RTSOFF ; VALUE TO DEASSERT RTS OUT (C),A ; DO IT ACIAA_INT0: LD HL,(ACIAA_HD) ; GET HEAD POINTER LD A,L ; GET LOW BYTE CP ACIAA_BUFEND & $FF ; PAST END? JR NZ,ACIAA_INT1 ; IF NOT, BYPASS POINTER RESET LD HL,ACIAA_BUF ; ... OTHERWISE, RESET TO START OF BUFFER ACIAA_INT1: LD A,E ; RECOVER BYTE READ LD (HL),A ; SAVE RECEIVED BYTE TO HEAD POSITION INC HL ; INCREMENT HEAD POINTER LD (ACIAA_HD),HL ; SAVE IT ; ; CHECK FOR MORE PENDING... IN A,(C) ; GET STATUS RRA ; READY BIT TO CF JR C,ACIAA_INT00 ; IF SET, DO SOME MORE OR $FF ; NZ SET TO INDICATE INT HANDLED RET ; AND RETURN ; ACIAB_INT: LD IY,ACIAB_CFG ; POINT TO CONFIG ; ; CHECK FOR RECEIVE PENDING LD C,(IY+3) ; STATUS PORT IN A,(C) ; GET STATUS AND $01 ; ISOLATE RECEIVE READY BIT RET Z ; IF NOT, RETURN WITH Z SET ; ACIAB_INT00: ; HANDLE PENDING RECEIVE INC C ; DATA PORT IN A,(C) ; READ PORT DEC C ; BACK TO CONTROL PORT LD E,A ; SAVE BYTE READ LD A,(ACIAB_BUFCNT) ; GET CURRENT BUFFER USED COUNT CP ACIAB_BUFSZ ; COMPARE TO BUFFER SIZE RET Z ; BAIL OUT IF BUFFER FULL, RCV BYTE DISCARDED INC A ; INCREMENT THE COUNT LD (ACIAB_BUFCNT),A ; AND SAVE IT CP ACIAB_BUFSZ - 5 ; BUFFER GETTING FULL? JR NZ,ACIAB_INT0 ; IF NOT, BYPASS DEASSERTING RTS LD A,ACIA_RTSOFF ; VALUE TO DEASSERT RTS OUT (C),A ; DO IT ACIAB_INT0: LD HL,(ACIAB_HD) ; GET HEAD POINTER LD A,L ; GET LOW BYTE CP ACIAB_BUFEND & $FF ; PAST END? JR NZ,ACIAB_INT1 ; IF NOT, BYPASS POINTER RESET LD HL,ACIAB_BUF ; ... OTHERWISE, RESET TO START OF BUFFER ACIAB_INT1: LD A,E ; RECOVER BYTE READ LD (HL),A ; SAVE RECEIVED BYTE TO HEAD POSITION INC HL ; INCREMENT HEAD POINTER LD (ACIAB_HD),HL ; SAVE IT ; ; CHECK FOR MORE PENDING... IN A,(C) ; GET STATUS RRA ; READY BIT TO CF JR C,ACIAB_INT00 ; IF SET, DO SOME MORE OR $FF ; NZ SET TO INDICATE INT HANDLED RET ; AND RETURN ; ; DRIVER FUNCTION TABLE ; ACIA_FNTBL: .DW ACIA_IN .DW ACIA_OUT .DW ACIA_IST .DW ACIA_OST .DW ACIA_INITDEV .DW ACIA_QUERY .DW ACIA_DEVICE #IF (($ - ACIA_FNTBL) != (CIO_FNCNT * 2)) .ECHO "*** INVALID ACIA FUNCTION TABLE ***\n" #ENDIF ; ; ; ACIA_IN: LD A,(IY+2) ; GET MODULE ID OR A ; SET FLAGS JR Z,ACIAA_IN ; HANDLE MODULE 0 DEC A ; TEST FOR NEXT JR Z,ACIAB_IN ; HANDLE MODULE 1 CALL PANIC ; ELSE FATAL ERROR RET ; ACIAA_IN: CALL ACIAA_IST ; RECEIVED CHAR READY? JR Z,ACIAA_IN ; LOOP TILL WE HAVE SOMETHING IN BUFFER HB_DI ; AVOID COLLISION WITH INT HANDLER LD A,(ACIAA_BUFCNT) ; GET COUNT DEC A ; DECREMENT COUNT LD (ACIAA_BUFCNT),A ; SAVE SAVE IT CP 5 ; BUFFER LOW THRESHOLD JR NZ,ACIAA_IN0 ; IF NOT, BYPASS SETTING RTS LD C,(IY + 3) ; C := ACIA CMD PORT LD A,ACIA_RTSON ; ASSERT RTS OUT (C),A ; DO IT ACIAA_IN0: LD HL,(ACIAA_TL) ; GET BUFFER TAIL POINTER LD E,(HL) ; GET BYTE INC HL ; BUMP TAIL POINTER LD A,L ; GET LOW BYTE CP ACIAA_BUFEND & $FF ; PAST END? JR NZ,ACIAA_IN1 ; IF NOT, BYPASS POINTER RESET LD HL,ACIAA_BUF ; ... OTHERWISE, RESET TO START OF BUFFER ACIAA_IN1: LD (ACIAA_TL),HL ; SAVE UPDATED TAIL POINTER HB_EI ; INTERRUPTS OK AGAIN XOR A ; SIGNAL SUCCESS RET ; AND DONE ; ACIAB_IN: CALL ACIAB_IST ; RECEIVED CHAR READY? JR Z,ACIAB_IN ; LOOP TILL WE HAVE SOMETHING IN BUFFER HB_DI ; AVOID COLLISION WITH INT HANDLER LD A,(ACIAB_BUFCNT) ; GET COUNT DEC A ; DECREMENT COUNT LD (ACIAB_BUFCNT),A ; SAVE SAVE IT CP 5 ; BUFFER LOW THRESHOLD JR NZ,ACIAB_IN0 ; IF NOT, BYPASS SETTING RTS LD C,(IY + 3) ; C := ACIA CMD PORT LD A,ACIA_RTSON ; ASSERT RTS OUT (C),A ; DO IT ACIAB_IN0: LD HL,(ACIAB_TL) ; GET BUFFER TAIL POINTER LD E,(HL) ; GET BYTE INC HL ; BUMP TAIL POINTER LD A,L ; GET LOW BYTE CP ACIAB_BUFEND & $FF ; PAST END? JR NZ,ACIAB_IN1 ; IF NOT, BYPASS POINTER RESET LD HL,ACIAB_BUF ; ... OTHERWISE, RESET TO START OF BUFFER ACIAB_IN1: LD (ACIAB_TL),HL ; SAVE UPDATED TAIL POINTER HB_EI ; INTERRUPTS OK AGAIN XOR A ; SIGNAL SUCCESS RET ; AND DONE ; ; ; ACIA_OUT: CALL ACIA_OST ; READY FOR CHAR? JR Z,ACIA_OUT ; LOOP IF NOT LD C,(IY + 3) ; C := ACIA CMD PORT INC C ; BUMP TO DATA PORT OUT (C),E ; SEND CHAR FROM E XOR A ; SIGNAL SUCCESS RET ; ; ; ACIA_IST: LD A,(IY+2) ; GET MODULE ID OR A ; SET FLAGS JR Z,ACIAA_IST ; HANDLE MODULE 0 DEC A ; TEST FOR NEXT JR Z,ACIAB_IST ; HANDLE MODULE 1 CALL PANIC ; ELSE FATAL ERROR RET ; ACIAA_IST: LD A,(ACIAA_BUFCNT) ; GET BUFFER UTILIZATION COUNT OR A ; SET FLAGS JP Z,CIO_IDLE ; NOT READY, RETURN VIA IDLE PROCESSING RET ; AND DONE ; ACIAB_IST: LD A,(ACIAB_BUFCNT) ; GET BUFFER UTILIZATION COUNT OR A ; SET FLAGS JP Z,CIO_IDLE ; NOT READY, RETURN VIA IDLE PROCESSING RET ; AND DONE ; ; ; ACIA_OST: LD C,(IY + 3) ; CMD PORT IN A,(C) ; GET STATUS AND $02 ; ISOLATE BIT 2 (TX EMPTY) 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 ; ; ; ACIA_INITDEV: HB_DI ; AVOID CONFLICTS ; ; PROGRAM THE ACIA CHIP LD C,(IY + 3) ; COMMAND PORT LD A,$FF ; MASTER RESET OUT (C),A ; DO IT LD A,ACIA_RTSON ; NORMAL OPERATION OUT (C),A ; DO IT ; ; 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 ; HB_EI ; READY FOR INTS AGAIN XOR A ; SIGNAL SUCCESS RET ; RETURN ; ; ; ACIA_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 ; ; ; ACIA_DEVICE: LD D,CIODEV_ACIA ; D := DEVICE TYPE LD E,(IY) ; E := PHYSICAL UNIT XOR A ; SIGNAL SUCCESS RET ; ; ACIA DETECTION ROUTINE ; ACIA_DETECT: ;LD C,ACIA_BASE ; BASE PORT ADDRESS LD C,(IY+3) ; BASE PORT ADDRESS CALL ACIA_DETECT2 ; CHECK IT JR Z,ACIA_DETECT1 ; FOUND IT, RECORD IT ;LD C,ACIA_ALTBASE ; ALT BASE PORT ADDRESS ;CALL ACIA_DETECT2 ; CHECK IT ;JR Z,ACIA_DETECT1 ; FOUND IT, RECORD IT LD A,ACIA_NONE ; NOTHING FOUND RET ; DONE ; ACIA_DETECT1: ; ACIA FOUND, RECORD IT ;LD A,C ; BASE PORT ADDRESS TO A ;LD (IY + 3),A ; SAVE ACTIVE BASE PORT LD A,ACIA_ACIA ; RETURN CHIP TYPE RET ; DONE ; ACIA_DETECT2: ; LOOK FOR ACIA AT PORT ADDRESS IN C LD A,$03 ; MASTER RESET OUT (C),A ; DO IT IN A,(C) ; GET STATUS OR A ; CHECK FOR ZERO RET NZ ; RETURN IF NOT ZERO LD A,$02 ; CLEAR MASTER RESET OUT (C),A ; DO IT ; CHECK FOR EXPECTED BITS: ; TDRE=1, DCD & CTS = 0 AND %00001110 ; BIT MASK FOR "STABLE" BITS CP %00000010 ; EXPECTED VALUE RET ; RETURN RESULT, Z = CHIP FOUND ; ; ; ACIA_PRTCFG: ; ANNOUNCE PORT CALL NEWLINE ; FORMATTING PRTS("ACIA$") ; FORMATTING LD A,(IY) ; DEVICE NUM CALL PRTDECB ; PRINT DEVICE NUM PRTS(": IO=0x$") ; FORMATTING LD A,(IY + 3) ; GET BASE PORT CALL PRTHEXBYTE ; PRINT BASE PORT ; PRINT THE ACIA TYPE CALL PC_SPACE ; FORMATTING LD A,(IY + 1) ; GET ACIA TYPE BYTE RLCA ; MAKE IT A WORD OFFSET LD HL,ACIA_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 ACIA WAS DETECTED LD A,(IY + 1) ; GET ACIA 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 ; XOR A RET ; ; ; ACIA_TYPE_MAP: .DW ACIA_STR_NONE .DW ACIA_STR_ACIA ACIA_STR_NONE .DB "$" ACIA_STR_ACIA .DB "ACIA$" ; ; WORKING VARIABLES ; ACIA_DEV .DB 0 ; DEVICE NUM USED DURING INIT ; ; RECEIVE BUFFERS ; ACIAA_RCVBUF: ACIAA_BUFCNT .DB 0 ; CHARACTERS IN RING BUFFER ACIAA_HD .DW ACIAA_BUF ; BUFFER HEAD POINTER ACIAA_TL .DW ACIAA_BUF ; BUFFER TAIL POINTER ACIAA_INTP .DW ACIAA_INT ; INT HANDLER POINTER ACIAA_BUF .FILL 32,0 ; RECEIVE RING BUFFER ACIAA_BUFEND .EQU $ ; END OF BUFFER ACIAA_BUFSZ .EQU $ - ACIAA_BUF ; SIZE OF RING BUFFER ; ACIAB_RCVBUF: ACIAB_BUFCNT .DB 0 ; CHARACTERS IN RING BUFFER ACIAB_HD .DW ACIAB_BUF ; BUFFER HEAD POINTER ACIAB_TL .DW ACIAB_BUF ; BUFFER TAIL POINTER ACIAB_INTP .DW ACIAB_INT ; INT HANDLER POINTER ACIAB_BUF .FILL 32,0 ; RECEIVE RING BUFFER ACIAB_BUFEND .EQU $ ; END OF BUFFER ACIAB_BUFSZ .EQU $ - ACIAB_BUF ; SIZE OF RING BUFFER ; ; ACIA PORT TABLE ; ACIA_CFG: ACIAA_CFG: ; ACIA MODULE A CONFIG .DB 0 ; DEVICE NUMBER (SET DURING INIT) .DB 0 ; ACIA TYPE (SET DURING INIT) .DB 0 ; MODULE ID .DB ACIAA_BASE ; BASE PORT (SET DURING DETECT) .DW DEFSERCFG ; LINE CONFIGURATION .DW ACIAA_RCVBUF ; POINTER TO RCV BUFFER STRUCT ACIAB_CFG: ; ACIA MODULE B CONFIG .DB 0 ; DEVICE NUMBER (SET DURING INIT) .DB 0 ; ACIA TYPE (SET DURING INIT) .DB 1 ; MODULE ID .DB ACIAB_BASE ; BASE PORT (SET DURING DETECT) .DW DEFSERCFG ; LINE CONFIGURATION .DW ACIAB_RCVBUF ; POINTER TO RCV BUFFER STRUCT ; ACIA_CNT .EQU ($ - ACIA_CFG) / 8