PAGE 56,132
TITLE COMIO: USE COM1 PORT FOR INTERRUPT DRIVEN I/O.
NAME COM1IO
.386C ; ENABLE USE OF 32BIT INSTRUCTIONS; MUST DECLARE SEGMENTS AS "USE16" FOR REAL MODE.
; Microsoft C version 1.52 CALLABLE ROUTINES TO BRING IN BYTE DATA ON THE "AT" compatible SERIAL PORT
; SUBROUTINE RST8259
;
REGBASE EQU 3F8H ; REGISTER BASE AS SET ON COM1.
; DEFINE THE REGISTERS.
; DATA REGISTER, WHEN B7 OF LINE CONTROL= 0.
DATABF EQU REGBASE ; DATA BUFFER: B7:B0
;
; INTERRUPT ENABLE REGISTER WHEN B7 OF LINE CONTROL= 0.
INTNBL EQU REGBASE+ 1 ; INTERRUPT ENABLE REGISTER.
; INTERRUPT ENABLE MASKS.
DTRCVD EQU 00000001B ; INTERRUPT ON DATA RECEIVED.
SNDMTY EQU 00000010B ; INTERRUPT ON DATA TRANSMIT EMPTY.
RCVERR EQU 00000100B ; INTERRUPT ON DATA RECEIVE ERROR.
MDMCHG EQU 00001000B ; INTERRUPT ON MODEM REGISTER CHANGE.
;
; INTERRUPT ID REGISTER.
INTRID EQU REGBASE+ 2 ; INTERRUPT ID.
INTPND EQU 00000001B ; INTERRUPT PENDING MASK, BIT 0 SET WHEN
; INTERRUPT IS PENDING.
; INTERRUPT ID TAGS: INT ID IS STORED IN BITS 2:1 OF INTRID.
IDMASK EQU 00000110B
MDMINT EQU 00000000B ; MODEM CAUSED INTERRUPT.
SNDINT EQU 00000010B ; TRANSMIT REGISTER EMPTY.
RCVINT EQU 00000100B ; RECEIVED DATA.
ERRINT EQU 00000110B ; ERROR CAUSED INTERRUPT.
;
; LINE CONTROL REGISTER.
LNCNTL EQU REGBASE+ 3 ; LINE CONTROL REGISTER.
; BIT LEVEL DEFINITIONS.
BITLEN5 EQU 00000000B ; 5 BIT WORDS.
BITLEN6 EQU 00000001B ; 6 BIT WORDS.
BITLEN7 EQU 00000010B ; 7 BIT WORDS.
BITLEN8 EQU 00000011B ; 8 BIT WORDS.
STOP1BT EQU 00000000B ; ONE STOP BIT.
STOP2BT EQU 00000100B ; TWO STOP BITS.
PARITY EQU 00001000B ; PARITY
EVNPRTY EQU 00010000B ; EVEN PARITY
ODDPRTY EQU 00000000B ; ODD PARITY
STKPRTY EQU 00100000B ; STICK PARITY
SETBRK EQU 01000000B ; SET BREAK.
SETBAUD EQU 10000000B ; ENABLES SETTING OF BAUD RATE.
;
; MODEM CONTROL REGISTER.
MDCNTL EQU REGBASE+ 4 ; MODEM CONTROL REGISTER.
; MODEM CONTROL BITS.
DTR EQU 00000001B ; DATA TERMINAL READY
RTS EQU 00000010B ; REQUEST TO SEND.
OUT1 EQU 00000100B ; SPARE 1.
OUT2 EQU 00001000B ; Interrupt Mask: MUST BE SET TO ALLOW INTERRUPTS.
LOOPBK EQU 00010000B ; LOOP BACK.
;
; LINE STATUS REGISTER.
LNSTAT EQU REGBASE+ 5 ; LINE STATUS.
; DEFINE BIT MASKS FOR LINE STATUS.
DTARCV EQU 00000001B ; DATA RECEIVED.
RCVOFL EQU 00000010B ; RECEIVE OVERRUN.
PRTYERR EQU 00000100B ; PARITY ERROR.
FRAMERR EQU 00001000B ; FRAMING ERROR.
BRKDTC EQU 00010000B ; BREAK DETECT.
TXEMTY EQU 00100000B ; TRANSMIT BUFFER EMPTY.
TXSHMT EQU 01000000B ; TRANSMIT SHIFT REGISTER EMPTY.
TIMEOUT EQU 10000000B ; TIME OUT.
;
; MODEM STATUS REGISTER.
MDSTAT EQU REGBASE+ 6 ; MODEM STATUS.
;
; BAUD RATE DIVISOR REGISTERS WHEN B7 OF LINE CONTROL= 1.
BAUDLO EQU REGBASE ; LOW BYTE OF BAUD RATE DEVISOR.
BAUDHI EQU REGBASE+ 1 ; HIGH BYTE OF REGBASE.
; COMMON BAUD RATE DIVISORS TO CONVERT 119000 CYCLES TO BAUD RATE CLOCK.
B110H EQU 04H ; 110 BAUD HI BYTE.
B110L EQU 17H ; 110 BAUD LOW BYTE.
B300H EQU 01H ; 300 BAUD HIGH.
B300L EQU 80H ; 300 BAUD LOW.
B1200H EQU 00H ; 1200 BAUD HIGH
B1200L EQU 60H ; 1200 BAUD LOW.
B1800H EQU 00H ; 1800 BAUD HIGH
B1800L EQU 40H ; 1800 BAUD LOW.
B2400H EQU 00H ; 2400 BAUD HIGH
B2400L EQU 30H ; 2400 BAUD LOW.
B3600H EQU 00H ; 3600 BAUD HIGH
B3600L EQU 20H ; 3600 BAUD LOW.
B4800H EQU 00H ; 4800 BAUD HIGH
B4800L EQU 18H ; 4800 BAUD LOW.
B9600H EQU 00H ; 9600 BAUD HIGH
B9600L EQU 0CH ; 9600 BAUD LOW.
B19200H EQU 00H ; 19200 BAUD HIGH
B19200L EQU 06H ; 19200 BAUD LOW.
B38400H EQU 00H ; 38400 BAUD HIGH
B38400L EQU 03H ; 38400 BAUD LOW.
B57600H EQU 00H ; 57600 BAUD HIGH
B57600L EQU 02H ; 57600 BAUD LOW.
B115200H EQU 00H ; 115200 BAUD HIGH
B115200L EQU 01H ; 115200 BAUD LOW.
;
;
;
; MISCELLANEOUS.
; DEFINE INTERRUPT MASK REGISTER.
INTCTL EQU 021H ; INTERRUPT IRQ0: IRQ7
LOWBIT EQU 1 ; LOW BIT.
READY EQU 0FFH ; DATA READY SIGNAL.
NOTRDY EQU 0 ; DATA NOT READY.
CLEAR EQU 0 ; CLEAR PORT.
NORMAL EQU 0H ; NORMAL COMPLETION.
INTRST EQU 020H ; INTERRUPT RESET COMMAND.
INTREG EQU 020H ; INTERRUPT PRIORITY REGISTER.
INTMSK EQU 021H ; INT MASK REGISTER.
SLVRST EQU 0A0H ; SLAVE 8259 REGISTER.
SLVMSK EQU 0A1H ; SLAVE 8259 MASK REGISTER.
LINEFD EQU 0AH ; LINE FEED VALUE.
CRETRN EQU 0DH ; CARRIAGE RETURN.
PUBLIC _CMCOM1Q
PUBLIC _COM1LOADED
;+++ BEGIN / CMCOM1Q/
CMCOM1Q SEGMENT PAGE COMMON USE16 'DATA'
QUESIZ EQU 128
EMPTY EQU 07FFFH ; QUEUE EMPTY VALUE
NOTMTY EQU 02FFH ; QUEUE NOT EMPTY VALUE.
QUFULL EQU 01FFH ; QUEUE OVERFLOW VALUE.
QUNTFL EQU 03FFH ; QUEUE NOT FULL.
QUEMIN EQU 128 ; MINIMUM SPACE ON QUEUE BEFORE PAUSING.
;
_CMCOM1Q LABEL FAR ; Microsoft C will use this as the external structure CMCOM1Q
RCVQUE DB QUESIZ DUP (?) ; DATA QUEUE.
HEAD DW 0 ; CURRENT HEAD OF QUEUE.
; HEAD IS INCREMENTED AFTER EACH QUEUE WRITE.
; IF NEW HEAD = TAIL, OVERFLOW OCCURRED.
; QUEMTY IS SET TO NOTMTY.
TAIL DW 0 ; CURRENT TAIL OF QUEUE.
; 1ST, QUEMTY IS CHECKED. IF NOTMTY, DATA IS
; READ AND TAIL IS INCREMENTED AFTER THE READ.
; IF NEW TAIL= HEAD, QUEUE IS EMPTY.
OVRFLO DW QUNTFL ; QUEUE OVERFLOW INDICATOR.
QUEMTY DW EMPTY ; QUEUE EMPTY.
QSPACE DW QUESIZ ; SPACE LEFT ON QUEUE.
QSTOP DW EMPTY ; QUEUE PAUSED INDICATOR.
DTAERR DW 0 ; DATA RECEIVER ERROR!
MDMERR DW 0 ; MODEM ERROR
UNKINT DW 0 ; UNKNOWN ERROR
_COM1LOADED LABEL FAR
DB 0 ; SET WHEN HANDLER IS LOADED.
CMCOM1Q ENDS
;--- END / CMCOM1Q/
;
PUBLIC _CMWRT1Q
;+++ BEGIN / CMWRT1Q/
CMWRT1Q SEGMENT PAGE COMMON USE16 'DATA'
WRTQSIZ EQU 1024
WRTEMPTY EQU 07FFFH ; QUEUE EMPTY VALUE
WRTNOTMTY EQU 02FFH ; QUEUE NOT EMPTY VALUE.
WRTQFULL EQU 01FFH ; QUEUE OVERFLOW VALUE.
WRTQNTFL EQU 03FFH ; QUEUE NOT FULL.
;
_CMWRT1Q LABEL FAR ; Microsoft C will use this as the external structure CMWRT1Q
WRTQUE DB WRTQSIZ DUP (021H); DATA QUEUE.
WRTHEAD DW 0 ; CURRENT HEAD OF QUEUE.
; HEAD IS INCREMENTED AFTER EACH QUEUE WRITE.
; IF NEW HEAD = TAIL, OVERFLOW OCCURRED.
; QUEMTY IS SET TO NOTMTY.
WRTTAIL DW 0 ; CURRENT TAIL OF QUEUE.
; 1ST, QUEMTY IS CHECKED. IF NOTMTY, DATA IS
; READ AND TAIL IS INCREMENTED AFTER THE READ.
; IF NEW TAIL= HEAD, QUEUE IS EMPTY.
WRTOVRFLO DW WRTQNTFL ; QUEUE OVERFLOW INDICATOR.
WRTQSPACE DW WRTQSIZ ; SPACE LEFT ON QUEUE.
WRTQSTOP DW WRTEMPTY ; QUEUE PAUSED INDICATOR.
TXBUSY DW WRTEMPTY ; TRANSMIT INDICATOR.
CMWRT1Q ENDS
;--- END / CMWRT1Q/
CODE SEGMENT USE16
SUBTTL COM1HANDLER
; INTERRUPT HANDLER FOR COM1 PORT.
INTBGN PROC FAR
ASSUME CS:CODE,DS:NOTHING,ES:NOTHING,SS:NOTHING
PUBLIC _COM1HNDL ; DECLARE AS PUBLIC, I.E. EXERNAL.
OLDINTC DD ? ; HOLDER FOR INTERRUPT C ADDRESS.
_COM1HNDL LABEL FAR ; Serial Port IO HANDLER.
PUSH AX ; SAVE AX.
PUSH BX ; SAVE BX.
PUSH CX ; SAVE CX.
PUSH DX ; SAVE DX.
PUSH DI ; SAVE DI.
PUSH DS ; SAVE DS.
MOV AX,CMCOM1Q ; POINT TO COM1 COMMON BLOCK.
MOV DS,AX
; GET INTERRUPT TYPE.
MOV DX,INTRID ; GET PORT ADDRESS.
IN AL,DX ; GET THE VALUE.
MOV BX,AX ; COPY THE VALUE FOR LATER USE.
AND AL,ERRINT ; DID AN ERROR CAUSE THE INTERRUPT?
CMP AL,ERRINT
JNE NOERR
MOV DS:[DTAERR],NOTMTY ; SET ERROR INDICATOR.
MOV DX,LNSTAT ; GET LINE STATUS REGISTER.
IN AL,DX ; READING LINE STATUS RESETS INTERRUPT.
NOERR LABEL NEAR
; JMP INTFNS ; FINISH UP THIS INTERRUPT.
MOV AX,BX ; GET STORED VALUE.
AND AL,RCVINT
CMP AL,RCVINT ; TEST FOR DATA RECEIVED.
JNE NOREAD ; WAS IT A READ?
; GET DATA REGISTER.
; MAKE SURE LINE CONTROL IS SET TO ADDRESS DATA BUFFERS.
MOV DX,LNCNTL ; SET TO LINE CONTROL.
IN AL,DX ; GET THE VALUE.
MOV AH,AL ; COPY IT.
AND AL,NOT SETBAUD ; AND OFF THE SET BAUD BIT.
OUT DX,AL
MOV DX,DATABF ; LOAD DX WITH ADDRESS OF DATA BUFFER.
IN AL,DX ; INPUT DATA.
MOV DI,DS:[HEAD] ; GET HEAD OF QUEUE.
MOV BYTE PTR DS:[DI+RCVQUE],AL ; STORE THE BYTE.
MOV DS:[QUEMTY],NOTMTY ; SET QUEUE Not EMPTY
; RESET LINE CONTROL TO ORIGINAL VALUE.
MOV DX,LNCNTL
MOV AL,AH ; GET ORIGINAL VALUE.
OUT DX,AL ; RESTORE IT.
INC DI ; POINT TO NEXT QUEUE POSITION.
CMP DI,QUESIZ ; OVER THE END?
JNE HDRINC
XOR DI,DI ; WRAP HEAD POINTER BACK TO ZERO.
HDRINC LABEL NEAR
MOV DS:[HEAD],DI ; STORE THE INCREMENTED HEADER.
MOV DX,DS:[TAIL] ; GET TAIL POINTER.
CMP DI,DX ; IS NEW HEAD = TAIL?
JNE NOVFLO ; IF NOT EQUAL, IS OK.
MOV DS:[OVRFLO],QUFULL ; THE QUEUE IS FULL: SET FLAG
NOVFLO LABEL NEAR ; FINISHED.
; HOW MUCH SPACE IS LEFT?
MOV AX,DS:[QSPACE] ; GET QSPACE INDICATOR.
DEC AX ; WE JUST USED UP ONE BYTE.
MOV DS:[QSPACE],AX ; STORE THE NEW VALUE.
CMP AX,QUEMIN ; IS THERE ENOUGH SPACE LEFT ON THE QUEUE?
JA NOPAUS ; YES, THINGS ARE FINE
NOP ; A CONTROL S could be sent here to SLOW THINGS DOWN.
NOPAUS LABEL NEAR
; JMP INTFNS ; FINISH UP THIS INTERRUPT
;
NOREAD LABEL NEAR ; NOT A READ.
; CHECK FOR MULTIPLE CONDITIONS THAT COULD CAUSE THE INTERRUPT.
MOV AX,BX ; RESTORE IIR VALUE.
AND AL,SNDINT
CMP AL,SNDINT ; DID THE TRANSMIT BUFFER EMPTY CAUSE THE INTERRUPT?
JNE NOSEND
MOV AX,CMWRT1Q ; POINT TO COM1 WRITE COMMON BLOCK.
MOV DS,AX
; HOW MUCH SPACE IS LEFT?
MOV AX,DS:[WRTQSPACE] ; GET QSPACE INDICATOR.
CMP AX,WRTQSIZ ; IS IT EMPTY?
JNE WRTDTA
; ALL SPACE IS AVAILABLE, THE QUEUE IS EMPTY.
MOV DX,MDCNTL ; CLEAR THE RTS (REQUEST TO SEND) BIT.
MOV AL,OUT2 OR DTR ; ENABLE DATA TERMINAL READY
OUT DX,AL
JMP NOSEND ; WE ARE DONE.
WRTDTA LABEL NEAR
INC AX ; WE WILL SEND ONE BYTE.
MOV DS:[WRTQSPACE],AX ; STORE THE NEW VALUE.
; WRITE TO THE DATA REGISTER.
; MAKE SURE LINE CONTROL IS SET TO ADDRESS DATA BUFFERS.
MOV DX,LNCNTL ; SET TO LINE CONTROL.
IN AL,DX ; GET THE VALUE.
MOV AH,AL ; COPY IT.
AND AL,NOT SETBAUD ; AND OFF THE SET BAUD BIT.
OUT DX,AL ; SET TO ADDRESS THE DATA BUFFERS.
MOV DI,DS:[WRTTAIL] ; GET TAIL OF WRITE QUEUE.
MOV AL,BYTE PTR DS:[DI+WRTQUE]; GET THE BYTE TO TRANSMIT.
MOV DX,DATABF ; LOAD DX WITH ADDRESS OF DATA BUFFER.
OUT DX,AL ; OUTPUT DATA.
; RESET LINE CONTROL TO ORIGINAL VALUE.
MOV DX,LNCNTL
MOV AL,AH ; GET ORIGINAL VALUE.
OUT DX,AL ; RESTORE IT.
INC DI ; POINT TO NEXT TAIL QUEUE POSITION.
CMP DI,WRTQSIZ ; OVER THE END?
JNE WRTINC
XOR DI,DI ; WRAP TAIL POINTER BACK TO ZERO.
WRTINC LABEL NEAR
MOV DS:[WRTTAIL],DI ; STORE THE INCREMENTED HEADER.
; READING THE INTERRUPT IDENTIFICATION REGISTER CLEARS THIS INTERRUPT.
; JMP INTFNS ; FINISH UP THIS INTERRUPT
NOSEND LABEL NEAR
MOV AX,BX ; RESTORE VALUE.
AND AL,MDMINT ; HOW ABOUT A MODEM ERROR?
CMP AL,MDMINT
JNE INTERR ; TEST THAT THE INTERRUPT WAS PROCESSED.
MOV AX,CMCOM1Q
MOV DS,AX
XOR CX,CX
MOV DX,MDSTAT ; READ THE MODEM STATUS REGISTER
IN AL,DX
INC DS:[MDMERR]
JMP INTFNS ; WE KNOW WE PROCESSED THE INTERRUPT
INTERR LABEL NEAR ; UNKNOWN INTERRUPT?
MOV AX,CMCOM1Q
MOV DS,AX
ADD DS:[UNKINT],CX
INTFNS LABEL NEAR
POP DS
POP DI ; RESTORE DI.
POP DX ; RESTORE DX.
POP CX ; RESTORE CX.
POP BX ; RESTORE BX.
MOV AL,INTRST ; RESTORE
OUT INTREG,AL ; INTERRUPTS.
POP AX ; RESTORE REGISTERS.
; JMP DWORD PTR CS:[OLDINTC] ; DAISY-CHAIN INTERRUPTS.
IRET ; DO NOT DAISY-CHAIN INTERRUPTS.
INTBGN ENDP
;
INITCD PROC FAR
PUBLIC _COM1INT
_COM1INT LABEL FAR ; INITIALIZE COM1 INTERRUPT HANDLER.
PUSH AX
PUSH DX
PUSH DS
; MAKE SURE THAT THE RX BUFFER IS EMPTY.
; MAKE SURE LINE CONTROL IS SET TO ADDRESS DATA BUFFERS.
MOV DX,LNCNTL ; SET TO LINE CONTROL.
AND AL,NOT SETBAUD ; AND OFF THE SET BAUD BIT.
OUT DX,AL ; SET TO ADDRESS THE DATA BUFFER.
RXCLEAR LABEL NEAR
MOV DX,LNSTAT
IN AL,DX ; GET THE VALUE.
AND AL,DTARCV ; CHECK THE DATA RECEIVED BIT.
JZ RXEMPTY ; IS IT CLEAR?
; SOMETHING IS IN THERE!
MOV DX,DATABF ; LOAD DX WITH ADDRESS OF DATA BUFFER.
IN AL,DX ; INPUT DATA, THIS SHOULD CLEAR THE DATA RECEIVED BIT.
JMP RXCLEAR ; REPEAT UNTIL EMPTY.
RXEMPTY LABEL NEAR
XOR AX,AX ; SET DS TO 0000.
MOV DS,AX
CLI ; DISABLE INTERRUPTS
; SET UP MODEM AND LINE CONTROL.
MOV DX,MDCNTL
MOV AL,OUT2 OR DTR ; ENABLE INTERRUPTS (OUT2) AND DATA TERMINAL READY
OUT DX,AL
MOV AX,WORD PTR DS:[030H] ; GET OFFSET
MOV WORD PTR CS:[OLDINTC],AX ; STORE IN CODE SEGMENT.
MOV AX,WORD PTR DS:[032H] ; GET SEGMENT
MOV WORD PTR CS:[OLDINTC+2],AX
MOV WORD PTR DS:[030H],OFFSET _COM1HNDL ; LOAD OFFSET OF INT HANDLER
MOV WORD PTR DS:[032H],SEG _COM1HNDL ; LOAD SEGMENT OF INT HANDLER
; ENABLE INTERRUPTS ON THE SERIAL PORT FOR DATA RECEIVED.
STI ; ENABLE INTERRUPTS.
; INITIALIZE QUEUE POINTERS AND STATUS.
MOV AX,CMCOM1Q ; LOAD UP THE COM2 RECEIVE COMMON BLOCK.
MOV DS,AX
XOR DX,DX ; SET AX TO ZERO
MOV WORD PTR DS:[HEAD],DX ; CURRENT HEAD OF QUEUE.
MOV WORD PTR DS:[TAIL],DX ; CURRENT TAIL OF QUEUE.
MOV WORD PTR DS:[OVRFLO],QUNTFL ; QUEUE OVERFLOW INDICATOR.
MOV WORD PTR DS:[QUEMTY],EMPTY ; QUEUE EMPTY INDICATOR.
MOV WORD PTR DS:[QSPACE],QUESIZ ; SPACE LEFT ON QUEUE.
MOV WORD PTR DS:[QSTOP],EMPTY ; QUEUE PAUSED INDICATOR.
MOV WORD PTR DS:[DTAERR],EMPTY ; RECEIVE ERROR INDICATOR.
MOV BYTE PTR DS:[_COM1LOADED],0FFH ; SET INDICATOR THAT HANDLER IS LOADED.
; AND INIT THE WRITE QUEUE.
MOV AX,CMWRT1Q ; LOAD UP THE COM2 RECEIVE COMMON BLOCK.
MOV DS,AX
MOV WORD PTR DS:[WRTHEAD],DX ; CURRENT HEAD OF QUEUE.
MOV WORD PTR DS:[WRTTAIL],DX ; CURRENT TAIL OF QUEUE.
MOV WORD PTR DS:[WRTOVRFLO],WRTQNTFL ; QUEUE OVERFLOW INDICATOR.
MOV WORD PTR DS:[WRTQSPACE],WRTQSIZ ; SPACE LEFT ON QUEUE.
MOV WORD PTR DS:[WRTQSTOP],WRTEMPTY ; QUEUE PAUSED INDICATOR.
MOV WORD PTR DS:[TXBUSY],WRTEMPTY ; TRANSMIT INDICATOR.
MOV AL,MDMCHG OR RCVERR OR SNDMTY OR DTRCVD ; OUTPUT INTERRUPT ENABLE.
MOV DX,INTNBL ; TO THE INTERRUPT ENABLE PORT.
OUT DX,AL
XOR AL,AL ; CLEAR AL.
OUT INTMSK,AL ; SET MASK REGISTER TO 0.
MOV AL,INTRST ; RESTORE
OUT INTREG,AL ; INTERRUPTS.
POP DS
POP DX
POP AX
RET
INITCD ENDP
RSTCD PROC FAR
PUBLIC _COM1RST
_COM1RST LABEL FAR ; RESET INTERRUPTS.
PUSH DX
PUSH AX
PUSH DS ; SAVE DS
PUSH ES
XOR AX,AX ; SET ES TO 0000.
MOV ES,AX
CLI ; DISABLE INTERRUPTS.
MOV AX,WORD PTR CS:[OLDINTC] ; RESTORE INTERRUPT VECTOR
MOV WORD PTR ES:[030H],AX ; OFFSET.
MOV AX,WORD PTR CS:[OLDINTC+2] ; RESTORE ORIGINAL INTERRUPT
MOV WORD PTR ES:[032H],AX ; SEGMENT.
; DISABLE INTERRUPTS ON THE SERIAL PORT.
XOR AL,AL ; OUTPUT NO INTERRUPTS ENABLED.
MOV DX,INTNBL ; TO THE INTERRUPT ENABLE PORT.
OUT DX,AL
STI ; RESTORE INTERRUPTS.
; RESET QUEUE TO ORIGINAL STATE.
MOV AX,CMCOM1Q ; POINT TO COM COMMON BLOCK.
MOV DS,AX
XOR AX,AX ; SET TAIL AND HEAD TO ZERO.
MOV DS:[HEAD],AX ; CURRENT HEAD OF QUEUE.
MOV DS:[TAIL],AX
; SET OVERFLOW TO QUEUE-NOT-FULL.
MOV DS:[OVRFLO],QUNTFL
; SET QUEUE EMPTY TO EMPTY.
MOV DS:[QUEMTY],EMPTY
; SET SPACE LEFT ON QUEUE TO FULL SIZE.
MOV DS:[QSPACE],QUESIZ
; RESET PAUSE FLAG.
MOV DS:[QSTOP],EMPTY
MOV BYTE PTR DS:[_COM1LOADED],0 ; SET INDICATOR THAT HANDLER IS UNLOADED.
POP ES
POP DS
POP AX
POP DX
RET
RSTCD ENDP
SUBTTL INPUT/ OUTPUT SUBROUTINES.
COM1SUBS PROC FAR
PUBLIC _RDCOM1
_RDCOM1 LABEL FAR
; LOAD THE NEXT VALUE OF THE COM QUEUE.
PUSH DS ; SAVE DATA SEGMENT.
MOV AX,CMCOM1Q ; SET DS TO CMCOM1Q SEGMENT.
MOV DS,AX
; CHECK QUEUE EMPTY BUFFER.
MOV AX,DS:[QUEMTY] ; GET QUEMTY.
CMP AX,EMPTY
JNE GETVAL
POP DS ; QUEUE IS EMPTY, RETURN.
RET
GETVAL LABEL NEAR
PUSH SI ; Save Register
MOV SI,DS:[TAIL] ; GET TAIL.
MOV AL,BYTE PTR DS:[SI+RCVQUE] ; GET VALUE POINTED TO BY TAIL.
XOR AH,AH
INC SI ; INCREMENT TAIL.
CMP SI,QUESIZ ; OVER THE END?
JNE TALINC
XOR SI,SI ; WRAP TAIL POINTER BACK TO ZERO.
TALINC LABEL NEAR
MOV DS:[TAIL],SI ; STORE NEW TAIL.
CLI ; DISABLE INTERRUPTS WHILE CHECKING HEAD AND QSPACE.
CMP SI,DS:[HEAD] ; IS TAIL=HEAD?
JNE MORDTA
MOV DS:[QUEMTY],EMPTY ; QUEUE IS EMPTY.
MORDTA LABEL NEAR
; INCREMENT QSPACE.
INC DS:[QSPACE] ; QSPACE = QSPACE+ 1
; DO WE NEED TO SEND A CONTROL Q?
MOV SI,DS:[QSTOP] ; GET QUEUE STOPPED FLAG.
CMP SI,NOTMTY ; IS THE FLAG SET?
JNE NOTSTP
MOV DS:[QSTOP],EMPTY ; RESET FLAG.
; SEND OUT CONTROL Q at this point if ^S and ^Q signalling is in use..
NOTSTP LABEL NEAR
; CHECK FOR OVERFLOW.
MOV SI,QUFULL
CMP SI,DS:[OVRFLO]
JNE QUECHK
MOV AX,QUFULL ; OVERFLOW OCCURRED.
QUECHK LABEL NEAR
; VALUE IS RETURNED IN AX.
STI ; ENABLE INTERRUPTS.
POP SI
POP DS
RET ; RETURN
CHARS EQU 6 ; Offset of Length
STRING EQU 8 ; OFFSET OF STRING.
PUBLIC _WRCOM1Q
_WRCOM1Q LABEL FAR
; WRITE A STRING TO THE COM1 QUEUE AND SEND USING INTERRUPTS.
PUSH BP
MOV BP,SP
PUSH DS ; PUSH DS.
PUSH ES
PUSH CX
PUSH DX
PUSH SI
PUSH DI
MOV CX,WORD PTR SS:[BP+CHARS] ; GET NUMBER OF BYTES.
LES SI,DWORD PTR SS:[BP+STRING] ; GET ADDRESS OF BUFFER.
MOV AX,CMWRT1Q ; GET COM1 TRANSMIT COMMON BLOCK.
MOV DS,AX
MOV DX,MDCNTL
MOV AL,OUT2 OR RTS OR DTR ; ENABLE DATA TERMINAL READY AND READY TO SEND
OUT DX,AL
; IS THE QUEUE EMPTY?
CLI ; DISABLE INTERRUPTS WHILE CHECKING QUEUE SIZE.
MOV DI,DS:[WRTQSPACE] ; GET WRITE QUEUE INDICATOR.
CMP DI,WRTQSIZ ; IS IT EMPTY?
JNE QUEDTA
; THE TRANSMIT BUFFER IS EMPTY AND MUST BE "PRIMED" BY SENDING OUT THE FIRST BYTE.
; CHECK THAT THE PHYSICAL TX BUFFER IS EMPTY.
MOV DX,LNSTAT ; WAIT TILL ITS GONE.
TXTST3 LABEL NEAR ; TRANSMIT FINISH LOOP.
IN AL,DX ; GET LINE STATUS.
AND AL,TXEMTY
JZ TXTST3 ; LOOP UNITL NON-ZERO.
MOV DX,MDCNTL
MOV AL,OUT2 OR RTS OR DTR ; ENABLE DATA TERMINAL READY AND REQUEST TO SEND
OUT DX,AL
MOV DX,LNCNTL ; SET TO LINE CONTROL.
IN AL,DX ; GET THE VALUE.
MOV AH,AL ; SAVE ORIGINAL VALUE.
AND AL,NOT SETBAUD ; AND OFF THE SET BAUD BIT.
OUT DX,AL ; SEND IT.
MOV AL,ES:[SI] ; GET THE FIRST BYTE TO SEND.
INC SI ; POINT TO THE NEXT BYTE.
MOV DX,DATABF ; SEND THE FIRST BYTE TO THE DATA PORT.
OUT DX,AL
MOV AL,AH ; RESTORE LINE CONTROL.
MOV DX,LNCNTL
OUT DX,AL ; BACK IT GOES.
DEC CX ; DECREMENT THE COUNT.
MOV AX,DI ; IF NEXT BRANCH OCCURS, ALL SPACE IS AVAILABLE.
CMP CX,0 ; DID WE ONLY HAVE TO SEND ONE BYTE?
STI ; ENABLE INTERRUPTS.
JE TXDONE ; WAHOO!
QUEDTA LABEL NEAR
STI ; ENABLE INTERRUPTS
NOP
MOV AX,DS:[WRTQSPACE] ; DO WE HAVE SPACE ON THE QUEUE?
CLI ; DISABLE INTERRUPTS TO UPDATE QSPACE.
CMP AX,0
JE QUEDTA ; WAIT FOR SOME SPACE TO CLEAR UP.
DEC AX ; UPDATE AVAILABLE QUEUE SPACE, PREVENT INTERRUPT HANDLER FROM
MOV DS:[WRTQSPACE],AX ; MODIFYING IT AT THE SAME TIME
STI ; ENABLE INTERRUPTS.
MOV DI,DS:[WRTHEAD] ; GET THE HEAD OF THE QUEUE.
MOV AL,ES:[SI] ; GET NEXT BYTE TO QUEUE UP.
INC SI ; POINT TO NEXT BYTE
MOV DS:[DI+WRTQUE],AL ; PUT IT ON THE QUEUE.
INC DI ; POINT TO NEXT SPOT IN QUEUE
CMP DI,WRTQSIZ
JNE HEADINC
XOR DI,DI ; RESET IT TO ZERO.
HEADINC LABEL NEAR
MOV DS:[WRTHEAD],DI
LOOP QUEDTA
MOV AX,DS:[WRTQSPACE]
TXDONE LABEL NEAR
POP DI
POP SI
POP DX
POP CX
POP ES
POP DS
POP BP
RET ; RETURN
;
PUBLIC _WRCOM1P
_WRCOM1P LABEL FAR
; WRITE A STRING TO THE COM PORT USING POLLING
PUSH BP
MOV BP,SP
PUSH DS ; PUSH DS.
PUSH ES
PUSH CX
PUSH DX
PUSH SI
; LOAD DX WITH ADDRESS OF HANDLE.
MOV CX,WORD PTR SS:[BP+CHARS] ; GET NUMBER OF BYTES.
LES SI,DWORD PTR SS:[BP+STRING] ; GET ADDRESS OF BUFFER.
MOV AX,CMWRT1Q ; LOAD UP THE CMWRT1Q SEGMENT.
MOV DS,AX
MTYDTA LABEL NEAR
MOV AX,DS:[WRTQSPACE] ; DO WE HAVE SPACE ON THE QUEUE?
CMP AX,WRTQSIZ ; THE QUEUE HAS TO BE EMPTY BEFORE WE CAN DO A POLLED OUTPUT.
JNE MTYDTA ; WAIT FOR THE QUEUE TO EMPTY.
MOV DX,MDCNTL
MOV AL,OUT2 OR RTS OR DTR ; ENABLE DATA TERMINAL READY
OUT DX,AL
MOV DX,LNCNTL ; SET TO LINE CONTROL.
IN AL,DX ; GET THE VALUE.
PUSH AX ; SAVE ORIGINAL VALUE.
AND AL,NOT SETBAUD ; AND OFF THE SET BAUD BIT.
OUT DX,AL ; SEND IT.
MOV DX,LNSTAT ; WAIT TILL ITS GONE.
TXTST2 LABEL NEAR ; TRANSMIT FINISH LOOP.
IN AL,DX ; GET LINE STATUS.
AND AL,TXEMTY
JZ TXTST2 ; LOOP UNITL NON-ZERO.
SEND2 LABEL NEAR
MOV DX,DATABF ; SEND DATA.
MOV AL,BYTE PTR ES:[SI] ; GET NEXT BYTE.
OUT DX,AL
MOV DX,LNSTAT ; WAIT TILL ITS GONE.
TXFNS2 LABEL NEAR ; TRANSMIT FINISH LOOP.
IN AL,DX ; GET LINE STATUS.
AND AL,TXEMTY
JZ TXFNS2 ; LOOP UNITL NON-ZERO.
INC SI ; POINT TO NEXT BYTE.
LOOP SEND2
MOV DX,MDCNTL
MOV AL,OUT2 OR DTR ; ENABLE DATA TERMINAL READY
MOV DX,LNCNTL ; SET TO LINE CONTROL.
POP AX ; GET ORIGINAL VALUES BACK.
OUT DX,AL ; Send it out THE VALUE.
POP SI
POP DX
POP CX
POP ES
POP DS
POP BP
MOV AX,NORMAL
RET ; RETURN
;
PUBLIC _COM1RDY
_COM1RDY LABEL FAR
; RETURN CONDITION OF QUEMTY
PUSH DS ; SAVE DATA SEGMENT.
MOV AX,CMCOM1Q ; SET DS TO CMCOM1Q SEGMENT.
MOV DS,AX
MOV AX,DS:[QUEMTY] ; GET VALUE OF QUEMTY .
POP DS ; RESTORE DATA SEGMENT.
RET ; RETURN
;
PUBLIC _WRQ1FLUSH
_WRQ1FLUSH LABEL FAR
; EMPTY THE COM PORT WRITE QUEUE.
PUSH DS ; PUSH DS.
MOV AX,CMWRT1Q ; LOAD UP THE CMWRT1Q SEGMENT.
MOV DS,AX
FLUSHQ LABEL NEAR
MOV AX,DS:[WRTQSPACE] ; DO WE HAVE SPACE ON THE QUEUE?
CMP AX,WRTQSIZ ; THE QUEUE HAS TO BE EMPTY.
JNE FLUSHQ ; WAIT FOR THE QUEUE TO EMPTY.
POP DS
RET ; RETURN
PUBLIC _SETBAUD1
_SETBAUD1 LABEL FAR
BAUDRATE EQU 6
PUSH BP ; SAVE BP
MOV BP,SP ; GET BAUD RATE FROM ARGUMENT LIST
PUSH DS ; PUSH DS.
PUSH DX
PUSH CX
PUSH BX
MOV AX,CMWRT1Q ; LOAD UP THE CMWRT1Q SEGMENT.
MOV DS,AX
MOV CX,WORD PTR SS:[BP+BAUDRATE] ; GET BAUD RATE FROM THE ARGUMENT LIST. VALUES GO UP TO 11520.
FLUSHQ2 LABEL NEAR
MOV AX,DS:[WRTQSPACE] ; DO WE HAVE SPACE ON THE QUEUE?
CMP AX,WRTQSIZ ; THE QUEUE HAS TO BE EMPTY.
JNE FLUSHQ2 ; WAIT FOR THE QUEUE TO EMPTY.
; CHECK THAT THE PHYSICAL TX BUFFER IS EMPTY.
MOV DX,LNSTAT ; WAIT TILL ITS GONE.
TXTST4 LABEL NEAR ; TRANSMIT FINISH LOOP.
IN AL,DX ; GET LINE STATUS.
AND AL,TXEMTY
JZ TXTST4 ; LOOP UNITL NON-ZERO.
; GET BAUD RATE DIVISORS.
CMP BX,11520 ; use 11520 for 115200 baud.
JNE CHK57600
MOV BX,B115200L ; BAUD RATE DIVISOR FOR 115200.
JMP SETRATE
CHK57600 LABEL NEAR
CMP CX,57600 ; IS ANYTHING IN THE UPPER 16 BITS?
JNE CHK38400
MOV BX,B57600L ; BAUD RATE DIVISOR FOR 57600.
JMP SETRATE
CHK38400 LABEL NEAR
CMP CX,38400 ; IS ANYTHING IN THE UPPER 16 BITS?
JNE CHK19200
MOV BX,B38400L ; BAUD RATE DIVISOR FOR 38400.
JMP SETRATE
CHK19200 LABEL NEAR
CMP CX,19200 ; IS ANYTHING IN THE UPPER 16 BITS?
JNE CHK9600
MOV BX,B19200L ; BAUD RATE DIVISOR FOR 19200.
JMP SETRATE
CHK9600 LABEL NEAR
MOV BX,B9600L ; BAUD RATE DIVISOR FOR 9600.
SETRATE LABEL NEAR
; SET NEW BAUD RATE.
MOV DX,LNCNTL ; ADDRESS BAUD RATE REGISTERS.
MOV AL,SETBAUD ; SET LINE CONTROL TO MAP BAUD RATE REGISTERS.
OUT DX,AL ; SET TO BAUD GENERATOR.
MOV DX,BAUDLO ; GET LOW BYTE OF BAUD RATE.
MOV AL,BL ; SET TO 9600 BAUD.
OUT DX,AL
INC DX ; SET TO BAUD RATE HIGH.
XOR AL,AL ; SET HIGH BYTE
OUT DX,AL
; SET TO 8 BITS NO PARITY, 1 STOP BIT.
MOV DX,LNCNTL ; ADDRESS LINE CONTROL.
MOV AL,BITLEN8 OR STOP1BT
OUT DX,AL
; SET UP MODEM AND LINE CONTROL.
MOV DX,MDCNTL
; MOV AL,0BH ; ENABLE DATA TERMINAL READY
MOV AL,OUT2 OR DTR ; ENABLE INTERRUPTS AND DATA TERMINAL READY
OUT DX,AL
POP BX
POP CX
POP DX
POP DS
POP BP
RET
COM1SUBS ENDP
RSTPIC PROC FAR
PUBLIC _RST8259
_RST8259 LABEL FAR ; RESET THE 8259 PROGRAMMABLE
; INTERRUPT CONTROLLERS.
CLI ; DISABLE INTERRUPTS.
; THIS CODE RE-INITIALIZES THE 8259 MASTER.
MOV AL,11H ; ICW1: EDGE, MASTER, ICW4
OUT INTREG,AL ; SEND IT
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
; INSTRUCTION SLOWLY.
MOV AL,08H ; ICW2: INT VECTORS 8-F.
OUT INTMSK,AL
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
MOV AL,04H ; ICW3: MASTER, SLAVE ON IR2.
OUT INTMSK,AL
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
; INSTRUCTION SLOWLY.
MOV AL,01H ; ICW4: MASTER, 8086 MODE.
OUT INTMSK,AL
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
; THIS CODE RE-INITIALIZES THE 8259 SLAVE.
MOV AL,11H ; ICW1: EDGE MODE, ICW4
OUT SLVRST,AL ; SEND IT
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
; INSTRUCTION SLOWLY.
MOV AL,070H ; ICW2: INT VECTORS 70H-7FH.
OUT SLVMSK,AL
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
MOV AL,02H ; ICW3: SLAVE ID 2 (010).
OUT SLVMSK,AL
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
; INSTRUCTION SLOWLY.
MOV AL,01H ; ICW4: 8086 MODE.
OUT SLVMSK,AL
JMP SHORT $+2 ; I/O WAIT STATE: JUMPS TO NEXT
STI ; RESTORE INTERRUPTS.
RET
RSTPIC ENDP
CODE ENDS
;
STACK SEGMENT USE16 WORD STACK 'STACK'
STAKSZ EQU 16
IF STAKSZ
DB STAKSZ DUP (?)
ENDIF
STACK ENDS
END