; Kermit system dependent module for IBM-PC

; Edit History
;  Make break be 275 ms, DT 5:51pm  Thursday, 6 December 1984

	public	serini, serrst, clrbuf, outchr, coms, vts, dodel,
	public	ctlu, cmblnk, locate, prtchr, dobaud, clearl,
	public	dodisk, getbaud, beep,
	public	count, xofsnt, puthlp, putmod, clrmod, poscur
	public	sendbr, machnam, setktab, setkhlp, lclini, showkey
	include msdefs.h

false	equ	0
true	equ	1
mntrgh	equ	bufsiz*3/4	; High point = 3/4 of buffer full.

; constants used by serial port handler

BRKBIT	EQU	040H		; Send-break bit. 
TIMER	EQU	40H		; Use to issue short beep.
PORT_B	EQU	61H		; Port B address.
MCONF	EQU	11H		; Machine configuration. 
KEYB	EQU	16H
BIOS	EQU	10H

MDMDAT1	EQU	03F8H		; Address of modem port (data). [19b]
MDMSTS1	EQU	03FDH		; Address of modem port	status. [19b]
MDMCOM1	EQU	03FBH		; Address of modem port command. [19b]
MDMDAT2	EQU	02F8H		; Port 2 address. [19b]
MDMSTS2	EQU	02FDH		; Port 2 status. [19b]
MDMCOM2	EQU	02FBH		; Port 2 command. [19b]
MDMINP	EQU	1		; Input ready bit.

MDMINTV	EQU	0030H		; Address of modem port interrupt vector.
MDINTV2 EQU	002CH		; Address for port 2. [19b] 
MDMINTO	EQU	0EFH		; Mask to enable interrupt for modem port.
MDINTO2 EQU	0F7H		; Enable interrupt level 3. [19b]
MDMINTC	EQU	010H		; Bit to set to disable interrupts for modem.
MDINTC2 EQU	008H		; Disable IRQ3. [19b]

INTCONT	EQU	0021H		; Address of 8259 interrupt controller ICW2-3.
INTCON1	EQU	0020H		; Address of 8259 ICW1.
EOICOM	EQU	0064H		; End of interrupt.
EOICOM2	EQU	0063H		; End of interrupt for COM2. [19b]

; external variables used:
; drives - # of disk drives on system
; flags - global flags as per flginfo structure defined in pcdefs
; trans - global transmission parameters, trinfo struct defined in pcdefs
; portval - pointer to current portinfo structure (currently either port1
;    or port2)
; port1, port2 - portinfo structures for the corresponding ports

; global variables defined in this module:
; xofsnt, xofrcv - tell whether we saw or sent an xoff.
; setktab - keyword table for redefining keys (should contain a 0 if
;    not implemented)
; setkhlp - help for setktab.

datas 	segment	public 'datas'
	extrn	drives:byte,flags:byte, trans:byte
	extrn	portval:word, port1:byte, port2:byte

setktab	db	12
	mkeyw	'BACKSPACE',0eh
	mkeyw	'F1',3bh
	mkeyw	'F2',3ch
	mkeyw	'F3',3dh
	mkeyw	'F4',3eh
	mkeyw	'F5',3fh
	mkeyw	'F6',40h
	mkeyw	'F7',41h
	mkeyw	'F8',42h
	mkeyw	'F9',43h
	mkeyw	'F10',44h
	mkeyw	'SCAN',-1

setkhlp	db	cr,lf,'Keyname: backspace, f1, ... f10, or "SCAN" follwed by '
	db	'decimal scan code$'
