	Title	COM - Interrupt Driven Communications Software
	Page	86,132
	include b:struct.mac
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;:								    ::
;:	Copyright, 1982,  University of B.C.  Computing Centre	    ::
;:								    ::
;:	Permission to copy  without fee  all  or  part of this	    ::
;:	material  is  granted  provided  that  copies  are not	    ::
;:	made  or  distributed  for direct commercial advantage,     ::
;:	and that this copyright notice is retained in the copy.     ::
;:								    ::
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::


;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
;:								    ::
;:   Modified for use with SIMTERM and PASCAL calling sequences     ::
;:   Functions added for controlling and examining line status.     ::
;:   Modified to use the 'structured' macros for easier reading.    ::
;:								    ::
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

	Name	DRIVERS

	;8250 Asynchronous Communications Element - Registers
	;  The following EQUs assume that the base address is
	;  in BX.

DLH	EQU	[bx+1]		;Divisor Latch High byte
DLL	EQU	[bx]		;Divisor Latch Low  byte
IER	EQU	[bx+1]		;Interrupt Enable Register
IIR	EQU	[bx+2]		;Interrupt Identification Register
IOR	EQU	[bx]		;Input Output Register
LCR	EQU	[bx+3]		;Line Control Register
LSR	EQU	[bx+5]		;Line Status  Register
MCR	EQU	[bx+4]		;Modem Control Register
MSR	EQU	[bx+6]		;Modem Status  Register

rs232_base equ	400H		;address of rs232 adapters

	;8259A Priority Interrupt Controller - Registers

INTA0	EQU	20H		;Register 0
INTA1	EQU	21H		;Register 1

	;8259A Priority Interrupt Controller - Commands

EOI	EQU	20H		;Non-specific EOI
timer_low equ	es:[46CH]	; low word of timer

	;COM Flags Mask Bits

F_T_ACT EQU	00000001B	;Transmitter Active
;F_BREAK EQU	 00000010B	 ;Break sequence started
F_XCONT EQU	00000100B	;XON/XOFF control enabled
F_XOFFR EQU	00001000B	;XOFF recieved/don't transmit
F_XOFFT EQU	00010000B	;XOFF transmitted/drain buffer
F_SXOFF EQU	00100000B	;Send XOFF
F_SXON	EQU	01000000B	;Send XON

	;Constants

RBUF_SIZE EQU	2048		;Receive buffer size
FENCE_1 EQU	rbuf_size/4	;Send XON fence
FENCE_2 EQU	50		;Send XOFF fence
SET_BRK EQU	01000000B	;Set-Break flag of LCR
XOFF	EQU	00010011B	;ASCII DC3
XON	EQU	00010001B	;ASCII DC1

	;ERR_FLAG masks

chg_lsr equ	0001B		; change in LSR
chg_msr equ	0010B		; change in MSR
buf_ful equ	0100B		; receive buffer full

	PAGE

data	segment public 'data'
	public	err_flag
err_flag db	0		; record any error conditions
	public	lsr_value
lsr_value db	?		; LSR at change
	public	msr_value
msr_value db	?		; MSR at change
	public	recv_reset
recv_reset db	0		; flag to reset receiver buffer on BREAK
data	ends
dgroup	group	data

Communications	SEGMENT PARA PUBLIC 'CODE'

com_base dw	?		; base address of the Async Port
irq_num db	?		; user specified IRQ
int_base dw	?		; interrupt @ corresonding to IRQ
ds_pascal dw	?		; DS on entry (PASCAL Data segment)
CIVA	DW	?		;Comm Interrupt Vector Address
CIVS	DW	?		;Comm Interrupt Vector Segment
FLAGS	DB	?		;COM state Flags
B_COUNT DB	?		;Break shutoff counter
R_INDEX DW	?		;Recieve buffer index pointer
R_LENTH DW	?		;Recieve buffer length
R_BUFF	DB	rbuf_size DUP(?)     ;Recieve character Buffer
r_buff_end equ	$-1
T_INDEX DW	?		;Transmit buffer index pointer
T_LENTH DW	?		;Transmit buffer length
T_BUFF	DB	256  DUP(?)	;Transmit character Buffer
t_buff_end equ	$-1




	PUBLIC	COM_Begin,COM_End
	PUBLIC	breaker
	PUBLIC	COM_Get
	PUBLIC	send


	PAGE

COM_Begin	PROC	FAR
	ASSUME	CS:Communications,DS:nothing

