
	title term

; edit history
; Fixed destructive tab problem (received from many people)
; Fixed insert/delete line problem when at bottom/top of screen.
; Newlines never scroll in inverse video lines
; Implemented cursor save/restore (from Univ. Md.)
; "Echo" status on mode corrected.
; JD, 6 December 1984

	public	term, gss		; entry points
	include	msdefs.h

; some character definitions

chesc	equ	27
bel	equ	7

print_out equ	05h			; dos function to print to printer
pbout	equ	02h			; dos function to print a character
prscan	equ	72h			; print-screen scan code...
upscan	equ	49h			; up page
dnscan	equ	51h			; down page
ctlup	equ	84h			; ctl-up page
ctldn	equ	76h			; ctl-down page
homscn	equ	47h			; home screen
endscn	equ	4fh			; end of screen
screen	equ	10h			; bios screen call
kb	equ	16h			; keyboard interrupt
alt_shift equ	8H			; alt shift key down
ctl_shift equ	4H			; ctl key down
left_shift equ	2H			; left shift key down
right_shift equ	1H			; right shift key down

timer	equ	40h			; timer port
bel_prt	equ	61h			; speaker control

crt_status equ	3dah			; crt status port
disp_enb   equ	8			; display enable bit

uparr	equ	48h			; scan codes for arrow keys
dnarr	equ	50h
lftarr	equ	4bh
rgtarr	equ	4dh


modfrm	struc				; format of mode line
	db	'Esc chr: '
m_echr	db	2 dup (?)
	db	', Port: '
m_prt	db	1 dup (?)
	db	', Speed: '
m_baud	db	4 dup (?)
	db	', Parity: '
m_par	db	4 dup (?)
	db	', Echo: '
m_echo	db	3 dup (?)
	db	', Type '
m_hlp	db	2 dup (?)
	db	'? for Help'
modfrm	ends

datas	segment	public 'datas'
waste	db	100h dup (?)		;*** need this junk because assembler
					;*** generates non-relocatable offsets
					;*** for things like 
					;*** "sub di,offset foo"
					;*** if offset foo < 100H
; stuff for screen routines
flags	db	?			; status flags...
flags1	db	0			; internal flags.
prtscr	equ	80h			; print screen pressed
lnwrap	equ	40h			; line wrap enabled.
inited	equ	08h			; been here before...
cursor	dw	?
esc_ch	db	?
argadr	dw	?			; address of arg blk
ckeys	db	0,prscan,dnscan,upscan,endscn,homscn,ctlup,ctldn
	db	uparr,dnarr,lftarr,rgtarr
lckeys	equ	$-ckeys
; ckacts must parallel ckeys above...
ckacts	dw	trnbrk,trnprs,upwpg,dnwpg,endwnd,homwnd,dnwind,upwind
	dw	trnupw,trndnw,trnlfw,trnrgw

uptrn	db	esc,'A'
dntrn	db	esc,'B'
rgtrn	db	esc,'C'
lftrn	db	esc,'D'

spctab	db	chesc,cr,lf,bs,tab,bel
lspctab	equ	$-spctab
spcjmp	dw	outesc,outcr,outlf,outbs,outtab,outbel	; must match spctab
esctab	db	'YABCDEFGHIJKLM'
	db	'NOZ@[pq<vw'
	db	'jk'
lesctab	equ	$-esctab
; escjmp must parallel esctab above
escjmp	dw	movcur,curup,curdwn,currt,outbs,clrscr,outign,outign,curhom
	dw	revind,clreow,clreol,inslin,dellin,delchr,noins
	dw	vtident,entins,doansi
	dw	invvid,nrmvid,outign,dowrap,nowrap
	dw	savecur,restcur
vtidstr	db	chesc,'/K'
lvtidst	equ	$-vtidstr
coord	dw	?
insmod	db	?
wcoord	dw	?
ttstate	dw	outtt0
curattr	db	?			; current attribute
ansarg	db	?			; ansi argument value
igncnt	db	?			; # of chars to ignore
beldiv	dw	2dch			; 550 hz?
crt_mode db	?
crt_cols db	?
crt_lins db	?	
low_rgt	dw	?			; lower right corner of window
; key redefinitions
ktrntab	dw	?			; address of translation table
krpltab	dw	?			; address of replacement table
tmptab	db	0eh,3bh			; scan code for bs, f1
ktlen	dw	?
modbuf	modfrm	<>			; mode line buffer
; routine to call for captured output
captrtn	dw	?
oldcur	dw	0			; save'd cursor position
; some static data for mode line
unkbaud	db	'Unk '			; must be 4 chars...
baudn	db	'45.5'
	db	'  50'
	db	'  75'
	db	' 110'
	db	' 135'
	db	' 150'
	db	' 300'
	db	' 600'
	db	'1200'
	db	'1800'
	db	'2000'
	db	'2400'
	db	'4800'
	db	'9600'