brkval	db	0		; What to send for a break.
brkadr	dw	0		; Where to send it.
modem	mdminfo	<MDMDAT1,MDMSTS1,MDMCOM1,MDMINTO,MDMINTC,EOICOM,MDMINTV>
erms20	db	cr,lf,'?Warning: System has no disk drives$' ; [21a]
erms40	db	cr,lf,'?Warning: Unrecognized baud rate$'
badbd	db	cr,lf,'Unimplemented baud rate$'
machnam	db	'IBM-PC$'
crlf	db	cr,lf,'$'
delstr  db	BS,' ',BS,'$' 	; Delete string. [21d]
clrlin  db	cr,'$'			; Clear line (just the cr part).
savsci	dw	?		; Save for serial port interrupt vector. [14]
savscs	dw	?		; Ditto.  [14]  
savbr1	dw	?		; "Break" interrupt vector. [25]
savbr2	dw	?		; Ditto. [25]
portin	db	0		; Has comm port been initialized. [21c]
xofsnt	db	0		; Say if we sent an XOFF.
xofrcv	db	0		; Say if we received an XOFF.
tmp	db	?,'$'
temp	dw	0
temp1	dw	?		; Temporary storage.
temp2	dw	?		; Temporary storage.

ontab	db	02H		; Two entries.
	db	03H,'OFF$'	; Should be alphabetized.  [19a]
	dw	00H
	db	02H,'ON$'
	dw	01H

comptab	db	04H
	db	01H,'1$'
	dw	01H
	db	01H,'2$'
	dw	00H
	db	04H,'COM1$'
	dw	01H
 	db	04H,'COM2$'
	dw	00H

; this table is indexed by the baud rate definitions given in
; pcdefs.  Unsupported baud rates should contain FF.
bddat	label	word
	dw	0FFH		; 45.5 baud  -- Not supported.
	dw	900H		; 50 baud
	dw	600H		; 75 baud
	dw	417H		; 110 baud
	dw	359H		; 134.5 baud
	dw	300H		; 150 baud
	dw	180H		; 300 baud
	dw	0C0H		; 600 baud
	dw	60H		; 1200 baud
	dw	40H		; 1800 baud
	dw	3AH		; 2000 baud
	dw	30H		; 2400 baud
	dw	18H		; 4800 baud
	dw	0CH		; 9600 baud
	dw	0FFH		; 19200 baud -- Not supported.
	dw	0FFH		; 38400 baud -- Not supported.

; variables for serial interrupt handler

source	db	bufsiz DUP(?)	; Buffer for data from port.
srcpnt	dw	0		; Pointer in buffer (DI).
count	dw	0		; Number of chars in int buffer.
savesi	dw	0		; Save SI register here.	
telflg	db	0		; Are we acting as a terminal.
mst	dw	0		; Modem status address.
mdat	dw	0		; Modem data address.
mdeoi	db	0		; End-of-Interrupt value.

rbtrn	db	7fH		; rubout

shkbuf	db	300 dup (?)	; room for definition
shkmsg	db	'  Scan code: '
shkmln	equ	$-shkmsg
shkms1	db	cr,lf,'  Definition: '
shkm1ln	equ	$-shkms1
datas	ends

code	segment	public
	extrn	comnd:near, dopar:near, defkey:near, gss:near
	assume	cs:code,ds:datas

; local initialization

lclini	proc	near
	mov	ax,0eH		; scan code for arrow key
	mov	si,offset rbtrn	; translate to rubout
	mov	cx,1		; one char translation
	call	defkey
	mov brkval,BRKBIT	; What to send for a break.
	mov ax,modem.mdcom	; Where to send it.
	mov brkadr,ax
	ret
lclini	endp

; this is called by Kermit initialization.  It checks the
; number of disks on the system, sets the drives variable
; appropriately.  Returns normally.  

DODISK	PROC	NEAR
	int mconf			; Get equipment configuration.
	mov ah,al			; Store AL value for a bit.
	and al,01H			; First, look at bit 0.
	jz dodsk0			; No disk drives -- forget it.
	mov al,ah			; Get back original value.
	mov cl,6			; Shift over bits 6 and 7.
	shr al,cl			; To positions 0 and 1.
	inc al				; Want 1 thru 4 (not 0 thru 3).
	mov drives,al			; Remember how many. 
	ret
dodsk0:	mov ah,prstr			; Print a warning message.
	mov dx,offset erms20		; I'm not sure if things will
	int dos				; work with only a cassette.
	mov drives,0			; Say there aren't any drives.
	ret
DODISK	ENDP

