.macro CLC ANDCC #$FE .endm .macro SEC ORCC #1 .endm .macro CLI ANDCC #$EF .endm .macro SEI ORCC #$10 .endm ; Title: Interrupt input and output using a 6850 ACIA ; and multiple character buffers. ; Name: SINTB ; Purpose: This program consists of 5 subroutines which ; perform interrupt driven input and output using ; a 6850 ACIA. ; ; INCH ; Read a character. ; INST ; Determine input status (whether input buffer is empty). ; OUTCH ; Write a character. ; OUTST ; Determine output status (whether output buffer is full). ; INIT ; Initialize 6850 ACIA and interrupt system. ; ; Entry: ; INCH ; No parameters. ; INST ; No parameters. ; OUTCH ; Register A = character to transmit ; OUTST ; No parameters. ; INIT ; No parameters. ; ; Exit: ; INCH ; Register A = character. ; INST ; Carry = 0 if input buffer is empty, ; 1 if character is available. ; OUTCH ; No parameters ; OUTST ; Carry = 0 if output buffer is not ; full, 1 if it is full. ; INIT ; No parameters. ; ; Registers Used: ; INCH ; A,CC,X ; INST ; A,CC ; OUTCH ; A,CC,X ; OUTST ; A,CC ; INIT ; A,X ; ; Time: ; ; INCH ; Approximately 86 cycles if a character is available ; INST ; 21 cycles ; OUTCH ; Approximately 115 cycles if output buffer is ; not full and output interrupt is expected. ; OUTST ; 26 cycles ; INIT ; 106 cycles ; IOSRVC ; 44 cycles minimum if the interrupt is not ours ; 112 cycles to service an input interrupt ; 148 cycles to service an output interrupt ; These include the 21 cycles required for the ; processor to respond to an interrupt. ; Size: ; Program 235 bytes ; Data 11 bytes plus size of buffers ; ; Buffers: ; ; The routines assume two buffers starting at ; address IBUF and OBUF. ; The Length of the buffers in bytes are IBSZ and OBSZ. ; ; For the input buffer, IHEAD is the address of the ; oldest character (the next one the main ; program should read), ITAIL is the address of the ; next empty element (the next one the service routine should fill), ; and ICNT is the number of bytes currently filled with characters. ; ; For the output buffer, OHEAD is the address of the ; character (the next one the service routine should send), ; OTAIL is the address of the next empty element ; (the next one the main program should fill), and ; OCNT is the number of bytes currently filled with characters. ; ; Note: ; Wraparound is provided on both buffers, so that ; the currently filled area may start anywhere ; and extend through the end of the buffer and ; back to the beginning. ; ; For example, if the output buffer is 40 hex bytes long, ; the section filled with characters could extend from ; OBUF +32H (OHEAD) through OBUF+10H (0TAIL1). ; That is, there are 19H filled bytes occupying addresses ; OBUF+32H through OBUF+10H. ; The buffer thus looks like a television picture with the ; vertical hold skewed, so that the frame starts above ; the bottom of the screen, leaves off at the top, ; and continues at the bottom. ; ; 6850 ACIA (UART) EQUATES ; ARBITRARY 6850 ACIA MEMORY ADDRESSES ; ACIADR EQU $A400 ; ACIA DATA REGISTER ACIASR EQU $A401 ; ACIA STATUS REGISTER ACIACR EQU $A401 ; ACIA CONTROL REGISTER ; ; TRS-80 COLOR COMPUTER INTERRUPT VECTOR ; INTVEC EQU $0100 ; VECTOR TO INTERRUPT SERVICE ROUTINE ; ; READ CHARACTER FROM INPUT BUFFER ; INCH: JSR INST ; GET INPUT STATUS BCC INCH ; BRANCH (WAIT) IF NO CHARACTER AVAILABLE DEC ICNT ; REDUCE INPUT BUFFER COUNT BY 1 LDX IHEAD ; GET CHARACTER FROM HEAD OF INPUT BUFFER LDA ,X ; JSR INCIPTR ; MOVE HEAD POINTER UP 1 WITH WRAPAROUND STX IHEAD RTS ; ; RETURN INPUT STATUS (CARRY = 1 IF INPUT DATA AVAILABLE) ; INST: CLC ; CLEAR CARRY, INDICATING BUFFER EMPTY TST ICNT ; TEST INPUT BUFFER COUNT BEQ EXINST ; BRANCH (EXIT) IF BUFFER EMPTY SEC ; SET CARRY TO INDICATE DATA AVAILABLE EXINST: RTS ; RETURN, CARRY INDICATES WHETHER DATA ; IS AVAILABLE ; ; WRITE A CHARACTER INTO OUTPUT BUFFER ; OUTCH: PSHS A ; SAVE CHARACTER TO WRITE ; WAIT UNTIL OUTPUT BUFFER NOT FULL, STORE NEXT CHARACTER WAITOC: JSR OUTST ; GET OUTPUT STATUS BCS WAITOC ; BRANCH (WAIT) IF OUTPUT BUFFER FULL INC OCNT ; INCREASE OUTPUT BUFFER COUNT BY 1 LDX OTAIL ; POINT AT NEXT EMPTY BYTE IN BUFFER PULS A ; GET CHARACTER STA ,X ; STORE CHARACTER AT TAIL OF BUFFER JSR INCOPTR ; MOVE TAIL POINTER UP 1 STX OTAIL TST OIE ; TEST OUTPUT INTERRUPT EXPECTED FLAG BNE EXWAIT JSR OUTDAT ; OUTPUT CHARACTER IMMEDIATELY IF ; OUTPUT INTERRUPT NOT EXPECTED EXWAIT: RTS ; OUTPUT STATUS (CARRY = 1 IF OUTPUT BUFFER FULL) ; OUTST: LDA OCNT ; GET OUTPUT BUFFER COUNT CMPA #SZOBUF ; IS OUTPUT BUFFER FULL? SEC ; SET CARRY, INDICATING OUTPUT BUFFER ; FULL BEQ EXOUTS ; BRANCH (EXIT) IF OUTPUT BUFFER FULL CLC ; INDICATE OUTPUT BUFFER NOT FULL EXOUTS: RTS ; CARRY =1 1 IF BUFFER FULL, 0 IF NOT ; ; INITIALIZE 6850 ACIA, INTERRUPT SYSTEM ; INIT: ; DISABLE INTERRUPTS DURING INITIALIZATION BUT SAVE ; PREVIOUS STATE OF INTERRUPT FLAG ; PSHS CC ; SAVE CURRENT FLAGS (PARTICULARLY I FLAG) SEI ; DISABLE INTERRUPTS DURING ; INITIALIZATION ; ; INITIALIZE COLOR COMPUTER INTERRUPT VECTOR ; LDX INTVEC ; GET CURRENT INTERRUPT VECTOR STX NEXTSR ; SAVE IT AS ADDRESS 0F ; NEXT SERVICE ROUTINE LDX #IOSRVC ; GET ADDRESS OF OUR SERVICE ROUTINE STX INTVEC ; SAVE IT AS INTERRUPT VECTOR ; ; INITIALIZE BUFFER COUNTERS AND POINTERS, INTERRUPT FLAG ; CLR ICNT ; INPUT BUFFER EMPTY CLR OCNT ; OUTPUT BUFFER EMPTY CLR OIE ; INDICATE NO OUTPUT INTERRUPT EXPECTED LDX #IBUF ; INPUT HEAD/TAIL POINT TO BASE STX IHEAD ; ADDRESS OF INPUT BUFFER STX ITAIL LDX #OBUF ; OUTPUT HEAD/TAIL POINT TO BASE STX OHEAD ; ADDRESS OF OUTPUT BUFFER STX OTAIL ; ; INITIALIZE 6850 ACIA ; LDA #00000011b ; MASTER RESET 6850 ACIA (NOTE IT STA ACIACR ; HAS NO RESET INPUT) LDA #10010001b STA ACIACR ; SET ACIA OPERATING MODE ; ; BIT 7 = 1 TO ENABLE INPUT INTERRUPTS ; BITS 6,S = 0 TO DISABLE OUTPUT ; INTERRUPTS ; BITS 4,3,2 = 100 FOR 8 DATA BITS, ; 2 STOP BITS ; BITS 1,0 = 01 FOR DIVIDE BY 16 CLOCK MODE PULS CC ; RESTORE FLAGS (THIS REENABLES INTERRUPTS ; IF THEY HERE ENABLED WHEN INIT HAS ; CALLED) RTS ; ; INPUT/OUTPUT INTERRUPT SERVICE ROUTINE ; IOSRVC: ; ; GET ACIA STATUS: ; BIT 0 = 1 IF AN INPUT INTERRUPT, ; BIT 1 = 1 IF AN OUTPUT INTERRUPT ; LDA ACIASR LSRA ; MOVE BIT 0 TO CARRY BCS RDHDLR ; BRANCH IF AN INPUT INTERRUPT LSRA ; MOVE BIT 1 TO CARRY BCS WRHDLR ; BRANCH IF AN OUTPUT INTERRUPT ; ; INTERRUPT HAS NOT OURS, TRY NEXT SOURCE ; JMP [NEXTSR] ; INTERRUPT IS FROM ANOTHER SOURCE ; ; SERVICE INPUT INTERRUPTS ; RDHDLR: LDA ACIADR ; READ DATA FROM ACIA LDB ICNT ; ANY ROOM IN INPUT BUFFER? CMPB #SZIBUF BEQ EXITRH ; BRANCH (EXIT) IF NO ROOM IN INPUT BUFFER INC ICNT ; INCREMENT INPUT BUFFER COUNT LDX ITAIL ; STORE CHARACTER AT TAIL OF INPUT BUFFER STA ,X JSR INCIPTR ; INCREMENT TAIL POINTER WITH WRAPAROUND STX ITAIL EXITRH: RTI ; ; OUTPUT (WRITE) INTERRUPT HANDLER ; WRHDLR: TST OCNT ; TEST OUTPUT BUFFER COUNT BEQ NODATA ; BRANCH IF NO DATA TO TRANSMIT JSR OUTDAT ; ELSE OUTPUT DATA TO 6850 ACIA RTI ; ; IF AN OUTPUT INTERRUPT OCCURS HHEN NO DATA IS AVAILABLE, ; WE MUST DISABLE IT TO AVOID AN ENDLESS LOOP. HHEN THE NEXT CHARACTER ; IS READY, IT MUST BE SENT IMMEDIATELY SINCE NO INTERRUPT HILL ; OCCUR. THIS STATE IN WHICH AN OUTPUT INTERRUPT HAS OCCURRED ; BUT HAS NOT BEEN SERVICED IS INDICATED BY CLEARING OIE (OUTPUT ; INTERRUPT EXPECTED FLAG). ; NODATA: CLR OIE ; DO NOT EXPECT AN INTERRUPT RTI ; ; ************************************ ; ROUTINE: OUTDAT ; PURPOSE: SEND CHARACTER TO 6850 ACIA FROM THE OUTPUT BUFFER ; ENTRY: X CONTAINS THE ADDRESS OF THE CHARACTER TO SEND ; EXIT: NONE ; REGISTERS USED: A,X,CC ; *************************************** OUTDAT: LDA ACIASR ANDA #00000010b ; IS ACIA OUTPUT REGISTER EMPTY? BEQ OUTDAT ; BRANCH (WAIT) IF REGISTER NOT EMPTY LDX OHEAD ; GET HEAD OF OUTPUT BUFFER LDA ,X ; GET CHARACTER FROM HEAD OF BUFFER STA ACIADR ; SEND DATA TO ACIA JSR INCOPTR ; INCREMENT POINTER WITH HRAPAROUND DEC OCNT ; DECREMENT OUTPUT BUFFER COUNTER LDA #10110001b STA ACIACR ; ENABLE 6850 INPUT AND OUTPUT INTERRUPTS ; 8 DATA BITS, 2 STOP BITS, DIVIDE BY ; 16 CLOCK LDA #$FF STA OIE ; INDICATE OUTPUT INTERRUPTS ENABLED RTS ; ; ************************************** ; ROUTINE: INCIPTR ; PURPOSE: INCREMENT POINTER INTO INPUT ; BUFFER WITH WRAPAROUND ; ENTRY: X = POINTER ; EXIT: X = POINTER INCREMENTED WITH WRAPAROUND ; REGISTERS USED: CC ; ************************************** INCIPTR: LEAX 1,X ; INCREMENT POINTER BY 1 CMPX #EIBUF ; COMPARE POINTER, END OF BUFFER BNE RETINC ; BRANCH IF NOT EQUAL LDX #IBUF ; IF EQUAL, SET POINTER BACK TO BASE OF ; BUFFER RETINC: RTS ; ; ************************************** ; ROUTINE: INCOPTR ; PURPOSE: INCREMENT POINTER INTO OUTPUT ; BUFFER WITH WRAPAROUND ; ENTRY: X = POINTER ; EXIT: X = POINTER INCREMENTED WITH HRAPAROUND ; REGISTERS USED: CC ; ************************************** INCOPTR: LEAX 1,X ; INCREMENT POINTER BY 1 CMPX #EOBUF ; COMPARE POINTER, END OF BUFFER BNE RETONC ; BRANCH IF NOT EQUAL LDX #OBUF ; IF EQUAL, SET POINTER BACK TO ; BASE OF BUFFER RETONC: RTS ; ; DATA SECTION ; IHEAD: RMB 2 ; POINTER TO OLDEST CHARACTER IN INPUT ; BUFFER (NEXT CHARACTER TO READ) ITAIL: RMB 2 ; POINTER TO NEWEST CHARACTER IN INPUT ; BUFFER (LAST CHARACTER READ) ICNT: RMB 1 ; NUMBER OF CHARACTERS IN INPUT BUFFER OHEAD: RMB 2 ; POINTER TO OLDEST CHARACTER IN OUTPUT ; BUFFER (LAST CHARACTER WRITTEN) OTAIL: RMB 2 ; POINTER TO NEWEST CHARACTER IN OUTPUT ; BUFFER (NEXT CHARACTER TO SEND) OCNT: RMB 1 ; NUMBER OF CHARACTERS IN OUTPUT BUFFER SZIBUF EQU 10 ; SIZE OF INPUT BUFFER IBUF: RMB SZIBUF ; INPUT BUFFER EIBUF EQU $ ; END OF INPUT BUFFER SZOBUF EQU 10 ; SIZE OF OUTPUT BUFFER OBUF: RMB SZOBUF ; OUTPUT BUFFER EOBUF EQU $ ; END OF OUTPUT BUFFER OIE: RMB 1 ; OUTPUT INTERRUPT EXPECTED ; ( 0 = NO INTERRUPT EXPECTED, ; FF = INTERRUPT EXPECTED) NEXTSR: RMB 2 ; ADDRESS OF NEXT INTERRUPT SERVICE ; ROUTINE ; ; SAMPLE EXECUTION: ; ; CHARACTER EQUATES ; ESCAPE EQU $1B ; ASCII ESCAPE CHARACTER TESTCH EQU 'A' ; TEST CHARACTER = A SC9C: SC9C: JSR INIT ; INITIALIZE 6850 ACIA, INTERRUPT SYSTEM ; ; SIMPLE EXAMPLE READ AND ECHO CHARACTERS ; UNTIL AN ESC IS RECEIVED LOOP: JSR INCH ; READ CHARACTER PSHS A JSR OUTCH ; ECHO CHARACTER PULS A CMPA #ESCAPE ; IS CHARACTER AN ESCAPE? BNE LOOP ; STAY IN LOOP IF NOT ; ; AN ASYNCHRONOUS EXAMPLE ; OUTPUT "A" TO CONSOLE CONTINUOUSLY BUT ALSO LOOK AT ; INPUT SIDE, READING AND ECHOING ANY INPUT CHARACTERS. ; ASYNLP: ; OUTPUT AN "A" IF OUTPUT IS NOT BUSY ; JSR OUTST ; IS OUTPUT BUSY? BCC ASYNLP ; JUMP IF IT IS LDA #TESTCH JSR OUTCH ; OUTPUT CHARACTER ; ; CHECK INPUT PORT ; ECHO CHARACTER IF ONE IS AVAILABLE ; EXIT ON ESCAPE CHARACTER ; JSR INST ; IS INPUT DATA AVAILABLE? BCS ASYNLP ; JUMP IF NOT (SEND ANOTHER "A") JSR INCH ; GET CHARACTER CMPA #ESCAPE ; IS IT AN ESCAPE CHARACTER? BEQ DONE ; BRANCH IF IT IS JSR OUTCH ; ELSE ECHO CHARACTER BRA ASYNLP ; AND CONTINUE DONE: BRA SC9C ; REPEAT TEST END