baudnsiz  equ	14			; # of baud rates known (tbl size / 4)
parnams	db	'Even'
	db	'Mark'
	db	'None'
	db	'Odd '			; must be 4 chars
	db	'Spc '
offmsg	db	'Off'
onmsg	db	'On '
lclmsg	db	'Lcl'
remmsg	db	'Rem'
; storage for multi-window stuff
swidth	equ	80
slen	equ	24
npgs	equ	5			; # of pages on each side
bsize	equ	swidth*slen*npgs*2
scrsav	dw	swidth*slen dup (0700H)	; a blank screen

; circular buffer.  To work properly, the buffer size should be an exact
; multiple of swidth*2
cbuf	struc
pp	dw	?			; place ptr in buffer
bend	dw	?			; end of buffer
orig	dw	?			; buffer origin
lcnt	dw	0			; # of lines in buffer.
cbuf	ends

topbuf	db	bsize dup (?)
botbuf	db	bsize dup (?)		; top and bottom windows
tlbuf	db	swidth*2 dup (?)
blbuf	db	swidth*2 dup (?)
twnd	cbuf	<topbuf,topbuf+bsize-1,topbuf,0>
bwnd	cbuf	<botbuf,botbuf+bsize-1,botbuf,0>
portno	db	?
prton	db	'Printer: on'
prtnlen	equ	$-prton
prtoff	db	'Printer: off'
prtflen	equ	$-prtoff
datas	ends

code	segment	public			; code segment
	extrn	prtchr:near,outchr:near,sendbr:near
	assume	cs:code,ds:datas,es:datas

scrini	proc	near			; init screen stuff
	mov	ah,15			; read video state...
	int	screen
	mov	crt_mode,al		; save crt mode
	cmp	ah,crt_cols		; is real # of cols < passed?
	jge	scrin1			; no
	mov	crt_cols,ah		; yes, save # of cols
scrin1:	mov	dl,crt_cols		; # of cols again
	mov	dh,crt_lins		; and # of rows
	dec	dl
	dec	dh
	mov	low_rgt,dx		; save away window address
	mov	insmod,0		; not in insert mode
	mov	dx,cursor		; assume old cursor
	test	flags1,inited		; have we been here before?
	jnz	scrin4			; yes, use old cursor
	mov	curattr,07		; else set nice screen attribute
	mov	ttstate,offset outtt0	; normal screen state
	mov	ah,3			; figure out where cursor is
	xor	bh,bh			; page 0
	int	screen			; read cursor position
	cmp	dh,crt_lins		; past logical end of screen?
	jb	scrin2			; no, keep going
	mov	dh,byte ptr low_rgt+1	; yes, just use lower right corner
scrin2:	cmp	dl,crt_cols		; maybe past right margin
	jb	scrin3			; no, use the way it is
	mov	dl,byte ptr low_rgt
scrin3:	mov	cursor,dx		; init cursor
scrin4:	mov	ah,2
	xor	bh,bh
	int	screen			; set cursor in case it moved
	ret
scrini	endp

argini	proc	near			; read passed arguments
	mov	bx,argadr		; base of argument block
	mov	al,[bx].flgs		; get flags
	and	al,capt+emheath+havtt+trnctl+lclecho+modoff
	mov	flags,al		; mask for allowable and save
	and	flags1,not (prtscr)	; these are allowable
					; (others remain).
	mov	al,[bx].prt
	cmp	al,portno		; using same port?
	je	argin1			; yes, go on
	and	flags1,not inited	; else re-init stuff
argin1:	mov	portno,al		; update port number
	mov	al,[bx].cols
	mov	crt_cols,al
	mov	al,[bx].rows
	mov	crt_lins,al		; init # of rows and cols
	mov	ax,[bx].captr
	mov	captrtn,ax		; buffer capture routine
	mov	ax,[bx].belld
	mov	beldiv,ax		; bell divisor
	mov	ax,[bx].klen
	mov	ktlen,ax		; length of key redef tbl
	mov	ax,[bx].ktab
	mov	ktrntab,ax		; save key translation table
	mov	ax,[bx].krpl
	mov	krpltab,ax
	mov	al,[bx].escc
	mov	esc_ch,al
	ret				; that's it
argini	endp

modlin	proc	near			; turn on mode line
	mov	al,esc_ch
	mov	modbuf.m_echr,' '	; first char is initial space
	mov	modbuf.m_hlp,' '	; goes here too.
	cmp	al,32			; printable?
	jnb	modl1			; yes, keep going
	add	al,40h			; made printable
	mov	modbuf.m_echr,'^'	; note control char
	mov	modbuf.m_hlp,'^'
