;-----------------------------------------------------------------------------
; name		: TEPROC.ASM
; object	: Tiny Editor - general routines
; type		: include file
; version	: 1.7
; date		: Feb 20, 2000
; author	: J R Ferguson
;-----------------------------------------------------------------------------

;
;compute video buffer offset
;
;in:		dh	row
;		dl	col
;out:		di	video buffer offset
;changes:	ax,flags
;
tovidofs:	mov	ax,[maxwincol]
		mul	dh
		add	al,dl
		adc	ah,0
		add	ax,ax
		mov	di,ax
		ret

		page
;
;display prompt, get ASCIIZ string
;default is current value of destination string
;
;in/out:	same as getstr
;
getstrmod:	mov	al,1
		jmp	short getstr1


;
;display prompt, get ASCIIZ string
;default is empty string
;
;in:	cs:si	prompt address (ASCIIZ)
;	cs:di	destination string address (cx+1 bytes)
;	cx	max string length
;out:	flags	C	escaped
;		NC,Z	success, string is empty
;		NC,NZ	success, string not empty
;	al	last char read
;
getstr:		xor	al,al
getstr1:	mov	[strmax],cx
		mov	[prmptr],si
		mov	[strbeg],di
		and	al,al			;if current value is default
		jne	getstr5a		; use ^R entrypoint

getstr2:	mov	byte ptr [inpstr],0	;reset input buffer and count
		mov	cx,[strmax]
getstr3:	push	cx			;display prompt
		mov	si,[prmptr]
		call	prompt
		mov	si,offset inpstr	;use current str as default
		call	promptat
		mov	di,si
		pop	cx
getstr4:	xor	ah,ah			;read keyboard char
		int	16h
		or	al,al			;extended keycode:
		jz	getstr4			; ignore
		caseal	escape,getstresc	;esc: abort
		caseal	<'U'-ctrl>,getstresc	;^U: abort
		caseal	cr,getstrok		;cr: return ok
		caseal	rubout,getstr2		;^<BS>: reset string
		jumpif	al,ne,bs,getstr5	;bs:
		jumpif	di,be,<offset inpstr>,getstr4; ignore if start of string
		dec	di			; else delete char left
		mov	byte ptr cs:[di],0
		inc	cx
		jmp	short getstr3
getstr5:	jumpif	al,ne,<'R'-ctrl>,getstr6;^R:
getstr5a:	mov	si,[strbeg]		; re-display previous entry
		mov	di,offset inpstr
		call	cpystrz
		mov	si,offset inpstr
		call	strlen
		mov	cx,[strmax]
		sub	cx,ax
		jmp	short getstr3
getstr6:	jumpif	al,ne,<'P'-ctrl>,getstr7;^P:
		xor	ah,ah			; get second char
		int	16h
		and	al,al			; extended keycode?
		jne	getstr6a		; no
		mov	al,ah			; yes: map to 128..255
		or	al,80h
getstr6a:	jmp	short getstr7a

getstr7:	call	isprint			;other key:
		jnc	getstr4			; ignore if not printable
getstr7a:	jcxz	getstr4			; ignore if string too long
		mov	cs:[di],al		; insert char
		inc	di
		mov	byte ptr cs:[di],0	; append 0-byte
		dec	cx			; dec char count
		jmp	short getstr3

getstresc:	stc
		jmp	short getstrret

getstrok:	cmp	di,offset inpstr	;normal exit: set flags
		pushf
		mov	si,offset inpstr	;copy inputbuf to destination
		mov	di,[strbeg]
		call	cpystrz
		popf
getstrret:	ret

		page
;
;display ASCIIZ prompt after current prompt, ask Yes/No
;
;in:	cs:si	prompt text pointer
;out:	flags	NC = answer was Yes (Y,y)
;		C  = answer was No (N,n,CR,ESC)
;	al	character read (uppercase)
;
noyesat:	call	promptat
		mov	si,offset xnoyes
		call	promptat
		push	dx
		call	setwincur
		pop	dx
		jmp	short noyes1			; wait for response


;
;display ASCIIZ prompt, ask yes/no, default No
;
;in:	cs:si	prompt text pointer
;out:	flags	NC = answer was Yes (Y,y)
;		C  = answer was No (N,n,CR,ESC,^U)
;	al	character read (uppercase)
;
noyes:		call	prompt
		mov	si,offset xnoyes
		call	promptat
noyes1:		push	dx				;save prompt row,col
		mov	ah,0
		int	16h
		pop	dx
		and	ah,ah
		jz	noyes2
		call	toupper
		caseal	<'Y'>,yesnox1
		caseal	<'N'>,yesnox0
		caseal	cr,yesnox0
		caseal	escape,yesnox0
		jumpif	al,ne,<'U'-ctrl>,noyes2
		mov	al,escape
		jmp	short yesnox0
	
noyes2:		beep					;unknown key: beep
		jmp	short noyes1			; wait for next key
		
		page
;
;display ASCIIZ prompt, ask yes/no, default Yes
;
;in:	cs:si	prompt text pointer
;out:	flags	NC = answer was Yes (Y,y,CR)
;		C  = answer was No (N,n,ESC,^U)
;	al	character read (uppercase)
;
yesno:		call	prompt
		mov	si,offset xyesno
		call	promptat