;procedure COM_Begin( Port	: word;      [bp+12]  (IRQ [bp+13])
;		      Speed	: word;      [bp+10]
;		      LCR	: word;      [bp+8]
;		      X_Control : boolean ); [bp+6]

	PUSH	BP		;Save frame pointer
	MOV	BP,SP		;Get new frame pointer
	mov	ax,ds
	mov	ds_pascal,ax	; Save DS to PASCAL data segment
	push	ds
	mov	ax,cs		;setup DS addressability
	mov	ds,ax
	assume	ds:communications
	XOR	AX,AX		;Clear AX
	MOV	ES,AX		;Address bottom of memory
	MOV	FLAGS,AL	;Initialize state flags
	MOV	B_COUNT,AL	;Zero Break Shutoff Counter
	MOV	R_LENTH,AX	;Zero Receive buffer Length
	MOV	T_LENTH,AX	;Zero Transmit buffer Length
	MOV	R_INDEX,OFFSET R_BUFF
	MOV	T_INDEX,OFFSET T_BUFF
	mov	si,[bp+12]	; com port #
	and	si,0FFH 	; mask off the port#
	shl	si,1
	mov	bx,es:rs232_base[si]	   ; pickup the base address
	mov	com_base,bx	; save for the rest of the program
	MOV	AL,10000000B	;Set Divisor-Latch bit
	lea	dx,lcr
	OUT	DX,AL		;Write LCR
	MOV	AX,[BP+10]	;Get Bit Rate Divisor
	lea	dx,dll
	OUT	DX,AL		;Write Divisor Latch Low byte
	MOV	AL,AH		;Get high part of divisor
	lea	DX,DLH
	OUT	DX,AL		;Write Divisor Latch High byte

	MOV	AL,[BP+8]	;Get Line Control Register settings
	AND	AL,00111111B	;Mask out unwanted stuff
	lea	DX,LCR
	OUT	DX,AL		;Write LCR

	.if	<byte ptr [bp+6]> ne 0
	    OR	    FLAGS,F_XCONT   ;Turn on XON/XOFF control
	.endif

	mov	al,[bp+13]	; user specified IRQ
	xor	ah,ah		; clear high byte
	shl	ax,1
	shl	ax,1		; X 4
	mov	si,ax
	add	si,20H		; base of 8259A interrupts
	mov	int_base,si	; save it

	CLI			;Disable interrupts

	MOV	AX,ES:[si+2]	;Get CIV segment
	MOV	CIVS,AX 	;Save CIVS
	MOV	AX,ES:[si]	;Get CIV address
	MOV	CIVA,AX 	;Save CIVA
	MOV	ES:[si+2],CS	;Patch Interrupt Vector
	MOV	WORD PTR ES:[si],OFFSET COM_I

	STI			;Enable interrupts

	MOV	AL,00001011B	;Modem Control Register Settings
	lea	DX,MCR
	OUT	DX,AL		;Write MCR

	lea	DX,MSR		;Get address of Modem Status Register
	IN	AL,DX		;Get Modem Status

	lea	DX,LSR		;Get address of Line Status Register
	IN	AL,DX		;Get Line Status

	lea	DX,IOR		;Get address of I/O Register
	IN	AL,DX		;Get spurious reciever data

	MOV	AL,00001111B	;Interrupt Enable Register Settings
	lea	DX,IER
	OUT	DX,AL		;Write IER

	mov	cl,[bp+13]	; user specified IRQ
	mov	ch,1		; bit for mask
	shl	ch,cl		; shift mask to proper position
	mov	irq_num,ch	; save it
	not	ch		; complement
	IN	AL,INTA1	;Get IRQ mask from 8259A
	AND	AL,ch		;Mask out IRQ4 bit
	OUT	INTA1,AL	;Enable IRQ4

	pop	ds
	POP	BP		;Restore Frame Pointer
	RET	8		;Return to caller

COM_Begin	ENDP

	PAGE

COM_End 	PROC	FAR

;procedure COM_End;

	push	ds
	mov	ax,cs		;setup addressability for DS
	mov	ds,ax
	mov	bx,com_base
	XOR	AX,AX		;Clear AX

	lea	DX,IER
	OUT	DX,AL		;Turn off comm interrupts

	IN	AL,INTA1	;Get 8259A IRQ mask
	OR	AL,irq_num	;Mask in IRQ4 bit
	OUT	INTA1,AL	;Disable IRQ4

	XOR	AX,AX		;Clear AX
	MOV	ES,AX		;Address bottom of mermory

	CLI			;Disable interrupts

	mov	si,int_base
	MOV	AX,CIVS 	;Restore Comm Interrupt Vector
	MOV	ES:[si+2],AX
	MOV	AX,CIVA
	MOV	ES:[si],AX

	STI			;Enable interrupts

	pop	ds
	RET			;Return to caller

