[net.sources] Talking PC6300 For The Blind, part 10 of 11

eklhad@ihuxv.UUCP (02/11/87)

;------------------------------------------------------------------------------
;	Talking console device driver for the AT&T PC6300.
;	Written by Karl Dahlke, September 1986.
;	Property of AT&T Bell Laboratories, all rights reserved.
;	This software is in the public domain and may be freely
;	distributed to anyone, provided this notice is included.
;	It may not, in whole or in part, be incorporated in any commercial
;	product without AT&T's explicit permission.
;------------------------------------------------------------------------------

;	terminal.asm: dumb terminal emulator

;	little more than a bidirectional cat running at interupt level.
;	given the talking device driver, I neither need nor want more.

;	must be a .com program, run exe2bin.

BUFSZ	equ 256 ; size of input and output circular buffers
UDSZ	equ 1024 ; size of upload/download temp buffer
STKSZ	equ 256 ; size of the stack

XOFF	equ 19 ; flow control supported in both directions
XON	equ 17

;	terminal can be on com1 or com2
COM	equ 2

RSBASE equ 4f0h-COM*100h    ;base of address of aux. port registers
DATREG equ RSBASE + 8     ;data register
LDL equ RSBASE + 8        ;low divisor latch
HDL equ RSBASE + 9        ;high divisor latch
IER equ RSBASE + 9        ;interrupt enable register
IIR equ RSBASE + 0ah        ;interrupt identification register
LCR equ RSBASE + 0bh        ;line control register
MCR equ RSBASE + 0ch        ;modem control register
LSR equ RSBASE + 0dh        ;line status register
MSR equ RSBASE + 0eh        ;modem status register


;	buffers follow text, in uninitialized memory
rxbuf	equ offset PGROUP: udata+0
txbuf	equ offset PGROUP: udata+BUFSZ
udarea	equ offset PGROUP: udata+BUFSZ+BUFSZ
maxmem	equ offset PGROUP: udata+BUFSZ+BUFSZ+UDSZ+STKSZ


PGROUP  group   PROG, DATA, UDATA
	assume  cs:PGROUP, ds:PGROUP


PROG    segment para public 'PROG'

	org 100h

term	proc near
;	is there enough memory?
	sti
	cmp sp,maxmem-2
	jae memok
	mov ah,9
	mov dx,offset PGROUP: nmm
	int 21h
	mov ax,4c01h
	int 21h
	memok:

	cli
	cld
	xor ax,ax
	mov ds,ax

;	init the serial port
	mov dx,MCR
	mov al,0
	out dx,al
	mov dx,LSR         ;reset line status
	in al,dx
	mov dx,DATREG      ; clear any input
	in al,dx
	mov dx,MSR
	in al,dx

;	set baud rate to 1200 baud
	mov dx,LCR
	mov al,83h ; access divisor latch
	out dx,al
	mov dx,LDL
	mov al,96       ;low byte of divisor
	out dx,al
	mov dx,HDL
	mov al,0       ;high byte of divisor
	out dx,al
	mov dx,LCR
	mov al,3 ; 8 bits, no parity
	out dx,al

;	set interrupt vector
	mov word ptr [bx+34h-COM*4],offset PGROUP: ih_com
	mov [bx+36h-COM*4],cs

	mov dx,IER                 ;enable interrupts on 8250
	mov al,1 ; only receive interrupt initially
	out dx,al

	in al,21h                     ;set enable bit on 8259
	and al,0e7h+COM*8 ; bit 4/3 is serial interrupt
	nop ; timing
	nop
	out 21h,al

	mov dx,MCR
	mov al,7+COM*4 ; dtr and rts set
;	extra bit (8[4]) is needed to generate interrupts.
	out dx,al


	push cs
	pop ds

main:	; infinite loop
	sti
;	receive buffer to screen
	mov bx,rxtail
	cmp bx,rxhead
	jnz input
	jmp noinp
input:	mov al,[bx]
	and al,7fh
	mov bx,indl ; in download?
	or bx,bx
	jz nodl
	cmp al,26 ; eof
	jz wrfl
	mov [bx],al
	inc bx
	mov indl,bx
	cmp bx,udarea+UDSZ
	jb incrx
wrfl:	push ax
	mov ah,40h
	mov cx,bx
	mov bx,udfd
	mov dx,udarea
	sub cx,dx
	jz zlen
	int 21h ; write block
	jnc z1len
badwr:	mov dx,offset PGROUP: cwm
	mov ah,9
	int 21h ; display string
	pop ax
	jmp short dldone
z1len:	cmp ax,cx
	jnz badwr

zlen:	mov indl,udarea
	mov dl,'.'
	mov ah,6
	int 21h ; output '.'
	pop ax
	cmp al,26
	jnz incrx
dldone:	mov indl,0
	mov bx,udfd
	mov ah,3eh
	int 21h ; close file
	mov udfd,0
	jmp short incrx

nodl:	mov ah,6
	mov dl,al
	int 21h ; display character