yesno1:		push	dx				;save prompt row,col
		mov	ah,0
		int	16h
		pop	dx
		and	ah,ah
		jz	yesno2
		call	toupper
		caseal	<'Y'>,yesnox1
		caseal	<'N'>,yesnox0
		caseal	cr,yesnox1
		caseal	escape,yesnox0
		jumpif	al,ne,<'U'-ctrl>,yesno2
		mov	al,escape
		jmp	short yesnox0
	
yesno2:		beep					;unknown key: beep
		jmp	short yesno1			; wait for next key
		
yesnox0:	call	yesnoecho
		stc
		ret
yesnox1:	call	yesnoecho
		clc
		ret

yesnoecho:	push	ax
		call	tovidofs
		pop	ax
		jmp	putinv

		page
;
;display ASCIIZ prompt cs:si
;
;in:	cs:si	prompt text pointer
;out:	cs:si	prompt text end pointer
;	dh	prompt row
;	dl	prompt end column
;
prompt:		mov	dh,byte ptr [prmwinrow]
		xor	dl,dl
;;		jmp	short promptat


;
;display ASCIIZ prompt cs:si at row dh, column dl
;
;in:	cs:si	prompt text pointer
;	dh	prompt row
;	dl	prompt column
;out:	cs:si	prompt text end pointer
;	dh	unchanged
;	dl	prompt end column
;
promptat:	call	tovidofs
prompt1:	lodsb
		or	al,al
		jz	prompt2
		ifnctrl	al,prompt1a
		inc	dl
		xpush	<ax,dx>
		mov	al,'^'
		call	putinv
		pop	dx
		pop	ax
		add	al,ctrl
prompt1a:	inc	dl
		push	dx
		call	putinv
		pop	dx
		jmp	short prompt1
prompt2:	dec	si
		push	dx
		call	setcur
		call	clreolN
		pop	dx
		setsta	drwinf
		ret

		page
;
;clear to end-of-line, invers attribute
;
;in :	dh	row
;	dl	column
;
clreolI:	mov	bh,[attrinv]
		jmp	short clreol


;
;clear to end-of-line, normal attribute
;
;in :	dh	row
;	dl	column
;
clreolN:	mov	bh,[attrnorm]
;;		jmp	short clreol


;
;clear to end-of-line
;
;in :	bh	display attribute
;	dh	row
;	dl	column
;
clreol:		call	tovidofs
		xor	dh,dh
		mov	cx,[maxwincol]
		sub	cx,dx
		jcxz	clreolX
		mov	al,' '
clreol1:	call	putchr
		loop	clreol1
clreolX:	ret

		page
;
;put wait message
;
putwait:	mov	[msgptr],offset xstawait
;;		jmp	putsta


;
;(re)write status info line or error message
;
putsta:		ressta	drwsta
		call	rowcol				;fill current row/col
putsta1:	mov	si,[msgptr]			;fill status message
		mov	di,offset xstamsg
		call	cpystr
		mov	si,offset xstaidn		;if status msg <> ident
		jumpif	si,e,[msgptr],putsta2
		mov	[msgptr],offset xstaidn 	; reset status msg
		setsta	drwsta				; rewrite next time
putsta2:	mov	al,' '				;fill text change mark
		iffalse	<byte ptr [txtchgsw]>,putsta3
		mov	al,cmchg
putsta3:	mov	[xstachg],al
		mov	si,offset xstatus		;display status line
		mov	dh,byte ptr [stawinrow]
		jmp	short putstr


;
;(re)write info line
;
putinf:		ressta	drwinf
		mov	si,offset xfkeys
		mov	dh,byte ptr [prmwinrow]
;;		jmp	short putstr

		page
;
;put interpreted ASCIIZ string to video buffer
;
;in:	dh	start row
;	ds:si	ASCIIZ string
;out:	dh	end row
;	dl	end col
;
;The following special characters in the string are recognized
;	0	end of string
;	1	switch to normal attribute
;	2	switch to invers attribute
;       3       switch to highlight attribute
;	9 (tab)	space to next tab field
;	13 (cr)	end-of-line, continue on next row
;
;The display attribute defaults to normal
;
putstr:		mov	bh,[attrnorm]			;set default attribute
		mov	[attrcur],bh

putstr0:	xor	dl,dl				;start at column 0
		call	tovidofs

putstr1:	lodsb					;get char
		ifzero	al,putstreol			;check end of string

		jumpif	al,ne,1,putstr2			;set normal attribute
		mov	bh,[attrnorm]
		mov	[attrcur],bh
		jmp	short putstr1

putstr2:	jumpif	al,ne,2,putstr3			;set invers attribute
		mov	bh,[attrinv]
		mov	[attrcur],bh
		jmp	short putstr1

putstr3:	jumpif	al,ne,3,putstr4			;set highlight attrib
		mov	bh,[attrhigh]
		mov	[attrcur],bh
		jmp	short putstr1

putstr4:	ifntab	al,putstr6			;tab
putstr5:	mov	al,' '
		call	putstrchr
		test	dl,07h
		jnz	putstr5
		jmp	short putstr1

putstr6:	jumpif	al,ne,cr,putstr7		;cr
		push	dx
		call	putstreol
		pop	dx
		inc	dh
		jmp	short putstr0

