         PAGE  ,132
;
;  INTERRUPT DRIVEN REPLACEMENT FOR IBM BIOS INTERRUPT 14
;
;  WRITTEN BY BOB MURPHY, NOVEMBER 1984.
;
;  YOU MAY WANT TO CHANGE THE COMMUNICATION BUFFER SIZE FOR COM1 AND COM2
;  DEPENDING ON THE RECIEVE SPEED, AND TYPE OF COMMUNICATIONS YOU ARE
;  DOING. THE 2K BUFFER IS GOOD FOR MOST 1200 BAUD APPLICATIONS, BUT WILL
;  BE EXCESSIVELY BIG FOR HUMAN-SPEED COMMUNICATIONS. THIS WILL HANDLE
;  CONTINUOUS DATA TRANSFER AT 9600 BAUD, ASSUMING THE APPLICATION USING IT
;  CAN ALSO KEEP UP. YOU WILL WANT TO USE A LARGER BUFFER FOR HIGHER
;  SPEEDS IF YOU ARE DOING FILE TRANSFERS.
;
;  REDUCE THE COM2 BUFFERS TO A SINGLE BYTE IF THERE IS ONLY ONE COMM
;  CHANNEL ON YOUR PC. DO NOT REMOVE THEM ENTIRELY.
;
;
;
;  INPUT PARAMETERS ARE THE SAME AS THE REGULAR BIOS ROUTINES
;  FOR FUNCTIONS (AH) = 0 TO (AH) = 4
;
;  READ CHARACTER NOWAIT - AH = 5
;  WILL READ A CHARACTER IF ONE IS AVAILABLE, AND RETURN IMMEDIATLY. THE
;  STATUS BYTE IN THE AH MUST BE CHECKED BY THE USER. IF AH BIT 0 = 1, THEN
;  THE AL CONTAINS A VALID CHARACTER. IF THAT BIT IS OFF, THEN THE AL CON-
;  TAINS GARBAGE.
;
;
;
;  THE STATUS RETURNED IS A BIT DIFFERENT THAN THE IBM ROUTINE IN THAT
;  THE BREAK DETECT BIT (AH BIT 4) IS SET IF A COMM BUFFER OVERFLOW OCCURS.  THE
;  ERROR FLAGS DO NOT NECESSARILY APPLY TO THE CHARACTER BEING READ.  THEY MAY
;  HAVE BEEN SET BY A LATER ARRIVING CHARACTER.  IN SHORT, THE ERROR BITS IN THE
;  AH ARE NOT 'REAL TIME'. ANY TIME THE STATUS IS READ (ON ANY COMMAND) THE
;  AH ERROR BITS ARE RESET FOR THE NEXT TIME.
;
;  THE TX HOLD REG EMPTY BIT INDICATES THERE IS SPACE IN THE TX OUTPUT BUFFER
;  THE TX SHIFT REG EMPTY BIT IS USED BY THE OUTBOUND SEND ROUTINE TO DETERMINE
;  IF IT NEEDS TO SEND A CHARACTER TO INITIATE THE TRANSMISSION PROCESS. THIS
;  BIT IS NOT USED BY ANY SOFTWARE THAT I KNOW OF, AND YOU SHOULD NOT USE IT.
;
;
;
         PAGE
     INCLUDE MACROB.INC
         BEGINCOM INT14
         JMP   INIT000             ;INITIALIZE VECTORS
BAUDTABL LABEL WORD
         DW    1047                ;110 BAUD
         DW    768                 ;150 BAUD
         DW    384                 ;300 BAUD
         DW    192                 ;600 BAUD
         DW    96                  ;1200 BAUD
         DW    48                  ;2400 BAUD
         DW    24                  ;4800 BAUD
         DW    12                  ;9600 BAUD
ROUTINES LABEL WORD
         DW    COMMAND0            ;INITIALIZE COMMUNICATIONS CHANNEL
         DW    COMMAND1            ;TRANSMIT CHARACTER IN AL ACROSS CHANNEL
         DW    COMMAND2            ;WAIT FOR CHARACTER, AND RETURN IN AL
         DW    COMMAND3            ;RETURN STATUS WORD (2 BYTES)
         DW    COMMAND4            ;RECIEVE CHARACTER NOWAIT
MAXCMD   DW    ($-ROUTINES)/2      ;MAXIMUM COMMAND VALUE (MAX AH VALUE)
RS232_BASE    EQU WORD PTR ES:0    ;ADDRESS OF RS232 BASE ADDR TBL IN SEG 40
RS232_TIM_OUT EQU BYTE PTR ES:07CH ;RS232 TIME OUT VALUE IN LO CORE
         PAGE