; show the definition of a key.  The terminal argument block (which contains
; the address and length of the definition tables) is passed in ax.
; Returns a string to print in AX, length of same in CX.
; Returns normally.
showkey	proc	near
	push	es
	push	ax		; save the ptr
	mov	bx,ds
	mov	es,bx		; address data segment
	cld
showk1:	xor	ah,ah
	int	keyb		; read a char
	push	ax		; save the character
	call	gss		; get shift state
	pop	bx
	mov	ah,al		; shift state to ah
	mov	al,bh		; scan code to al
	push	ax		; remember scan code
	mov	di,offset shkbuf
	mov	si,offset shkmsg
	mov	cx,shkmln
	rep	movsb		; copy in initial message
	call	nout		; write out scan code
	mov	si,offset shkms1
	mov	cx,shkm1ln	; second message
	rep	movsb
	pop	ax		; get scan code back
	pop	bx		; and terminal arg block
	mov	cx,[bx].klen	; and length
	jcxz	showk2		; no table, not defined
	push	di		; remember output ptr
	mov	di,[bx].ktab	; get key table
	repne	scasw		; search for a definition for this
	mov	si,di		; remember result ptr
	pop	di		; get output ptr back
	jne	showk2		; not defined, forget it
	sub	si,[bx].ktab	; compute offset from beginning
	sub	si,2		; minus 2 for pre-increment
	add	si,[bx].krpl	; get index into replacement table
	mov	si,[si]		; pick up replacement
	mov	cl,[si]		; get length
	mov	ch,0
	inc	si
	rep	movsb		; copy into buffer
showk2:	mov	ax,offset shkbuf ; this is buffer
	mov	cx,di
	sub	cx,ax		; length
	pop	es
	ret			; and return
showkey	endp

; Clear the input buffer. This throws away all the characters in the
; serial interrupt buffer.  This is particularly important when
; talking to servers, since NAKs can accumulate in the buffer.
; Returns normally.

CLRBUF	PROC	NEAR
	cli
	mov ax,offset source
	mov srcpnt,ax
	mov savesi,ax
	mov count,0
	sti
	ret
CLRBUF	ENDP

; Clear to the end of the current line.  Returns normally.

CLEARL	PROC	NEAR
	mov ah,3		; Clear to end of line.
	mov bh,0
	int bios		; Get current cursor position
	mov cx,dx
	mov dl,79
	mov ah,7
	mov al,0
	mov bh,7
	int bios
	ret
CLEARL	ENDP

; Put the char in AH to the serial port.  This assumes the
; port has been initialized.  Should honor xon/xoff.  Skip returns on
; success, returns normally if the character cannot be written.

outchr:	mov bp,portval
	cmp ds:[bp].floflg,0	; Are we doing flow control.
	je outch2		; No, just continue.
	xor cx,cx		; clear counter
outch1:	cmp xofrcv,true		; Are we being held?
	jne outch2		; No - it's OK to go on.
	loop outch1		; held, try for a while
	mov xofrcv,false	; timed out, force it off and fall thru.
outch2:	push dx			; Save register.
	sub cx,cx
	mov al,ah		; Parity routine works on AL.
	call dopar		; Set parity appropriately.
	mov ah,al		; Don't overwrite character with status.
	mov dx,modem.mdstat	; Get port status.
outch3:	in al,dx
	test al,20H		; Transmitter ready?
	jnz outch4		; Yes
	loop outch3
	 jmp outch5		; Timeout
outch4:	mov al,ah		; Now send it out
	mov dx,modem.mddat
	out dx,al
	pop dx
	jmp rskp
outch5:	pop dx
	ret

; This routine blanks the screen.  Returns normally.

CMBLNK	PROC	NEAR		; This is stolen from the IBM example.
	mov cx,0
	mov dx,184FH
	mov bh,7
	mov ax,600H
	int bios
	ret
CMBLNK  ENDP

; Locate: homes the cursor.  Returns normally.

LOCATE  PROC	NEAR
	mov dx,0		; Go to top left corner of screen.
	jmp poscur
LOCATE  ENDP