COM_End 	ENDP

	PAGE

COM_I	PROC	FAR		;COM Interrupt service routine
	STI			;Allow further interrupts
	PUSH	AX
	PUSH	BX
	PUSH	DX
	PUSH	DI
	PUSH	DS

	mov	ax,cs		;setup addressability for DS
	mov	ds,ax
	mov	bx,com_base
	lea	DX,IIR
	IN	AL,DX		;Get class of interrupt
	.if	al e 110B	; change in receive line status
	    push    ds
	    mov     ax,ds_pascal
	    mov     ds,ax
	    assume  ds:dgroup
	    lea     DX,LSR	    ;Get line status
	    IN	    AL,DX
	    mov     lsr_value,al    ;save value at change
	    or	    err_flag,chg_lsr
	    pop     ds
	    assume  ds:communications
	    JMP     FINISH
	.endif

	.if	al e 100B  *LONG*	;Data received
	    lea     DX,IOR	    ;Get address of I/O Register
	    IN	    AL,DX	    ;Get recieved byte
	    .ifs    flags,f_xcont	; XON/XOFF control
		mov	ah,al		; mask off the parity
		and	ah,7FH		;   bit on the character
		.if	ah e xoff
		    OR	    FLAGS,F_XOFFR   ;To stop transmitter
		    JMP     FINISH
		.endif

		.if	ah e xon	; XON received
		    AND     FLAGS,NOT F_XOFFR	    ;To release transmitter
		    .ifs    flags,f_t_act
			JMP	FINISH
		    .endif
		    JMP     TRANS	; start xmitter
		.endif

		.ifc	flags,f_xofft	; if XOFF not transmitted
		    .if     r_lenth g <size r_buff - fence_2> ; and receive buffer almost full
			OR	FLAGS,F_SXOFF + F_XOFFT ; transmit XOFF
			.ifc	flags,f_t_act	; xmitter not active
			    MOV     AH,AL	    ;Save recieved character
			    MOV     AL,XOFF	    ;Get XOFF character
			    lea     DX,IOR	    ;Get address of I/O Register
			    OUT     DX,AL	    ;Send XOFF
			    OR	    FLAGS,F_T_ACT   ;Mark transmitter active
			    XOR     FLAGS,F_SXOFF   ;Clear Send-XOFF flag
			    MOV     AL,AH	    ;Restore recieved character
			.endif
		    .endif
		.endif
	    .endif

	    .if     r_lenth ge <size r_buff>	;if buffer full, ignore character
		push	ds
		mov	ax,ds_pascal
		mov	ds,ax
		assume	ds:dgroup
		or	err_flag,buf_ful
		pop	ds
		assume	ds:communications
		jmp	finish
	    .endif

	    MOV     DI,R_INDEX	    ;Get pointer into R_BUFF
	    MOV     [DI],AL	    ;Put new byte into buffer
	    INC     R_LENTH	    ;Increase buffer length by 1 byte
	    inc     r_index
	    .if     di e <offset r_buff_end>  ;check for wrap around
		mov	r_index,offset r_buff
	    .endif
	    jmp     finish
	.endif

	.if	al e 10B	; Transmitter is EMPTY

	    .ifs    flags,f_sxoff	; should we send an XOFF
		XOR	FLAGS,F_SXOFF	;Reset Send_XOFF Flag
		MOV	AL,XOFF 	;Get XOFF character
		lea	DX,IOR		;Get address of I/O Register
		OUT	DX,AL		;Transmit XOFF
		JMP	FINISH
	    .endif

	    .ifs    flags,f_sxon	; should we send an XON
		XOR	FLAGS,F_SXON	;Reset Send_XON flag
		MOV	AL,XON		;Get XON character
		lea	DX,IOR		;Get address of I/O Register
		OUT	DX,AL		;Transmit XON
		JMP	FINISH
	    .endif

	    .ifc    flags,f_xoffr		;OK to continue transmitting