COM0000: STI                       ;INTERRUPTS BACK ON
         PUSH  BX
         PUSH  CX
         PUSH  DX
         PUSH  SI
         PUSH  ES
         PUSH  DS                  ;SAVE WHAT WE CLOBBER
         MOV   BX,CS
         MOV   DS,BX               ;MAKE THIS SEGMENT ADDRESSABLE
         MOV   SI,DX               ;RS232 CARD SELECT TO SI
         SHL   SI,1                ;WORD OFFSET
         MOV   BX,40H              ;ADDRESS OF BIOS CORE AREA
         MOV   ES,BX               ;STASH IN DATA SEG
         MOV   DX,RS232_BASE [SI]  ;GET RS232 BASE ADDRESS
         OR    DX,DX               ;TEST FOR NO CARD PRESENT
         JZ    COM0090             ;RETURN
         MOV   BL,AH               ;GET COMMAND INTO 2 BYTES
         SUB   BH,BH               ; IN THE BX REGISTER
         CMP   BX,MAXCMD           ;IS IT A VALID COMMAND?
         JGE   COM0090             ;NO-RETURN
         SHL   BX,1                ;MULTIPLY BY 2 FOR TRUE INDEX VALUE
         CALL  ROUTINES [BX]       ;CALL APPROPRIATE COMMAND
;
;  RETURN FROM INTERRUPT 14 - NORMAL OR ABEND
;
COM0090:
         POP   DS
         POP   ES
         POP   SI
         POP   DX                  ;RETURN FROM INT14
         POP   CX
         POP   BX
         IRET
         PAGE
;******************************************************************************
;
; INITIALIZE COMMUNICATIONS PORT
;
; DX HAS PORT ADDRESS, AH HAS PARMS. BITS 5-7 = BAUD RATE
; SI HAS CARD # (0-3)                     3-4 = PARITY
;                                          2  = # STOP BITS
;                                         0-1 = # DATA BITS
;
;******************************************************************************
COMMAND0 PROC  NEAR
         MOV   AH,AL               ;SAVE INIT PARMS IN AH
         ADD   DX,03               ;POINT TO LINE CONTROL REGISTER (xFB)
         MOV   AL,80H              ;OPEN DIVISOR LATCH
         OUT   DX,AL               ;SET DLAB = 1
;
;  DETERMINE BAUD RATE DIVISOR, AND PUT IT IN THE BX REG
;
         MOV   BL,AH               ;BAUD RATE PARMS TO BL
         MOV   CL,04               ;SHIFT VALUE
         ROL   BL,CL               ;MOVE TO ALIGN
         AND   BX,0EH              ;BX HAS INDEX INTO BAUD RATE DIVISOR TABLE
         MOV   BX,BAUDTABL [BX]    ;GET DIVISOR VALUE TO BX REGISTER
;
; SET BAUD RATE IN THE 8250
;
         SUB   DX,2                ;POINT TO DIVISOR LATCH MSB (xF9)
         MOV   AL,BH               ;GET HI DIVISOR VALUE
         OUT   DX,AL               ;STICK IN DIVISOR LATCH
         DEC   DX                  ;POINT TO DIVISOR LATCH LSB (xF8)
         MOV   AL,BL               ;GET LOW ORDER DIVISOR VALUE
         OUT   DX,AL               ;SAVE DIVISOR VALUE
;
; SET UP PARITY, STOP BITS, AND WORD LENGTH IN THE 8250
;
         ADD   DX,3                ;POINT TO LINE CTRL REG  (xFB)
         MOV   AL,AH               ;INPUT PARMS TO AL
         AND   AL,00011111B        ;BAUD BITS OFF
         OUT   DX,AL               ;SET UP LCR, CLOSE DIVISOR LATCH
;
;  ENABLE THE 8250 INTERRUPTS
;
         INC   DX                  ;POINT TO MODEM CTRL REG (xFC)
         MOV   AL,00001101B        ;SET UP OUT 2, DSR, AND RTS
         OUT   DX,AL               ;ENABLE OUT 2 ON MCR (RQD FOR INTS)
         SUB   DX,3                ;POINT TO INT ENABLE REGISTER (xF9)
         MOV   AL,00001111B        ;ENABLE ALL INTERRUPTS
         OUT   DX,AL               ;PROGRAM THE IER