putstr7:	call	putstrchr
		jmp	short putstr1

putstreol:	mov	bh,[attrcur]
		push	dx
		call	clreol
		pop	dx
		ret

putstrchr:	mov	bh,[attrcur]			;put character
		push	dx
		call	putchr
		pop	dx
		inc	dl
putstrX:	ret
		page

;
;write ASCIIZ message at current position via DOS
;
;in:		ds:si	offset message
;
outmsg:		mov	dl,[si]
		and	dl,dl
		jz	putstrX
		mov	ah,02h				;output char
		int	21h
		inc	si
		jmp	short outmsg


;
;re-display complete window
;
putwin:		ressta	drwwin+drwdwn+drwrow
		mov	si,[pnt.winref]
		xor	cx,cx				;screen start row = 0
		jmp	short putdwn1


;
;re-display from current row
;
putdwn:		ressta	drwdwn+drwrow
		mov	si,[pnt.curtxt]
		call	_getsol
		mov	cx,[curtxtrow]			;cx = screen start row
		sub	cx,[wintxtrow]
putdwn1:	push	cx
		call	putrow1
		pop	cx
		inc	cx
		jumpif	cx,be,[maxwinrow],putdwn1
		ret

		page
;
;put current row to video buffer
;
putrow:		ressta	drwrow
		mov	si,[pnt.curtxt]
		call	_getsol
		mov	cx,[curtxtrow]			;cx = screen row
		sub	cx,[wintxtrow]
putrow1:	xor	bp,bp				;bp = display col
		mov	dx,bp
		mov	dh,cl
		call	tovidofs			;di = vid buf offset
		mov	ax,[windspcol]			;compute max abs col
		add	ax,[maxwincol]
		ifzero	<byte ptr [ctrmode]>,putrow2
		dec	ax
putrow2:	mov	[maxabscol],ax

putrowLoop:	jumpif	si,nb,[pnt.endtxt],putrowEof	;check end-of-text
		mov	al,es:[si]			;get char
		caseal	cr,putrowCR
		caseal	lf,putrowLF
		caseal	ff,putrowFF
		jumpif	bp,nb,[maxabscol],putrowMore
		ifntab	al,putrowLoopB
putrowLoopA:	mov	al,' '				;tab
		call	optput
		mov	ax,bp
		and	ax,07h
		jnz	putrowLoopA
		jmp	short putrowLoopEnd

putrowLoopB:	ifnctrl	al,putrowLoopC			;ctr-char: check mode
	ifzero	<byte ptr [ctrmode]>,putrowLoopEnd	; 0:no display
	jumpif	<byte ptr [ctrmode]>,e,1,putrowLoopC	; 1:literal
		push	ax				; 2:^x (WS style)
		mov	al,'^'
		call	optput
		pop	ax
		add	al,ctrl
putrowLoopC:	call	optput				;other char
putrowLoopEnd:	inc	si
		jmp	short putrowLoop

putrowMore:	mov	al,cmmore
		jmp	short putrowEol

putrowEof:	mov	al,cmeof			;end-of-file
		jmp	short putrowEol

putrowFF:	mov	al,cmff				;form feed
		jmp	short putrowEol

putrowLF:	mov	al,cmlf				;line feed
		jmp	short putrowEol

putrowCR:	inc	si				;carriage return
	jumpif	si,nb,[pnt.endtxt],putrowCR1
	jumpif	<byte ptr es:[si]>,e,lf,putrowCRLF	;check cr-lf
putrowCR1:	dec	si
		mov	al,cmcr
		jmp	short putrowEol
putrowCRLF:	mov	al,cmcrlf
;;		jmp	short putrowEol

putrowEol:	push	ax
putrowEol1:	jumpif	bp,nb,[maxabscol],putrowEol2
		mov	al,' '
		call	optput
		jmp	short putrowEol1
putrowEol2:	pop	ax
		ifzero	<byte ptr [ctrmode]>,putrowRet
		call	puthigh
putrowRet:	jmp	_nxtsol

		page
;
;put highlighted character to video
;in:	al	character
;	di	video buffer offset
;
puthigh:	mov	bh,[attrhigh]
		jmp	short putchr


;
;put invers character to video
;in:	al	character
;	di	video buffer offset
;
putinv:		mov	bh,[attrinv]
		jmp	short putchr


;
;put normal character to video
;in:	al	character
;	di	video buffer offset
;
putnorm:	mov	bh,[attrnorm]
;;		jmp	short putchr


;
;put character to video buffer
;in:		al	character
;		bh	display attribute
;		di	video buffer offset
;out:		di	new video buffer offset
;changes:	ah,bl,dx
;
putchr:		mov	bl,al
		mov	es,[vidseg]
IF CGASNOW
		mov	dx,[vidsta]
;
;	wait for retrace mode
;	may be changed to "jmp	short $+3"
;
snowcheck:	in	al,dx
		ror	al,1
		jnb	snowcheck
ENDIF
		mov	ax,bx
		stosw
		mov	es,[textseg]
		ret

		page