modl1:	mov	modbuf.m_echr+1,al	; fill in character
	mov	modbuf.m_hlp+1,al
	mov	bx,argadr		; get argument block
	mov	al,[bx].baudb		; get baud bits
	mov	si,offset unkbaud	; assume unknown baud
	cmp	al,baudnsiz		; too big?
	jnb	modl2			; yes, use default
	mov	cl,2			; each is 4 bytes long
	shl	al,cl
	mov	ah,0
	add	ax,offset baudn
	mov	si,ax
modl2:	mov	cx,size m_baud		; length of baud space
	mov	di,offset modbuf.m_baud
	rep	movsb			; copy in baud rate
	mov	al,[bx].parity		; get parity code
	mov	cl,2			; each is 4 bytes long...
	shl	al,cl
	mov	ah,0
	add	ax,offset parnams	; names of parity settings
	mov	si,ax
	mov	cx,4			; each is 4 long
	mov	di,offset modbuf.m_par
	rep	movsb
	mov	si,offset remmsg	; Assume remote echoing.
	test	flags,lclecho		; Is remote side echoing?
	jz	modl4			; Yes, keep going
	mov	si,offset lclmsg	; Else it's local echoing. 
modl4:	mov	cx,3			; size of on/off
	mov	di,offset modbuf.m_echo
	rep	movsb
	mov	al,'1'
	cmp	portno,1		; port 1?
	je	modl5			; yes, keep going
	mov	al,'2'
modl5:	mov	modbuf.m_prt,al		; fill in port number
	mov	cx,size modfrm		; this is size of mode line
	mov	si,offset modbuf	; mode line image
; alternate entry to write an alternate mode line
modwrt:	push	cx
	push	si			; save mode line and size
	mov	dx,24 * 100h		; 25th line for mode line
	push	word ptr curattr	; save current attributes
	mov	curattr,70h		; want inverse video
	call	clreol			; clear to end of line...
	pop	word ptr curattr	; restore attributes
	mov	dx,24 * 100h
	mov	bh,0
	mov	ah,2			; set cursor position
	int	screen
	pop	si
	pop	cx			; restore these
modl6:	lodsb				; get a byte
	mov	ah,14			; write to terminal
	mov	bh,0			; page 0
	int	screen
	loop	modl6			; write out entire mode line
	mov	dx,cursor
	mov	ah,2
	mov	bh,0
	int	screen			; put cursor back where it belongs
	ret				; and return
modlin	endp

clrmod	proc	near			; clear mode line
	mov	ax,600h			; blank window
	mov	cx,24 * 100h		; beginning of window
	mov	dx,24 * 100h + 79	; end of window
	mov	bh,07			; nice attribute
	int	screen			; clear mode line
	ret				; and return
clrmod	endp

term	proc	near			; terminal emulator entry point

	mov	argadr,ax		; save argument ptr
	push	es			; save caller's extra segment address
	mov	ax,seg datas
	mov	es,ax

	call	argini			; init options from arg address

	call	scrini			; init screen stuff

	test	flags1,inited		; have we run yet?
	jz	term1			; no, forget this part
	call	restscr			; restore screen
term1:	or	flags1,inited		; remember we've run already.

	call	clrmod			; empty mode line
	test	flags,modoff		; is mode line disabled?
	jnz	lp			; yes, skip it
	call	modlin			; turn on mode line

lp:	call	portchr			; char at port?
	 jnc	chkinp			; no, keep going
	call	outtty			; print on terminal

chkinp:	mov	ah,1
	int	kb
	jz	lp			; nothing available...
	xor	ah,ah
	int	kb			; get the char from the buffer
	push	ax			; save character temporarily
	call	gss			; get shift state into al
	mov	bl,al			; save shift state
	pop	ax

	cmp	al,esc_ch		; escape character?
	je	quit			; yes, stop here

	call	trnout			; translate if nec., output to prt
	jmp	chkinp			; and keep going

quit:	call	clrmod			; erase mode line
	call	savescr			; save screen

	mov	al,flags
	mov	bx,argadr
	mov	[bx].flgs,al		; update flags in arg block
	pop	es			; restore segment register
	ret				; and return to caller

term	endp

; get shift state into al.  We only care about shift, ctl, and alt keys.
; right shift is collapsed into left shift.
gss	proc	near
	mov	ah,2
	int	kb			; get current shift state
	mov	bl,al			; copy for a moment
	and	bl,right_shift		; mask out all but right shift
	shl	bl,1			; move right shift to left shift pos
	or	al,bl			; collapse shift bits
	and	al,(left_shift + alt_shift + ctl_shift)
	ret
gss	endp

; save the screen so we can restore it
; maybe save cursor also.
savescr	proc	near
	push	ds
	mov	si,0
	mov	di,offset scrsav	; place to put screen
	mov	cx,80*24		; # of words on screen
	call	scrseg
	push	ax			; save screen segment
	call	scrwait			; wait for screen to be ready
	pop	ds			; address screen
	rep	movsw			; save the screen
	pop	ds			; restore this
	ret				; and return
savescr	endp