;
; ELIMINATE ANY PENDING INTS BEFORE IRQ CHANNEL ENABLE
;
         DEC   DX                  ;POINT TO RX REGISTER (xF8)
         IN    AL,DX               ;KILL ANY RECIEVE INTERRUPT
         INC   DX
         INC   DX                  ;POINT TO IIR (xFA)
         IN    AL,DX               ;KILL ANY THRE INTS
         ADD   DX,3                ;POINT TO LINE STATUS REG (xFD)
         IN    AL,DX               ;KILL LINE STATUS INTERRUPTS
         INC   DX                  ;POINT TO MSR (xFE)
         IN    AL,DX               ;KILL MODEM STATUS INTERRUPTS
         AND   AL,11110000B        ;KEEP THE MODEM STATUS BITS
         MOV   COM1MODM [SI],AL    ;ISR STATUS REFLECTS REAL WORLD
         MOV   AL,60H              ;SET UP THRE,TSRE STATUS
         MOV   COM1LINE [SI],60H   ;SET LINE STATUS BITS
;
; RESET BUFFER POINTERS BEFORE IRQ INITIALIZATION
;
         MOV   BX,COM1STRT [SI]    ;GET START OF BUFFER (IN) POINTER
         MOV   COM1SPTR [SI],BX    ;START POINTER RESET
         MOV   COM1EPTR [SI],BX    ;END POINTER RESET
         MOV   BX,OUT1STRT [SI]    ;GET START OF BUFFER (OUT) POINTER
         MOV   OUT1SPTR [SI],BX    ;START POINTER RESET
         MOV   OUT1EPTR [SI],BX    ;END POINTER RESET
;
;  ENABLE THE IRQ CHANNEL ON THE 8259 INTERRUPT CONTROLLER
;
         MOV   CX,SI               ;GET THE ADAPTER INVOLVED
         SHR   CL,1                ;MAKE VALUE 0 OR 1
         NEG   CL                  ;MAKE 0 OR -1 (-1 = COM2)
         ADD   CL,4                ;SET SHIFT COUNT TO 3 OR 4 (COM1=4)
         MOV   AH,0FEH             ;SET UP MASK
         ROL   AH,CL               ;ALIGN FOR INTERRUPT ENABLE ON 8259
         CLI                       ;STOP INTS WHILE WE SCREW W/8259
         IN    AL,21H              ;GET CURRENT INTERRUPT MASK BYTE
         AND   AL,AH               ;ENABLE THE 8259 INTERRUPT
         OUT   21H,AL              ;RIGHT NOW
         STI                       ;INTS BACK ON, COM1 OR 2 IS ACCEPTING
;
;  RETURN FULL (2 BYTE) STATUS INFORMATION
;
         CALL  COMMAND3            ;DO A FULL STATUS REQUEST
         RET                       ;AND WERE DONE
COMMAND0 ENDP
         PAGE
;******************************************************************************
;
; SEND CHARACTER IN AL OVER COM LINE (DTR AND CTS ARE SET ON)
;
;******************************************************************************
COMMAND1 PROC  NEAR
         MOV   BH,AL               ;SAVE INCOMING CHARACTER
;
;    TURN ON DTR AND CTS IF THEY ARE OFF
;
         ADD   DX,4                ;POINT TO MODEM CONTROL REGISTER (xFC)
         IN    AL,DX               ;GET CURRENT MCR CONTENTS
         OR    AL,03               ;INSURE DTR AND RTS ARE ON
         OUT   DX,AL               ;SEND BACK TO MCR
;
;    CHECK DSR AND RTS FROM OTHER END
;
         MOV   AH,80H              ;ASSUME A TIME OUT WILL OCCUR
         CALL  WAITDSR             ;WAIT FOR DSR AND CTS FROM THE OTHER END
         JC    COM0295             ;DSR OR CTS NOT READY - GO ERROR
         MOV   BL,RS232_TIM_OUT [SI] ;GET TIME OUT VALUE FOR THIS PORT
         SUB   CX,CX               ;DELAY
COM0220: TEST  COM1LINE [SI],20H   ;SPACE AVAIL IN BUFFER?
         JNZ   COM0230             ;YES-GO SEND THE CHARACTER
         LOOP  COM0220             ;ELSE-WAIT ON THE ISR TO GIVE US SOME ROOM
         DEC   BL                  ;DEC TIME OUT COUNT
         JNZ   COM0220             ;AND CONTINUE TO WAIT FOR XMIT
         JMP   SHORT COM0295       ;FALL THRU = TIME OUT ERROR