incrx:	mov bx,rxtail
	inc bx
	cmp bx,rxbuf+BUFSZ
	jnz wrap4
	mov bx,rxbuf
wrap4:	mov rxtail,bx
	mov dx,rxhead
	sub dx,bx
	jae wrap6
	add dx,BUFSZ
wrap6:	cmp dx,50
	jnz noinp
	cli
	cmp sentxf,0
	jz noinp
	mov flow,XON
	mov sentxf,0
	jmp begx1

;	from keyboard to transmit buffer
noinp:	sti
	mov bx,inul
	or bx,bx
	jz g1
	cmp bx,ultop
	jb nr1
	mov ah,3fh
	mov bx,udfd
	or bx,bx
	jnz ulmore ; udfd set to 0 if the last read reached eof
	cmp lastc,26 ; if we sent a control-z, send an erase character
	jnz nocth
	mov bx,udarea
	mov byte ptr [bx],8
	mov inul,bx
	inc bx
	mov ultop,bx
nr1:	jnz noread
nocth:	mov ah,6
	mov dl,13
	int 21h ; display cr
	mov ah,6
	mov dl,10
	int 21h ; display nl
	mov inul,0
	jmp short g1
ulmore:	mov dx,udarea
	mov cx,UDSZ
	int 21h ; read block
	jnc rdok
	mov ah,3eh
	int 21h ; close file
	mov udfd,0
	mov ah,9
	mov dx,offset PGROUP: crm
	int 21h ; display string
	mov inul,0
g1:	jmp short getkey
rdok:	cmp ax,cx
	jz noeof
	push ax
	mov ah,3eh
	int 21h ; close file
	mov udfd,0
	pop ax
noeof:	mov bx,udarea
	mov inul,bx
	add ax,bx
	mov ultop,ax
	mov ah,6
	mov dl,'.'
	int 21h ; display '.'
	jmp short getkey ; in case 0 bytes read
noread:	mov bx,txtail
	cmp bx,txhead
	jnz getkey
	mov bx,inul
	mov al,[bx]
	mov lastc,al
	inc bx
	mov inul,bx
	cmp al,10
	jnz nolf
;	a linefeed, don't send it to the host computer
;	in addition, we delay here.
;	for some reason, the vax needs this pause after cr, to digest the line
	mov cx,8000h
	loop $
	jz getkey
nolf:	jmp outal

getkey:	mov dl,0ffh
	mov ah,6
	int 21h ; getchar
	jz l1 ; no keyboard characters
	cmp al,0
	jz alt
	jmp noalt
alt:	mov dl,0ffh
	mov ah,6
	int 21h ; getchar

	cmp al,26h
	jz exit
	cmp al,2dh
	jnz n2d
;	hang up, drop dtr
	mov dx,MCR
	mov al,0
	out dx,al

exit:	in al,21h
	or al,24-COM*8
	nop
	nop
	out 21h,al
	mov bx,udfd
	or bx,bx
	jz noopen
	mov ah,3eh
	int 21h ; close file
noopen:	mov ax,4c00h
	int 21h ; goodbye

n2d:	cmp al,16h
	jnz n16
;	upload
	call fname
	jz l1
	mov ax,3d00h
	int 21h ; open file
	jnc ok2
	mov dx,offset PGROUP: cam
	jc out1st
ok2:	mov udfd,ax
	mov ax,udarea
	mov inul,ax
	mov ultop,ax
	mov lastc,0
l1:	jmp main

n16:	cmp al,20h
	jnz n20
;	download
	call fname
	jz l1
	mov ah,3ch
	xor cx,cx
	int 21h ; create file
	jnc ok1
	mov dx,offset PGROUP: ccm
	jc outstr
ok1:	mov udfd,ax
	mov indl,udarea
	jmp main

n20:	cmp al,13h
	jnz n13
;	toggle baud rate
	mov dx,LCR
	cli
	mov al,83h ; access divisor latch
	out dx,al
	mov dx,LDL
	mov ax,480
	sub ax,curbaud
	mov curbaud,ax
	out dx,al
	mov dx,HDL
	mov al,ah       ;high byte of divisor
	out dx,al
	mov dx,LCR
	mov al,3 ; 8 bits, no parity
	out dx,al
	sti
	mov dx,offset PGROUP: baud12
	or ah,ah
	jz outstr
	mov dx,offset PGROUP: baud3
out1st:	jmp short outstr

n13:	cmp al,1fh
	jnz n1f
;	report status: dsr, carrier, and cts
	mov dx,MSR
	in al,dx
	mov dl, '-'
	test al,20h
	jz nodsr
	mov dl,'a'
nodsr:	mov swm+2,dl
	mov dl, '-'
	test al,80h
	jz nocar
	mov dl,'c'
nocar:	mov swm+3,dl
	mov dl, '-'
	test al,10h
	jz nocts
	mov dl,'s'
nocts:	mov swm+4,dl
	mov dx,offset PGROUP: swm