; write a line in inverse video at the bottom of the screen...
; the line is passed in dx, terminated by a $.  Returns normally.
putmod	proc	near
	push	dx		; preserve message
	mov	cx,1800h
	mov	dx,184fh
	mov	ax,600h		; scroll to clear the line
	mov	bh,70h		; inverse video
	int	bios
	mov	dx,1800h	; now address line 24
	call	poscur
	pop	dx		; get message back
	mov	ah,prstr
	int	dos		; write it out
	ret			; and return
putmod	endp

; clear the mode line written by putmod.  Returns normally.
clrmod	proc	near
	mov	cx,1800h
	mov	dx,184fh
	mov	ax,600h
	mov	bh,7h
	int	bios
	ret
clrmod	endp

; put a help message on the screen.  This one uses reverse video...
; pass the message in ax, terminated by a null.  Returns normally.
puthlp	proc	near
	push	ax		; preserve this
	mov	si,ax		; point to it
	mov	dh,1		; init counter
puthl1:	lodsb			; get a byte
	cmp	al,lf		; linefeed?
	jne	puthl2		; no, keep going
	inc	dh		; count it
	jmp	puthl1		; and keep looping
puthl2:	cmp	al,0		; end of string?
	jne	puthl1		; no, keep going
	mov	ax,600h		; scroll to clear window
	xor	cx,cx		; from top left
	mov	dl,4fh		; to bottom right of needed piece
	mov	bh,70h		; inverse video
	int	bios
	call	locate		; home cursor
	pop	si		; point to string again
puthl3:	lodsb			; get a byte
	cmp	al,0		; end of string?
	je	puthl4		; yes, stop
	mov	ah,14
	int	bios		; else write to screen
	jmp	puthl3		; and keep going
puthl4:	mov	dx,24 * 100H	; go to last line
	jmp	poscur		; position and return
puthlp	endp

; Set the baud rate for the current port, based on the value
; in the portinfo structure.  Returns normally.

DOBAUD	PROC	NEAR
	mov bp,portval
	mov temp1,ax		; Don't overwrite previous rate. [25]
	mov ax,ds:[bp].baud	; Check if new rate is valid. [25]
	mov tmp,2
	mul tmp			; Get index into baud table.
	mov bx,offset bddat	; Start of table.
	add bx,ax
	mov ax,[bx]		; The data to output to port.
	cmp ax,0FFH		; Unimplemented baud rate.
	jne dobd0
	mov ax,temp1		; Get back orginal value.
	mov ds:[bp].baud,ax	; Leave baud rate as is.
	mov ah,prstr
	mov dx,offset badbd	; Give an error message.
	int dos
	ret
dobd0:	mov temp1,ax		; Remember value to output. [25]
	mov dx,modem.mdcom	; LCR -- Initialize baud rate. [19b]
	in al,dx
	mov bl,al
	or ax,80H
	out dx,al
	mov dx,modem.mddat	; [19b]
	mov ax,temp1
	out dx,al
	inc dx
	mov al,ah
	out dx,al
	mov dx,modem.mdcom	; [19b]
	mov al,bl
	out dx,al
	ret
DOBAUD	ENDP

; Get the current baud rate from the serial card and set it
; in the portinfo structure for the current port.  Returns normally.
; This is used during initialization.

GETBAUD	PROC	NEAR
	mov dx,modem.mdcom	; Get current Line Control Register value.
	in al,dx
	mov bl,al		; Save it.
	or ax,80H		; Turn on to access baud rate generator.
	out dx,al
	mov dx,modem.mddat	; Divisor latch.
	inc dx
	in al,dx		; Get hi order byte.
	mov ah,al		; Save here.
	dec dx
	in al,dx		; Get lo order byte.
	push ax	
	mov dx,modem.mdcom
	mov al,bl		; Restore old value.
	out dx,al
	pop ax
	cmp ax,0FFFFH		; Who knows what this is.
	je getb2
	mov bx,offset bddat	; Find rate's offset into table.
	mov cl,0		; Keep track of index.