;
;put char to video buffer if within window bounds
;
;in:	al	char
;	bp	current window display col
;out:	bp	new window display col
;
optput:		jumpif	bp,b,[windspcol],optput2	;check window bounds
		jumpif	bp,ae,[maxabscol],optput2
		mov	bh,[attrnorm]			;assume normal attr
		iffalse	<byte ptr [blkshwsw]>,optput1	;if block show active
		jumpif	si,b,[pnt.begblk],optput1	;and in block bounds
		jumpif	si,ae,[pnt.endblk],optput1
		mov	bh,[attrinv]			; use invers attribute
optput1:	call	putchr
optput2:	inc	bp
optputX:	ret


;
;insert ASCIIZ string at segment [textseg], offset [pnt.curtxt]
;position [pnt.curtxt] after inserted string
;
;in :	ds:si	string address
;out:	carry	NC = ok, C = out of memory
;
insstr:		ifzero	<byte ptr [si]>,insstrX

		push	si
		call	strlen
		mov	cx,ax
		pop	si

		call	moveup
		jc	insstrX

		mov	di,[pnt.curtxt]
		rep	movsb
		mov	[pnt.curtxt],di
		clc
insstrX:	ret


;
;check if remainder of line has only spaces or tabs
;
;in:	si	text pointer
;out:	flags	NC = empty line
;chg:	al
;
emptyfwd:	push	si
emptyfwd1:	call	_ateol
		jnc	emptyfwd2
		inc	si
		ifwhite	al,emptyfwd1
		stc
emptyfwd2:	pop	si
		ret

		page
;
;smart tab
;insert spaces to align to next word on previous non-empty line
;
smarttab:	mov	si,[pnt.curtxt]			;to prev non-empty line
smarttab0:	ifzero	<word ptr [curtxtrow]>,smarttabX;return if at top row
		call	_prvsol
		call	emptyfwd
		jnc	smarttab0
		xor	cx,cx				;col = 0

smarttab1:	jumpif	cx,nb,[virtxtcol],smarttab2	;to virtual text col
		call	_right
		jc	smarttabX			;eol: nop
		jmp	short smarttab1

smarttab2:	call	_right				;to next separator
		jc	smarttabX			;eol: nop
		ifnsep	al,smarttab2

smarttab3:	call	_right				;to next word
		jc	smarttabX			;eol: nop
		ifsep	al,smarttab3

		dec	cx				;compute tab distance
		sub	cx,[virtxtcol]
		jna	smarttabX			;<= 0: nop

		iftrue	<byte ptr [isrtsw]>,smarttab4	;if in overwrite mode
		add	[virtxtcol],cx			; position cursor
		setsta	updptr+drwrow+drwsta
		jmp	short smarttabX

smarttab4:	mov	byte ptr [txtchgsw],true	;else indent
		call	fill
		setsta	updcol+drwrow+drwsta
smarttabX:	ret
				

;
;insert tab char
;
;in:	al	HT char
;
instab:		iffalse	<byte ptr [indtsw]>,inschr	;if auto-indent off
		iftrue	<byte ptr [wrapsw]>,inschr	;or wrap mode:ins char
		jmp	smarttab			;else smart tab


;
;process ^P: insert next character literal
;
ctrpcmd:	call	ctrprompt
;;		jump	short inschr

		page
;
;insert char
;
;in:	al	char
;
inschr:		setsta	updcol+drwrow+drwsta
		mov	byte ptr [txtchgsw],true
		call	_iseol				;if eol-char
		jc	inschr1
		setsta	updrow+drwwin			; reposition,redraw
inschr1:	mov	si,[pnt.curtxt]
		iftrue	<byte ptr [isrtsw]>,inschr2	;if not (insert-mode
		jumpif	si,nb,[pnt.endtxt],inschr2	;	 or end-of-text
		mov	bl,al
		call	_ateol				;	 or end-of-line
		mov	al,bl
		jnc	inschr2				;	)
		xchg	al,es:[si]			; overwrite
		call	savechr				; save old char
		jmp	short inschr3			;else

inschr2:	iffalse	<byte ptr [wrapsw]>,inschr2b	; if in word wrap mode
		mov	cx,[curtxtcol]			;  if beyond rgt mar
		jumpif	cx,b,[rgtmar],inschr2a
		ifnsep	al,inschr2a			;  and it's a separator
		push	ax
		call	wrap				;   wrap line
		pop	ax
		call	_iseol				;   if end-of-line char
		jnc	inschr2b			;    insert it
		jmp	short inschrX			;   else ignore
inschr2a:	sub	cx,[lftmar]			;  if before lft mar
		jae	inschr2b
		neg	cx
		push	ax
		call	fill				;   insert left margin
		pop	ax				;   then insert char
inschr2b:	mov	cx,1				; insert char
		call	moveup
		jc	inschrX
		mov	di,[pnt.curtxt]
		stosb
inschr3:	inc	word ptr [pnt.curtxt]		;endif
inschrX:	ret


;
;wrap line
;
wrap:		mov	si,[pnt.curtxt]
		mov	[pnt.wrptxtend],si
		call	_getsol
		mov	word ptr [curtxtcol],0
		call	reformat0
		mov	cx,[lftmar]			;adjust window col
		jumpif	cx,nb,[windspcol],wrap1		;if needed
		mov	[windspcol],cx