COM0230: MOV   AL,BH               ;RESTORE CHARACTER IN AL
;
         CLI                       ;WERE GONNA TALK, SO DONT INTERRUPT
                                   ;THIS WILL HOLD OFF THE LAST THRE INT
         TEST  COM1LINE [SI],40H   ;IS THE INTERRUPT ROUTINE RUNNING
         JNZ   COM0260             ;NO-TRANSMIT THE CHARACTER IN THIS RTN
;                                  ;=40=TX BUFFER COMPLETLY EMPTY
;
; THE XMIT ISR IS GOING, BUFFER THE CHARACTER
;
         MOV   BX,OUT1EPTR [SI]    ;GET END OF BUFFER POINTER
         MOV   [BX],AL             ;QUEUE UP THE CHARACTER FOR TRANSMISSION
         INC   BX                  ;POINT TO NEXT AVL CHAR POS
         CMP   BX,OUT1ENDB [SI]    ;POINTING OFF THE END?
         JNE   COM0240             ;NO-DONT REPOSITION
         MOV   BX,OUT1STRT [SI]    ;ELSE-RESET TO BEGINNING OF BUFFER
COM0240: CMP   BX,OUT1SPTR [SI]    ;BUFFER OVERRUN OCCUR?
         JNE   COM0250             ;NO-THEN WERE OK
         OR    COM1LINE,20H        ;POST BUFFER FULL FLAG
COM0250: STI                       ;WERE DONE-SO RESTORE INTERRUPTS
         MOV   OUT1EPTR [SI],BX    ;SET BUFFER POSITION FOR NEXT CHARACTER
         JMP   SHORT COM0290       ;RESTORE INTS AND RETURN
;
; THE XMIT ISR IS NOT GOING, SO WE WILL SEND A CHARACTER TO START IT UP
;
COM0260:
         AND   COM1LINE [SI],10111111B  ;TURN OFF TX BUFFER EMPTY (STRT ISR)
                                   ;THIS IS THE SHIFT REG EMPTY BIT
         STI                       ;DONT NEED TO HOLD OFF INTS FOR THIS
         SUB   DX,4                ;POINT TO XMIT HOLDING REGISTER (xF8)
         OUT   DX,AL               ;PUT CHAR IN TX HOLD REG. INTERRUPT WILL
                                   ;OCCUR WHEN IT HAS BEEN SENT.
COM0290: CALL  HALFSTAT            ;GET STATUS OF LINE
COM0295: RET
COMMAND1 ENDP
         PAGE
;******************************************************************************
;
;  RECIEVE CHARACTER FROM COMM-WAIT FOR COMM READY
;
;******************************************************************************
COMMAND2 PROC NEAR
         MOV   BL,RS232_TIM_OUT [SI] ;GET TIME OUT VALUE
         SUB   CX,CX
COM0320: TEST  COM1LINE [SI],1     ;IS THERE A CHAR READY FLAG POSTED
         JNZ   COM0340             ;YES-GET OUT OF KILL TIME LOOP
         LOOP  COM0320             ;ELSE-WAIT ON CHARACTER
         DEC   BL                  ;DEC TIME OUT COUNT
         JNZ   COM0320             ;AND KEEP TRYING
         CALL  HALFSTAT            ;GET LINE STATUS
         OR    AH,80H              ;SET TIME OUT ERROR ALSO
         JMP   SHORT COM0390       ;FAIL WITH TIME OUT
COM0340: CALL  COMMAND4            ;CALL RECIEVE CHARACTER NOWAIT
         CALL  HALFSTAT            ;SET LINE STATUS FLAGS
COM0390: AND   AH,10011110B        ;ONLY RETURN ERROR BITS
         RET                       ;RETURN WITH EITHER A CHARACTER OR AN ERROR
COMMAND2 ENDP
         PAGE
;******************************************************************************
;
;  COMM PORT STATUS REQUEST        BX GETS CLOBBERED
;
;******************************************************************************
COMMAND3 PROC  NEAR
         CLI                       ;KILL RS232 INTERRUPTS
         MOV   AL,COM1MODM [SI]    ;GET MODEM STATUS INTERRUPT DATA
         MOV   BL,AL               ;SAVE MODEM STATUS DATA
         AND   AL,10110000B        ;CLEAR THE DELTAS (AND STICKY RING INDICATOR)
         MOV   COM1MODM [SI],AL    ;WE READ IT, SO WE CAN TURN OFF BITS
         MOV   AH,COM1LINE [SI]    ;GET THE LINE STATUS FLAGS
         MOV   BH,AH               ;COPY THEM
         AND   BH,01100001B        ;KILL THE ERROR BITS (BUT DONT KILL RX AVL)
         MOV   COM1LINE [SI],BH    ;SAVE CHANGE OF STATE IN CORE
         STI                       ;INTS OK NOW THAT FLAGS HAVE BEEN GOTTEN
         MOV   AL,BL               ;RESTORE THE MODEM STATUS DATA
         RET                       ;AND RETURN TO CALLER