getb0:	cmp ax,[bx]
	je getb1
	inc cl
	cmp cl,baudsiz		; At the end of the list.
	jge getb2
	add bx,2
	jmp getb0
getb1:	mov ch,0
	mov bp,portval
	mov ds:[bp].baud,cx	; Set baud rate.
	ret
getb2:	mov ah,prstr
	mov dx,offset erms40
	int dos
	ret
GETBAUD	ENDP

; skip returns if no character available at port,
; otherwise returns with char in al, # of chars in buffer in dx.
PRTCHR  PROC	NEAR
	call chkxon		; see if we need to xon
	cmp count,0
	jnz prtch2
	jmp rskp		; No data - check console.
prtch2:	mov si,savesi
	lodsb			; get a byte
	cmp si,offset source + bufsiz	; bigger than buffer?
	jb prtch1		; no, keep going
	mov si,offset source	; yes, wrap around
prtch1:	dec count
	mov savesi,si 
	mov dx,count		; return # of chars in buffer
	ret
PRTCHR  ENDP

; local routine to see if we have to transmit an xon
chkxon	proc	near
	push	bx
	mov	bx,portval
	cmp	[bx].floflg,0	; doing flow control?
	je	chkxo1		; no, skip all this
	cmp	xofsnt,false	; have we sent an xoff?
	je	chkxo1		; no, forget it
	cmp	count,mntrgh	; below trigger?
	jae	chkxo1		; no, forget it
	mov	ax,[bx].flowc	; ah gets xon
	call	outchr		; send it
	nop
	nop
	nop			; in case it skips
	mov	xofsnt,false	; remember we've sent the xon.
chkxo1:	pop	bx		; restore register
	ret			; and return
chkxon	endp

; Send a break out the current serial port.  Returns normally.
SENDBR	PROC	NEAR
	push cx
	push dx
	push ax
	xor cx,cx		; Clear loop counter.
	mov dx,brkadr		; Port address.  [19b]
	in al,dx		; Get current setting.
	or al,brkval		; Set send-break bit(s).
	out dx,al		; Start the break.
	push ax
	mov ax,275		; # of ms to wait
	call pcwait		; hold break for desired interval
	pop ax
	xor al,brkval		; Clear send-break bit(s).
	out dx,al		; Stop the break.
	pop ax
	pop dx
	pop cx
	ret			; And return.
SENDBR	ENDP

; Wait for the # of milliseconds in ax
; Thanks to Bernie Eiben for this one.
pcwait	proc	near
	mov	cx,240		; inner loop counter for 1 millisecond
pcwai1:	sub	cx,1		; inner loop takes 20 clock cycles
	jnz	pcwai1
	dec	ax		; outer loop counter
	jnz	pcwait		; wait another millisecond
	ret
pcwait	endp

; Position the cursor according to contents of DX:
; DH contains row, DL contains column.  Returns normally.

POSCUR	PROC	NEAR
	mov ah,2		; Position cursor.
	mov bh,0
	int bios
	ret
POSCUR	ENDP

; Delete a character from the terminal.  This works by printing
; backspaces and spaces.  Returns normally.

DODEL	PROC	NEAR
	mov ah,prstr
	mov dx,offset delstr	; Erase weird character.
	int dos			
	ret
DODEL	ENDP

; Move the cursor to the left margin, then clear to end of line.
; Returns normally.

CTLU	PROC	NEAR
	mov ah,prstr
	mov dx,offset clrlin
	int dos
	call clearl
	ret
CTLU	ENDP

; set the current port.  

COMS	PROC	NEAR
	mov dx,offset comptab
	mov bx,0
	mov ah,cmkey
	call comnd
	 jmp r
	push bx
	mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp comx		;  Didn't get a confirm.
	 nop
	pop bx
	mov flags.comflg,bl	; Set the comm port flag.
	cmp flags.comflg,1	; Using Com 1?
	jne coms0		; Nope.
	mov ax,offset port1
	mov portval,ax
	mov modem.mddat,MDMDAT1	; Set COM1 defaults.
	mov modem.mdstat,MDMSTS1
	mov modem.mdcom,MDMCOM1
	mov modem.mddis,MDMINTC
	mov modem.mden,MDMINTO
	mov modem.mdmeoi,EOICOM
	mov modem.mdintv,MDMINTV
	mov brkadr,MDMCOM1
	ret
