TITLE COM_PKG2 -- Last updated 10/6/85 PAGE 75,132 ; ; modified to use MSC calling sequence. ; jrr 3/86 ; ; Communications Package for the IBM PC, XT, AT and PCjr ; and strict compatibles. ; May be copied and used freely -- This is a public domain program ; Developed by Richard Gillmann, John Romkey, Jerry Saltzer, ; Craig Milo Rogers, Dave Mitton and Larry Afrin. ; ; We'd sure like to see any improvements you might make. ; Please send all comments and queries about this package ; to GILLMANN@USC-ISIB.ARPA ; ; o Supports both serial ports simultaneously ; o All speeds to 19200 baud ; o Compatible with PC, XT, AT and PCjr. ; o Built in XON/XOFF flow control option ; o Assembly language calling conventions ; o Logs all comm errors ; o Direct connect or modem protocol ; ; MAXIMUM BUFFER SIZES R_SIZE EQU 500 ; SIZE OF RECEIVE BUFFERS S_SIZE EQU 500 ; SIZE OF TRANSMIT BUFFERS rhl/4/86 ; INTERRUPT NUMBERS INT_COM1 EQU 0CH ; COM1: FROM 8259 INT_COM2 EQU 0BH ; COM2: FROM 8259 ; 8259 PORTS INTA00 EQU 20H ; 8259A PORT, A0 = 0 INTA01 EQU 21H ; 8259A PORT, A0 = 1 ; COM1: LEVEL 4 IRQ4 EQU 2*2*2*2 ; 8259A OCW1 MASK, M4=1, A0=0 NIRQ4 EQU NOT IRQ4 AND 0FFH ; COMPLEMENT OF ABOVE EOI4 EQU 4 OR 01100000B ; 8259A OCW2 SPECIFIC IRQ4 EOI, A0=0 ; COM2: LEVEL 3 IRQ3 EQU 2*2*2 ; 8259A OCW1 MASK, M3=1, A0=0 NIRQ3 EQU NOT IRQ3 AND 0FFH ; COMPLEMENT OF ABOVE EOI3 EQU 3 OR 01100000B ; 8259A OCW2 SPECIFIC IRQ3 EOI, A0=0 ; FLOW CONTROL CHARACTERS CONTROL_Q EQU 11H ; XON CONTROL_S EQU 13H ; XOFF ; MISC. DOS EQU 21H ; DOS FUNCTION CALLS PAGE ; ; ROM BIOS Data Area ; RBDA SEGMENT AT 40H RS232_BASE DW 4 DUP(?) ; ADDRESSES OF RS232 ADAPTERS RBDA ENDS ; ; ROM PC-Type IDENT ; ROM SEGMENT AT 0F000H ORG 0FFFEH ROMID DB ? ; 0FFH=PC OR EARLY XT, 0FEH=XT, 0FDH=JR ROM ENDS PAGE ; ; TABLE FOR EACH SERIAL PORT ; SP_TAB STRUC PORT DB ? ; 1 OR 2 ; PARAMETERS FOR THIS INTERRUPT LEVEL INT_COM DB ? ; INTERRUPT NUMBER IRQ DB ? ; 8259A OCW1 MASK NIRQ DB ? ; COMPLEMENT OF ABOVE EOI DB ? ; 8259A OCW2 SPECIFIC END OF INTERRUPT ; INTERRUPT HANDLERS FOR THIS LEVEL INT_HNDLR DW ? ; OFFSET TO INTERRUPT HANDLER OLD_COM_OFF DW ? ; OLD HANDLER'S OFFSET OLD_COM_SEG DW ? ; OLD HANDLER'S SEGMENT ; ATTRIBUTES INSTALLED DB ? ; IS PORT INSTALLED ON THIS PC? (1=YES,0=NO) BAUD_RATE DW ? ; 19200 MAX CONNECTION DB ? ; M(ODEM), D(IRECT) PARITY DB ? ; N(ONE), O(DD), E(VEN), S(PACE), M(ARK) STOP_BITS DB ? ; 1, 2 XON_XOFF DB ? ; E(NABLED), D(ISABLED) ; FLOW CONTROL STATE HOST_OFF DB ? ; HOST XOFF'ED (1=YES,0=NO) PC_OFF DB ? ; PC XOFF'ED (1=YES,0=NO) ; ERROR COUNTS ERROR_BLOCK DW 8 DUP(?) ; EIGHT ERROR COUNTERS ; 8250 PORTS DATREG DW ? ; DATA REGISTER IER DW ? ; INTERRUPT ENABLE REGISTER IIR DW ? ; INTERRUPT IDENTIFICATION REGISTER LCR DW ? ; LINE CONTROL REGISTER MCR DW ? ; MODEM CONTROL REGISTER LSR DW ? ; LINE STATUS REGISTER MSR DW ? ; MODEM STATUS REGISTER ; BUFFER POINTERS START_TDATA DW ? ; INDEX TO FIRST CHARACTER IN X-MIT BUFFER END_TDATA DW ? ; INDEX TO FIRST FREE SPACE IN X-MIT BUFFER START_RDATA DW ? ; INDEX TO FIRST CHARACTER IN REC. BUFFER END_RDATA DW ? ; INDEX TO FIRST FREE SPACE IN REC. BUFFER ; BUFFER COUNTS SIZE_TDATA DW ? ; NUMBER OF CHARACTERS IN X-MIT BUFFER SIZE_RDATA DW ? ; NUMBER OF CHARACTERS IN REC. BUFFER ; BUFFERS TDATA DB S_SIZE DUP(?) ; SEND BUFFER RDATA DB R_SIZE DUP(?) ; RECEIVE BUFFER SP_TAB ENDS ; SP_TAB EQUATES ; WE HAVE TO USE THESE BECAUSE OF PROBLEMS WITH STRUC EOVFLOW EQU ERROR_BLOCK ; BUFFER OVERFLOWS EOVRUN EQU ERROR_BLOCK+2 ; RECEIVE OVERRUNS EBREAK EQU ERROR_BLOCK+4 ; BREAK CHARS EFRAME EQU ERROR_BLOCK+6 ; FRAMING ERRORS EPARITY EQU ERROR_BLOCK+8 ; PARITY ERRORS EXMIT EQU ERROR_BLOCK+10 ; TRANSMISSION ERRORS EDSR EQU ERROR_BLOCK+12 ; DATA SET READY ERRORS ECTS EQU ERROR_BLOCK+14 ; CLEAR TO SEND ERRORS DLL EQU DATREG ; LOW DIVISOR LATCH DLH EQU IER ; HIGH DIVISOR LATCH PAGE ; put the data in the DGROUP segment ; far calls enter with DS pointing to DGROUP ; DGROUP GROUP _DATA _DATA SEGMENT PUBLIC 'DATA' ; DIV50PC EQU 2304 ; DIVISOR FOR 50 BAUD (PC,XT) DIV50JR EQU 2237 ; DIVISOR FOR 50 BAUD (JR) DIV50 DW 2304 ; ACTUAL DIVISOR FOR 50 BAUD IN USE CURRENT_AREA DW AREA1 ; CURRENTLY SELECTED AREA ; DATA AREAS FOR EACH PORT AREA1 SP_TAB <1,INT_COM1,IRQ4,NIRQ4,EOI4> ; COM1 DATA AREA AREA2 SP_TAB <2,INT_COM2,IRQ3,NIRQ3,EOI3> ; COM2 DATA AREA _DATA ENDS PAGE COM_TEXT SEGMENT PARA PUBLIC 'CODE' ASSUME CS:COM_TEXT,DS:DGROUP,ES:NOTHING PUBLIC _select_port PUBLIC _save_com PUBLIC _install_com PUBLIC _restore_com PUBLIC _open_com PUBLIC _close_com PUBLIC _dtr_on PUBLIC _dtr_off PUBLIC _r_count PUBLIC _s_count PUBLIC _receive_com PUBLIC _send_com PUBLIC _sendi_com PUBLIC _send_local PUBLIC _break_com PUBLIC _com_errors PAGE ; ; SELECT WHICH PORT IS TO BE "ACTIVE" ; [bp+6] = port number _select_port PROC FAR push bp mov bp,sp mov AX,[bp+6] ; get aguement TEST AL,1 ; FIRST PORT? JZ SP1 ; IF NOT, IT MUST BE SECOND PORT MOV AX,OFFSET DGROUP:AREA1 ; SELECT COM1 DATA AREA JMP SHORT SPX ; CONTINUE SP1: MOV AX,OFFSET DGROUP:AREA2 ; SELECT COM2 DATA AREA SPX: MOV CURRENT_AREA,AX ; SET SELECTION IN MEMORY mov sp,bp pop bp RET ; DONE _select_port ENDP PAGE ; ; SAVE ORIGINAL COM VECTOR ; _save_com PROC FAR push bp mov bp,sp push si MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA PUSH ES ; SAVE EXTRA SEGMENT MOV AREA1.INT_HNDLR,OFFSET INT_HNDLR1 MOV AREA2.INT_HNDLR,OFFSET INT_HNDLR2 MOV AH,30H ; GET DOS VERSION NUMBER INT DOS ; DOS FUNCTION CMP AL,2 ; AT LEAST DOS 2? JB SVC1 ; JUMP IF DOS 1 ; SAVE OLD VECTOR WITH DOS 2 CALLS MOV AH,35H ; FETCH INTERRUPT VECTOR CONTENTS MOV AL,INT_COM[SI] ; INTERRUPT NUMBER INT DOS ; DOS 2 FUNCTION MOV OLD_COM_OFF[SI],BX ; SAVE MOV BX,ES ; ES:BX MOV OLD_COM_SEG[SI],BX ; FOR LATER RESTORATION JMP SHORT SVCX ; DONE ; SAVE OLD VECTOR WITH DOS 1 CALLS SVC1: MOV AX,0 ; ZERO SEGMENT MOV ES,AX ; ES POINTS TO INTERRUPT VECTORS MOV BL,INT_COM[SI] ; OUR INTERRUPT NUMBER MOV BH,0 ; BL -> BX SHL BX,1 ; TIMES FOUR SHL BX,1 ; IS OFFSET OF VECTOR IN MEMORY MOV AX,WORD PTR ES:[BX] ; SAVE MOV OLD_COM_OFF[SI],AX ; THE ADD BX,2 ; OLD MOV AX,WORD PTR ES:[BX] ; INTERRUPT MOV OLD_COM_SEG[SI],AX ; VECTOR SVCX: POP ES ; RESTORE ES pop si mov sp,bp pop bp RET ; DONE _save_com ENDP PAGE ; ; INSTALL_COM: INSTALL THE ACTIVE PORT ; ; SET 8250 PORTS FROM RS-232 BASE IN ROM BIOS DATA AREA ; INITIALIZE PORT CONSTANTS AND ERROR COUNTS ; INSTALL INTERRUPT VECTOR ; ; return ax=1 on success ax=0 on failure ; _install_com PROC FAR push bp mov bp,sp push si MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA PUSH ES ; SAVE EXTRA SEGMENT CMP INSTALLED[SI],1 ; ALREADY INSTALLED? JNE INSTOK ; NO, CONTINUE JMP INSTX ; ELSE JUMP IF ALREADY INSTALLED ; CLEAR ERROR COUNTS INSTOK: MOV WORD PTR EOVFLOW[SI],0 ; BUFFER OVERFLOWS MOV WORD PTR EOVRUN[SI],0 ; RECEIVE OVERRUNS MOV WORD PTR EBREAK[SI],0 ; BREAK CHARS MOV WORD PTR EFRAME[SI],0 ; FRAMING ERRORS MOV WORD PTR EPARITY[SI],0 ; PARITY ERRORS MOV WORD PTR EXMIT[SI],0 ; TRANSMISSION ERRORS MOV WORD PTR EDSR[SI],0 ; DATA SET READY ERRORS MOV WORD PTR ECTS[SI],0 ; CLEAR TO SEND ERRORS ; SENSE PC TYPE AND SET DIVISOR ACCORDINGLY MOV BX,ROM ; HIGH ROM SEGMENT MOV ES,BX ; TO ES ASSUME ES:ROM MOV DIV50,DIV50PC ; ASSUME PC OR XT CMP ROMID,0FDH ; IS IT A PCjr? JNE INST0 ; JUMP IF NOT MOV DIV50,DIV50JR ; ELSE SET JR DIVISOR ; SET 8250 PORT ADDRESSES INST0: MOV BX,RBDA ; ROM BIOS DATA AREA MOV ES,BX ; TO ES ASSUME ES:RBDA TEST PORT[SI],1 ; PORT 1? JZ INST1 ; JUMP IF NOT MOV AX,3F8H ; COM1 BASE PORT ADDRESS JMP SHORT INST2 ; CONTINUE INST1: MOV AX,2F8H ; COM2 BASE PORT ADDRESS INST2: CMP AX,RS232_BASE ; INSTALLED? JE INST2A ; JUMP IF SO CMP AX,RS232_BASE+2 ; INSTALLED? JNE INST666 ; JUMP IF NOT INST2A: MOV BX,DATREG ; OFFSET OF TABLE OF PORTS MOV CX,7 ; LOOP SIX TIMES INST3: MOV WORD PTR [SI][BX],AX ; SET PORT ADDRESS INC AX ; NEXT PORT ADD BX,2 ; NEXT WORD ADDRESS LOOP INST3 ; RS232 BASE LOOP ; RESET VECTOR TO POINT TO OUR HANDLER MOV AREA1.INT_HNDLR,OFFSET INT_HNDLR1 MOV AREA2.INT_HNDLR,OFFSET INT_HNDLR2 MOV AH,25H ; SET INTERRUPT VECTOR CONTENTS MOV AL,INT_COM[SI] ; INTERRUPT NUMBER MOV DX,OFFSET DGROUP:INT_HNDLR[SI] ; OUR INTERRUPT HANDLER PUSH DS ; SAVE DATA SEGMENT PUSH CS ; COPY CS POP DS ; TO DS INT DOS ; DOS FUNCTION POP DS ; RECOVER DATA SEGMENT ; PORT INSTALLED INSTX: MOV INSTALLED[SI],1 ; PORT INSTALLED POP ES ; RESTORE ES mov ax,1 pop si mov sp,bp pop bp RET ; DONE ; PORT NOT INSTALLED INST666:MOV INSTALLED[SI],0 ; PORT NOT INSTALLED ON THIS PC POP ES ; RESTORE ES mov ax,0 pop si mov sp,bp pop bp RET ; DONE _install_com ENDP PAGE ; ; RESTORE ORIGINAL INTERRUPT VECTOR ; _restore_com PROC FAR push bp mov bp,sp push si MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA MOV INSTALLED[SI],0 ; PORT IS NO LONGER INSTALLED MOV AH,25H ; SET INTERRUPT VECTOR FUNCTION MOV AL,INT_COM[SI] ; INTERRUPT NUMBER MOV DX,OLD_COM_OFF[SI] ; OLD OFFSET TO DX MOV BX,OLD_COM_SEG[SI] ; OLD SEG PUSH DS ; SAVE DS MOV DS,BX ; TO DS INT DOS ; DOS FUNCTION POP DS ; RECOVER DS pop si mov sp,bp pop bp RET ; DONE _restore_com ENDP PAGE ; ; OPEN_COM ON CURRENT PORT ; ; CLEAR BUFFERS ; RE-INITIALIZE THE INTEL 8250 UART ; ENABLE INTERRUPTS ON THE INTEL 8259 INTERRUPT CONTROL CHIP ; ; [bp+6] = BAUD RATE ; [bp+8] = CONNECTION: M(ODEM), D(IRECT) ; [bp+10] = PARITY: N(ONE), O(DD), E(VEN), S(PACE), M(ARK) ; [bp+12] = STOP BITS: 1, 2 ; [bp+14] = XON/XOFF: E(NABLED), D(ISABLED) ; _open_com PROC FAR push bp mov bp,sp push si MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA CLI ; INTERRUPTS OFF mov ax,[bp+6] MOV BAUD_RATE[SI],AX ; SET mov bh,[bp+8] MOV CONNECTION[SI],BH ; ARGS mov bl,[bp+10] MOV PARITY[SI],BL ; IN mov ch,[bp+12] MOV STOP_BITS[SI],CH ; MEMORY mov cl,[bp+14] MOV XON_XOFF[SI],CL ; RESET FLOW CONTROL MOV HOST_OFF[SI],0 ; HOST FLOWING MOV PC_OFF[SI],0 ; PC FLOWING ; RESET BUFFER COUNTS AND POINTERS MOV START_TDATA[SI],0 MOV END_TDATA[SI],0 MOV START_RDATA[SI],0 MOV END_RDATA[SI],0 MOV SIZE_TDATA[SI],0 MOV SIZE_RDATA[SI],0 TEST INSTALLED[SI],1 ; PORT INSTALLED? JNZ OC1 ; SKIP IF SO JMP OCX ; ELSE ABORT OC1: ; RESET THE 8250 MOV AL,0 MOV DX,MCR[SI] OUT DX,AL JMP $+2 ; I/O DELAY FOR JR MOV DX,LSR[SI] ; RESET LINE STATUS CONDITION IN AL,DX JMP $+2 ; I/O DELAY FOR JR MOV DX,DATREG[SI] ; RESET RECSIVE DATA CONDITION IN AL,DX JMP $+2 ; I/O DELAY FOR JR MOV DX,MSR[SI] ; RESET MODEM DELTAS AND CONDITIONS IN AL,DX ; CONVERT PASSED BAUD RATE TO 8250 DIVISOR MOV AX,50 ; 50 BAUD MUL DIV50 ; TIMES ITS DIVISOR DIV BAUD_RATE[SI] ; OTHER SPEEDS ARE PROPORTIONAL MOV BX,AX ; RESULT TO BX ; SET 8250 DIVISOR MOV DX,LCR[SI] ; LINE CONTROL REGISTER MOV AL,80H ; HI BIT ON OUT DX,AL ; SET DLAB = 1 JMP $+2 ; I/O DELAY FOR JR MOV DX,WORD PTR DLL[SI] ; LEAST SIGNIFICANT BYTE MOV AL,BL ; LSB FROM TABLE OUT DX,AL ; SET LSB ON 8250 JMP $+2 ; I/O DELAY FOR JR MOV DX,WORD PTR DLH[SI] ; MOST SIGNIFICANT BYTE MOV AL,BH ; MSB FROM TABLE OUT DX,AL ; SET MSB ON 8250 JMP $+2 ; I/O DELAY FOR JR ; SET PARITY AND NUMBER OF STOP BITS MOV AL,03H ; NONE OR SPACE PARITY IS THE DEFAULT CMP PARITY[SI],'O' ; ODD PARITY REQUESTED? JNE P1 ; JUMP IF NOT MOV AL,0AH ; SELECT ODD PARITY JMP SHORT P3 ; CONTINUE P1: CMP PARITY[SI],'E' ; EVEN PARITY REQUESTED? JNE P2 ; JUMP IF NOT MOV AL,1AH ; SELECT EVEN PARITY JMP SHORT P3 ; CONTINUE P2: CMP PARITY[SI],'M' ; MARK PARITY REQUESTED? JNE P3 ; JUMP IF NOT MOV AL,2AH ; SELECT MARK PARITY P3: TEST STOP_BITS[SI],2 ; 2 STOP BITS REQUESTED? JZ STOP1 ; NO OR AL,4 ; YES STOP1: MOV DX,LCR[SI] ; LINE CONTROL REGISTER OUT DX,AL ; SET 8250 PARITY MODE AND DLAB=0 ; ENABLE INTERRUPTS ON 8259 AND 8250 IN AL,INTA01 ; SET ENABLE BIT ON 8259 AND AL,NIRQ[SI] OUT INTA01,AL MOV DX,IER[SI] ; ENABLE INTERRUPTS ON 8250 MOV AL,5 ; RECEIVE & LINE ERROR OUT DX,AL JMP $+2 ; I/O DELAY FOR JR MOV DX,MCR[SI] ; SET DTR AND ENABLE INT DRIVER MOV AL,0BH OUT DX,AL OCX: STI ; INTERRUPTS ON pop si mov sp,bp pop bp RET ; DONE _open_com ENDP PAGE ; ; CLOSE_COM - TURNS OFF INTERRUPTS FROM THE COMMUNICATIONS PORT ; _close_com PROC FAR push bp mov bp,sp push si MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ CCX ; ABORT IF NOT ; TURN OFF 8250 MOV DX,IER[SI] MOV AL,0 OUT DX,AL ; TURN OFF 8259 MOV DX,INTA01 IN AL,DX OR AL,IRQ[SI] JMP $+2 ; DELAY FOR AT OUT DX,AL CCX: pop si mov sp,bp pop bp RET _close_com ENDP PAGE ; ; DTR_OFF - TURNS OFF DTR TO TELL MODEMS THAT THE TERMINAL HAS GONE AWAY ; AND TO HANG UP THE PHONE ; _dtr_off PROC FAR push bp mov bp,sp push si PUSHF ; SAVE FLAGS PUSH AX ; SAVE REGS PUSH DX PUSH SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ DFX ; ABORT IF NOT MOV DX,MCR[SI] MOV AL,08H ; DTR OFF, RTS OFF, OUT2 ON OUT DX,AL DFX: POP SI ; RECOVER REGS POP DX POP AX POPF ; RECOVER FLAGS pop si mov sp,bp pop bp RET _dtr_off ENDP ; ; DTR_ON - TURNS DTR ON ; _dtr_on PROC FAR push bp mov bp,sp push si PUSHF ; SAVE FLAGS PUSH AX ; SAVE REGS PUSH DX PUSH SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ DNX ; ABORT IF NOT MOV DX,MCR[SI] MOV AL,0BH OUT DX,AL DNX: POP SI ; RECOVER REGS POP DX POP AX POPF ; RECOVER FLAGS pop si mov sp,bp pop bp RET ; DONE _dtr_on ENDP PAGE ; ; R_COUNT - RETURNS NUMBER OF BYTES IN THE RECEIVE BUFFER IN AX ; total in DX ; _r_count PROC FAR push bp mov bp,sp push si PUSHF ; SAVE FLAGS PUSH SI ; SAVE SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA MOV AX,0 ; NOTHING RECEIVED IF NOT INSTALLED mov dx,R_SIZE TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ RCX ; ABORT IF NOT MOV AX,SIZE_RDATA[SI] ; GET NUMBER OF BYTES USED RCX: POP SI ; RESTORE SI POPF ; RESTORE FLAGS pop si mov sp,bp pop bp RET _r_count ENDP PAGE ; ; RECEIVE - RETURNS THE NEXT CHARACTER FROM THE RECEIVE BUFFER IN AL ; AND REMOVES IT FROM THE BUFFER ; THE PARITY BIT IS STRIPPED OFF ; _receive_com PROC FAR push bp mov bp,sp push si PUSHF ; SAVE FLAGS PUSH BX ; SAVE REGS PUSH SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA mov ax,-1 ; -1 if bad call TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ RCVX ; ABORT IF NOT CMP SIZE_RDATA[SI],0 ; ANY CHARACTERS? JE RCVX ; ABORT IF NOT ; GOOD CALL mov ah,0 ; good call MOV BX,START_RDATA[SI] ; GET POINTER TO OLDEST CHAR MOV AL,RDATA[SI][BX] ; GET CHAR FROM BUFFER ; MOD BY LBA, 10/6/85 FOR PROPER SUPPORT OF NO PARITY COMMUNICATIONS CMP PARITY[SI],'N' ; ARE WE RUNNING WITH NO PARITY? JE L11 ; IF SO, DON'T STRIP HIGH BIT ; END OF MOD AND AL,7FH ; STRIP PARITY BIT L11: INC BX ; BUMP START_RDATA CMP BX,R_SIZE ; SEE IF PAST END JB L12 ; IF NOT THEN SKIP MOV BX,0 ; ADJUST TO BEGINNING L12: MOV START_RDATA[SI],BX ; SAVE THE NEW START_RDATA VALUE DEC SIZE_RDATA[SI] ; ONE LESS CHARACTER CMP XON_XOFF[SI],'E' ; FLOW CONTROL ENABLED? JNE RCVX ; DO NOTHING IF DISABLED CMP HOST_OFF[SI],1 ; HOST TURNED OFF? JNE RCVX ; JUMP IF NOT CMP SIZE_RDATA[SI],R_SIZE/20 ; RECEIVE BUFFER NEARLY EMPTY? JGE RCVX ; DONE IF NOT MOV HOST_OFF[SI],0 ; TURN ON HOST IF SO PUSH AX ; SAVE RECEIVED CHAR MOV AL,CONTROL_Q ; TELL HIM TO TALK CLI ; TURN OFF INTERRUPTS CALL SENDII ; SEND IMMEDIATELY INTERNAL STI ; INTERRUPTS BACK ON POP AX ; RESTORE RECEIVED CHAR RCVX: POP SI ; RECOVER REGS POP BX POPF ; RECOVER FLAGS pop si mov sp,bp pop bp RET ; DONE _receive_com ENDP PAGE ; ; S_COUNT - RETURNS IN AX THE AMOUNT OF FREE SPACE ; REMAINING IN THE TRANSMIT BUFFER ; DX total size ; _s_count PROC FAR push bp mov bp,sp push si PUSHF ; SAVE FLAGS PUSH SI ; SAVE SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA MOV AX,0 ; NO SPACE LEFT IF NOT INSTALLED mov dx,S_SIZE TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ SCX ; ABORT IF NOT MOV AX,S_SIZE ; GET THE SIZE OF THE X-MIT BUFFER SUB AX,SIZE_TDATA[SI] ; SUBTRACT THE NUMBER OF BYTES USED SCX: POP SI ; RECOVER SI POPF ; RESTORE FLAGS pop si mov sp,bp pop bp RET _s_count ENDP PAGE ; ; SEND - SEND A CHARACTER ; [bp+6] = char ; _send_com PROC FAR push bp mov bp,sp push si mov al,[bp+6] PUSHF ; SAVE FLAGS PUSH AX ; SAVE REGS PUSH BX PUSH DX PUSH SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ L44 ; ABORT IF NOT CMP SIZE_TDATA[SI],S_SIZE ; BUFFER FULL? JL L4A ; JUMP IF NOT INC WORD PTR EOVFLOW[SI] ; BUMP ERROR COUNT JMP SHORT L44 ; PUNT L4A: MOV BX,END_TDATA[SI] ; BX POINTS TO FREE SPACE MOV TDATA[SI][BX],AL ; MOVE CHAR TO BUFFER INC BX ; INCREMENT END_TDATA CMP BX,S_SIZE ; SEE IF PAST END JL L4 ; IF NOT THEN SKIP MOV BX,0 ; ADJUST TO BEGINNING L4: MOV END_TDATA[SI],BX ; SAVE NEW END_TDATA INC SIZE_TDATA[SI] ; ONE MORE CHARACTER IN X-MIT BUFFER MOV DX,IER[SI] ; INTERRUPT ENABLE REGISTER IN AL,DX ; GET IT TEST AL,2 ; SEE IF TX INTERRUPTS ARE ENABLED JNZ L44 ; JUMP IF SO MOV AL,7 ; IF NOT THEN RCV, TX, LINE ERROR OUT DX,AL ; ARE ENABLED L44: POP SI ; RESTORE REGS POP DX POP BX POP AX POPF ; RESTORE FLAGS pop si mov sp,bp pop bp RET ; DONE _send_com ENDP PAGE ; ; SENDI - SEND A CHARACTER IMMEDIATELY ; [bp+6] = char to send ; _sendi_com PROC FAR push bp mov bp,sp push si mov al,[bp+6] PUSHF ; SAVE FLAGS PUSH AX ; SAVE REGS PUSH BX PUSH DX PUSH SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ LQ44 ; ABORT IF NOT CLI ; MASK INTERRUPTS CALL SENDII ; CALL INTERNAL SEND IMMEDIATE STI ; INTERRRUPTS BACK ON LQ44: POP SI ; RESTORE REGS POP DX POP BX POP AX POPF ; RESTORE FLAGS pop si mov sp,bp pop bp RET ; DONE _sendi_com ENDP PAGE ; ; INTERNAL ROUTINE ; DEPENDS ON CALLER TO KEEP INTERRUPTS CLEARED AND SET SI ; SENDI - SEND A CHARACTER IMMEDIATELY (PUT AT BEGINNING OF QUEUE) ; AL = CHAR TO WRITE ; SENDII PROC NEAR PUSH DX ; SAVE DX CMP SIZE_TDATA[SI],S_SIZE ; BUFFER FULL? JB LI4A ; JUMP IF NOT INC WORD PTR EOVFLOW[SI] ; BUMP ERROR COUNT MOV BX,START_TDATA[SI] ; BX POINTS TO FIRST CHAR IN BUFFER MOV TDATA[SI][BX],AL ; CLOBBER FIRST CHAR IN BUFFER JMP SHORT LI4B ; CONTINUE LI4A: MOV BX,START_TDATA[SI] ; BX POINTS TO FIRST CHAR IN BUFFER DEC BX ; BACKUP THE PTR CMP BX,-1 ; BEFORE BEGINNING? JNE LI4 ; JUMP IF NOT MOV BX,S_SIZE-1 ; POINT TO END IF SO LI4: MOV TDATA[SI][BX],AL ; MOVE CHAR TO BUFFER MOV START_TDATA[SI],BX ; SAVE NEW START_TDATA INC SIZE_TDATA[SI] ; ONE MORE CHARACTER IN X-MIT BUFFER LI4B: MOV DX,IER[SI] ; INTERRUPT ENABLE REGISTER IN AL,DX ; GET IT TEST AL,2 ; SEE IF TX INTERRUPTS ARE ENABLED JNZ LI44 ; JUMP IF SO MOV AL,7 ; IF NOT THEN RCV, TX, LINE ERROR OUT DX,AL ; ARE ENABLED LI44: POP DX ; RECOVER DX RET ; DONE SENDII ENDP PAGE ; ; S_LOCAL ; _send_local PROC FAR push bp mov bp,sp push si mov al,[bp+6] PUSHF ; SAVE FLAGS PUSH AX ; SAVE REGS PUSH BX PUSH SI MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ SLX ; ABORT IF NOT CLI ; INTERRUPTS OFF CMP SIZE_RDATA[SI],R_SIZE ; SEE IF ANY ROOM JB L13A ; SKIP IF ROOM INC WORD PTR EOVFLOW[SI] ; BUMP OVERFLOW COUNT JMP SHORT L14 ; PUNT L13A: MOV BX,END_RDATA[SI] ; BX POINTS TO FREE SPACE MOV RDATA[SI][BX],AL ; SEND DATA TO BUFFER INC BX ; INCREMENT END_RDATA POINTER CMP BX,R_SIZE ; SEE IF GONE PAST END JL L13 ; IF NOT THEN SKIP MOV BX,0 ; ELSE ADJUST TO BEGINNING L13: MOV END_RDATA[SI],BX ; SAVE VALUE INC SIZE_RDATA[SI] ; GOT ONE MORE CHARACTER L14: STI ; INTERRUPTS BACK ON SLX: POP SI ; RECOVER REGS POP BX POP AX POPF ; RECOVER FLAGS pop si mov sp,bp pop bp RET ; DONE _send_local ENDP PAGE ; ; BREAK - CAUSES A BREAK TO BE SENT OUT ON THE LINE ; _break_com PROC FAR push bp mov bp,sp push si PUSHF ; SAVE FLAGS PUSH AX ; SAVE REGS PUSH CX PUSH DX MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA TEST INSTALLED[SI],1 ; PORT INSTALLED? JZ BRX ; ABORT IF NOT MOV DX,LCR[SI] ; LINE CONTROL REGISTER IN AL,DX ; GET CURRENT SETTING JMP $+2 ; I/O DELAY FOR JR OR AL,40H ; TURN ON BREAK BIT OUT DX,AL ; SET IT ON THE 8250 MOV CX,0C000H ; WAIT APPROX. 1/4 SEC. BREAK1: LOOP BREAK1 ; BUSY WAIT AND AL,0BFH ; TURN OFF BREAK BIT OUT DX,AL ; RESTORE LINE CONTROL REGISTER BRX: POP DX ; RECOVER REGS POP CX POP AX POPF ; RECOVER FLAGS pop si mov sp,bp pop bp RET ; DONE _break_com ENDP PAGE ; ; COM_ERRORS - RETURN POINTER IN dx:ax TO ERROR COUNTS ; _com_errors PROC FAR push bp mov bp,sp mov ax,OFFSET DGROUP:CURRENT_AREA add ax,ERROR_BLOCK mov dx,ds mov sp,bp pop bp RET ; DONE _com_errors ENDP PAGE ; ; INTERNAL ROUTINE ; BUMP ERROR COUNTS FROM LINE STATUS IN AL ; E_BUMP PROC NEAR TEST AL,2 ; OVERRUN ERROR? JZ LSI1 ; JUMP IF NOT INC WORD PTR EOVRUN[SI] ; ELSE BUMP ERROR COUNT LSI1: TEST AL,4 ; PARITY ERROR? JZ LSI2 ; JUMP IF NOT INC WORD PTR EPARITY[SI] ; ELSE BUMP ERROR COUNT LSI2: TEST AL,8 ; FRAMING ERROR? JZ LSI3 ; JUMP IF NOT INC WORD PTR EFRAME[SI] ; ELSE BUMP ERROR COUNT LSI3: TEST AL,16 ; BREAK RECEIVED? JZ LSI4 ; JUMP IF NOT INC WORD PTR EBREAK[SI] ; ELSE BUMP ERROR COUNT LSI4: RET ; DONE E_BUMP ENDP PAGE ; ; INTERNAL ROUTINE ; MODEM SEND PROTOCOL ; M_PROTOCOL PROC NEAR CMP CONNECTION[SI],'M' ; MODEM CONNECTION? JNE S3 ; IF NOT, SKIP DSR & CTS PROTOCOL ; TELL MODEM WE'RE READY TO SEND MOV DX,MCR[SI] ; MODEM CONTROL REGISTER MOV AL,00001011B ; OUT 2, RTS, DTR OUT DX,AL ; TERMINAL READY, REQUEST TO SEND JMP $+2 ; I/O DELAY FOR JR ; WAIT UNTIL MODEM SAYS DATA SET READY MOV CX,1000 ; TIMEOUT COUNT MOV DX,MSR[SI] ; MODEM STATUS REGISTER S1: IN AL,DX ; GET MODEM STATUS TEST AL,20H ; DATA SET READY? JNZ S1X ; YES, TEST CLEAR TO SEND LOOP S1 ; NO, BUSY WAIT INC WORD PTR EDSR[SI] ; BUMP ERROR COUNT JMP SHORT S3 ; WE TIMED OUT S1X: ; WAIT UNTIL MODEM SAYS IT'S CLEAR TO SEND MOV CX,1000 ; TIMEOUT COUNT S2: IN AL,DX ; GET MODEM STATUS TEST AL,10H ; CLEAR TO SEND? JNZ S2X ; YES LOOP S2 ; NO, KEEP TRYING INC WORD PTR ECTS[SI] ; BUMP ERROR COUNT - WE TIMED OUT S2X: ; TEST FOR TRANSMITTER READY S3: MOV DX,LSR[SI] ; LINE STATUS REGISTER IN AL,DX ; GET LINE STATUS TEST AL,20H ; TRANSMITTER READY? JNZ S4 ; SKIP IF SO INC WORD PTR EXMIT[SI] ; ELSE BUMP ERROR COUNT S4: RET ; DONE M_PROTOCOL ENDP PAGE ; ; INTERNAL ROUTINES FOR FLOW CONTROL ; ; FLOW_IN - RESPOND TO FLOW CONTROL COMMANDS FROM HOST ; FLOW_OUT - ISSUE FLOW CONTROL COMMANDS TO HOST ; FLOW_IN PROC NEAR PUSH AX ; SAVE CHAR CMP XON_XOFF[SI],'E'; FLOW CONTROL ENABLED? JNE FI_2 ; DO NOTHING IF DISABLED AND AL,7FH ; STRIP PARITY CMP AL,CONTROL_S ; STOP COMMAND RECEIVED? JNE FI_1 ; JUMP IF NOT MOV PC_OFF[SI],1 ; WE MUST SHUT UP JMP SHORT FI_2 ; CONTINUE FI_1: CMP AL,CONTROL_Q ; GO COMMAND RECEIVED? JNE FI_2 ; NO, MUST BE NORMAL CHAR MOV PC_OFF[SI],0 ; WE START TALKING AGAIN FI_2: POP AX ; RESTORE CHAR RET ; DONE FLOW_IN ENDP ; FLOW_OUT PROC NEAR CMP XON_XOFF[SI],'E'; FLOW CONTROL ENABLED? JNE FO_X ; DO NOTHING IF DISABLED CMP HOST_OFF[SI],1 ; HOST TURNED OFF? JE FO_X ; JUMP IF SO CMP SIZE_RDATA[SI],R_SIZE/2 ; RECEIVE BUFFER NEARLY FULL? JLE FO_X ; DONE IF NOT MOV AL,CONTROL_S ; TURN OFF HOST IF SO CALL SENDII ; SEND IMMEDIATELY INTERNAL MOV HOST_OFF[SI],1 ; HOST IS NOW OFF FO_X: RET ; DONE FLOW_OUT ENDP PAGE ; ; INT_HNDLR1 - HANDLES INTERRUPTS GENERATED BY COM1: ; INT_HNDLR1 PROC FAR PUSH SI ; SAVE SI MOV SI,OFFSET AREA1 ; DATA AREA FOR COM1: JMP SHORT INT_COMMON ; CONTINUE ; ; INT_HNDLR2 - HANDLES INTERRUPTS GENERATED BY COM2: ; INT_HNDLR2 PROC FAR PUSH SI ; SAVE SI MOV SI,OFFSET AREA2 ; DATA AREA FOR COM2: ; ; BODY OF INTERRUPT HANDLER ; INT_COMMON: PUSH AX ; SAVE REGS PUSH BX PUSH CX PUSH DX PUSH BP PUSH DI PUSH DS PUSH ES MOV AX,SEG _DATA ; OUR DATA SEG MOV DS,AX ; TO DS ; CLEAR THE INTERRUPT CONTROLLER FLAG MOV DX,INTA00 ; 8259 CONTROL PORT MOV AL,EOI[SI] ; SPECIFIC END OF INTERRUPT OUT DX,AL ; CLEAR FLAG ; FIND OUT WHERE INTERRUPT CAME FROM AND JUMP TO ROUTINE TO HANDLE IT REPOLL: MOV DX,IIR[SI] ; READ INTERRUPT STATUS REGISTER IN AL,DX CMP AL,4 JE RX_INT ; IF FROM THE RECEIVER CMP AL,2 JE TX_INT ; IF FROM THE TRANSMITTER CMP AL,6 JE LSTAT_INT ; INTERRUPT BECAUSE OF LINE STATUS CMP AL,0 JE MSTAT_INT ; INTERRUPT BECAUSE OF MODEM STATUS JMP FAR PTR INT_END ; DONE, EXIT (DON'T FIX FAR PTR STUFF) LSTAT_INT: MOV DX,LSR[SI] ; READ AND IGNORE LINE STATUS IN AL,DX ; CALL E_BUMP ; JUST BUMP ERROR COUNTS JMP REPOLL ; SEE IF ANY MORE INTERRUPTS MSTAT_INT: MOV DX,MSR[SI] ; READ AND IGNORE MODEM STATUS IN AL,DX ; JMP REPOLL ; SEE IF ANY MORE INTERRUPTS TX_INT: CMP PC_OFF[SI],1 ; HAVE WE BEEN TOLD TO SHUT UP? JNE GOODTX1 ; JUMP IF NOT CMP HOST_OFF[SI],1 ; HAS HOST ALSO SHUT UP? JNE SEND_NO_MORE ; JUMP IF NOT ; CLEAR XON/XOFF DEADLOCK MOV PC_OFF[SI],0 ; WE SPEAK MOV HOST_OFF[SI],0 ; THEY REPLY MOV AL,CONTROL_Q ; BUT ONLY WHEN CALL SENDII ; WE LET THEM GOODTX1: CMP SIZE_TDATA[SI],0 ; SEE IF ANY MORE DATA TO SEND JG HAVE_DATA ; IF POSITIVE THEN THERE IS DATA TO SEND ; IF NO DATA TO SEND THEN RESET TX INTERRUPT AND RETURN SEND_NO_MORE: MOV DX,IER[SI] ; MOV AL,5 ; JUST RCV AND LINE ERROR OUT DX,AL ; ARE SET JMP REPOLL ; HAVE_DATA: CALL M_PROTOCOL ; DO MODEM PROTOCOL IF NECESSARY MOV BX,START_TDATA[SI] ; BX POINTS TO NEXT CHAR. TO BE SENT MOV AL,TDATA[SI][BX] ; GET DATA FROM BUFFER MOV DX,DATREG[SI] ; DX EQUALS PORT TO SEND DATA TO OUT DX,AL ; SEND DATA INC BX ; INCREMENT START_TDATA CMP BX,S_SIZE ; SEE IF GONE PAST END JB NTADJ ; IF NOT THEN SKIP MOV BX,0 ; RESET TO BEGINNING NTADJ: MOV START_TDATA[SI],BX ; SAVE START_TDATA DEC SIZE_TDATA[SI] ; ONE LESS CHARACTER IN X-MIT BUFFER JMP REPOLL RX_INT: MOV DX,DATREG[SI] ; 8250 DATA REGISTER IN AL,DX ; GET DATA CALL FLOW_IN ; RESPOND TO F.C. COMMANDS FROM HOST CMP SIZE_RDATA[SI],R_SIZE ; SEE IF ANY ROOM JL GOOD_RX1 ; CONTINUE IF SO INC WORD PTR EOVFLOW[SI] ; BUMP OVERFLOW ERROR COUNT JMP REPOLL ; PUNT GOOD_RX1: MOV BX,END_RDATA[SI] ; BX POINTS TO FREE SPACE MOV RDATA[SI][BX],AL ; MOVE DATA TO BUFFER INC SIZE_RDATA[SI] ; GOT ONE MORE CHARACTER INC BX ; INCREMENT END_RDATA POINTER CMP BX,R_SIZE ; SEE IF GONE PAST END JB NRADJ ; IF NOT THEN SKIP MOV BX,0 ; ELSE ADJUST TO BEGINNING NRADJ: MOV END_RDATA[SI],BX ; SAVE VALUE CALL FLOW_OUT ; ISSUE FLOW CONTROL COMMANDS TO HOST JMP REPOLL ; INT_END: POP ES ; RESTORE REGS POP DS POP DI POP BP POP DX POP CX POP BX POP AX POP SI ; RESTORE SI, TOO IRET INT_HNDLR2 ENDP INT_HNDLR1 ENDP COM_TEXT ENDS END