COMMAND3 ENDP
;
; CALL HALFSTAT TO RETURN LINE STATUS FLAGS IN AH. BH GETS CLOBBERED
;
HALFSTAT PROC  NEAR                ;TO GET LINE STATUS ONLY, COME HERE
         CLI
         MOV   AH,COM1LINE [SI]    ;GET THE LINE STATUS FLAGS
         MOV   BH,AH               ;COPY THEM
         AND   BH,01100001B        ;KILL THE ERROR BITS (BUT DONT KILL RX AVL)
         MOV   COM1LINE [SI],BH    ;SAVE CHANGE OF STATE IN CORE
         STI                       ;INTS OK NOW THAT FLAGS HAVE BEEN GOTTEN
         RET                       ;AND RETURN TO CALLER
HALFSTAT ENDP
;
; WAIT FOR DSR AND RTS FROM THE OTHER END - IF TIME OUT, RETURN CARRY SET
;
WAITDSR  PROC  NEAR
         MOV   BL,RS232_TIM_OUT [SI] ;GET TIME OUT VALUE
         SUB   CX,CX               ;LOOP VALUE
WAIT100: MOV   AL,COM1MODM [SI]    ;GET MODEM STATUS INTERRUPT DATA
         AND   AL,30H              ;IS DSR AND CTS ON?
         CMP   AL,30H
         JE    WAIT200             ;YES-RETURN NORMAL
         LOOP  WAIT100             ;ELSE - TRY AGAIN
         DEC   BL                  ;DEC TIME OUT COUNTER
         JNZ   WAIT100             ;AND TRY 65536 MORE TIMES
         STC                       ;FALL THRU = TIME OUT
WAIT200: RET                       ;RETURN TO CALLER
WAITDSR  ENDP
         PAGE
;*******************************************************************************
;
;  RETRIEVE CHARACTER FROM INTERRUPT BUFFER
;
;
;  RECIEVE CHARACTER NOWAIT. WILL RETURN CHARACTER IN AL IF ONE IS AVAILABLE.
;  THE STATUS BYTE IN AH SHOULD BE TESTED FOR A X'01' TO SEE IF A CHARACTER WAS
;  READ. IF BIT OFF, THEN AL  =  GARBAGE
;
;*******************************************************************************
COMMAND4 PROC  NEAR
         CLI                       ;DONT ALLOW RS232 INTS WHILE WE READ
         MOV   AH,COM1LINE [SI]    ;GET COMM STATUS FLAG
         AND   COM1LINE [SI],61H   ;AND ERASE ANY ERRS. LEAVE DATA AVL ALONE
         MOV   BX,COM1SPTR [SI]    ;GET START OF BUFFER POINTER
         CMP   BX,COM1EPTR [SI]    ;SAME AS END OF BUFFER POINTER?
         JE    COM0560             ;IF NO CHARS - KILL DATA AVL FLAG
;
;  GET A CHARACTER FROM THE BUFFER
;
COM0540:
         MOV   AL,[BX]             ;GET A CHARACTER FROM THE COMM BUFFER
         INC   BX                  ;
         CMP   BX,COM1ENDB [SI]    ;OFF THE END?
         JNE   COM0550             ;NO-DONT RESET
         MOV   BX,COM1STRT [SI]    ;ELSE-PICK UP START OF BUFFER ADDRESS
COM0550: MOV   COM1SPTR [SI],BX    ;SAVE UPDATED BUFFER POINTER
         CMP   BX,COM1EPTR [SI]    ;DID WE JUST REMOVE THE LAST CHARACTER
         JNE   COM0580             ;NO-DONT KILL DATA AVAILABLE
;
; BUFFER CLEARED OUT - KILL THE DATA AVAILABLE FLAG
;
COM0560: AND   COM1LINE [SI],0FEH  ;ELSE-BUFFER EMPTY, DATA AVL OFF
         AND   AH,0FEH             ;DO IN RETURN REG ALSO
;
;  CHAR READ, IF ANY - SO ITS MILLER TIME FOR THIS ROUTINE
;
COM0580: STI                       ;RE-ALLOW INTS
         RET                       ;AND HEAD OUT THE DOOR W/CHAR IN AL