; restore screen from scrsav buffer
restscr	proc	near
	push	es
	mov	si,offset scrsav	; source
	mov	di,0
	mov	cx,80*24
	call	scrseg
	mov	es,ax
	call	scrwait
	rep	movsw			; restore it
	pop	es
	ret
restscr	endp

; send the character in al out to the serial port
; handle echoing also...
outprt	proc	near
	test	flags,lclecho		; echoing?
	jz	outpr1			; no, forget it
	push	ax			; save char
	call	outtty			; print it
	pop	ax			; restore
outpr1:	mov	ah,al			; this is where outchr expects it
	call	outchr			; output to the port
	 nop
	 nop
	 nop				; skip returns...
	ret
outprt	endp

; returns with carry on if a character is available

portchr	proc	near
	call	prtchr			; character at port?
	 jmp	short portc1		; yes, go handle
	nop				; skip return is stupid...
	clc				; no carry -> no character
	ret				; and return...
portc1:	and	al,7fh			; we don't worry about parity here
	stc				; have a character
	ret				; and return
portchr	endp


; translate the scan code in ah according to the translate table
; given in ktrntab/krpltab, output to port.  If no translation,
; use ascii char in al. (should probably include shift state
; somewhere).  Shift state is in bl.
trnout	proc	near
	test	flags,havtt		; translate table given?
	jz	trnou3			; no, just output character
	push	ax			; save original value
	mov	al,ah			; put scan code into ah
	mov	ah,bl			; shift state into top half.
	mov	di,ktrntab		; pick up translate tbl
	mov	cx,ktlen		; length of tbl
	repne	scasw			; look for our key
	pop	ax			; recover character
	jne	trnou3			; not found, forget it
	sub	di,ktrntab		; get index into tbl
	sub	di,2			; (minus 2 for pre-increment)
	mov	bx,krpltab		; get replacement table
	mov	si,[bx][di]		; and addr of replacement
	mov	cl,[si]			; get first byte (length)
	xor	ch,ch			; clear high-order byte
	inc	si			; point to translation string
trnou2:	lodsb				; get a byte
	push	si
	push	cx			; save important registers
	call	outprt			; send to port
	pop	cx
	pop	si
	loop	trnou2			; send all chars
	ret				; and return
trnou3:	cmp	ah,4eh			;*** plus key thing?
	je	trnmod			; yes, go toggle mode line
	cmp	al,0			; is it a special code?
	jne	trnou4			; no, don't do this
	mov	al,ah			; get scan code
	mov	cx,lckeys		; length of table
	mov	di,offset ckeys		; table address
	repne	scasb
	mov	al,0			; ascii code was 0...
	jne	trnou4			; not found, keep going
	sub	di,offset ckeys+1	; get table offset
	shl	di,1			; shift for word offset
	jmp	ckacts[di]		; jump to appropriate routine
trnou4:	call	outprt			; just output single char
	ret				; and return

trnmod:	test	flags,modoff		; mode line already off?
	jnz	trnm1			; yes, go turn on
	call	clrmod			; no, clear mode line here
	or	flags,modoff		; turn on flag
	ret				; and return
trnm1:	call	modlin			; turn on mode line
	and	flags,not modoff	; clear flag
	ret				; and return

trnbrk:	mov	ah,dconio
	mov	dl,0ffH
	int	dos			; read the bogus ^C DOS gets.
	call	sendbr
	ret
trnprs:	xor	flags1,prtscr		; flip the flag
	and	flags,not modoff	; turn on mode line
	mov	si,offset prton
	mov	cx,prtnlen
	test	flags1,prtscr		; did it go on?
	jnz	trnpr1			; yes, say so
	mov	si,offset prtoff
	mov	cx,prtflen
trnpr1:	call	modwrt			; write into mode line
	ret				; and return

; common entry for arrow keys
trnarr:	mov	cx,2			; length is always 2
	jmp	trnou2			; go send definition

trnupw:	mov	si,offset uptrn
	jmp	trnarr

trndnw:	mov	si,offset dntrn
	jmp	trnarr

trnlfw:	mov	si,offset lftrn
	jmp	trnarr

trnrgw:	mov	si,offset rgtrn
	jmp	trnarr

trnout	endp

; move viewing window up (screen moves down).
; alternate entry upwin2 doesn't beep if invalid.
upwind	proc	near
	mov	ax,offset tlbuf	; place to put line temporarily
	mov	bx,offset twnd ; where to get lines from
	call	getcirc		; try to get a line
	jnc	upwin3		; have a line, go show it
	call	outbel		; else ring bel
	ret			; and return
upwin2:	mov	ax,offset tlbuf
	mov	bx,offset twnd
	call	getcirc
	jnc	upwin3
	ret			; this just rets if no line avail.