coms0:	mov ax,offset port2
	mov portval,ax
	mov modem.mddat,MDMDAT2	; Set COM2 defaults.
	mov modem.mdstat,MDMSTS2
	mov modem.mdcom,MDMCOM2
	mov modem.mddis,MDINTC2
	mov modem.mden,MDINTO2
	mov modem.mdmeoi,EOICOM2
	mov modem.mdintv,MDINTV2
	mov brkadr,MDMCOM2
	ret
comx:	pop bx
	ret
COMS	ENDP

; Set heath emulation on/off.

VTS	PROC	NEAR
	mov dx,offset ontab
	mov bx,0
	mov ah,cmkey
	call comnd
	 jmp r
	push bx
	mov ah,cmcfm
	call comnd		; Get a confirm.
	 jmp vt0		;  Didn't get a confirm.
	 nop
	pop bx
	mov flags.vtflg,bl	; Set the VT52 emulation flag.
	ret
vt0:	pop bx
	ret
VTS	ENDP

; initialization for using serial port.  This routine performs
; any initialization necessary for using the serial port, including
; setting up interrupt routines, setting buffer pointers, etc.
; Doing this twice in a row should be harmless (this version checks
; a flag and returns if initialization has already been done).
; SERRST below should restore any interrupt vectors that this changes.
; Returns normally.

SERINI	PROC	NEAR
	push es
	cmp portin,0		; Did we initialize port already? [21c]
	jne serin0		; Yes, so just leave. [21c]
	cli			; Disable interrupts
	cld			; Do increments in string operations
	xor ax,ax		; Address low memory
	mov es,ax
	mov bx,modem.mdintv	; Save serial card interrupt vector. [19b]
	mov ax,es:[bx]
	mov savsci,ax
	mov ax,offset serint	; And point it to my routine
	mov es:[bx],ax
	add bx,2		; Save CS register too. [19b]
	mov ax,es:[bx]
	mov savscs,ax
	mov es:[bx],cs
	mov portin,1		; Remember port has been initialize. 
	call clrbuf		; Clear input buffer. 
	mov ax,modem.mdstat
	mov mst,ax		; Use this address for status. 
	mov ax,modem.mddat
	mov mdat,ax		; Use this address for data. 
	mov al,modem.mdmeoi
	mov mdeoi,al		; Use to signify end-of-interrupt. 
	in al,21H		; Set up 8259 interrupt controller
	and al,modem.mden	; Enable INT3 or INT4. 
	out 21H,al
	mov dx,modem.mdcom	; Set up the serial card. 
	mov al,3
	out dx,al
	mov dl,0F9H
	mov al,1		; Set up interrupt enable register
	out dx,al
	mov dl,0FCH		; Enable interrupts from serial card
	mov al,0BH
	out dx,al
	sti			; Allow interrupts
	mov dl,0F8H
	in al,dx
serin0:	pop es
	ret			; We're done.
SERINI	ENDP

; Reset the serial port.  This is the opposite of serini.  Calling
; this twice without intervening calls to serini should be harmless.
; Returns normally.

SERRST	PROC	NEAR
	push es			; preserve this
	cmp portin,0		; Reset already? 
	je srst1		; Yes, just leave. 
	cli			; Disable interrupts
	mov dx,03FCH		; Disable modem interrupts
	cmp flags.comflg,1	; Using port 1 ? 
	je srst0		; Yes - continue. 
	mov dh,02		; Set for port 2. 
srst0:	mov al,3
	out dx,al
	in al,21H		; Interrupt controller
	or al,modem.mddis	; Inhibit IRQ3 or IRQ4. 
	out 21H,al
	xor bx,bx		; Address low memory
	mov es,bx
	mov bx,modem.mdintv	; Restore the serial card int vector 
	mov ax,savsci
	mov es:[bx],ax
	add bx,2		; Restore CS too. 
	mov ax,savscs
	mov es:[bx],ax
	mov portin,0		; Reset flag.
	sti
