[net.micro.trs-80] Fix for Tandy 2000 RS232 hangup

jpm@bnl44.UUCP (John McNamee) (10/01/85)

The following program patches the Tandy 2000 BIOS so it won't lock up at
random times during RS232 I/O. Assemble with MASM, process with LINK, and then
use EXE2BIN to create RS232.COM. Place RS232 in your AUTOEXEC file. If you
lack MASM or don't know how to use it, send me mail and maybe I can arrange to
send you a disk. I can only do this for limited number of people since I'm not
in the disk duplication business.

I've had this patch up on CompuServe for a number of months, and everybody
there seems very pleased with its operation. It has broken one program
(Tandy 2000 FIDO), but that program is known to have many bugs so I'm
not very concerned. The problem is probably not in my code since it works
with everything else.

This patch is available free for non-commercial use. I have retained a
contract killer to enforce my copyright. You wouldn't want you and your
family to become a murder statistic, would you?

[Note to Arpanet INFO-IBMPC moderator: Feel free to include this in your
online library.]

;----------------------------------------------------------------------------
;
;	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
-- 
John P. McNamee		decvax!philabs!sbcs!bnl44!jpm		jpm@BNL44.ARPA