upwin3:	mov	ax,offset blbuf	; place for bottom line
	call	getbot		; fetch bottom line
	mov	ax,offset blbuf
	mov	bx,offset bwnd
	call	putcirc		; save in circular buffer
	mov	ax,701h		; scroll down one line
	xor	cx,cx		; from top
	mov	dx,low_rgt	; to bottom
	mov	bh,curattr
	int	screen		; scroll it down
	mov	di,0		; offset for destination
	mov	si,offset tlbuf	; where to get line from
	mov	cx,swidth	; length of line
	push	es
	call	scrseg
	push	ax
	call	scrwait
	pop	es
	rep	movsw		; copy the line in
	pop	es		; restore this
	ret			; and return
upwind	endp


; move viewing window down a line (screen scrolls up)
; entry dwin2 does same w/out checking to see if scroll is legal
dnwind	proc	near
	mov	ax,offset blbuf	; place to put line temporarily
	mov	bx,offset bwnd ; where to get lines from
	call	getcirc		; try to get a line
	jnc	dnwin3		; have a line, go show it
	call	outbel		; else ring bel
	ret			; and return
dnwin2:	mov	ax,offset blbuf
	mov	bx,offset bwnd
	call	getcirc
	jnc	dnwin3
	ret			; this just rets if no line avail.
dnwin3:	call	scrprep		; save top line
	mov	ax,601h		; scroll up one line
	xor	cx,cx		; from top
	mov	dx,low_rgt	; to bottom
	mov	bh,curattr
	int	screen		; scroll it down
	mov	dx,low_rgt
	mov	dl,0		; get addr of last line
	call	scrloc
	mov	di,ax		; this is offset in dest
	mov	si,offset blbuf	; where to get line from
	mov	cx,swidth	; length of line
	push	es
	call	scrseg
	push	ax
	call	scrwait
	pop	es
	rep	movsw		; copy the line in
	pop	es		; restore this
	ret			; and return
dnwind	endp

; move viewing window down as much as possible...
endwnd	proc	near
	mov	cx,1000			; large number of lines
	jmp	dnwp1			; and enter dwnpg
endwnd	endp

; scroll viewing window down (contents move up) crt_lins times...
dnwpg	proc	near
	mov	cl,crt_lins
	mov	ch,0
dnwp1:	push	cx			; save this
	call	dnwin2
	pop	cx
	loop	dnwp1
	ret				; and return
dnwpg	endp

; home viewing window
homwnd	proc	near
	mov	cx,1000			; large # of lines
	jmp	upwp1			; join upwpg
homwnd	endp

; scroll viewing window up (screen moves down) a page
upwpg	proc	near
	mov	cl,crt_lins
	mov	ch,0
upwp1:	push	cx
	call	upwin2
	pop	cx
	loop	upwp1
	ret				; and return
upwpg	endp

; get the bottom line into the buffer pointed to by ax.
getbot	proc	near
	push	ds
	mov	di,ax			; save dest
	mov	cx,swidth
	mov	dx,low_rgt
	mov	dl,0
	call	scrloc
	mov	si,ax
	call	scrseg
	push	ax
	call	scrwait
	pop	ds
	rep	movsw
	pop	ds
	ret
getbot	endp

; put a line into the circular buffer.  Pass the buffer structure
; in bx, the pointer to the line in ax.
putcirc	proc	near
	push	si
	push	di
	push	cx
	push	dx
	mov	di,[bx].pp		; pick up buffer ptr
	add	di,2*swidth		; increment to next avail slot
	cmp	di,[bx].bend		; past end?
	jb	putci1			; no, leave alone
	mov	di,[bx].orig		; else start at beginning
putci1:	mov	[bx].pp,di		; update ptr
	mov	si,ax			; this is source
	mov	cx,swidth
	rep	movsw			; copy into buffer
	cmp	[bx].lcnt,npgs*slen	; can we increment it?
	jae	putci2			; no, keep going
	inc	[bx].lcnt		; else count this line
putci2:	pop	dx
	pop	cx
	pop	di
	pop	si			; restore registers
	ret
putcirc	endp

; get a line from the circular buffer, removing it from the buffer.
; returns with carry on if the buffer is empty.
; pass the buffer structure in bx, the buffer to copy the line into
; in ax.
getcirc	proc	near
	push	si
	push	di
	push	cx
	push	dx
	cmp	[bx].lcnt,0		; any lines in buffer?
	jne	getci1			; yes, ok to take one out.
	stc				; else set carry
	jmp	short getcir3		; and return
getci1:	mov	si,[bx].pp		; this is source
	mov	di,ax			; this is dest
	mov	cx,swidth		; # of chars to copy
	rep	movsw
	mov	si,[bx].pp		; get ptr again
	sub	si,2*swidth		; move back
	cmp	si,[bx].orig		; compare to origin
	jae	getcir2			; still in range, continue
	mov	si,[bx].bend		; else use end of buffer
	sub	si,2*swidth-1		; minus length of a piece