wrap1:		setsta	updrow+drwwin			;reposition/redraw
		ret

		page
;
;fill spaces at current text pointer
;
;in:	CX	number of spaces to fill
;
fill:		mov	di,[pnt.curtxt]
		call	moveup
		jc	fillX
		add	[pnt.curtxt],cx
		mov	al,' '
		rep	stosb
fillX:		ret


;
;insert/overwrite cr-lf and move to start of next line
;
newlin:		mov	byte ptr [txtchgsw],true
		iftrue	<byte ptr [isrtsw]>,newlin1	;if in overwrite mode
		mov	si,[pnt.curtxt]			; to start of next line
		call	_nxtsol
		jc	newlinX
		mov	[pnt.curtxt],si
		setsta	updrow+drwsta
		ret
							;else
newlin1:	iffalse	<byte ptr [wrapsw]>,newlin2	; if in word wrap mode
		mov	cx,[curtxtcol]			; and beyond rgt mar
		jumpif	cx,b,[rgtmar],newlin2
		call	wrap				;  wrap line
newlin2:	call	insnwl				; insert cr-lf
		mov	[pnt.curtxt],di			; set text ptr
		iffalse	<byte ptr [indtsw]>,newlin3	; if auto-indent
		iftrue	<byte ptr [wrapsw]>,newlin3	; and no word wrap
		call	indent				;  indent
newlin3:	setsta	updrow+drwwin+drwsta
newlinX:	ret


;
;indent to same column as first non-blank on previous line
;
indent:		mov	si,[pnt.curtxt]
		call	_prvsol
		xor	cx,cx				;text col = 0
indent1:	jumpif	<byte ptr es:[si]>,a,' ',indent2
		call	_rightNotEof
		jnc	indent1
		xor	cx,cx				;eol: reset col
indent2:	jmp	fill


;
;insert cr-lf at current position
;
;out:	carry	NC = ok, C = out of memory
;	di	text offset following cr-lf
;
insnwl:		setsta	drwdwn
		mov	byte ptr [txtchgsw],true
		iffalse	<byte ptr [trimsw]>,insnwl1
		call	trim
insnwl1:	mov	cx,2
		call	moveup
		jc	insnwlX
		mov	di,[pnt.curtxt]
		jc	insnwlX
		mov	ax,crlf
		stosw
insnwlX:	ret

		page
;
;delete trailing blanks to the left of current text ptr
;
trim:		mov	cx,[pnt.curtxt]			;if start-of-text
		jcxz	trimX				; exit

		mov	si,cx
trim1:		dec	si				;repeat char left
		mov	al,es:[si]			;until not space
		ifwhite	al,trim2			;   or not tab
		call	_iseol				;   or newline
		jnc	trim3
		jmp	short trim3
trim2:		loop	trim1				;   or start-of-text

		jmp	short trim4			;start-of-text: no adj
trim3:		inc	si				;adjust ptr
trim4:		mov	dx,[pnt.curtxt]			;compute blank count
		sub	dx,si
		jbe	trimX				;exit if <= 0
		mov	[pnt.curtxt],si			;adjust text ptr
		mov	byte ptr [txtchgsw],true
		call	movedn				;delete blanks
trimX:		ret


;
;save char at undo buffer tail
;
;in :	al	char
;
savechr:	mov	bx,[chrdelcnt]
		jumpif	bx,nb,maxchrdel,savechrX
		mov	[bx+chrdelbuf],al
		inc	bx
		mov	[chrdelcnt],bx
savechrX:	ret

		page
;
;make room for extra characters at at [pnt.curtxt]
;
;in:		cx		nr of characters
;out:		flags	C	out of memory, not moved
;			NC	success
;changes:	flags
;
moveup:		clc
		jcxz	moveupX
		xpush	<cx,si,di,bp>

		mov	si,[pnt.endtxt]			;src pointer
		mov	di,si				;dst pointer
		add	di,cx
		jc	moveupErr			;check overflow
		jumpif	di,a,maxtxtptr,moveupErr
		mov	cx,si				;count
		sub	cx,[pnt.curtxt]
		jcxz	moveup1
		dec	si
		dec	di
		std					;move characters up
		mov	ds,[textseg]
		rep	movsb
		mov	ds,cs:[progseg]
		cld

moveup1:	mov	cx,di				;adjust pointers
		sub	cx,si
		mov	si,[pnt.curtxt]
		mov	bp,[pnt.winref]
		call	incpnt
		mov	[pnt.curtxt],si			;leave current position
		jumpif	bp,ne,si,moveup2
		mov	[pnt.winref],bp

moveup2:	clc					;normal return
		jmp	short moveupRet

moveupErr:	mov	[msgptr],offset xstamem		;error return
		setsta	drwsta
		stc
moveupRet:	pop	bp
		pop	di
		pop	si
		pop	cx
moveupX:	ret

		page
;
;search start of previous line
;in,out: si = text pointer
;
_prvsol:	call	_prveol
;;		jmp	short _getsol


;
;get start of current line
;in,out: si = text pointer
;
_getsol:	mov	ds,[textseg]
_getsol1:	sub	si,1
		jc	_getsol2
		mov	al,[si]
		caseal	lf,_getsol2
		caseal	cr,_getsol2
		jumpif	al,ne,ff,_getsol1
