home *** CD-ROM | disk | FTP | other *** search
- TITLE COMM
- PAGE 83,132
- ; $Id: comm.asm 1.7 1993/05/31 01:39:23 ahd Exp $
- ;
- ; $Log: comm.asm $
- ;; Revision 1.7 1993/05/31 01:39:23 ahd
- ;; Add return to spin loop (fix by Bill Plummer)
- ;; Drop FIFO size to 8
- ;;
- ;; Revision 1.4 1993/05/30 00:20:02 ahd
- ;; Insert minor delay to allow slow modems to catch up
- ;;
- ;; Revision 1.2 1992/12/18 12:08:25 ahd
- ;; Add Plummer's fix for bad TASM assemble of com_errors
- ;;
- ;
- ; 14-Jun-93 plummer Add RET to spinloop routine
- ; 14-Jun-93 plummer Set FIFO thresholds to 8 rather than 16 bytes
- ; 18-May-93 plummer Define IO$DELAY and use in UART type determination
- ; 16-May-93 plummer Debug code to printout UART type
- ; 22-Apr-93 plummer Make case consistent in "TBuff" so it links properly
- ; 2-Dec-92 plummer Fix com_errors() again. Change got lost.
- ; Fix com_errors() to avoid problems with tasm. Plummer, 11/16/92
- ; 8259 EOI issued after interrupts serviced. Plummer, 3/25/92
- ; Fix botch in Set_Baud. Plummer, 3/20/92
- ; Put in Gordon Lee's cure from dropped interrupts. Plummer, 3/19/92
- ; TEMPORARY ioctl_com(). Plummer, 3/9/92.
- ; Clear OUT2 bit in UART. Some machines use it so enable IRQ. Plummer, 3/9/92
- ; Release send buffer if we can't assign a recv buffer. Plummer, 3/9/92
- ; Move EQU's outside of SP_TAB struc definition. ahd, 3/8/92.
- ; ahd changes: short jmp's out of range in INST, OPEN ??? (ahd, 3/?/92)
- ; open_com() leaves DTR unchanged so Drew's autobaud works. Plummer, 3/2/92
- ; Missing DX load in close_com() -- FIFO mode not cleared. Plummer, 3/2/92
- ; C calling convention does not require saving AX, BX, CX, DX. Plummer 2/23/92
- ; Flush consideration of the PC Jr. Wm. W. Plummer, 2/15/92
- ; Cleanup PUSHF/POPF and CLI/STI useage. Wm. W. Plummer, 2/15/92
- ; Make SENDII have Giles Todd's change. Wm. W. Plummer, 2/15/92
- ; Changes to Giles Todd's code to support dynamic buffers. Plummer, 2/3/92
- ; 26 Jan 92 Giles Todd Prime THR for UARTs which do not give a Tx empty
- ; interrupt when Tx interrupts are enabled.
- ; S_SIZE & R_SIZE may be set with -D to MASM. Wm. W. Plummer, 1/19/92
- ; Assign buffers dynamically. Wm. W. Plummer, 1/19/92
- ; Unfix byte length -- I screwed up. Wm. W. Plummer, 1/15/92
- ; Fix byte length with specific PARITY select. Wm. W. Plummer, 1/13/92
- ; Buffers up to 4096 per AHD. Wm. W. Plummer, 1/1/92
- ; Always use FIFO length of 16 on send side. Wm. W. Plummer, 12/30/91.
- ; Init DSR and CTS previous state from current status. Wm. Plummer, 12/30/91.
- ; UUPC conditional to disable v.24. Wm. W. Plummer, 12/30/91.
- ; Buffer sizes up to 2048 per ahd. Wm. W. Plummer, 12/15/91.
- ; dtr_on() switches to D connection if CTS&DSR don't come up. WWP, 12/15/91.
- ; New dtr_on() logic. Wm. W. Plummer, 12/11/91
- ; Fix bad reg. per report from user. Wm. W. Plummer, 12/11/91
- ; Semicolon before control-L's for MASM 5.00 per ahd. Wm. W. Plummer, 12/8/91
- ; Use AHD's handling of COM ports. Wm. W. Plummer, 11/29/91
- ; Buffer sizes reduced and required to be 2**N. Wm. W. Plummer, 11/11/91
- ; Accomodate V.24 requirements on DTR flaps. Wm. W. Plummer 10/15/91
- ; Revised DTR_ON_COM to solve user problem. Wm. W. Plummer, 10/3/91
- ; Make time delays independent of CPU speed. Wm. W. Plummer, 9/16/91
- ; Use interrupts to trace CD, DSR, Wm. W. Plummer, 9/16/91
- ; Remove modem control from TXI. Wm. W. Plummer, 9/13/91
- ; Completely redo the XOFF/XON logic. Too many races before. Wm. W. Plummer
- ; Revise interrupt dispatch for speed & function. William W. Plummer, 9/12/91
- ; Merge in ahd's changes to flush control Q,S when received as flow control
- ; SEND buffer allows one byte for a SENDII call. Avoids flow control
- ; lockups. - William W. Plummer, 8/30/91
- ; Support for NS16550A chip with SILO - William W. Plummer, 8/30/91
- ; Add modem_status() routine - William W. Plummer, 7/2/91
- ; Put wrong code under AHD conditional - William W. Plummer, 7/2/91
- ; Change TITLE, repair bad instr after INST3 - William W. Plummer, 7/1/91
- ; Modified to use COM1 thru COM4 - William W. Plummer, 2/21/91
- ; Eliminate (incomplete) support for DOS1 - William W. Plummer, 11/13/90
-
- ; Changes may be copied and modified with no notice. Copyrights and copylefts
- ; are consider silly and do not apply. -- William W. Plummer
-
- ; modified to use MSC calling sequence. jrr 3/86
- ;****************************************************************************
- ; Communications Package for the IBM PC, XT, AT 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
- ; o Built in XON/XOFF flow control option
- ; o C language calling conventions
- ; o Logs all comm errors
- ; o Direct connect or modem protocol
- PAGE;
- ;
- ; Buffer sizes -- *** MUST be powers of 2 ****
-
- IFDEF UUPC
- R_SIZE EQU 4096
- S_SIZE EQU 4096
- ENDIF
-
- ; If not set above, maybe on assembler command line. But if not, ...
- IFNDEF R_SIZE
- R_SIZE EQU 512 ; Recv buffer size
- ENDIF
- IFNDEF S_SIZE
- S_SIZE EQU 512 ; Send buffer size
- ENDIF
-
- ; INTERRUPT NUMBERS
- INT_COM1 EQU 0CH ; COM1: FROM 8259
- INT_COM2 EQU 0BH ; COM2: FROM 8259
- INT_COM3 EQU 0CH ; COM3: FROM 8259
- INT_COM4 EQU 0BH ; COM4: FROM 8259
- ; 8259 PORTS
- INTA00 EQU 20H ; 8259A PORT, A0 = 0
- INTA01 EQU 21H ; 8259A PORT, A0 = 1
- ; COM1: & COM3: 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: & COM4: 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
-
- ;
- ; ROM BIOS Data Area
- ;
- RBDA SEGMENT AT 40H
- RS232_BASE DW 4 DUP(?) ; ADDRESSES OF RS232 ADAPTERS
- RBDA ENDS
-
- PAGE;
- ;
- ; TABLE FOR EACH SERIAL PORT
- ;
- SP_TAB STRUC
- PORT DB ? ; 1 OR 2 OR 3 OR 4
- ; 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)
- URGENT_SEND DB ? ; We MUST send one byte (XON/XOFF)
- SEND_OK DB ? ; DSR and CTS are ON
- ; ERROR COUNTS
- ERROR_BLOCK DW 8 DUP(?); EIGHT ERROR COUNTERS
-
- ; UART PORTS - DATREG thru MSR must be in order shown.
- DATREG DW ? ; DATA REGISTER
- IER DW ? ; INTERRUPT ENABLE REGISTER
- IIR DW ? ; INTERRUPT IDENTIFICATION REGISTER (RO)
- LCR DW ? ; LINE CONTROL REGISTER
- MCR DW ? ; MODEM CONTROL REGISTER
- LSR DW ? ; LINE STATUS REGISTER
- MSR DW ? ; MODEM STATUS REGISTER
- UART_SILO_LEN DB ? ; Size of a silo chunk (1 for 8250)
- ;
- ; 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
- TBuff DD ? ; Pointer to transmit buffer
- RBuff DD ? ; Pointer to 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
-
- ;
- ; Equates having to do with the FIFO
- ;
- FCR EQU IIR ; FIFO Control Register (WO)
- ; Bits in FCR for NS16550A UART. Note that writes to FCR are ignored
- ; by other chips.
- FIFO_ENABLE EQU 001H ; Enable FIFO mode
- FIFO_CLR_RCV EQU 002H ; Clear receive FIFO
- FIFO_CLR_XMT EQU 004H ; Clear transmit FIFO
- FIFO_STR_DMA EQU 008H ; Start DMA Mode
- ; 10H and 20H bits are register bank select on some UARTs (not handled)
- FIFO_SZ_4 EQU 040H ; Warning level is 4 before end
- FIFO_SZ_8 EQU 080H ; Warning level is 8 before end
- FIFO_SZ_14 EQU 0C0H ; Warning level is 14 before end
- ;
- ; Commands used in code to operate FIFO. Made up as combinations of above
- ;
- FIFO_CLEAR EQU 0 ; Turn off FIFO
- FIFO_SETUP EQU FIFO_SZ_8 OR FIFO_ENABLE
- FIFO_INIT EQU FIFO_SETUP OR FIFO_CLR_RCV OR FIFO_CLR_XMT
- ;
- ; Miscellaneous FIFO-related stuff
- ;
- FIFO_ENABLED EQU 0C0H ; 16550 makes these equal FIFO_ENABLE
- FIFO_LEN EQU 8 ; Length of the transmit FIFO in a 16550A
- PAGE;
- ; put the data in the DGROUP segment
- ; far calls enter with DS pointing to DGROUP
- ;
- DGROUP GROUP _DATA
- _DATA SEGMENT PUBLIC 'DATA'
- ;
- 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
- AREA3 SP_TAB <3,INT_COM3,IRQ4,NIRQ4,EOI4> ; COM3 DATA AREA
- AREA4 SP_TAB <4,INT_COM4,IRQ3,NIRQ3,EOI3> ; COM4 DATA AREA
-
- IFDEF DEBUG
- ST8250: DB "8250$"
- ST16550: DB "16550$"
- STUART: DB " UART detected", 0DH, 0AH, '$'
- ENDIF
-
- _DATA ENDS
-
-
-
-
- COM_TEXT SEGMENT PARA PUBLIC 'CODE'
- ASSUME CS:COM_TEXT,DS:DGROUP,ES:NOTHING
-
- PUBLIC AREA1, AREA2, AREA3, AREA4
- PUBLIC _select_port
- PUBLIC _save_com
- PUBLIC _install_com
- PUBLIC _restore_com
- PUBLIC _open_com
- PUBLIC _ioctl_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
- IFNDEF UUPC
- PUBLIC _send_local
- ENDIF
- PUBLIC _break_com
- PUBLIC _com_errors
- PUBLIC _modem_status
- IFDEF DEBUG
- PUBLIC INST2, INST4
- PUBLIC OPEN1, OPEN2, OPENX
- PUBLIC DTRON1, DTRON6, DTRONF, DTRONS, DTRONX
- PUBLIC RECV1, RECV3, RECV4, RECVX
- PUBLIC SEND1, SENDX
- PUBLIC WaitN, WaitN1, WaitN2
- PUBLIC SENDII, SENDII2, SENDII4, SENDIIX
- PUBLIC SPINLOOP
- PUBLIC CHROUT, CHROUX
- PUBLIC BREAKX
- PUBLIC INT_HNDLR1, INT_HNDLR2, INT_HNDLR3, INT_HNDLR4
- PUBLIC INT_COMMON, REPOLL, INT_END
- PUBLIC LSI
- PUBLIC MSI
- PUBLIC TXI, TXI1, TXI2, TXI3, TXI9
- PUBLIC TX_CHR
- PUBLIC RXI, RXI0, RXI1, RXI2, RXI6, RXIX
- ENDIF
- PAGE;
- ; Notes, thoughts and explainations by Bill Plummer. These are intended to
- ; help those of you who would like to make modifications.
-
- ; Here's the order of calls in UUPC. The routines in COMM.ASM are called
- ; from ulib.c.
-
- ; First (when a line in system has been read?), ulib&openline calls
- ; select_port() ; Sets up CURRENT_AREA
- ; then, save_com() ; Save INT vector
- ; then, install_com() ; Init area, hook INT
- ; then, open_com(&cmd, 'D', 'N', STOP*T, 'D') ; Init UART, clr bufs
- ; then, dtr_on().
-
- ; At that point the line is up and running. UUPC calls ulib&sread()
- ; which calls, receive_com();
-
- ; And UUPC calls ulib&swrite()
- ; which calls, send_com();
-
- ; To cause an error that the receiver will see, UUPC calls ulib&ssendbrk();
- ; which calls, break_com();
-
- ; When all done with the line, UUPC calls ulib&closeline()
- ; which calls, dtr_off();
- ; then, close_com();
- ; then, restore_com(); ; Unhook INT
- ; and, stat_errors();
-
-
- ; Note: On the PC COM1 and COM3 share IRQ4, while COM2 and COM4 share IRQ3.
- ; BUT, only one device on a given IRQ line can be active at a time. So it is
- ; sufficient for UUPC to hook whatever IRQ INT its modem is on as long as it
- ; unhooks it when it is done with that COM port. COMM cannot be an installed
- ; device driver since it must go away when UUPC is done so that other devices
- ; on the same INTs will come back to life. Also, it is OK to have a static
- ; CURRENT_AREA containing the current area that UUPC is using.
-
- ; Note about using the NS16550A UART chip's FIFOs. These are operated as
- ; silos. In other words when an interrupt happens because the receive(send)
- ; FIFO is nearly full(empty), as many bytes as possible are transferred and
- ; the interrupt dismissed. Thus, the interrupt load is lowered.
-
-
- ; Concerning the way the comm line is brought up.
- ; There are two basic cases, the Direct ('D') connection and the Modem ('M')
- ; connection. For either UUPC calls dtr_on_com() to bring up the line. This
- ; causes Data Terminal Ready (DTR) and Request To Send (RTS) to be set. Note
- ; this is OK for a simple 3-wire link but may be REQUIRED for a COM port
- ; connected to an external modem.
-
- ; The difference between a D connection and an M connection is
- ; whether or not the PC can expect any signals back from the modem. If
- ; there is a simple 3-wire link, Data Set Ready will be floating.
- ; (Actually, some wise people jumper Data Terminal Read back to Data
- ; Set Ready so the PC sees its own DTR appear as DSR.) UUPC should be
- ; able to handle the simplest cable as a design feature. So both D and
- ; M connections send out DTR and RTS, but only the M connection expects
- ; a modem to respond.
-
- ; Then, if it is full modem connection (M), we wait for a few
- ; seconds hoping that both Data Set Ready (DSR) and Clear To Send (CTS)
- ; will come up. If they don't, the associated counters are incremented
- ; for subsequent printing in the error log. Note that no error is
- ; reported from COMM to UUPC at this point, although this would be a
- ; good idea. COMMFIFO.ASM forces the connection to be a D type and lets
- ; UUPC storm ahead with its output trying to
- ; establish a link, but the output is never sent due to one of the
- ; control signals being false. UUPC could check the modem status using
- ; a call which has been installed just for this purpose.
-
- ; Note, if you are going to connect your PC running UUPC to,
- ; say, a mainframe and you need hardware flow control (i.e., RTS-CTS
- ; handshaking), use a Modem connection. Using a simple 3-wire cable
- ; forbids hardware flow control and UUPC must be instructed to use a
- ; Direct connection. Refer to comments in the SYSTEMS file on how to
- ; make this selection.
-
- ; References used in designing the revisions to COMM.ASM:
- ; 1. The UNIX fas.c Driver code.
- ; 2. SLIP8250.ASM from the Clarkson driver set.
- ; 3. NS16550A data sheet and AN-491 from National Semiconductor.
- ; 4. Bell System Data Communications, Technical Reference for
- ; Data Set 103A, Interface Specification, February, 1967
- ; 5. Network mail regarding V.24
- ; 6. Joe Doupnik
- PAGE;
- ;
- ; void far select_port(int)
- ; Arg is 1..4 and specifies which COM port is referenced by
- ; all other calls in this package.
- ;
- _select_port PROC FAR
- push bp
- mov bp,sp
- mov AX,[bp+6] ; get aguement
- CMP AL,1 ; Port 1?
- JE SP1 ; Yes
- CMP AL,2 ; Port 2?
- JE SP2 ; Yes
- CMP AL,3 ; Port 3?
- JE SP3 ; Yes
- CMP AL,4 ; Port 4?
- JE SP4 ; Yes
- INT 20H ; N.O.T.A. ????? Halt for debugging!
- ; Assume port 1 if continued
- SP1: MOV AX,OFFSET DGROUP:AREA1 ; SELECT COM1 DATA AREA
- JMP SHORT SPX ; CONTINUE
- SP2: MOV AX,OFFSET DGROUP:AREA2 ; SELECT COM2 DATA AREA
- JMP SHORT SPX ; CONTINUE
- SP3: MOV AX,OFFSET DGROUP:AREA3 ; SELECT COM3 DATA AREA
- JMP SHORT SPX ; CONTINUE
- SP4: MOV AX,OFFSET DGROUP:AREA4 ; SELECT COM4 DATA AREA
- ;Fall into SPX
- SPX: MOV CURRENT_AREA,AX ; SET SELECTION IN MEMORY
- mov sp,bp
- pop bp
- RET
- _select_port ENDP
- PAGE;
- ;
- ; void far save_com(void)
- ; Save the interrupt vector of the selected COM port.
- ; N.B. save_com() and restore_com() call MUST be properly nested
- ;
- _save_com PROC FAR
- push bp
- mov bp,sp
- PUSH SI
- PUSH ES ; SAVE EXTRA SEGMENT
- MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
- MOV AREA1.INT_HNDLR,OFFSET INT_HNDLR1
- MOV AREA2.INT_HNDLR,OFFSET INT_HNDLR2
- MOV AREA3.INT_HNDLR,OFFSET INT_HNDLR3
- MOV AREA4.INT_HNDLR,OFFSET INT_HNDLR4
-
- ; Save old interrupt vector
- 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
- POP ES ; RESTORE ES
- POP SI
- mov sp,bp
- pop bp
- RET ; DONE
- _save_com ENDP
- PAGE;
- ;
- ; int far install_com(void)
- ;
- ; Install the selected COM port.
- ; Returns: 0: Failure
- ; 1: Success
- ;
- ; SET UART PORTS FROM RS-232 BASE IN ROM BIOS DATA AREA
- ; INITIALIZE PORT CONSTANTS AND ERROR COUNTS
- ;
- ; Assign blocks of memory for transmit and receive buffers
- ;
- _install_com PROC FAR
- push bp
- mov bp,sp
- PUSHF ; Save caller's interrupt state
- PUSH SI
- PUSH DI
- PUSH ES
- MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
- CMP INSTALLED[SI],1 ; Is port installed on this machine?
- JNE INST1 ; NO, CONTINUE
- JMP INST9 ; ELSE JUMP IF ALREADY INSTALLED
-
- ; Assign memory for transmit and receive buffers
-
- INST1: MOV BX,S_SIZE ; Send buffer size
- ADD BX,0FH ; Round up
- SHR BX,1 ; Must run on an XT
- SHR BX,1
- SHR BX,1
- SHR BX,1 ; Now have number of paragraphs
- MOV AX,4800H ; Allocate memory
- INT DOS
- JC INSTFAIL ; Give fail return
- MOV WORD PTR TBuff[SI],0 ; Save in private block for this port
- MOV WORD PTR TBuff[SI+2],AX
-
- MOV BX,R_SIZE ; Receive buffer size
- ADD BX,0FH ; Round up
- SHR BX,1 ; Must run on an XT
- SHR BX,1
- SHR BX,1
- SHR BX,1 ; Now have number of paragraphs
- MOV AX,4800H ; Allocate memory
- INT DOS
- JNC INSTSUCC ; Success --> Continue
-
- ; Unhand the send buffer assigned above
-
- MOV AX,WORD PTR TBuff+2[SI] ; Transmit buffer paragraph
- MOV ES,AX ; Honest. That's where the arg goes.
- MOV AX,4900H ; Release memory
- INT DOS
- ; Ignore error
- ; Fall into INSTFAIL
-
- INSTFAIL:
- JMP INST666 ; Failure --> Give failed response
-
- INSTSUCC:
- MOV WORD PTR RBuff[SI],0 ; Save in private block for this port
- MOV WORD PTR RBuff[SI+2],AX
-
- IFDEF DEBUG
- PUSH DI
- CLD ; Go up in memory
- XOR AX,AX ; A zero to store
- LES DI,TBuff[SI] ; Transmit buffer location
- MOV CX,S_SIZE ; Size of buffer
- REP STOSB ; Clear entire buffer
- LES DI,RBuff[SI] ; Receive buffer location
- MOV CX,R_SIZE ; Size of buffer
- REP STOSB ; Clear entire buffer
- POP DI
- ENDIF
- PAGE;
-
- ; CLEAR ERROR COUNTS
- CLI ; Stray interrupts cause havoc
- 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
-
- MOV BX,RBDA ; ROM BIOS DATA AREA
- MOV ES,BX ; TO ES
- ASSUME ES:RBDA
-
- ; Map port number (COMx) into IO Address using the RS232_Base[x] table in
- ; the BIOS data area. If any of the ports is missing there should be a
- ; zero in the table for this COM port. BIOS startup routines pack the table
- ; so that if you have a COM4 but no COM3, 2E8 will be found in 40:4 and 0
- ; will be in 40:6.
-
- ; N.B. The exact IO address in 40:x is irrelevant and may well be something
- ; other than the "standard" values if specially designed hardware is used.
- ; To minimize flack, we will use the standard value if the slot in the table
- ; is 0. The bad side effect of this is that (in the standard losing case of
- ; a COM4 but no COM3) both COM3 and COM4 will reference the hardware at 2E8.
-
- CMP PORT[SI],1 ; PORT 1?
- JE INST3F8 ; Yes
- CMP PORT[SI],2 ; PORT 2?
- JE INST2F8 ; Yes
- CMP PORT[SI],3 ; PORT 3?
- JE INST3E8 ; Yes
- CMP PORT[SI],4 ; PORT 4?
- JE INST2E8 ; Yes
- INT 20H ; NOTA. (Caller is screwed up badly)
-
- INST3F8:MOV AX,3F8H ; Standard COM1 location
- CMP RS232_BASE+0,0000H ; We have information?
- JE INST2 ; No --> Use default
- MOV AX,RS232_BASE+0 ; Yes --> Use provided info
- JMP SHORT INST2 ; CONTINUE
-
- INST2F8:MOV AX,2F8H ; Standard COM2 location
- CMP RS232_BASE+2,0000H ; We have information?
- JE INST2 ; No --> Use default
- MOV AX,RS232_BASE+2 ; Yes --> Use provided info
- JMP SHORT INST2 ; CONTINUE
-
- INST3E8:MOV AX,3E8H ; Standard COM3 location
- CMP RS232_BASE+4,0000H ; We have information?
- JE INST2 ; No --> Use default
- MOV AX,RS232_BASE+4 ; Yes --> Use provided info
- JMP SHORT INST2 ; CONTINUE
-
- INST2E8:MOV AX,2E8H ; Standard COM4 location
- CMP RS232_BASE+6,0000H ; We have information?
- JE INST2 ; No --> Use default
- MOV AX,RS232_BASE+6 ; Yes --> Use provided info
- ; Fall into INST2
-
-
- ; Now we have an IO address for the COMx that we want to use. If it is
- ; anywhere in RS232_Base, we know that it has been check and is OK to use.
- ; So, even if my 2E8 (COM4) appears in 40:6 (normally for COM3), I can use
- ; it.
-
- INST2: CMP AX,RS232_BASE ; INSTALLED?
- JE INST2A ; JUMP IF SO
- CMP AX,RS232_BASE+2 ; INSTALLED?
- JE INST2A ; JUMP IF SO
- CMP AX,RS232_BASE+4 ; INSTALLED?
- JE INST2A ; JUMP IF SO
- CMP AX,RS232_BASE+6 ; INSTALLED?
- JNE INST666 ; JUMP IF NOT
- ; Fall into INST2A
-
- 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
- MOV DX,FCR[SI] ; Get FIFO Control Register
- MOV AL,FIFO_INIT
- OUT DX,AL ; Try to initialize the FIFO
- CALL SPINLOOP ; Permit I/O bus to settle
- MOV DX,IIR[SI] ; Get interrupt ID register
- IN AL,DX ; See how the UART responded
- AND AL,FIFO_ENABLED ; Keep only these bits
- MOV CX,1 ; Assume chunk size of 1 for 8250 case
- CMP AL,FIFO_ENABLED ; See if 16550A
- JNE INST4 ; Jump if not
- MOV CX,FIFO_LEN
- INST4: MOV UART_SILO_LEN[SI],CL ; Save chunk size for XMIT side only
- MOV AL,FIFO_CLEAR
- OUT DX,AL
-
- MOV AH,25H ; SET INTERRUPT VECTOR CONTENTS
- MOV AL,INT_COM[SI] ; INTERRUPT NUMBER
- MOV DX,INT_HNDLR[SI] ; OUR INTERRUPT HANDLER [WWP]
- PUSH DS ; SAVE DATA SEGMENT
- PUSH CS ; COPY CS
- POP DS ; TO DS
- INT DOS ; DOS FUNCTION
- POP DS ; RECOVER DATA SEGMENT
-
- ; PORT INSTALLED
- INST9: MOV AX,1
- JMP SHORT INSTX
-
- ; PORT NOT INSTALLED
- INST666:MOV AX,0
- ;Fall into INSTX
-
- ; Common exit
- INSTX: MOV INSTALLED[SI],AL ; Indicate whether installed or not
- IFDEF DEBUG
- MOV DX,ST8250
- CMP UART_SILO_LEN[SI],1
- JE INSTXX
- MOV DX,ST16550
- INSTXX: MOV AH,9
- INT DOS ; Announce UART type
- MOV DX,STUART
- INT DOS
- ENDIF
- POP ES
- POP DI
- POP SI
- POPF ; Restore caller's interrupt state
- mov sp,bp
- pop bp
- RET
- _install_com ENDP
- PAGE;
- ;
- ; void far restore_com(void)
- ; Restore original interrupt vector and release storage
- ;
- _restore_com PROC FAR
- push bp
- mov bp,sp
- PUSHF
- PUSH SI
- PUSH ES
- MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
- CLI
- 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 our data segment
-
- MOV AX,WORD PTR TBuff+2[SI] ; Transmit buffer paragraph
- MOV ES,AX ; Honest. That's where the arg goes.
- MOV AX,4900H ; Release memory
- INT DOS
- ; Ignore error
- MOV AX,WORD PTR RBuff+2[SI] ; Receive buffer paragraph
- MOV ES,AX
- MOV AX,4900H
- INT DOS
- ; Ignore error
- POP ES
- POP SI
- POPF
- mov sp,bp
- pop bp
- RET
- _restore_com ENDP
- PAGE;
- ;
- ; void far open_com(int Baud, char Conn, char Parity, char Stops, char Flow);
- ;
- ; CLEAR BUFFERS
- ; RE-INITIALIZE THE UART
- ; ENABLE INTERRUPTS ON THE 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
- PUSHF
- PUSH SI
- MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
- CLI ; INTERRUPTS OFF
- TEST INSTALLED[SI],1 ; Port installed?
- JNZ OPEN1 ; Yes --> Proceed
- JMP OPENX ; No --> Get out
-
- OPEN1: 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
- MOV URGENT_SEND[SI],0 ; No (high priority) flow ctl
- MOV SEND_OK[SI],0 ; DTR&CTS are not on yet
-
- ; 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
-
- ;
- ; RESET THE UART
- MOV DX,MCR[SI] ; Modem Control Register
- IN AL,DX ; Get current settings
- AND AL,0FEH ; Clr RTS, OUT1, OUT2 & LOOPBACK, but
- OUT DX,AL ; Not DTR (No hangup during autobaud)
- MOV DX,MSR[SI] ; Modem Status Register
- IN AL,DX ; Get current DSR and CTS states.
- AND AL,30H ; Init PREVIOUS STATE FLOPS to current
- OUT DX,AL ; state and clear Loopback, etc.
- IN AL,DX ; Re-read to get delta bits & clr int
- AND AL,30H ; Leave the two critical bits
- CMP AL,30H ; Both on?
- JNE OPEN2 ; No. Leave SEND_OK zero.
- MOV SEND_OK[SI],1 ; Allow TXI to send out data
- OPEN2: MOV DX,FCR[SI] ; I/O Address of FIFO control register
- MOV AL,FIFO_CLEAR ; Disable FIFOs
- OUT DX,AL ; Non-16550A chips will ignore this
- MOV DX,LSR[SI] ; RESET LINE STATUS CONDITION
- IN AL,DX
- MOV DX,DATREG[SI] ; RESET RECEIVE DATA CONDITION
- IN AL,DX
- MOV DX,MSR[SI] ; RESET MODEM DELTAS AND CONDITIONS
- IN AL,DX
-
- CALL Set_Baud ; Set the baud rate from arg
- PAGE;
- ; SET PARITY AND NUMBER OF STOP BITS
- MOV AL,03H ; Default: NO PARITY + 8 bits data
-
- CMP PARITY[SI],'O' ; ODD PARITY REQUESTED?
- JNE P1 ; JUMP IF NOT
- MOV AL,0AH ; SELECT ODD PARITY + 7 bits data
- JMP SHORT P4 ; CONTINUE
- ;
- P1: CMP PARITY[SI],'E' ; EVEN PARITY REQUESTED?
- JNE P2 ; JUMP IF NOT
- MOV AL,1AH ; SELECT EVEN PARITY + 7 bits data
- JMP SHORT P4 ; CONTINUE
- ;
- P2: CMP PARITY[SI],'M' ; MARK PARITY REQUESTED?
- JNE P3 ; JUMP IF NOT
- MOV AL,2AH ; SELECT MARK PARITY + 7 bits data
- JMP SHORT P4
-
- P3: CMP PARITY[SI],'S' ; SPACE parity requested?
- JNE P4 ; No. Must be 'N' (NONE)
- MOV AL,3AH ; Select SPACE PARITY + 7 bits data
-
- P4: 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 UART PARITY MODE AND DLAB=0
-
- ; Initialize the FIFOs
- MOV DX,FCR[SI] ; I/O Address of FIFO control register
- MOV AL,FIFO_INIT ; Clear FIFOs, set size, enable FIFOs
- OUT DX,AL ; Non-16550A chips will ignore this
-
- ; ENABLE INTERRUPTS ON 8259 AND UART
- IN AL,INTA01 ; SET ENABLE BIT ON 8259
- AND AL,NIRQ[SI]
- OUT INTA01,AL
- MOV DX,IER[SI] ; Interrupt enable register
- MOV AL,0DH ; Line & Modem status, recv [GT]
- OUT DX,AL ; Enable those interrupts
-
- OPENX: POP SI
- POPF ; Restore interrupt state
- mov sp,bp
- pop bp
- RET ; DONE
- _open_com ENDP
- PAGE;
- ;
- ; void far ioctl_com(int Flags, int Arg1, ...)
- ; Flags have bits saying what to do or change (IGNORED TODAY)
- ; Arg1, ... are the new values
- ;
- _ioctl_com PROC FAR
- PUSH BP
- MOV BP,SP
- PUSHF ; Save interrupt context
- PUSH SI
- MOV SI,CURRENT_AREA ; Pointer to COMi private area
- CLI ; Prevent surprises
- TEST INSTALLED[SI],1
- JE IOCTLX ; No good. Just return.
- MOV AX,[BP+6] ; Flags
- ; Check bits here...
- MOV AX,[BP+8] ; Line speed
- MOV BAUD_RATE[SI],AX ; Save in parameter block
- CALL Set_Baud ; Set the baud rate in UART
- IOCTLX: POP SI
- POPF ; Restore interrupt state
- MOV SP,BP
- POP BP
- RET
- _ioctl_com ENDP
- PAGE;
- ; ioctl-called routines (internal) ...
-
- ; SI: COMi private block
- ; CALL Set_Baud
- ; Returns: (nothing)
- ; Clobber: AX, BX, DX
-
- Set_Baud PROC NEAR
- MOV AX,50
- MUL DIV50 ; Could be different on a PCJr!
- DIV BAUD_RATE[SI] ; Get right number for the UART
- MOV BX,AX ; Save it
- MOV DX,LCR[SI] ; Line Control Register
- IN AL,DX ; Get current size, stops, parity,...
- PUSH AX
- OR AL,80H ; DLAB bit
- OUT DX,AL ; Talk to the baud rate regs now
- MOV DX,WORD PTR DLL[SI] ; Least significant byte
- MOV AL,BL ; New value
- OUT DX,AL ; To UART
- MOV DX,WORD PTR DLH[SI] ; Most signifiant byte
- MOV AL,BH ; New value
- OUT DX,AL
- MOV DX,LCR[SI] ; Line Control Register
- POP AX
- OUT DX,AL ; Turn off DLAB, keep saved settings
- RET
- Set_Baud ENDP
- PAGE;
- ;
- ; void far close_com(void)
- ; Turn off interrupts from the COM port
- ;
- _close_com PROC FAR
- push bp
- mov bp,sp
- PUSHF
- PUSH SI
- MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
- TEST INSTALLED[SI],1 ; PORT INSTALLED?
- JZ CCX ; ABORT IF NOT
-
- ; TURN OFF UART and clear FIFOs in NS16550A
- CLI
- MOV DX,IER[SI]
- MOV AL,0
- OUT DX,AL ; No interrupts right now, please
- MOV DX,FCR[SI] ; FIFO Control Register
- MOV AL,FIFO_CLEAR ; Disable FIFOs
- OUT DX,AL
- MOV DX,MCR[SI] ; Modem control register
- XOR AL,AL ; OUT2 is IRQ enable on some machines,
- OUT DX,AL ; So, clear RTS, OUT1, OUT2, LOOPBACK
-
- ; TURN OFF 8259
- MOV DX,INTA01
- IN AL,DX
- OR AL,IRQ[SI]
- JMP $+2 ; DELAY FOR AT
- JMP $+2 ; DELAY FOR AT
- JMP $+2 ; DELAY FOR AT
- OUT DX,AL
-
- CCX: POP SI
- POPF ; Restore interrupt state
- mov sp,bp
- pop bp
- RET
- _close_com ENDP
- PAGE;
- ;
- ; void far dtr_off(void)
- ; Tells modem we are done. Remote end should hang up also.
- ;
- _dtr_off 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 DFX ; ABORT IF NOT
-
- MOV DX,MCR[SI] ; Modem Control Register
- IN AL,DX ; Get current state
- PUSH AX ; Save MCR
- MOV AL,08H ; DTR off, RTS off, OUT2 on
- OUT DX,AL
- POP AX ; Get previous state
- AND AL,1 ; Just look at the DTR bit
- JE DFX ; Not on. Don't clr. Don't wait.
- MOV AX,50 ; 50/100 of second
- IFNDEF UUPC
- CALL WaitN ; V.24 says it must be low >1/2 sec
- ENDIF
- DFX: POP SI
- mov sp,bp
- pop bp
- RET
- _dtr_off ENDP
- PAGE;
- ;
- ; void far dtr_on(void) Tell modem we can take traffic
- ;
- _dtr_on 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 DTRONF ; Suppress output if not
-
- ; Tell modem we are ready and want to send with line idle
-
- MOV DX,MCR[SI] ; Modem Control Register
- MOV AL,00001011B ; OUT 2, RTS, DTR
- OUT DX,AL
- CMP CONNECTION[SI],'D' ; Direct connection (no DSR,CTS)?
- JNE DTRON0 ; Go wait for DSR, CTS
- MOV SEND_OK[SI],1 ; Set output enable flag
- JMP SHORT DTRONS ; Give success return
-
- ; Wait for awhile to give the modem time to respond
-
- DTRON0: MOV AH,2CH ; Get time (H:M:S:H to CH:CL:DH:DL)
- INT 21H
- MOV BX,DX ; Save seconds&hundreths
- ADD BH,06 ; Allow a few seconds
- CMP BH,60 ; Wrap around check
- JL DTRON1 ; No wrap
- SUB BH,60
- DTRON1: CMP SEND_OK[SI],1 ; Did the modem come up?
- JE DTRONS ; Yes. Both DSR and CTS are true.
- INT 21H ; Get the time again
- CMP DX,BX ; Current time is passed the deadline?
- JB DTRON1 ; No, keep checking 'til time runs out
-
- ; Modem failed to come up. Bump counts that tell why.
- MOV DX,MSR[SI] ; MODEM STATUS REGISTER
- IN AL,DX ; GET MODEM STATUS
- TEST AL,20H ; DATA SET READY?
- JNZ DTRON6 ; Yup.
- INC WORD PTR EDSR[SI] ; BUMP ERROR COUNT
- DTRON6: TEST AL,10H ; Clear To Send?
- JNZ DTRONF ; That's OK.
- INC WORD PTR ECTS[SI] ; BUMP ERROR COUNT - WE TIMED OUT
- ; Fall into DTRONF
- PAGE;
- ; Failure return
-
- DTRONF: MOV SEND_OK[SI],1 ; Make believe DSR & CTS are up!!!
- MOV CONNECTION[SI],'D' ; Switch to DIR connection (MSTATINT)
- JMP SHORT DTRONX
-
-
- ; Successful return
-
- DTRONS: ; SEND_OK is on. Setting it again could confuse interrupt level
- ; Fall into DTRONX
-
- DTRONX: MOV AX,200H ; 2 Seconds
- IFNDEF UUPC
- CALL WaitN ; V.24 says 2 sec hi before data
- ENDIF
- POP SI
- mov sp,bp
- pop bp
- RET
- _dtr_on ENDP
- PAGE;
- ;
- ; Wait for specified time using the 18.2 ticks/second clock
- ;
- ; Call: AX has seconds,hundreths
- ; CALL WaitN
- ; Return: At least the requested time has passed
- ;
-
- WaitN PROC NEAR
- PUSH BP
- MOV BP,SP
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH AX ; Save a copy of the arg
- MOV AH,2CH ; Get time (H:M:S:H to CH:CL:DH:DL)
- INT DOS
- POP BX ; Recover S:H arg
- ADD BX,DX ; Determine deadline
- CMP BL,100 ; Wrap around?
- JL WaitN1 ; No
- SUB BL,100 ; Yes. Subtract 100 hundreths
- INC BH ; And add a second
- WaitN1: CMP BH,60 ; Wrap around check
- JL WaitN2 ; No wrap
- SUB BH,60 ; Forget about Days and Hours
- WaitN2: INT DOS ; Get the time again
- CMP DX,BX ; Is current time after the deadline?
- JB WaitN2 ; No, keep checking 'til time runs out
- POP DX
- POP CX
- POP BX
- POP CX
- MOV SP,BP
- POP BP
- RET
- WaitN ENDP
- PAGE;
- ;
- ; unsigned long r_count(void)
- ; Value is really two uints: Buffer size in high half, count in low.
- ; Count returned is <= number of chars waiting to be read.
- ; (More may come in after you asked.)
- ;
- _r_count PROC FAR
- push bp
- mov bp,sp
- PUSH SI
- MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
- XOR AX,AX ; Say nothing available if not inst'd
- MOV DX,R_SIZE ; Size of entire receive buffer
- TEST INSTALLED[SI],1 ; PORT INSTALLED?
- JZ RCX ; ABORT IF NOT
- MOV AX,SIZE_RDATA[SI] ; GET NUMBER OF BYTES USED
- RCX: POP SI
- mov sp,bp
- pop bp
- RET
- _r_count ENDP
- PAGE;
- ;
- ; char far receive_com(void)
- ; Returns AX: -1 if port not installed or no characters available
- ; or AX: the next character with parity stipped if not in P mode
- ;
- _receive_com PROC FAR
- push bp
- mov bp,sp
- PUSHF ; Save interrupt state
- PUSH SI
- PUSH ES
- MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
- mov ax,-1 ; -1 if bad call
- TEST INSTALLED[SI],1 ; PORT INSTALLED?
- JZ RECVX ; ABORT IF NOT
- CLI
- CMP SIZE_RDATA[SI],0 ; ANY CHARACTERS?
- JE RECVX ; Return -1 in AX
-
- mov ah,0 ; good call
- LES BX,RBuff[SI] ; Location of receive buffer
- ADD BX,START_RDATA[SI] ; GET POINTER TO OLDEST CHAR
- MOV AL,ES:[BX] ; Get character from buffer
- CMP PARITY[SI],'N' ; ARE WE RUNNING WITH NO PARITY? LBA
- JE RECV1 ; IF SO, DON'T STRIP HIGH BIT LBA
- AND AL,7FH ; STRIP PARITY BIT
- RECV1: MOV BX,START_RDATA[SI] ; Get the start index again
- INC BX ; BUMP START_RDATA
- AND BX,R_SIZE-1 ; Ring the pointer
- 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 RECVX ; DO NOTHING IF DISABLED
- CMP HOST_OFF[SI],1 ; HOST TURNED OFF?
- JNE RECVX ; JUMP IF NOT
- CMP SIZE_RDATA[SI],R_SIZE/16; RECEIVE BUFFER NEARLY EMPTY?
- JGE RECVX ; 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
- RECV3: CLI ; TURN OFF INTERRUPTS
- CMP URGENT_SEND[SI],1 ; Previous send still in progress?
- JNE RECV4 ; No. There is space now.
- STI ; Yes. Wait for interrupt to take it.
- JMP SHORT RECV3 ; Loop 'til it's gone
- RECV4: CALL SENDII ; SEND IMMEDIATELY INTERNAL
- POP AX ; RESTORE RECEIVED CHAR
-
- RECVX: POP ES
- POP SI
- POPF ; Restore interrupt state
- mov sp,bp
- pop bp
- RET
- _receive_com ENDP
- PAGE;
- ;
- ; unsigned long s_count(void)
- ; Value is really two uints: Buffer size in high half (returned in DX).
- ; Free space count in low (returned in AX).
- ; Count returned is <= number of chars which can be sent without blocking.
- ; (More may become available after you asked.)
- ;
- ; N.B. The free space might be negative (-1) if the buffer was full and then
- ; the program called SENDI or RXI required sending a control-S to squelch
- ; the remote sender. Return 0 in this case.
- ;
- _s_count PROC FAR
- push bp
- mov bp,sp
- PUSH SI
- MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
- MOV AX,0 ; NO SPACE LEFT IF NOT INSTALLED
- mov dx,S_SIZE-1 ; Leave 1 byte for a SENDII call
- TEST INSTALLED[SI],1 ; PORT INSTALLED?
- JZ SCX ; ABORT IF NOT
- MOV AX,S_SIZE-1 ; Size, keeping one aside for SENDII
- SUB AX,SIZE_TDATA[SI] ; Minus number in use right now
- JGE SCX ; Avoid returning negative number
- XOR AX,AX ; Return 0
- SCX: POP SI
- mov sp,bp
- pop bp
- RET
- _s_count ENDP
- PAGE;
- ;
- ; void far send_com(char)
- ; Send a character to the selected port
- ;
- _send_com PROC FAR
- push bp
- mov bp,sp
- PUSHF ; Save interrupt state
- PUSH SI
- PUSH ES
- MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
- TEST INSTALLED[SI],1 ; PORT INSTALLED?
- JZ SENDX ; ABORT IF NOT
-
- SEND1: CMP SIZE_TDATA[SI],S_SIZE-1 ; BUFFER FULL? (Leave room for SENDII)
- JGE SEND1 ; Wait for interrupts to empty buffer
- CLI
- LES BX,TBuff[SI] ; Pointer to buffer
- ADD BX,END_TDATA[SI] ; ES:BX points to free space
- MOV AL,[BP+6] ; Character to send
- MOV ES:[BX],AL ; Move character to buffer
- MOV BX,END_TDATA[SI] ; Get index of end
- INC BX ; INCREMENT END_TDATA
- AND BX,S_SIZE-1 ; Ring the pointer
- MOV END_TDATA[SI],BX ; SAVE NEW END_TDATA
- INC SIZE_TDATA[SI] ; ONE MORE CHARACTER IN X-MIT BUFFER
-
- TEST PC_OFF[SI],1 ; Were we stopped by a ^S from host?
- JNZ SENDX ; Yes. Don't enable interrupts yet.
- CALL CHROUT ; Put a character out to the UART
- SENDX: POP ES
- POP SI
- POPF ; Restore interrupt state
- mov sp,bp
- pop bp
- RET
- _send_com ENDP
- PAGE;
- ;
- ; void far sendi_com(char)
- ; Send a character immediately by placing it at the head of the queue
- ;
- _sendi_com PROC FAR
- push bp
- mov bp,sp
- PUSHF ; Save interrupt state
- PUSH SI
- mov al,[bp+6]
- MOV SI,CURRENT_AREA ; SI POINTS TO DATA AREA
- TEST INSTALLED[SI],1 ; PORT INSTALLED?
- JZ SENDIX ; Return if not
-
- SENDI3: CLI ; TURN OFF INTERRUPTS
- CMP URGENT_SEND[SI],1 ; Previous send still in progress?
- JNE SENDI4 ; No. There is space now.
- STI ; Yes. Wait for interrupt to take it.
- JMP SHORT SENDI3 ; Loop 'til it's gone
-
- SENDI4: CALL SENDII ; CALL INTERNAL SEND IMMEDIATE
-
- SENDIX: POP SI
- POPF ; Restore interrupt state
- mov sp,bp
- pop bp
- RET
- _sendi_com ENDP
- PAGE;
- ; SENDII(AL, SI) [internal routine]
- ; Put char at head of output queue so it will go out next
- ; Called from process level and (receive) interrupt level
- ; DEPENDS ON CALLER TO KEEP INTERRUPTS CLEARED AND SET SI
- ;
- SENDII PROC NEAR
- PUSH BX
- PUSH DX
- PUSH ES
- LES BX,TBuff[SI] ; Location of transmit buffer
- CMP SIZE_TDATA[SI],S_SIZE ; BUFFER FULL?
- JB SENDII2 ; JUMP IF NOT
- INC WORD PTR EOVFLOW[SI] ; BUMP ERROR COUNT (Can this happen?)
- ADD BX,START_TDATA[SI] ; ES:BX point to 1st chr in buffer
- MOV ES:[BX],AL ; Overwrite 1st character
- JMP SHORT SENDII4
-
- SENDII2:MOV DX,START_TDATA[SI] ; DX is index of 1st char
- DEC DX ; Back it up
- AND DX,S_SIZE-1 ; Ring it
- MOV START_TDATA[SI],DX ; Save new value
- ADD BX,DX ; Address within buffer
- MOV ES:[BX],AL ; Move character to buffer
- INC SIZE_TDATA[SI] ; ONE MORE CHARACTER IN X-MIT BUFFER
- MOV URGENT_SEND[SI],1 ; Flag high priority message
- ; No check for PC_OFF here. Flow control ALWAYS gets sent!
- SENDII4:CALL CHROUT ; Output a chr if possible
- SENDIIX:POP ES
- POP DX
- POP BX
- RET
- SENDII ENDP
-
- ;*---------------------------------------------------------------------*
- ;* s p i n l o o p *
- ;* *
- ;* Waste time to allow slow UART chips to catch up *
- ;*---------------------------------------------------------------------*
-
- SPINLOOP PROC NEAR
- PUSH CX
- MOV CX,100
- WASTERS:
- CALL CRET ; Better time waster than a 1-byte NOP
- LOOP WASTERS
- POP CX
- CRET: RET
- SPINLOOP ENDP
-
- ; CHROUT() Process level routine to remove a chr from the buffer,
- ; give it to the UART and adjust the pointer and count.
- ; If interrupts are disabled at entry, nothing is done.
- ; If a character is successfully output, Tx ints are enabled.
- ; Requires: SI pointing at the appropriate data area
- ; Clobbers: AX, BX, DX, ES
- ; Must preserve: CX in case there is a count there
-
- CHROUT PROC NEAR
- MOV DX,IER[SI] ; Interrupt Enable Register
- IN AL,DX
- TEST AL,2 ; Tx interrupts enabled?
- JNZ CHROUX ; Jump if not
- CMP SEND_OK[SI],1 ; See if Data Set Ready & CTS are on
- JNE CHROUX ; No. Still can't enable TX ints
- CALL TX_CHR ; Actually transmit the chr
- MOV DX,IER[SI] ; Interrupt Enable Register
- MOV AL,0FH ; Rx, Tx, Line & Modem enable bits
- OUT DX,AL ; Enable those interrupts
- CHROUX: RET
- CHROUT ENDP
- PAGE;
-
- IFNDEF UUPC
- ;
- ; void far send_local(char);
- ; Simulate a loopback by placing characters sent in recv buffer
- ;
- _send_local PROC FAR
- push bp
- mov bp,sp
- PUSHF
- PUSH SI
- PUSH ES
- 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 SL3 ; SKIP IF ROOM
- INC WORD PTR EOVFLOW[SI] ; BUMP OVERFLOW COUNT
- JMP SHORT SLX ; PUNT
-
- SL3: LES BX,RBuff[SI] ; Receive buffer location
- ADD BX,END_RDATA[SI] ; ES:BX POINTS TO FREE SPACE
- MOV AL,[BP+6] ; Get the byte to send
- MOV ES:[BX],AL ; Put into buffer
- MOV BX,END_RDATA[SI] ; Get the end pointer
- INC BX ; INCREMENT END_RDATA POINTER
- AND BX,R_SIZE-1 ; Ring the pointer
- MOV END_RDATA[SI],BX ; SAVE VALUE
- INC SIZE_RDATA[SI] ; GOT ONE MORE CHARACTER
-
- SLX: POP ES
- POP SI
- POPF ; Restore interrupt state
- mov sp,bp
- pop bp
- RET ; DONE
- _send_local ENDP
- ENDIF
- PAGE;
- ;
- ; void far break_com(void) Send a BREAK out to alert the remote end
- ;
- _break_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 BREAKX ; ABORT IF NOT
-
- MOV DX,LCR[SI] ; LINE CONTROL REGISTER
- IN AL,DX ; GET CURRENT SETTING
- OR AL,40H ; TURN ON BREAK BIT
- OUT DX,AL ; SET IT ON THE UART
- MOV AX,25 ; 25/100 of a second
- CALL WaitN
- MOV DX,LCR[SI] ; LINE CONTROL REGISTER
- IN AL,DX ; GET CURRENT SETTING
- AND AL,0BFH ; TURN OFF BREAK BIT
- OUT DX,AL ; RESTORE LINE CONTROL REGISTER
- BREAKX: POP SI
- mov sp,bp
- pop bp
- RET
- _break_com ENDP
- PAGE;
- ;
- ; ERROR_STRUCT far *com_errors(void)
- ; Returns a pointer to the table of error counters
- ;
- _com_errors PROC FAR
- push bp
- mov bp,sp
- PUSH SI
- MOV SI,CURRENT_AREA ; Point to block for selected port
- LEA AX,ERROR_BLOCK[SI] ; Offset to error counters
- MOV DX,DS ; Value is in DX:AX
- POP SI
- mov sp,bp
- pop bp
- RET
- _com_errors ENDP
-
-
-
-
-
-
- ;
- ; char far modem_status(void)
- ; Returns the modem status register in AL
- ;
- ; Bits are: 0x80: Carrier Detect
- ; 0x40: Ring Indicator
- ; 0x20: Data Set Ready
- ; 0x10: Clear To Send
- ; 0x08: Delta Carrier Detect (CD changed)
- ; 0x04: Trailing edge of RI (RI went OFF)
- ; 0x02: Delta DSR (DSR changed)
- ; 0x01: Delta CTS (CTS changed)
-
- _modem_status PROC FAR
- push bp
- mov bp,sp
- PUSH SI
- MOV SI,CURRENT_AREA ; Point to block for selected port
- MOV DX,MSR[SI] ; IO Addr of Modem Status Register
- IN AL,DX ; Get the live value
- XOR AH,AH ; Flush unwanted bits
- POP SI
- mov sp,bp
- pop bp
- RET
- _modem_status ENDP
- PAGE;
- ;
- ; INT_HNDLR1 - HANDLES INTERRUPTS GENERATED BY COM1:
- ;
- INT_HNDLR1 PROC FAR
- PUSH SI
- MOV SI,OFFSET DGROUP:AREA1 ; DATA AREA FOR COM1:
- JMP SHORT INT_COMMON ; CONTINUE
- ;
- ; INT_HNDLR2 - HANDLES INTERRUPTS GENERATED BY COM2:
- ;
- INT_HNDLR2 PROC FAR
- PUSH SI
- MOV SI,OFFSET DGROUP:AREA2 ; DATA AREA FOR COM2:
- JMP SHORT INT_COMMON ; CONTINUE
- ;
- ; INT_HNDLR3 - HANDLES INTERRUPTS GENERATED BY COM3:
- ;
- INT_HNDLR3 PROC FAR
- PUSH SI
- MOV SI,OFFSET DGROUP:AREA3 ; DATA AREA FOR COM3:
- JMP SHORT INT_COMMON ; CONTINUE
- ;
- ; INT_HNDLR4 - HANDLES INTERRUPTS GENERATED BY COM4:
- ;
- INT_HNDLR4 PROC FAR
- PUSH SI
- MOV SI,OFFSET DGROUP:AREA4 ; DATA AREA FOR COM4:
- ; Fall into INT_COMMON
- PAGE;
- ;
- ; BODY OF INTERRUPT HANDLER
- ;
- INT_COMMON: ; SI has been pushed and loaded
- PUSH AX
- PUSH BX
- PUSH CX
- PUSH DX
- PUSH DS
- PUSH ES
-
- MOV AX,DGROUP ; Offsets are relative to DGROUP [WWP]
- MOV DS,AX
-
-
- ; FIND OUT WHERE INTERRUPT CAME FROM AND JUMP TO ROUTINE TO HANDLE IT
- REPOLL: MOV DX,IIR[SI] ; Interrupt Identification Register
- IN AL,DX
- TEST AL,1 ; Check the "no interrupt present" bit
- JNZ INT_END ; ON means we are done
- MOV BL,AL ; Put where we can index by it
- AND BX,000EH ; Ignore FIFO_ENABLED bits, etc.
-
- JMP WORD PTR CS:INT_DISPATCH[BX]; Go to appropriate routine
-
- INT_DISPATCH:
- DW MSI ; 0: Modem status interrupt
- DW TXI ; 2: Transmitter interrupt
- DW RXI ; 4: Receiver interrupt
- DW LSI ; 6: Line status interrupt
- DW REPOLL ; 8: (Future use by UART makers)
- DW REPOLL ; A: (Future use by UART makers)
- DW RXI ; C: FIFO Timeout
- DW REPOLL ; E: (Future use by UART makers)
-
- INT_END: ; Now tell 8259 we handled that IRQ
-
- MOV DX,IER[SI] ; Gordon Lee's cure for dropped ints
- IN AL,DX ; Get enabled interrupts
- MOV AH,AL
- XOR AL,AL
- OUT DX,AL ; Disable UART interrupts
- MOV AL,EOI[SI] ; End of Interrupt. With input gone,
- OUT INTA00,AL ; Give EOI to 8259 Interrupt ctlr
- MOV AL,AH ; Get save interrupt enable bits
- OUT DX,AL ; Restore them, maybe causing a
- ; transition into the 8259!!!
- POP ES
- POP DS
- POP DX
- POP CX
- POP BX
- POP AX
- POP SI
- IRET
- PAGE;
- ;
- ; Line status interrupt
- ;
- LSI: MOV DX,LSR[SI] ; Line status register
- IN AL,DX ; Read line status & bump error counts
- 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: JMP REPOLL ; SEE IF ANY MORE INTERRUPTS
-
- ;
- ; Modem status interrupt
- ;
- MSI: MOV DX,MSR[SI] ; Modem Status Register
- IN AL,DX ; Read status & clear interrupt
- CMP CONNECTION[SI],'D' ; Direct connection - ignore int
- JE MSI0 ; Just noise on DSR,CTS pins
- AND AL,30H ; Expose CTS and Data Set Ready
- CMP AL,30H ; Both on?
- JE MSI0 ; Yes. Enable output at TXI
- XOR AL,AL
- JMP SHORT MSI1
-
- MSI0: MOV AL,1
- MSI1: MOV SEND_OK[SI],AL ; Put where TXI and send_com can see
- MOV DX,IER[SI] ; Let a TX int happen for thoro chks
- MOV AL,0FH ; Line & modem sts, recv, send.
- OUT DX,AL
- JMP REPOLL ; Check for other interrupts
- PAGE;
- ;
- ; Transmit interrupt
- ;
- TXI: CMP SEND_OK[SI],1 ; Harware (CTS & DSR on) OK?
- JNE TXI9 ; No. Must wait 'til cable right!
- MOV CX,1 ; Transfer count for flow ctl
- CMP URGENT_SEND[SI],1 ; Flow control character to send?
- JE TXI3 ; Yes. Always send flow control.
- CMP PC_OFF[SI],1 ; Flow control (XON/XOFF) OK?
- JE TXI9 ; Stifled & not urgent. Forget it.
-
- TXI1: MOV CL,UART_SILO_LEN[SI] ; MAX size chunk (1 for simple 8250)
- ; Too bad there is no "Tranmitter FIFO Full" indication!
- CMP SIZE_TDATA[SI],CX ; SEE IF ANY MORE DATA TO SEND
- JG TXI2 ; UART is the limit
- MOV CX,SIZE_TDATA[SI] ; Buffer space limited. Use that.
- TXI2: JCXZ TXI9 ; No data, disable TX ints
-
- TXI3: CALL TX_CHR ; Transmit a character
- LOOP TXI3 ; Keep going 'til silo is full
- MOV URGENT_SEND[SI],0 ; Tell process level we sent flow ctl
- JMP REPOLL
-
- ; IF NO DATA TO SEND, or can't send, RESET TX INTERRUPT AND RETURN
- TXI9: MOV DX,IER[SI]
- MOV AL,0DH ; Line & modem sts, recv, no send.
- OUT DX,AL
- JMP REPOLL
-
-
- ; TX_CHR Internal routine used to actually move a character from
- ; the transmit buffer to the UART and adjust pointers.
- ; Called from process and interrupt levels with interrutps
- ; enabled or disabled.
- ; Requires: SI pointing at data area for this UART
- ; Clobbers: AX, BX, DX, ES
- ; Must preserve: CX (Caller has count here).
-
- TX_CHR PROC NEAR
- LES BX,TBuff[SI] ; Pointer to buffer
- ADD BX,START_TDATA[SI] ; ES:BX points to next char to send
- MOV AL,ES:[BX] ; Get character from buffer
- MOV DX,DATREG[SI] ; I/O address of data register
- OUT DX,AL ; Output the character
- INC BX ; Bump the head pointer
- AND BX,S_SIZE-1 ; Ring it
- MOV START_TDATA[SI],BX ; Store back in private block
- DEC SIZE_TDATA[SI] ; One fewer in buffer now
- RET
- TX_CHR ENDP
- PAGE;
- ;
- ; Receive interrupt
- ;
- RXI:
- RXI0: MOV DX,LSR[SI] ; Line Status Register
- IN AL,DX ; Read it
- TEST AL,1 ; Check the RECV DATA READY bit
- JE RXIX ; No more data available
- MOV DX,DATREG[SI] ; UART DATA REGISTER
- IN AL,DX ; Get data, clear status
- CMP XON_XOFF[SI],'E' ; FLOW CONTROL ENABLED?
- JNE RXI2 ; No. Don't check for XON/XOFF
-
- ; Check each character for possible flow control (XON/XOFF)
- AND AL,7FH ; STRIP PARITY
- CMP AL,CONTROL_S ; STOP COMMAND RECEIVED?
- JNE RXI1 ; Jump if not. Might be ^Q though.
- MOV PC_OFF[SI],1 ; Stop output
- JMP SHORT RXI0 ; Don't store character
-
- RXI1: CMP AL,CONTROL_Q ; GO COMMAND RECEIVED?
- JNE RXI2 ; No. Not a flow control character
- MOV PC_OFF[SI],0 ; Enable output
- JMP SHORT RXI0 ; Don't store character
-
- ; Have a real data byte. Store if possible.
- RXI2: CMP SIZE_RDATA[SI],R_SIZE ; SEE IF ANY ROOM
- JL RXI6 ; CONTINUE IF SO
- INC WORD PTR EOVFLOW[SI] ; BUMP OVERFLOW ERROR COUNT
- JMP SHORT RXIX
-
- RXI6: LES BX,RBuff[SI] ; Receive buffer location
- ADD BX,END_RDATA[SI] ; ES:BX points to free space
- MOV ES:[BX],AL ; Put character in buffer
- INC SIZE_RDATA[SI] ; GOT ONE MORE CHARACTER
- MOV BX,END_RDATA[SI] ; Get the end index
- INC BX ; Bump it passed location just used
- AND BX,R_SIZE-1 ; Ring the pointer
- MOV END_RDATA[SI],BX ; SAVE VALUE
-
- ; See if we must tell remote host to stop outputting.
- CMP XON_XOFF[SI],'E' ; FLOW CONTROL ENABLED?
- JNE RXI0 ; No
- CMP HOST_OFF[SI],1 ; Already told remote to shut up?
- JE RXI0 ; Yes. Don't flood him with ^Ss
- CMP SIZE_RDATA[SI],R_SIZE/2 ; RECEIVE BUFFER NEARLY FULL?
- JLE RXIX ; No. No need to stifle remote end
- ; Would like to wait here for URGENT_SEND to go off if it is on.
- ; But we need to take a TX interrupt for that to happen.
- MOV AL,CONTROL_S ; TURN OFF HOST IF SO
- CALL SENDII ; SEND IMMEDIATELY INTERNAL
- MOV HOST_OFF[SI],1 ; HOST IS NOW OFF
- RXIX: JMP REPOLL
-
- INT_HNDLR4 ENDP
- INT_HNDLR3 ENDP
- INT_HNDLR2 ENDP
- INT_HNDLR1 ENDP
- COM_TEXT ENDS
- END
-