getcir2:mov	[bx].pp,si		; update ptr
	dec	[bx].lcnt		; decrement # of lines in buffer
	clc				; make sure no carry
getcir3:pop	dx
	pop	cx
	pop	di
	pop	si
	ret
getcirc	endp

; call before scrolling to save top line...
scrprep	proc	near
	push	ds
	mov	si,0			; offset of top line
	mov	cx,swidth		; length of line
	mov	di,offset tlbuf		; place to put line temporarily
	call	scrseg
	push	ax
	call	scrwait
	pop	ds
	rep	movsw			; copy the line
	pop	ds			; restore this
	mov	ax,offset tlbuf
	mov	bx,offset twnd		; this is where it goes
	call	putcirc			; put into buffer
	ret				; and return
scrprep	endp


; put the character in al to the screen
outtty	proc	near
	test	flags,capt		; capturing output?
	jz	outnoc			; no, forget this part
	push	ax			; save char
	call	captrtn			; give it captured character
	pop	ax			; restore character and keep going

outnoc:	test	flags1,prtscr		; should we be printing?
	jz	outnop			; no, keep going
	push	ax
	mov	ah,print_out
	mov	dl,al			; put character here for dos...
	int	dos
	pop	ax

outnop:	test	flags,emheath		; emulating heath?
	jnz	outnop1			; yup, go do something smart
	mov	dl,al
	mov	ah,pbout
	int	dos			; else let dos print char
	ret				; and return

outnop1:mov	dx,cursor		; these may need cursor...
	jmp	ttstate			; jump according to current state

outtt0:
	cmp	al,32			; special character?
	jb	outtt1			; yes, handle specially...

	cmp	insmod,0		; in insert mode?
	je	outnrm			; no, output normal
	push	ax			; save character
	call	inschr			; insert a character
	pop	ax
outnrm:	xor	bh,bh			; current page
	mov	cx,1			; only one char
	mov	bl,curattr		; with current attribute
	mov	ah,9
	int	screen			; put onto screen
	mov	dx,cursor		; get cursor pos
currt:	inc	dl			; bump col
	cmp	dl,crt_cols		; see if in range
	jb	setcur			; in range, go set cursor
	test	flags1,lnwrap		; in wrap mode?
	jz	outign			; no, just return w/out updating cursor
wrap:	xor	dl,dl
	inc	dh			; handle wrap
setcur:	cmp	dh,crt_lins
	jb	setc1			; not off end, keep going
	push	dx			; save row/col
	call	scrprep			; save top line in window buf
	mov	ax,0601h		; scroll up one line
	xor	cx,cx			; from 0,0
	mov	dx,low_rgt		; to 24,80
	mov	bh,7			; nice attribute
	int	screen			; do the scroll
	pop	dx
	mov	dh,crt_lins		; go to bottom line again...
	dec	dh
setc1:	xor	bh,bh			; page is 0
	mov	cursor,dx		; save cursor pos
	mov	ah,2
	int	screen			; set cursor
outign:	ret				; and return
; special character (in al)
outtt1:	mov	di,offset spctab	; special char table
	mov	cx,lspctab		; length of tbl
	repne	scasb			; look for char in tbl
	jz	outtt2			; found, go do something with it
	test	flags,trnctl		; are we allowed to print carets?
	jz	outign			; no, just ignore it.
	push	ax			; save char
	mov	al,'^'
	call	outtty			; print caret
	pop	ax
	add	al,'A'-1		; make printable
	jmp	outtty			; print, then return

outtt2:	mov	dx,cursor		; might need cursor pos
	sub	di,offset spctab+1	; get index of char
	shl	di,1			; double for word offset
	jmp	spcjmp[di]		; and go handle

; special char routines.  cursor is in dx, char in al

outlf:	inc	dh			; bump row
	jmp	setcur

outcr:	xor	dl,dl			; set col to 0
	jmp	setcur

outbs:	or	dl,dl
	jle	setcur			; col 0, can't back up
	dec	dl			; back up col
	jmp	setcur			; and use if reasonable

outtab:	mov	dl,byte ptr cursor	; get initial column
	add	dl,8			; tab is at most 8 columns
	and	dl,not 111b		; round down to a multiple of 8
	cmp	dl,crt_cols		; out of range?
	jb	setcur			; no, go set it
	test	flags1,lnwrap		; in wrap mode?
	jnz	outta1			; yes, wrap to next line
	mov	dl,byte ptr low_rgt	; else just move to right margin
	jmp	setcur
outta1:	jmp	wrap
	
; stolen from bios
outbel:	mov	al,10110110b		; timer initialization
	out	timer+3,al
	mov	ax,beldiv		; bel divisor
	out	timer+2,al
	mov	al,ah
	out	timer+2,al		; output divisor
	in	al,bel_prt
	mov	ah,al			; remember original value
	or	al,3			; turn speaker on
	out	bel_prt,al
	mov	cx,8888h