_getsol2:	inc	si
		mov	ds,cs:[progseg]
		ret
		

;
;skip end-of-line forward
;in,out: si = text pointer
;
_skfnwl:	jumpif	si,nb,[pnt.endtxt],_skfnwlX
		mov	al,es:[si]
		caseal	cr,_skfnwl1
		caseal	lf,_skfnwl2
		caseal	ff,_skfnwl2
		ret
_skfnwl1:	inc	si
		jumpif	si,nb,[pnt.endtxt],_skfnwlX
 		jumpif	<byte ptr es:[si]>,ne,lf,_skfnwlX
_skfnwl2:	inc	si
_skfnwlX:	ret


;
;search end of previous line
;in,out: si = text pointer
;
_prveol:	call	_getsol
;;		jmp	short _skbnwl


;
;skip end-of-line backward
;in,out: si = text pointer
;
_skbnwl:	and	si,si
		jz	_skbnwlX
		dec	si
		mov	al,es:[si]
		caseal	lf,_skbnwl1
		caseal	cr,_skbnwlX
		caseal	ff,_skbnwlX
		inc	si
		ret
_skbnwl1:	and	si,si
		jz	_skbnwlX
		dec	si
		jumpif	<byte ptr es:[si]>,e,cr,_skbnwlX
		inc	si
_skbnwlX:	ret

		page
;
;to next page
;
;in:	si	start text pointer
;out:	carry	C = end-of-file reached
;	bx	nr of rows gone down
;chg:	ax,cx,si
;
_nxtpag:	mov	cx,[maxwinrow]
		xor	bx,bx
_nxtpag1:	call	_nxtsol
		jc	_skbnwlX
		inc	bx
		loop	_nxtpag1
;;		jmp	_nxtsol			;check if last line


;
;to start of next line
;in :	si	buffer ptr
;out:	si	buffer ptr
;	flags	C = end-of-file
;
_nxtsol:	call	_geteol
		jc	_nxtsolX
		call	_skfnwl
		clc
_nxtsolX:	ret


;
;to end-of-line or end-of-file
;
;in :	si	buffer ptr
;out:	si	buffer ptr
;	flags	C = end-of-file
;
_geteol:	jumpif	si,nb,[pnt.endtxt],_iseolXC
		mov	al,es:[si]
		call	_iseol
		jnc	_nxtsolX
		inc	si
		jmp	short _geteol

;
;check if si is at end-of-line or end-of-file
;
;in:	si	text ptr
;out:	carry	NC = eol or eof
;	al	current char (if not eof)
;
_ateol:		jumpif	si,nb,[pnt.endtxt],_iseolX
		mov	al,es:[si]
;;		jmp	short _iseol


;
;check if al is (first or last) end-of-line char
;(does NOT check for end-of-file)
;
;in:	al	char
;out:	carry	NC = eol
;
_iseol:		caseal	cr,_iseolX
		caseal	lf,_iseolX
		caseal	ff,_iseolX
_iseolXC:	stc
_iseolX:	ret
		
		page
;
;to next word separator
;
;in,out:	si	text pointer
;
_nxtsep:	jumpif	si,nb,[pnt.endtxt],_nxtsep1
		mov	al,es:[si]
		inc	si
		ifnsep	al,_nxtsep
		dec	si
_nxtsep1:	ret


;
;return carry if al is printable
;
isprint:	jumpif	al,a,<'~'>,isprintXNC
		ifnctrl	al,_iseolXC
		iftab	al,_iseolXC
isprintXNC:	clc
		ret


;
;get filespec for block read/write
;
;in :	ds:si	prompt string address
;out:	carry	NC = ok, C = escaped or empty
;
getfspblk:	asksi	fspblk,maxfsp			;ask filespec
		jc	getfspblkX			;escape: return carry
		jz	_iseolXC			;empty : return carry
		mov	si,offset fspblk
		call	upstr
		clc					;normal return
getfspblkX:	ret


;
;copy arg fsp to ds:di, force extent ds:si
;
cpyfsp:		push	si
		mov	si,offset fspmain
cpyfsp1:	lodsb					;copy [path+]filename
		and	al,al
		jz	cpyfsp2
		caseal	<'.'>,cpyfsp2
		mov	[di],al
		inc	di
		jmp	short cpyfsp1
cpyfsp2:	pop	si
;;		jmp	cpystrz				;copy extent


;
;copy ASCIIZ string ds:si --> ds:di
;the ending 0-byte is also copied
;
cpystrz:	call	cpystr
		mov	[di],al
cpystrzX:	ret


;
;copy ASCIIZ string ds:si --> ds:di
;the ending 0-byte is NOT copied
;
cpystr:		lodsb
		and	al,al
		jz	cpystrzX
		mov	[di],al
		inc	di
		jmp	short cpystr

		page
;
;translate character to uppercase
;
;in :	al	character
;out:	al	character
;
toupper:	jumpif	al,b,<'a'>,toupperX
		jumpif	al,a,<'z'>,toupperX
		sub	al,'a'-'A'
toupperX:	ret


;
;translate ASCIIZ string to uppercase
;
;in :	ds:si	string address
;
upstr:		mov	al,[si]
		and	al,al
		jz	toupperX
		call	toupper
		mov	[si],al
		inc	si
		jmp	short upstr