outstr:	mov ah,9
	int 21h ; display string
	jmp main

n1f:	cmp al,30h
	jnz n30
;	send break
	mov dx,LCR         ;save the line control register
	in al,dx
	or al,40h ; set break condition
	nop
	out dx,al
;	wait a while, in break
	mov cx,0ffffh
	loop $
	and al,0bfh
	out dx,al
n30:	jmp main

noalt:	cmp inul,0 ; send no keyboard characters if uploading
	jnz n30
outal:	mov bx,txhead
	mov dx,bx ; save
	mov [bx],al
	inc bx
	cmp bx,txbuf+BUFSZ
	jnz wrap5
	mov bx,txbuf
wrap5:	cmp bx,txtail
	jz n30 ; buffer full, character not sent
	cli
	mov txhead,bx
	cmp dx,txtail
	jnz n30
begx1:	cmp noxmit,0
	jnz n30
	mov dx,IER
	mov al,3
	out dx,al
	jmp main


term	endp


;	interrupt handler for serial interrupts
ih_com	proc far
	cld
	push ds
	push cs
	pop ds
	push ax
	push bx
	push dx

repoll:
;	When determining what actions to take, I find the IIR nigh unto
;	useless.  We have to look at LSR and MSR anyways, 
;	so why bother with IIR?
;	we read IIR, only because the interrupt system requires it.
	mov dx,IIR
	in al,dx
	nop
	nop
	mov dx,LSR
	in al,dx
	test al,1
	jnz rxint
	test al,20h
	jnz txint

intend:	mov al,65h-COM
	out 20h,al
	pop dx
	pop bx
	pop ax
	pop ds
	iret

txint:	mov dx,DATREG
	mov bx,txtail
	cmp noxmit,0
	jnz intend
	mov al,flow
	cmp al,0
	jnz send
	cmp bx,txhead
	jz intend ; nothing to transmit
	mov al,[bx]    ;get data from buffer
	inc bx
	cmp bx,txbuf+BUFSZ
	jnz wrap1
	mov bx,txbuf
wrap1:	mov txtail,bx
send:	out dx,al ; send data
	mov flow,0

	;if no more data to send then reset tx interrupt and return
	cmp bx,txhead
	jnz intend
stopx:	mov dx,IER
	mov al,1
	out dx,al
	jmp short intend

rxint:	mov dx,DATREG ; get input byte
	in al,dx
	cmp al,XOFF
	jnz noxoff
	inc noxmit
	jnz stopx
noxoff:	cmp al,XON
	jnz noxon
	mov noxmit,0
	mov bx,txtail
	cmp bx,txhead
	jz repoll
begx:	mov dx,IER
	mov al,3
	out dx,al
r1:	jmp repoll

noxon:	mov bx,rxhead
	mov [bx],al
	inc bx
	cmp bx,rxbuf+BUFSZ
	jnz wrap2
	mov bx,rxbuf
wrap2:	cmp bx,rxtail
	jz r1 ; sorry, buffer full, throw character away
	mov rxhead,bx
	sub bx,rxtail ; how full is receive buffer
	jae wrap3
	add bx,BUFSZ
wrap3:	cmp bx,BUFSZ-50
	jnz r1
	cmp sentxf,0
	jnz r1
	mov flow,XOFF
	inc sentxf
	cmp noxmit,0
	jz begx
	jnz r1

ih_com	endp

;	get filename to be transfered
fname	proc near
	mov ah,9
	mov dx,offset PGROUP: fnm
	int 21h ; display string
;	get file name using MS-DOS getline function call
	mov ah,0ah
	mov dx,offset PGROUP: fnlim
	int 21h ; get string
	mov bh,0
	mov bl,fnlen
	mov fnstr[bx],0 ; ASCII Z string
	mov dx,offset PGROUP: fnstr
	or bl,bl
	ret
fname	endp

PROG	ends

DATA    segment para public 'DATA'

indl	dw 0
inul	dw 0
ultop	dw 0
udfd	dw 0 ; file descripter

rxhead	dw rxbuf
rxtail	dw rxbuf
txhead	dw txbuf
txtail	dw txbuf

fnlim	db 50
fnlen	db 0
fnstr	db 50 dup(?)

curbaud	dw 96
noxmit	db 0
sentxf	db 0
flow	db 0
lastc	db 0

swm	db 13,10,"---",13,10,36
baud12	db 13,10,"1200 baud",13,10,36
baud3	db 13,10,"300 baud",13,10,36
fnm	db "file name? ",36
cam	db "cannot open file",13,10,36
ccm	db "cannot create file",13,10,36
crm	db 13,10,"cannot read from file",13,10,36
cwm	db 13,10,"cannot write to file",13,10,36
nmm	db "insufficient memory",13,10,36

DATA	ends

UDATA    segment para public 'UDATA'
UDATA	ends

	end term

-- 
	You know  ...  if it ain't patina, it's verdigris.
	Karl Dahlke   ihnp4!ihnet!eklhad