;----------------------------------------------------------------------------
;
;	Replacement for the Tandy 2000 RS232 handler
;
;	Copyright 1985 by John McNamee.
;
;	This software is made available for non-commercial use. If I find
;	you selling it, my lawyers will teach you a lesson in the copyright
;	laws that you will never forget.
;
;----------------------------------------------------------------------------
;
;	There are some differences between these routines and the BIOS.
;	I do not check Data Set Ready, or any other modem status, before
;	sending characters. If the UART is ready, I send the character.
;	This makes it easier to do machine-to-machine hardwire connections.
;	I do not fully implement the XON/XOFF processing. Only "HOST" type
;	processing is done: if the other side sends an XOFF to the 2000, I
;	stop transmitting until I get an XON. Nothing is done if the 2000
;	input buffer fills up; no XOFF is sent, and excess characters are
;	just ignored. This was the easiest way to implement things, and
;	should be OK for most people. It should be noted that the IBM PC
;	doesn't do either type, so many programs handle all their own
;	XON/XOFF processing already.
;
;----------------------------------------------------------------------------

bufsiz	equ	256			;Size of input buffer

code	segment	public 'code'
	assume	cs:code, ds:code

	org	100h
entry:	jmp	setup

;----------------------------------------------------------------------------
;	Runtime data area
;----------------------------------------------------------------------------
port12:	db	?			;Copy of last byte sent to port 12
hold:	db	?			;XON/XOFF status flags
inptr:	dw	?			;Buffer input pointer
outptr:	dw	?			;Buffer output pointer
inbuf:	db	bufsiz dup (?)

;----------------------------------------------------------------------------
;	INT 14 handler
;----------------------------------------------------------------------------
int14	proc	near
	push	bx
	push	cx
	push	dx
	push	ds
	push	cs
	pop	ds			;DS=CS

	cmp	ah,00			;Reset Comm Port
	jnz	not_0
	and	ah,3			;We only want bits 0 and 1
	mov	byte ptr hold,dh	;Set XON/XOFF flags
	pushf
	db	9Ah			;Opcode for long jump
oldint:	dw	?,?
	call	flush			;Flush the buffer
	mov	al,36h			;Disable transmitter
	call	outctl			;Output to control port
	call	stat
	jmp	exit14

not_0:	cmp	ah,01			;Transmit Character
	jnz	not_1
	call	send
	jmp	exit14

not_1:	cmp	ah,02			;Receive Character
	jnz	not_2
	call	recv
	jmp	exit14

not_2:	cmp	ah,03			;Get Current Comm Status
	jnz	not_3
	call	stat
	jmp	exit14

not_3:	cmp	ah,04			;Flush Comm Buffer
	jnz	exit14
	call	flush

exit14:	pop	ds
	pop	dx
	pop	cx
	pop	bx
	iret
int14	endp

;----------------------------------------------------------------------------
;	Send a character
;----------------------------------------------------------------------------
send	proc	near
	push	ax
	mov	al,37h			;Enable transmitter
	call	outctl
	mov	cx,0000			;Timeout counter
	mov	dl,2
wait1:	in	al,12h			;Get UART status
	test	al,01			;UART ready?
	jz	wait2
	test	byte ptr hold,10h	;Is "stop transmitting" flag set?
	jz	send1
wait2:	loop	wait1
	dec	dl			;Bump counter
	jnz	wait1
	pop	ax
	call	stat			;Get status
	or	ah,80h			;Set timeout error
	ret
send1:	pop	ax
	out	10h,al			;Send character
	call	stat			;Return status
	ret
send	endp

;----------------------------------------------------------------------------
;	Receive a character
;----------------------------------------------------------------------------
recv	proc	near
	cli
	mov	bx,outptr
	cmp	bx,inptr		;Is buffer empty?
	jnz	recv1
	sti				;Allow interrupt to happen
	nop
	nop
	jmp	recv			;Go back and try again
recv1:	mov	al,cs:[bx]		;Get character
	inc	bx
	cmp	bx,offset inbuf+bufsiz	;At end of buffer?
	jnz	recv2
	mov	bx,offset inbuf		;Wrap around
recv2:	mov	outptr,bx
	mov	ah,00h			;No errors returned
	sti
	ret
recv	endp

;----------------------------------------------------------------------------
;	Flush input buffer
;----------------------------------------------------------------------------
flush	proc	near
	cli
	and	dh,3
	mov	byte ptr hold,dh
	mov	ax,offset inbuf
	mov	outptr,ax
	mov	inptr,ax
	sti
	ret
flush	endp

;----------------------------------------------------------------------------
;	Get status
;----------------------------------------------------------------------------
stat	proc	near
	xor	ah,ah
	push	bx
	cli
	mov	bx,inptr
	cmp	bx,outptr
	sti
	pop	bx
	jz	stat1
	or	ah,01h			;There is data in the buffer
stat1:	in	al,12h			;Get UART status
	push	ax
	mov	al,byte ptr port12	;Get last value sent to UART
	call	outctl			;Send it again to clear error status
	pop	ax
	test	al,10h			;Overrun error?
	jz	stat2
	or	ah,02h
stat2:	test	al,08h			;Parity error?
	jz	stat3
	or	ah,04h