outbe1:	loop	outbe1			; wait a while
	mov	al,ah
	out	bel_prt,al		; turn bell off
	ret				; and return

outesc:	mov	ttstate,offset escseq	; expect escape sequence.
	ret				; and return

; escape-char handling routines
escseq:	mov	ttstate,offset outtt0	; put state back to normal
	mov	di,offset esctab	; escape char tbl
	mov	cx,lesctab		; length of tbl
	repne	scasb			; look for it in tbl
	jz	escsq1			; found, go use it
	jmp	outtty			; not there, just print it
escsq1:	sub	di,offset esctab+1	; get offset into tbl
	shl	di,1			; convert to word offset
	jmp	escjmp[di]		; and go dispatch on it

; escape dispatch routines
revind:	cmp	dh,0
	jle	revin1
	dec	dh			; back up a row
	jmp	setcur			; and go set cursor
revin1:	push	dx			; save cursor pos
	mov	ax,701h			; scroll down one line
	xor	cx,cx			; from top
	mov	dx,low_rgt		; to bottom
	mov	bh,curattr
	int	screen			; scroll it down
	pop	dx			; restore cursor.
	mov	dh,0			; set row back to 0
	jmp	setcur

curup:	cmp	dh,0			; w/in range?
	jle	curu1			; no, skip this
	dec	dh			; else back up
curu1:	jmp	setcur			; and go set position

curdwn:	inc	dh
	jmp	setcur			; increment row (setcur can scroll!)

; currt is above

clrscr:	call	curhom			; go home cursor
	jmp	clreow			; then clear to end of window

curhom:	xor	dx,dx			; move to 0,0
	jmp	setcur

clreow:	cmp	dl,0			; at beginning of line?
	jz	clrw1			; yes, skip this part...
	push	dx			; remember cursor pos
	call	clreol			; clear to end of this line
	pop	dx
	inc	dh			; bump row
	xor	dl,dl			; start from col 0
clrw1:	cmp	dh,crt_lins		; last line on screen
	jnb	clrw2			; if not in range, forget it
	mov	ax,700h			; clear whole window
	mov	cx,dx			; this is beginning
	mov	dx,low_rgt
;	mov	dx,174fh		; this is lower right corner
	mov	bh,curattr		; default attribute
	int	screen			; go clear it
clrw2:	ret				; and return

clreol:	push	es
	mov	cl,crt_cols		; last col + 1
	sub	cl,dl			; this is # of chars to move
	xor	ch,ch
	jcxz	clrl1
	call	scrloc			; compute screen location (to ax)
	mov	di,ax
	call	scrseg
	mov	es,ax			; address screen segment
	call	scrwait			; wait for retrace
	mov	ah,curattr		; current attribute
	mov	al,' '			; fill char
	rep	stosw			; fill line with spaces
clrl1:	pop	es
	ret				; and return

inslin:	mov	al,1			; scroll one line
; alternate entry if inserting more then one line
inslin1:mov	ch,dh			; start at current row
	xor	cl,cl			; column 0
	mov	dx,low_rgt
	mov	ah,7h			; scroll down.
	mov	bh,curattr		; attribute
	cmp	ch,dh			; moving last line down?
	jne	insli2			; no, keep going
	mov	al,0			; yes, just clear it
insli2:	int	screen
	ret

dellin:	mov	al,1			; scroll 1 line
; alternate entry if deleting more than one line
dellin1:mov	ch,dh			; start at current row
	xor	cl,cl			; column 0
	mov	dx,low_rgt
;	mov	dx,174fh		; to bottom of screen
	mov	ah,6h			; scroll up.
	mov	bh,curattr		; attribute
	cmp	ch,dh			; deleting last line?
	jne	delli2			; no, go on
	mov	al,0			; yes, just blank it
delli2:	int	screen
	ret

delchr:	push	ds
	push	es
	pushf			; these may get changed...
	mov	cl,crt_cols
	dec	cl
	sub	cl,dl		; from what we're fiddling)
	xor	ch,ch
	jcxz	delch1		; none to move, forget it
	call	scrloc		; compute location
	mov	di,ax
	mov	si,ax
	add	si,2		; source is next position over
	call	scrseg		; pick up screen segment
	push	ax		; put screen segment onto stack
	mov	es,ax		; and in destination segment
	call	scrwait		; wait for retrace
	pop	ds		; address screen segment
	rep	movsw		; delete it
	mov	byte ptr [di],' ' ; kill char at end of line
delch1:	popf
	pop	es
	pop	ds
	ret

inschr:	push	ds
	push	es		; save these as well
	pushf			; might as well save flags...
	mov	dx,cursor	; this is place to do it
	mov	cl,crt_cols
	dec	cl
