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