COMMAND4 ENDP
         PAGE
         ORG   0400H
;*COMMISR**********************************************************************
;
; RS232 INTERRUPT HANDLER
;
;******************************************************************************
;
;  POINTERS AND FLAGS FOR COMM BUFFERS
;
COM1SPTR DW    COM1BUF             ;START OF COMM BUFFER 1
COM2SPTR DW    COM2BUF             ;START OF COMM BUFFER 2
;
COM1EPTR DW    COM1BUF             ;END POINTER FOR COMM BUFFER 1
COM2EPTR DW    COM2BUF             ;END POINTER FOR COMM BUFFER 2
;
COM1STRT DW    OFFSET COM1BUF      ;ADDRESS OF COM1 BUFFER START
COM2STRT DW    OFFSET COM2BUF      ;ADDRESS OF COM2 BUFFER START
;
COM1ENDB DW    OFFSET COM1END      ;ADDRESS OF COM1 BUFFER END
COM2ENDB DW    OFFSET COM2END      ;ADDRESS OF COM2 BUFFER END
;
OUT1SPTR DW    OUT1BUF             ;START OF OUTPUT COMM BUFFER 1
OUT2SPTR DW    OUT2BUF             ;START OF OUTPUT COMM BUFFER 2
;
OUT1EPTR DW    OUT1BUF             ;END POINTER FOR OUT COMM BUFFER 1
OUT2EPTR DW    OUT2BUF             ;END POINTER FOR OUT COMM BUFFER 2
;
OUT1STRT DW    OFFSET OUT1BUF      ;ADDRESS OF OUTPUT COM1 BUFFER START
OUT2STRT DW    OFFSET OUT2BUF      ;ADDRESS OF OUTPUT COM2 BUFFER START
;
OUT1ENDB DW    OFFSET OUT1END      ;ADDRESS OF COM1 OUTPUT BUFFER END
OUT2ENDB DW    OFFSET OUT2END      ;ADDRESS OF COM2 OUTPUT BUFFER END
         PAGE
COM1LINE DB    60H                 ;LINE STAT FOR COMM BUFFER 1
;                                    80 = TIME OUT (USED BY RD/WRT RTN)
;                                    40 = TSRE =1= LAST CHAR HAS BEEN SENT
;                                         AND THE TX BUFFER IS EMPTY
;                                    20 = THRE =1= SPACE AVAILABLE IN TX BUF
;                                    10 = BREAK DETECT/BUFFER OVERFLOW
;                                    08 = FRAMING ERROR
;                                    04 = PARITY ERROR
;                                    02 = OVERRUN ERROR
;                                    01 = DATA AVAILABLE
COM1MODM DB    0                   ;MODEM STATUS BYTE FOR COM1
;                                    80 = RLSD (CARRIER DETECT)
;                                    40 = RING INDICATOR
;                                    20 = DATA SET READY
;                                    10 = CLEAR TO SEND
;                                    08 = DELTA RLSD (CARRIER DETECT)
;                                    04 = DELTA RING INDICATOR
;                                    02 = DELTA DATA SET READY
;                                    01 = DELTA CLEAR TO SEND
COM2LINE DB    60H                 ;LINE STAT FOR COMM BUFFER 2
COM2MODM DB    0                   ;MODEM STATUS BYTE FOR COM2
;
COMMBASE DW    0                   ;STARTING I/O ADDRESS FOR COMM CARD
;
;  INPUT BUFFER FOR COMM CHANNEL 1
;
COM1BUF  DW    2048 DUP (?)
COM1END  EQU   $
;
;  INPUT BUFFER FOR COMM CHANNEL 2
;
COM2BUF  DW    2048 DUP (?)
COM2END  EQU   $
;
; OUTPUT BUFFER FOR COMM CHANNEL 1
;
OUT1BUF  DW    256 DUP (?)
OUT1END  EQU   $
;
; OUTPUT BUFFER FOR COMM CHANNEL 2
;
OUT2BUF  DW    256 DUP (?)
OUT2END  EQU   $
         PAGE
;
;  INTERRUPT SERVICE ROUTINES FOR COM1 AND COM2
;
ISRTABLE LABEL WORD                ;INTERRUPT SERVICE ROUTINE ADDR TABLE
         DW    ISRMODEM            ;MODEM STATUS ROUTINE (PRI 4)
         DW    ISRXMIT             ;XMIT DATA (PRI 3)
         DW    ISRRECV             ;RECIEVE DATA (PRI 2)
         DW    ISRLINE             ;LINE STATUS (PRI 1)