;	mov	cl,79		; this is last col to move, +1 for length
	sub	cl,dl		; compute distance to end
	xor	ch,ch		; clear top half of offset
	jcxz	insch1		; nothing to move...
	mov	dl,crt_cols
	sub	dl,2		; last col to move
;	mov	dl,78		; this is address of last col to move
	call	scrloc		; compute pos
	mov	si,ax
	mov	di,ax
	add	di,2		; destination is one byte over...
	std			; remember to move us backwards
	call	scrseg		; find screen segment
	mov	es,ax
	push	ax		; save screen seg on stack
	call	scrwait		; wait until save to write
	pop	ds		; address screen segment
	rep	movsw		; move each char and attribute
insch1:	popf
	pop	es
	pop	ds
	ret			; and return

noins:	mov	insmod,0		; turn off insert mode
	ret				; and return

movcur:	mov	wcoord,2		; want two coordinates...
	mov	ttstate,offset getcoord
	ret				; and return

vtident: mov	si,offset vtidstr
	mov	cx,lvtidst
vtid1:	lodsb				; get a byte from the string
	push	si			; have to save from outprt
	push	cx
	call	outprt			; send to serial port
	pop	cx
	pop	si
	loop	vtid1			; go thru all chars
	ret				; and return

entins:	mov	insmod,0ffh		; enter insert mode...
	ret				; and return

doansi:	mov	ansarg,0		; ansi argument is 0 (default)
	mov	ttstate,offset getaarg	; state is get ansi argument
	ret

getaarg:cmp	al,'0'
	jb	getaa1			; in range for digit?
	cmp	al,'9'
	ja	getaa1
	sub	al,'0'			; convert to binary
	mov	dl,al			; tuck away
	mov	al,ansarg
	mov	dh,10
	mul	dh			; shift sum
	add	al,dl			; add in this digit (what about ovfl?)
	mov	ansarg,al
	ret				; and return

getaa1:	cmp	al,'?'			; the dreaded question mark?
	jne	getaa2
	mov	ttstate,offset ignn	; we ignore these...
	mov	igncnt,2		; this is how many chars come after him
	ret

getaa2:	mov	ttstate,offset outtt0	; reset state
	mov	dx,cursor		; this needs cursor position
	mov	bl,ansarg
	xchg	al,bl			; put argument in nice place
	cmp	bl,'L'			; insert line?
	jne	getaa3
	jmp	inslin1			; and go do it

getaa3:	cmp	bl,'M'			; maybe delete line?
	jne	getaa4
	jmp	dellin1

getaa4:	ret				; ignore.

invvid:	mov	curattr,70h		; attribute for inverse video
	ret

nrmvid:	mov	curattr,07h		; attribute for normal video
	ret

dowrap:	or	flags1,lnwrap		; turn on wrap mode
	ret				; and return

nowrap:	and	flags1,not lnwrap	; turn off wrap mode
	ret				; and return

; get a coordinate.
getcoord:
	sub	al,32			; coordinates offset by 32
	mov	si,wcoord
	dec	si
	mov	byte ptr coord[si],al	; fill in appropriate coordinate
	mov	wcoord,si		; update flag
	jnz	getco1			; more needed, can't do anything yet
	mov	ttstate,offset outtt0	; reset state
	mov	dx,coord		; get coordinates
	jmp	setcur			; and go jump there
getco1:	ret

; ignore following igncnt characters
ignn:	dec	igncnt			; decrement count
	jnz	ignn1
	mov	ttstate,offset outtt0	; put state back to normal if done
ignn1:	ret

; save cursor
savecur:
	mov	oldcur,dx
	ret

; restore cursor
restcur:
	mov	dx,oldcur
	jmp	setcur
	
outtty	endp

; computes screen location to ax, given row and col in dx.
; trashes ax,bx
scrloc	proc	near
	mov	al,dh		; get row
	mov	bl,crt_cols	;** row size
	mul	bl		; multiply by row size
	xor	dh,dh		; clear col
	add	ax,dx		; this is current position
	sal	ax,1		; double for attributes
	ret
scrloc	endp

; puts current screen segment in ax
scrseg	proc	near
	mov	ax,0b000h		; assume bw for now
	cmp	crt_mode,7		; 7 is bw (***)
	je	scrse1
	mov	ax,0b800h		; color card
scrse1:	ret
scrseg	endp

; wait for retrace so can write to screen memory
scrwait	proc	near
	cmp	crt_mode,7		; bw mode?
	je	scrwa3			; yes, no waiting
	push	dx
	mov	dx,crt_status
scrwa1:	in	al,dx
	test	al,disp_enb		; display enable?
	jnz	scrwa1			; yes, keep waiting
scrwa2:	in	al,dx
	test	al,disp_enb		; now wait for it to go off
	jz	scrwa2			; so can have whole cycle
	pop	dx
scrwa3:	ret				; that was easy...
scrwait	endp
code	ends

if1
	%out [End of pass 1]
else
	%out [End of assembly]
endif

	end

