www.pudn.com > tcpipstack.rar > CASYNCMS.ASM


;CASYNCMS.ASM - for Microsoft C 
;Based on CASYNC.ASM by Curt Klinsing 
;A set of C callable functions to support 
;interrupt driven character I/O on the IBM PC. Input 
;is buffered, output is polled. 
;910609	rr	minor cleanup 
;930811 rr	conversion for microsoft C 
;931213	rr	fix various bugs (I hope) 
;940529	rr	move to 9600 
 
.286p 
.seq 
 
; ----- Equates ----------------------------------------------------- 
 
BASE		equ	03F8H	;BASE FOR SERIAL BOARD (COM1) 
IER		equ	BASE+1	;Interrup Enable Register 
LCR		equ	BASE+3	;Line control register 
MCR		equ	BASE+4	;modem control register 
LSR		equ	BASE+5	;line status register 
MSR		equ	BASE+6	;modem status register 
 
; Bits in IER 
ERBFI		equ	01H	;enable 'data-ready' interrupt bit 
 
; Bits in LSR 
THRE		equ	20H	;8250 tbe flag 
 
IntCtlr		equ	21H	;OCW 1 FOR 8259 CONTROLLER 
EnblIRQ4	equ	0EFH	;Enable COMMUNICATIONS (IRQ4) 
MaskIRQ4	equ	10H	;BIT TO DISABLE COMM INTERRUPT (IRQ4) 
 
RS8259		equ	20H	;OCW 3 FOR 8259 
RSTINT		equ	64H	;SPECIFIC EOI FOR COMM INTERRUPT 
 
DosCall		equ	21h	;INTERRUPT NUMBER FOR DOS CALL 
 
BUFSIZ		equ	512	;Max NUMBER OF CHARS (was 512) 
SetIntVect	equ	25H	;SET INTERRUPT VECTOR FUNCTION NUMBER 
 
;divisor is 48 for 2400 baud, 12 for 9600 baud, 
;	96 for 1200 baud 
 
BAUDRATEDIVISOR	equ	12 
 
; ----- Data segment ------------------------------------------------ 
 
_DATA	SEGMENT	PARA PUBLIC 'DATA' 
 
circ_buf	db	BUFSIZ DUP(?)	;ALLOW MaxIMUM BUFFERED CHARACTERS 
buf_top		equ	$ - 1		;KEEP TRACK OF THE TOP OF THE BUFFER 
 
circ_in		dw	offset circ_buf	;POINTER TO LAST CHAR. PLACED 
					;IN BUFFER 
circ_cur	dw	offset circ_buf	;POINTER TO NEXT CHAR. TO BE 
					;RETRIEVED FROM BUFFER 
circ_cnt	dw	0		;COUNT OF CHARACTERS USED IN BUFFER 
_DATA	ENDS 
 
; ----- Code segment ------------------------------------------------ 
 
_TEXT	SEGMENT PARA PUBLIC 'CODE'	 
	ASSUME	CS: _TEXT, DS: _DATA 
 
;int far inp_status( void ) 
;Returns the number of characters available in the input buffer. 
 
	PUBLIC	_inp_status 
_inp_status PROC FAR 
	push	ds 
	mov	ax,_DATA 
	mov	ds,ax 
	mov	ax,word ptr circ_cnt 
	pop	ds 
	ret 
_inp_status ENDP 
 
;void far inp_flush( void ) 
;Flush the input buffer. 
 
	PUBLIC	_inp_flush 
_inp_flush PROC FAR 
	push	ds 
	mov	ax,_DATA 
	mov	ds,ax 
	mov	bx,offset circ_buf 
	mov	word ptr circ_in,bx 
	mov	word ptr circ_cur,bx 
	xor	ax,ax 
	mov	word ptr circ_cnt,ax 
	pop	ds 
	ret 
_inp_flush ENDP 
 
;--------- Init ----------------------------------- 
;Program initialization: 
;-- Set up vector for RS232 interrupt (0CH) 
;-- Enbl IRQ4 
;-- Enbl RS232 interrupt on data ready 
;--------------------------------------------------- 
;void far init_comm( void ) 
 
	PUBLIC	_init_comm 
_init_comm PROC FAR 
	push	bp		;in case damaged by dos call 
	cli			;disable interrupts 
 
;---- Set up INT x'0C' for IRQ4 (COM1) 
 
	push	ds		;save DS 
	mov	ax,cs 
	mov	ds,ax		;put CS into DS 
	mov	dx,offset IntHdlr ;relative address of interrupt handler 
	mov	al,0CH		;interrupt number for comm. 
	mov	ah,SetIntVect	;function number for setting int vector 
	int	DosCall		;set interrupt in 8086 table 
	pop	ds		;restore DS 
 
;---- Enbl IRQ4 on 8259 interrupt controller 
 
	cli			;disable interrupts - again 
 
	in	al,IntCtlr	;get current masks 
	and	al,EnblIRQ4	;Reset IRQ4 mask 
	out	IntCtlr,al	;And restore to IMR 
 