;
;compute ASCIIZ string length
;
;in :	ds:si	string address
;out:	ax	string length
;
strlen:		xor	ax,ax
strlen1:	ifzero	<byte ptr [si]>,toupperX
		inc	ax
		inc	si
		jmp	short strlen1


;
;update row, col status
;
rowcol:		mov	ax,[curtxtrow]
		inc	ax
		mov	di,offset xstarow
		mov	cl,xstarowsiz
		call	utoa
		mov	ax,[curtxtcol]
		inc	ax
		mov	di,offset xstacol
		mov	cl,xstacolsiz
;;		jmp	utoa

		page
;
;unsigned number to ascii text
;
;in :	ax	number
;	cl	field width
;	ds:di	destination string address
;out:	ds:di	address of last char written
;
;remarks:
; The number is written left-justified in unsigned decimal representation
; into the specified field. 
; Immediately following this field one extra character is appended:
;   "*" in case of overflow (number too large),
;   " " (space) if the number fits in the field.
;
utoa:		mov	bx,10000		;start value of divisor
		mov	ch,1			;zero suppression flag
utoa1:		mov	dx,0
		div	bx			;dx:ax/bx -> ax, remainder dx
		and	al,al			;check for 0 digit
		jnz	utoa2
		and	ch,ch			;if so: check zero suppression
		jnz	utoa3
utoa2:		xor	ch,ch			;disable zero suppression
		jcxz	utoaov			;check overflow
		dec	cl			;adjust char count
		add	al,'0'			;store digit
		mov	[di],al
		inc	di
utoa3:		xpush	<dx,cx>			;save remainder
		mov	ax,bx			;divisor /= 10
		mov	dx,0
		mov	cx,10
		div	cx
		mov	bx,ax
		pop	cx
		pop	ax			;ax= remainder
		jumpif	bx,ne,1,utoa4		;if divisor=1 (last digit)
		xor	ch,ch			; no more zero suppression
utoa4:		and	bx,bx			;loop while divisor > 0
		jnz	utoa1

		xor	ch,ch
		jcxz	utoaok
utoa5:		mov	byte ptr [di],' '	;right fill with spaces
		inc	di
		loop	utoa5
utoaok:		mov	byte ptr [di],' '	;clear overflow marker
		ret

utoaov:		mov	byte ptr [di],'*'	;set overflow marker
utoaX:		ret


;
;unsigned integer to ASCIIZ string
;
;in :	ax	number
;	cl	max string length
;	ds:di	destination string address
;
utos:		mov	si,di
		call	utoa
utos1:		mov	byte ptr [di],0		;make ASCIIZ
		jumpif	di,na,si,utosX		;remove trailing blanks
		dec	di
		ifsep	<byte ptr [di]>,utos1
utosX:		ret

		page
;
;ASCIIZ string to unsigned integer
;
;in :	ds:si	string address
;out:	ax	number
;	flags	C = error, NC = success
;	ds:si	address of last char read
;
atou:		xor	ax,ax
		xor	bh,bh
		mov	cx,10
atou1:		mov	bl,[si]
		and	bl,bl
		je	utoaX
		sub	bl,'0'
		jc	utoaX
		cmp	bl,10
		cmc
		jc	utoaX
		mul	cx
		add	ax,bx
		inc	si
		jmp	short atou1

		page
;
;load file
;
;out:	flags	C = error
;	ax	dskread returncode
;	ds:si	(long) error message address
;
load:
staxz <wintxtrow,windspcol,curtxtrow,virtxtcol,curtxtcol,curdspcol,pnt.endtxt>
stal  <blkshwsw,txtchgsw>

		mov	es,[progseg]			;init text pointers
		mov	si,offset dflpnt
		mov	di,offset pnt
		mov	cx,pntcnt
		rep	movsw
		mov	al,' '				;clear status line fsp
		mov	cx,maxfsp
		mov	di,offset xstafsp
		rep	stosb
		mov	es,[textseg]

		mov	si,offset fspmain		;make fsp uppercase
		call	upstr
		mov	si,offset fspmain		;update status line fsp
		mov	di,offset xstafsp
		call	cpystr

		mov	si,offset fspmain		;if not empty fsp
		ifzero	<byte ptr [si]>,loadXNC
		mov	si,offset fspmain
		call	dskread				; read file
		mov	[pnt.endtxt],cx
		mov	si,offset xrmem
		jumpif	ax,e,errmem,loadXC		;  file too big
		mov	si,offset xrread
		jumpif	ax,e,errread,loadXC		;  read error
loadXNC:	clc					;ok or not found
		ret
loadXC:		stc
loadX:		ret

		page