trans:		.if	t_lenth g 0	    ; data in xmit buffer
		    OR	    FLAGS,F_T_ACT   ;Flag transmitter as active
		    MOV     DI,T_INDEX	    ;Get pointer into T_BUFF
		    MOV     AL,[DI]	    ;Get byte from buffer
		    DEC     T_LENTH	    ;Reduce buffer length by 1 byte
		    lea     DX,IOR	    ;Get address of I/O register
		    OUT     DX,AL	    ;Transmit byte.
		    inc     t_index
		    .if     di e <offset t_buff_end>	    ;wrap around?
			mov	t_index,offset t_buff
		    .endif
		.else
		    AND     FLAGS,NOT F_T_ACT	    ;Set Transmitter not Active
		.endif
	    .else
		and	flags,not f_t_act   ; mark `not active'
	    .endif
	.else

	    push    ds
	    mov     ax,cs:ds_pascal
	    mov     ds,ax
	    assume  ds:dgroup
	    lea     DX,MSR	    ;Get modem status
	    IN	    AL,DX
	    mov     msr_value,al
	    or	    err_flag,chg_msr
	    pop     ds
	    assume  ds:communications
	.endif

FINISH: CLI			;Turn off interrupts
	MOV	AL,EOI		;End Of Interrupt command
	OUT	INTA0,AL	;Signal 8259A of EOI
	POP	DS
	POP	DI
	POP	DX
	POP	BX
	POP	AX
	IRET

COM_I	ENDP


breaker PROC	FAR
	assume	ds:nothing

;procedure COM_Break;
	mov	bx,com_base
	mov	ax,ds_pascal	; setup ES->PASCAL data segment
	mov	es,ax
	assume	es:dgroup
	cli
	lea	DX,LCR		;Get address of Line Control Register
	IN	AL,DX		;Get LCR settings
	OR	AL,SET_BRK	;Set set-break flag
	OUT	DX,AL		;Start break sequence
	XOR	AX,AX		;Clear AX
	MOV	T_LENTH,AX	;Empty transmit buffer
	and	flags,not f_t_act	; mark xmitter `not active'
	.if	recv_reset ne 0
	    mov     r_lenth,ax	; reset the receiver buffer
	.endif
	assume	es:nothing
	sti
	mov	es,ax		; ES points to low core
	mov	ax,timer_low	; pickup current time
	.repeat 		; wait for time to expire
	    mov     cx,timer_low
	    sub     cx,ax
	.until	cx a 2
	cli
	in	al,dx
	and	al,not set_brk	; stop the break sequence
	out	dx,al
	sti
	RET			;Return to caller

breaker ENDP

	PAGE

COM_Get 	PROC	FAR
	assume	ds:nothing

;function COM_Get( var Character : char ) : boolean

	.if	r_lenth le 0	; receive buffer is empty
	    MOV     AX,1	    ;Return buffer empty code to caller
	    RET     2
	.endif

	PUSH	BP		;Save frame pointer
	MOV	BP,SP		;Get new frame pointer

	push	ds
	mov	ax,ds		;setup ES to point to PASCAL data space
	mov	es,ax

	mov	ax,cs		;setup addressability for DS
	mov	ds,ax
	assume	ds:communications
	mov	bx,com_base
	CLI			;Disable interrupts
	MOV	SI,R_INDEX	;Get pointer into recieve buffer
	SUB	SI,R_LENTH	;Subtract length of buffer
	.if	si l <offset r_buff>	; check if pointer needs adjustment
	    add     si,size r_buff
	.endif

	MOV	AL,[SI] 	;Get character from buffer
	DEC	R_LENTH 	;Reduce size of buffer by 1 byte
	MOV	DI,[BP+6]	;Get address of parameter
	MOV	es:[DI],AL	;Store result of COM Get
	.ifs	flags,f_xcont	;XON/XOFF control specified
	    .ifs    flags,f_xofft	;XOFF was transmitted
		.if	r_lenth le fence_1	;buffer is below the low water mark
		    OR	    FLAGS,F_SXON    ;Set Send-XON flag
		    AND     FLAGS,NOT F_XOFFT	    ;Clear XOFF-transmitted flag
		    .ifc    flags,f_t_act	;xmitter not active
			MOV	AL,XON		;Get XON character
			lea	DX,IOR		;Get address of I/O Register
			OUT	DX,AL		;Send XON
			OR	FLAGS,F_T_ACT	;Mark transmitter active
			XOR	FLAGS,F_SXON	;Clear Send-XON flag
		    .endif
		.endif
	    .endif
	.endif

	XOR	AX,AX		;Return O.K. code to caller
	STI			;Enable interrupts
	pop	ds
	POP	BP		;Restore frame pointer
	RET	2		;Return to caller

COM_Get 	ENDP



	PAGE

send	PROC	FAR