stat3:	test	al,20h			;Framing error?
	jz	stat4
	or	ah,08h
stat4:	test	al,08h			;Break detected?
	jz	stat5
	or	ah,10h
stat5:	test	al,01h			;TxRDY?
	jz	stat6
	or	ah,60h
stat6:	mov	al,20h			;Fake modem status (CTS and DSR set)
	ret
stat	endp

;----------------------------------------------------------------------------
;	INT 72 handler
;----------------------------------------------------------------------------
int72	proc	near
	cli
	push	ax
	push	bx
	push	cx
	push	dx
	push	ds
	push	cs
	pop	ds			;DS = CS

;	Get UART status. Check if a received character is ready.
	in	al,12h
	test	al,02h
	jz	exit72

;	Get character from UART and stuff in buffer
	in	al,10h
	push	ax
	and	al,7Fh			;Strip parity
	test	byte ptr hold,02	;Should we do XON/XOFF processing?
	jz	int72b
	cmp	al,'S'-40h		;Did we get an XOFF?
	jne	int72a
	or	byte ptr hold,10h	;Set "stop transmitting" flag
	pop	ax
	jmp	exit72
int72a:	cmp	al,'Q'-40h		;Did we get an XON?
	jne	int72b
	and	byte ptr hold,0EFh	;Clear "stop transmitting" flag
	pop	ax
	jmp	exit72
int72b:	pop	ax
	mov	bx,inptr		;Get buffer input pointer
	mov	[bx],al			;Store character in buffer
	inc	bx
	cmp	bx,offset inbuf+bufsiz	;At end of buffer
	jnz	ckfull
	mov	bx,offset inbuf		;Wrap around
ckfull:	cmp	bx,outptr		;Buffer full?
	jz	exit72
	mov	inptr,bx

;	Clear interrupt and return
exit72:	mov	al,36h			;Leave transmitter disabled
	call	outctl
	mov	dx,0060h
	mov	al,20h
	out	dx,al
	mov	al,0Bh
	out	dx,al			;Clear 8259 interrupt controler
	sti
	nop
	nop
	in	al,dx
	or	al,al
	jnz	not186
	cli
	mov	dx,0FF22h
	mov	ax,8000h
	out	dx,ax			;Clear 186 interrupt controler
not186:	sti
	pop	ds
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	iret
int72	endp

;----------------------------------------------------------------------------
;	Output byte to UART control port
;----------------------------------------------------------------------------
outctl	proc	near
	out	12h,al
	mov	byte ptr port12,al
	ret
outctl	endp

;----------------------------------------------------------------------------
;	Initialize everything
;----------------------------------------------------------------------------
setup	proc	near
	push	cs
	pop	ds			;DS=CS

;	Test version number
	mov	ah,30h
	int	21h			;GetVersion
	cmp	ax,0B02h		;Is it 02.11.xx?
	jz	ver_ok
	mov	dx,offset badver
	call	prtmsg			;Print bad version message
	mov	ax,4C01h
	int	21h			;Exit
ver_ok:

;	Get old vector
	mov	ax,3514h		;Get vector for INT 14h
	int	21h
	mov	oldint+2,es		;Save old interrupt segment
	mov	oldint,bx		;Save old interrupt offset
	mov	ax,es
	cmp	ax,0060h		;Already installed
	jz	not_in
	mov	dx,offset inserr
	call	prtmsg			;Print already installed message
	mov	ax,4C01h
	int	21h			;Exit
not_in:

;	Setup pointers
	cli				;We need silence for this next trick
	xor	al,al
	mov	byte ptr hold,al
	mov	ax,offset inbuf
	mov	inptr,ax		;Set input pointer
	mov	outptr,ax		;Set output pointer

;	Install new vectors
	mov	ax,2514h		;Set vector for INT 14h
	mov	dx,offset int14
	int	21h
	mov	ax,2553h		;Set vector for INT 53h
	int	21h
	mov	ax,2572h		;Set vector for INT 72h
	mov	dx,offset int72
	int	21h
	sti

;	Initialize RS232 port to 300 baud, 8 bits, No parity, 1 stop bit
	mov	ah,00h
	mov	al,43h
	mov	dx,0000h
	int	14h

;	Let the user know everything is OK and exit
	mov	dx,offset insmsg
	call	prtmsg			;Print installed message
	mov	ax,offset setup		;First byte after resident code
	mov	cl,4
	shr	ax,cl			;Turn into paragraph count
	inc	ax			;Keep one extra paragraph
	mov	dx,ax			;DX=final paragraph count
	mov	ax,3100h		;Keep process
	int	21h
setup	endp

;----------------------------------------------------------------------------
;	Print message
;----------------------------------------------------------------------------
prtmsg	proc	near
	mov	ah,09
	int	21h
	ret
prtmsg	endp

	db	'Copyright 1985 by John McNamee.'
insmsg:	db	13,10,'RS232 fix for Tandy 2000 BIOS installed.',13,10,'$'
badver:	db	13,10,'This fix is only needed on MSDOS 02.11.02!',13,10,'$'
inserr:	db	13,10,'RS232 fix already installed!',13,10,'$'

code	ends

	end	entry