;
;Insert contents of disk file at [textseg]:[pnt.curtxt] 
;
;in:	ds:si	address of ASCIIZ filename
;out:	ax	returncode
;	flags	Z = ok, NZ = error (returncode > 0)
;	cx	number of bytes read
;
;returncodes:
; 0 = success
; 1 = file not found
; 2 = out of memory
; 3 = disk read error
;
dskread:	mov	dx,si			;open file
		call	dskopnrd
		jc	dskreadNfd		;file not found

		mov	ax,4202h		;move file pointer to eof
		mov	bx,[dskiod]
		xor	cx,cx
		xor	dx,dx
		int	21h
		jc	dskreadErr
		and	dx,dx			;dx:ax = file size
		jnz	dskreadMem
		mov	[dskcnt],ax

		mov	ax,4200h		;reset file pointer
		mov	bx,[dskiod]
		xor	cx,cx
		xor	dx,dx
		int	21h
		jc	dskreadErr

		mov	cx,[dskcnt]
		call	moveup			;make space
		jc	dskreadMem

		mov	ah,3Fh			;read block
		mov	bx,[dskiod]
		mov	cx,[dskcnt]
		mov	dx,[pnt.curtxt]
		mov	ds,[textseg]
		int	21h
		mov	ds,cs:[progseg]
		jc	dskreadErr
		push	ax			;nr of bytes actually read
		call	dskclose		;close file
		pop	cx
		call	cleaneof		;cleanup eof area
		xor	ax,ax			;normal return
		ret

dskreadNfd:	mov	ax,errfnf		;error: file not found
		jmp	short dskreadErr2

dskreadMem:	mov	ax,errmem		;error: out of memory
		jmp	short dskreadErr1

dskreadErr:	mov	ax,errread		;disk read error
dskreadErr1:	push	ax
		call	dskclose
		pop	ax
dskreadErr2:	mov	cx,0
dskreadRet:	and	ax,ax
		ret


;
;remove trailing eof-characters and nulls
;and delete possibly over-allocated text buffer area 
;
;in:	cx		number of bytes read, starting at es:[pnt.curtxt]
;	[dskcnt]	number of bytes allocated before read
;out:	cx		read byte count after cleanup
;
cleaneof:	jcxz	cleaneof3		;if (cx != 0) {
		mov	dx,cx			; dx= old length
		mov	di,[pnt.curtxt]		; di= end ptr
		add	di,cx
cleaneof1:	dec	di			; while ( ((al=[--di]) == 0)
		mov	al,es:[di]
		and	al,al
		je	cleaneof2
		cmp	al,eofchr		;	or (al == eofchr) )
		jne	cleaneof3
cleaneof2:	loop	cleaneof1		;  --cx;
cleaneof3:	xpush	<cx,[pnt.curtxt]>	;}
		add	[pnt.curtxt],cx		;movedn(curtxt+cx,dx-cx);
		sub	dx,cx
		call	movedn
		xpop	<[pnt.curtxt],cx>
		ret

		page
;
;write to disk file
;ask permission to overwrite if it exists
;
;in:	ds:dx		address ASCIIZ filespec
;	[textseg]:si	start of block to write
;	cx		byte count
;out:	carry		NC = ok, C = error
;
;in case of error, an appropriate message is displayed in the status line
;and a beep sounds
;
dskwriteif:	mov	[dskfsp],dx
		mov	[dskptr],si
		mov	[dskcnt],cx

		call	dskopnrd		;if file exists
		jc	dskwrite1
		call	dskclose
		mov	si,offset xexist	; ask permission to overwrite
		call	noyes
		jc	dskwriteXNC		; no: return
		jmp	short dskwrite1


;
;write to disk file unconditionally
;in/out: see dskwriteif
;
dskwrite:	mov	[dskfsp],dx
		mov	[dskptr],si
		mov	[dskcnt],cx

dskwrite1:	call	putwait
		mov	ah,3Ch				;create/truncate file
		mov	cx,fatrarch			;set archive attribute
		mov	dx,[dskfsp]
		int	21h
		jc	dskwriteErr1			; create error
		mov	[dskiod],ax

		mov	bx,ax
		mov	ah,40h				;write block
		mov	cx,[dskcnt]
		mov	dx,[dskptr]
		mov	ds,[textseg]
		int	21h
		mov	ds,cs:[progseg]
		jc	dskwriteErr			; write error
		jumpif	ax,ne,cx,dskwriteErr		; partial write

		call	dskclose			;close file
dskwriteXNC:	clc					;normal exit
		ret

dskwriteErr:	call	dskclose			;error exit
dskwriteErr1:	mov	[msgptr],offset xstawrite
		call	putsta
		beep
		stc
		ret

		page
;
;read open file, save handle in [dskiod] on success
;
;in:	ds:dx	ASCIIZ filespec
;out:	carry	NC = success, C = file not found
;
dskopnrd:	mov	ax,3D00h
		int	21h
		mov	[dskiod],ax
		ret


;
;close file handle [dskiod]
;
dskclose:	mov	ah,3Eh
		mov	bx,[dskiod]
		int	21h
		ret


;
;test ^Break, ^U or <ESC>, discard other keys from keyboard buffer
;
;out:	carry	NC = ok, C = ^U or <Esc> pressed
;
testesc:	mov	ah,01h			;read keyboard status
		int	16h
		jz	dskwriteXNC		;no key: continue
		mov	ah,00h			;read key
		int	16h
		and	ax,ax			;ctr-break: return
		jz	testescXC
		caseal	escape,testescXC	;escape or ^U: return
		jumpif	al,ne,<'U'-ctrl>,testesc;other key: test again
testescXC:	stc
		ret

;--- END TEPROC.ASM ----------------------------------------------------------