ISR1000: STI                       ;ALLOW OTHER INTERRUPTS
         PUSH  SI
         MOV   SI,0                ;INDICATE COMM CARD 1
         JMP   SHORT ISR0100       ;SKIP TO MAINLINE
ISR2000: STI                       ;ALLOW OTHER INTERRUPTS
         PUSH  SI
         MOV   SI,2                ;INDICATE COMM CARD 2
;
;  MAINLINE INTERRUPT RTN
;
ISR0100: PUSH  AX                  ;SAVE ALL REGS USED
         PUSH  BX
         PUSH  CX
         PUSH  DX
         PUSH  DS
;
; SET UP BASE REGISTERS
;
         MOV   AX,40H              ;ADDRESS OF RS232 CARD I/O PORT ADDRESSES
         MOV   DS,AX               ;DS = CS FOR DATA BUFFERS
         MOV   DX,WORD PTR [SI]    ;GET COMM CARD BASE ADDRESS
         MOV   AX,CS
         MOV   DS,AX               ;DS POINTS TO PGM FOR USE OF COMM BUFFERS
         MOV   COMMBASE,DX         ;SAVE COMM CARD BASE FOR SECOND TIME THROUGH
ISR0120: MOV   DX,COMMBASE         ;GET STARTING I/O ADDRESS FOR COMM CARD
         INC   DX
         INC   DX                  ;GET PORT FOR INT IDENTIFICATION REG
         IN    AL,DX               ;GET IIR CONTENTS
         TEST  AL,1                ;IS AN INTERRUPT PENDING?
         JNZ   ISR0140             ;NO-EOI TO 8259 & LEST GET OUT OF HERE
         CBW                       ;ELSE-GET INDEX INTO TABLE
         MOV   BX,AX               ;COPY TO USEFUL REGISTER
         CALL  ISRTABLE [BX]       ;CALL APPROPRIATE ROUTINE
         JMP   ISR0120             ;AND KEEP CHECKING UNTIL ALL INTS DONE
ISR0140:
;
;      SIGNAL END OF INTERRUPT TO 8259
;
         CLI                       ;KILL INTERRUPTS
         MOV AL,20H                ;NON-SPECIFIC EOI
         OUT 20H,AL                ;SEND IT
         STI                       ;RE-ALLOW INTERRUPTS
;
;  END OF INTERRUPT CODE
;
         POP   DS
         POP   DX
         POP   CX
         POP   BX
         POP   AX
         POP   SI
         IRET
         PAGE
;**ISRLINE*********************************************************************
;
;  LINE STATUS ERROR HANDLER
;
;******************************************************************************
ISRLINE  PROC  NEAR
         ADD   DX,3                ;LINE STATUS REGISTER (xFD)
         IN    AL,DX               ;GET STATUS
         AND   AL,00011110B        ;MASK NON-ERROR BITS
         OR    COM1LINE [SI],AL    ;AND SAVE IN COMM FLAGS
         RET
ISRLINE  ENDP



;**ISRRECV*********************************************************************
;
;  INBOUND CHARACTER INTERRUPT SERVICE ROUTINE
;
;******************************************************************************
ISRRECV  PROC  NEAR
;
; GET INCOMING CHARACTER AND BUFFER IT
;
         DEC   DX
         DEC   DX                  ;RECEIVE BUFFER REGISTER (xF8)
         SUB   AH,AH               ;SET FLAGS = 0
         IN    AL,DX               ;GET INPUT CHARACTER
         MOV   BX,COM1EPTR [SI]    ;COM-BUF END POINTER
         MOV   DX,BX               ;SAVE POINTER BEFORE INCREMENT
         INC   BX                  ;BUMP POINTER
         CMP   BX,COM1ENDB [SI]    ;PAST END?
         JNE   ISR0220             ;JUMP IF NOT
         MOV   BX,COM1STRT [SI]    ;ELSE POINT TO START
ISR0220: CMP   BX,COM1SPTR [SI]    ;OVERFLOW IF HEAD = TAIL
         JE    ISR0240             ;OVERFLOW
         MOV   COM1EPTR [SI],BX    ;AND NEW INPUT POINTER
         MOV   BX,DX               ;GET UNBUMPED POINTER BACK
         MOV   [BX],AL             ;NO OVERFLOW, SAVE CHAR IN COM1BUF
         OR    AH,1
         JMP   ISR0280             ;WERE ALL DONE RECIEVING CHARACTER