;function send( Buffer : lstring ) : integer;
;		8-size, 6-@

	PUSH	BP		;Save frame pointer
	MOV	BP,SP		;Get new frame pointer
	mov	ax,ds		;save DS in ES
	mov	es,ax
	push	ds
	mov	ax,cs		;setup addressability for DS
	mov	ds,ax
	assume	ds:communications
	mov	bx,com_base
	MOV	CX,[BP+8]	;Get length of string to write
	MOV	AX,T_LENTH	;Get length of transmit buffer
	ADD	AX,CX		;Add string length
	.if	ax g <size t_buff>	;xmit buffer not large enough
	    XOR     AX,AX	    ;Return False, no room in buffer
	    pop     ds
	    POP     BP		    ;Restore frame pointer
	    RET     4		    ;Return to caller
	.endif

	cli			;lock out interrupts till pointer updated
	MOV	SI,[BP+6]	;Get address of string to send
	MOV	DI,T_INDEX	;Get pointer into buffer
	ADD	DI,T_LENTH	;Compute end of buffer
	.if	di g <offset t_buff_end>	; check for wrap around
	    sub     di,size t_buff
	.endif
	add	t_lenth,cx
	sti

	.repeat
	    mov     al,es:[si]	; pickup data from PASCAL
	    mov     [di],al	; store in buffer
	    inc     si
	    inc     di
	    .if     di g <offset t_buff_end>   ;check for wrap around
		mov	di,offset t_buff
	    .endif
	.until loop

	cli
	.ifc	flags,f_t_act+f_xoffr	; xmitter is not active
	    OR	    FLAGS,F_T_ACT   ;Mark transmitter active
	    MOV     SI,T_INDEX	    ;Get addr of first char in buffer
	    MOV     AL,[SI]	    ;Get first character in buffer
	    DEC     T_LENTH	    ;Reduce buffer length by 1 byte
	    inc     t_index
	    .if     t_index g <offset t_buff_end>	;check for wrap around
		mov	t_index,offset t_buff
	    .endif
	    lea     DX,IOR	    ;Get address of I/O Register
	    OUT     DX,AL	    ;Start transmitter going
	.endif
	sti

	mov	ax,-1		; return OK status
	pop	ds
	POP	BP		;Restore frame pointer
	RET	4		;Return to caller

send	ENDP

;
; function x_cont(new : boolean) : boolean
;   sets the new value for XON/XOFF control and returns the OLD value
;
	public	x_cont
	assume	ds:nothing
x_cont	proc	far
	push	bp
	mov	bp,sp
	xor	ax,ax		;set to FALSE
	cli
	.ifs	flags,f_xcont
	    inc     ax		    ;set to TRUE
	.endif
	and	flags,not f_xcont	;clear the flag
	.if	<byte ptr [bp+6]> ne 0
	    or	    flags,f_xcont
	.endif
	sti
	pop	bp
	ret	2
x_cont	endp


;
;  procedure clearterm;
;	Clear the interrupt so stray data will not kill the system
;
	public	clearterm
clearterm proc	far
	mov	bx,com_base
	lea	dx,mcr		; MCR
	in	al,dx
	and	al,not 1000B	; clear interrupt control bit
	out	dx,al
	lea	dx,ier		; IER
	xor	al,al
	out	dx,al		; clear all interrupts
	ret
clearterm endp

;
; procedure dropline
;
;	'drop' the communications line
;
	public	dropline
dropline proc far
	mov	bx,com_base
	lea	dx,mcr
	in	al,dx
	and	al,0f0H 	; clear the DTR and interrupt flags
	out	dx,al
	ret
dropline endp
;
; toggle TR  -- for VENTEL autodialler
;
	public	toggle_tr

toggle_tr proc far
	mov	bx,com_base
	xor	ax,ax
	mov	es,ax		; set ES to low memory
	lea	dx,mcr
	in	al,dx
	push	ax		; save original value
	and	al,0fcH 	; clear the DTR and interrupt flags
	out	dx,al
	mov	ax,timer_low
	.repeat
	    mov     cx,timer_low
	    sub     cx,ax
	.until	cx ae 18	; wait 1 second
	pop	ax		; retrieve original value
	out	dx,al
	ret
toggle_tr endp

; function modem_status : byte;
;
;	returns the contents of the modem status register
	public	modem_status
modem_status proc	far
	mov	bx,com_base
	lea	dx,msr
	in	al,dx
	ret
modem_status endp

Communications	ENDS

	check$
	END
                                                                                                             
