Metropoli BBS
VIEWER: int14.asm MODE: TEXT (ASCII)
	 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 MACROS.ASM
	 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

[ RETURN TO DIRECTORY ]