srst1:	pop es
	ret			; All done.
SERRST	ENDP

; serial port interrupt routine.  This is not accessible outside this
; module, handles serial port receiver interrupts.

SERINT  PROC  NEAR
	push bx
	push dx
	push ax
	push es
	push di
	push ds
	push bp
	push cx
	cld
	mov ax,seg datas
	mov ds,ax		; address data segment
	mov es,ax
	mov di,srcpnt		; Registers for storing data.
	mov dx,mst		; Asynch status port. [19b]
	in al,dx
	test al,mdminp		; Data available?
	jz retint		; Nope.
	mov dx,mdat		; [19b]
	in al,dx
	cmp telflg,0		; File transfer or terminal mode? [17c]
	jz srint0
	and al,7FH		; Terminal mode (7 bits only). 
srint0: or al,al
	jz retint		; Ignore nulls.
	mov ah,al
	and ah,7fH		; strip parity temporarily
	cmp ah,7FH		; Ignore rubouts, too.
	jz retint
	mov bp,portval
	cmp ds:[bp].floflg,0	; Doing flow control?
	je srint2		; Nope.
	mov bx,ds:[bp].flowc	; Flow control char (BH = XON, BL = XOFF).
	cmp al,bl		; Is it an XOFF?
	jne srint1		; Nope, go on.
	mov xofrcv,true		; Set the flag.
	jmp retint
srint1:	cmp al,bh		; Get an XON?
	jne srint2		; No, go on.
	mov xofrcv,false	; Clear our flag.
	jmp retint
srint2:	stosb
	cmp di,offset source + bufsiz
	jb srint3		; not past end...
	mov di,offset source	; wrap buffer around
srint3:	inc count
	cmp ds:[bp].floflg,0	; Doing flow control?
	je retint		; No, just leave.
	cmp xofsnt,true		; Have we sent an XOFF?
	je retint		; Yes.
	cmp count,mntrgh	; Past the high trigger point?
	jbe retint		; No, we're within our limit.
	mov ah,bl		; Get the XOFF.
	call outchr		; Send it.
	nop
	nop
	nop			; ignore failure.
	mov xofsnt,true		; Remember we sent it.
retint:	mov srcpnt,di
	sti
	mov al,mdeoi		; [19b]
	out intcon1,al		; Send End-of-Interrupt to 8259.
	pop cx
	pop bp
	pop ds
	pop di
	pop es
	pop ax
	pop dx
	pop bx
intret:	iret
SERINT	ENDP

; Produce a short beep.  The PC DOS bell is long enough to cause a loss
; of data at the port.  Returns normally.

BEEP	PROC	NEAR
	mov al,10110110B	; Gen a short beep (long one losses data.)
	out timer+3,al		; Code snarfed from Technical Reference.
	mov ax,533H
	out timer+2,al
	mov al,ah
	out timer+2,al
	in al,port_b
	mov ah,al
	or al,03
	out port_b,al
	sub cx,cx
	mov bl,1
beep0:  loop beep0
	dec bl		
	jnz beep0
	mov al,ah
	out port_b,al
	ret
BEEP	ENDP 
 
; put the number in ax into the buffer pointed to by di.  Di is updated
nout	proc	near
	mov	dx,0		; high order is always 0.
	mov	bx,10
	div	bx		; divide to get digit
	push	dx		; save remainder digit
	or	ax,ax		; test quotient
	jz	nout1		; zero, no more of number
	call	nout		; else call for rest of number
nout1:	pop	ax		; get digit back
	add	al,'0'		; make printable
	stosb			; drop it off
	ret			; and return
nout	endp

; Jumping to this location is like retskp.  It assumes the instruction
;   after the call is a jmp addr.
 
RSKP	PROC	NEAR
	pop bp
	add bp,3
	push bp
	ret
RSKP	ENDP
 
; Jumping here is the same as a ret.
 
R	PROC	NEAR
	ret
R	ENDP

code	ends 
	end