;--- Set up 8250 port 
 
	;divisor is 48 for 2400 baud, 12 for 9600 baud, 
	;	96 for 1200 baud 
 
	mov	dx,LCR 
	mov	al,083h		;DLAB = 1 
	out	dx,al		;to access baud rate divisors 
 
	mov	dx,BASE 
	mov	ax,BAUDRATEDIVISOR ;set baud rate divisor lsb 
	out	dx,al 
	inc	dx		;set baud rate divisor msb 
	mov	al,ah 
	out	dx,al 
 
	mov	dx,LCR 
	mov	al,03h		;8 bits, no parity, 1 stop 
	out	dx,al 
 
;---  Enbl 8250 data ready interrupt 
 
	mov	dx,IER		;Interrupt Enbl Register 
	mov	al,ERBFI	;Enable 'data-ready' interrupt 
	out	dx,al 
 
;---  Enbl OUT2 on 8250 
 
	mov	dx,MCR		;modem control register 
	mov	al,0BH		;Enable OUT2, DCD and DTR 
	out	dx,al 
 
	pop	bp 
	sti			;enable interrupts 
	ret 
_init_comm ENDP 
 
;void far uninit_comm( void ) 
;Removes the interrupt structure 
;installed by init_comm(). Must be 
;done before passing control to the DOS, else chars received 
;will be stored into the next program loaded! 
 
	PUBLIC	_uninit_comm	 
_uninit_comm PROC FAR 
	push bp 
 
;--- Disable IRQ4 on 8259 
 
	cli 
	in	al,IntCtlr		;GET OCW1 FROM 8259 
	or	al,MaskIRQ4		;DISABLE COMMUNICATIONS INTERRUPT 
	out	IntCtlr,al 
 
;--- Disable 8250 data ready interrupt 
 
	mov	dx,LCR		;DX ==> LCR 
	in	al,dx		;Reset DLAB for IER access 
	and	al,7FH 
	out	dx,al 
	mov	dx,IER		;Interrupt Enbl Register 
	mov	al,0		;Disable all 8250 interrupts 
	out	dx,al 
 
;---  Disable OUT2 on 8250 
 
	mov	dx,MCR		;modem control register 
	mov	al,0		;Disable OUT2 
	out	dx,al 
 
	sti			;reenable interrupts 
	pop	bp 
	ret 
_uninit_comm ENDP 
 
;int far inp_char( void ) 
;Return a character from the input 
;buffer. Assumes you have called 
;inp_status() to see if theres any characters to get. 
 
	PUBLIC	_inp_char 
_inp_char PROC FAR 
	push	ds 
	mov	ax,_DATA 
	mov	ds,ax			;put _DATA in DS 
	mov	bx,word ptr circ_cur 
	xor	ax,ax 
	mov	al,[bx]			;get next char from circ_buf 
	dec	word ptr circ_cnt	;decrement circ_buf COUNT 
	cmp	bx,offset buf_top 
	jz	reset_cur		;JUMP IF SO 
	inc	bx			;ELSE, BUMP PTR 
	jmp	upd_cur 
reset_cur: 
	mov	bx,offset circ_buf	;RESET circ_cur TO BOTTOM OF BUF. 
upd_cur: 
	mov	word ptr circ_cur,bx	;SAVE NEW PTR 
	pop	ds 
	ret 
_inp_char ENDP 
 
;void far outp_char( int c ) 
;Output the character to the 
;char c;serial port. This is not buffered 
;or interrupt driven. 
 
	PUBLIC	_outp_char 
_outp_char PROC FAR 
	push	bp 
	mov	bp,sp 
comout: 
	mov	dx,LSR 
	in	al,dx			;get 8250 status 
	and	al,THRE			;check for transmitter ready 
	jz	comout			;jump if not to wait 
	mov	al,[bp+6]		;get char to al 
	mov	dx,BASE			;data port 
	out	dx,al			;output char to 8250 
	pop	bp 
	ret 
_outp_char ENDP 
 
; ----- RECEIVE INTERRUPT HANDLER ----------------------------------- 
 
	PUBLIC	IntHdlr 
IntHdlr	PROC FAR	 
	cli				;disable interrupts 
	push	dx 
	push	bx 
	push	ax 
	push	ds 
	mov	ax,_DATA 
	mov	ds,ax 
	mov	bx,word ptr circ_in	;GET circ_buf IN PTR 
	mov	dx,BASE			;GET DATA PORT NUMBER 
	in	al,dx			;GET RECEIVED CHARACTER 
	cmp	word ptr circ_cnt,BUFSIZ	;SEE IF circ_buf ALREADY FULL 
	jz	clnup			;jump if so, discard char 
savch: 
	mov	[bx],al			;SAVE NEW CHARACTER IN circ_buf 
	inc	word ptr circ_cnt	;BUMP circ_buf COUNT 
	cmp	bx,offset buf_top	;ARE WE AT THE TOP OF THE circ_buf? 
	jz	reset_in		;JUMP IF SO 
	inc	bx			;ELSE, BUMP PTR 
	jmp	into_buf 
reset_in: 
	mov	bx,offset circ_buf	;RESET circ_in TO BOTTOM OF BUF. 
into_buf: 
	mov	word ptr circ_in,bx	;SAVE NEW PTR 
clnup: 
	mov	al,RSTINT 
	out	RS8259,al	;ISSUE SPECIFIC EOI FOR 8259 
	pop	ds		;GET BACK ENTERING DS 
	pop	ax 
	pop	bx 
	pop	dx 
	sti 
	iret 
IntHdlr	ENDP 
 
_TEXT ENDS 
 
END