ISR0240: OR    AH,10H              ;SET OVERFLOW/BREAK FLAG
ISR0280: OR    COM1LINE [SI],AH    ;PUT IN LINE STATUS FIELD
         RET
ISRRECV  ENDP
         PAGE
;**ISRXMIT*********************************************************************
;
;  OUTBOUND CHARACTER INTERRUPT SERVICE ROUTINE
;
;  POST XMIT SHIFT REG EMPTY WHEN LAST CHAR PLACED IN XMIT HOLD REG.
;  KILL XMIT HOLD REG EMPTY WHEN XMIT BUFFER IS FILLED UP
;
;******************************************************************************
ISRXMIT  PROC  NEAR
;
         MOV   AH,40H              ;ASSUME WE WILL SEND LAST CHARACTER
         MOV   BX,OUT1SPTR [SI]    ;GET START OF BUFFER POINTER
         CMP   BX,OUT1EPTR [SI]    ;IS THE BUFFER EMPTY?
         JE    ISR0480             ;YES-SKIP THE DATA TRANSMISSION
         DEC   DX
         DEC   DX                  ;POINT TO XMIT HOLDING REGISTER (xF8)
         MOV   AL,[BX]             ;GET CHARACTER FROM BUFFER
         OUT   DX,AL               ;SEND IT ON ITS WAY
         INC   BX                  ;BUMP THE OUTPUT BUFFER POINTER
         CMP   BX,OUT1ENDB [SI]    ;POINTING AT THE END OF THE BUFFER
         JNE   ISR0420             ;NO-POINTER IS OK
         MOV   BX,OUT1STRT [SI]    ;ELSE - POINT TO BUFFER START
ISR0420: MOV   OUT1SPTR [SI],BX    ;SAVE NEW BUFFER POINTER
         OR    AH,20H              ;SEND BACK A TX HOLD REG EMPTY INDICATION
         CMP   OUT1EPTR [SI],BX    ;DID WE JUST EMPTY THE BUFFER?          *T
         JE    ISR0480             ;YES-THEN THERE WONT BE ANY MORE INTS   *T
         AND   AH,10111111B        ;AND SET BIT SAYS WERE OPERATING
ISR0480: OR    COM1LINE [SI],AH    ;STUFF IT IN THE FLAGS
         RET
ISRXMIT  ENDP
;**ISRMODEM********************************************************************
;
;  MODEM STATUS INTERRUPT SERVICE ROUTINE
;
;******************************************************************************
ISRMODEM PROC  NEAR
         ADD   DX,4                ;POINT TO MODEM STATUS REGISTER (xFE)
         IN    AL,DX               ;GET CONTENTS & CLEAR INTERRUPT
         MOV   AH,AL
         AND   AH,11110000B        ;GET ACTUAL SIGNAL LINE STATUS
         OR    AL,COM1MODM [SI]    ;OR IN PREVIOUS LINE STATUS
         AND   AL,01001111B        ;GET DELTA VALUES ALONE
                                   ;AND LEAVE RING INDICATOR ON
         OR    AL,AH               ;SEVERAL DELTA VALUES, BUT ONLY CURRENT
         MOV   COM1MODM [SI],AL    ;  MODEM STATUS INTO MODEM STATUS FIELD
         RET                       ;AND WERE DONE
ISRMODEM ENDP
PRGMEND  EQU   $                   ;END OF CODE TO SAVE IN CORE
         PAGE
;**NONRES**********************************************************************
;
;  THE FOLLOWING IS NON-RESIDENT CODE
;
;******************************************************************************
;
;  PROGRAM INITIALIZATION
;
INIT000: MOV   DX,OFFSET COM0000   ;POINT TO START OF ROUTINE
         @DOS  25H,14H             ;RESET VECTOR 14
         MOV   DX,OFFSET ISR1000   ;ADDRESS OF COM 1 HANDLER
         CLI                       ;STOP INTS DURING 8259 VECTOR UPDATE
         @DOS  25H,0CH             ;RESET VECTOR 0C  (IRQ4-FOR COM1)
         MOV   DX,OFFSET ISR2000   ;ADDRESS OF COM 2 HANDLER
         @DOS  25H,0BH             ;RESET VECTOR 0B  (IRQ3-FOR COM2)
         STI
         MOV   DX,OFFSET PRGMEND   ;POINT TO END OF CODE TO KEEP FOREVER
         INT   27H                 ;TERMINATE & STAY RESIDENT
         ENDCOM INT14
