	NAME	mssscp
; File MSSSCP.ASM
	include mssdef.h
;	Copyright (C) 1982, 1995, Trustees of Columbia University in the 
;	City of New York.  The MS-DOS Kermit software may not be, in whole 
;	or in part, licensed or sold for profit as a software product itself,
;	nor may it be included in or distributed with commercial products
;	or otherwise distributed by commercial concerns to their clients 
;	or customers without written permission of the Office of Kermit 
;	Development and Distribution, Columbia University.  This copyright 
;	notice must not be removed, altered, or obscured.
;
; Edit history
; 12 Jan 1995 version 3.14
; Last edit
; 12 Jan 1995
; 27 August 1992 version 3.13
; 6 Sept 1991 version 3.11
; 4 June 1991 Make label searches be global so the next higher level is
;  searched if the present level does not contain the target.
; 6 June 1989 Limit echoing of received chars by OUTPUT to 100, to quench
;  verbose hosts. From Dan Norstedt.
; 4 May 1989 Make input/reinput scan buffer be allocated dynamically. Allow
;  for that size to be set via Environment as Kermit initializes.
;       
; MS Kermit Script routines, DEC-20 style.
; Extensively rewritten for MS Kermit 2.29a by Joe R. Doupnik 5 July 86
;;
;    Created June, 1986 a.d.	By James Sturdevant
;					  A. C. Nielsen Co. Mpls.
;					  8401 Wayzata Blvd.
;					  Minneapolis, Mn. 55426
;					  (612)546-0600
;;;;;;;;
; Kermit command level usages and this file's entry points:
; Clear - clears serial port buffers. Procedure scclr.
; Echo text - displays text on local screen. Proc scecho.
; Pause [time] - waits indicated number of seconds (default is Input
;	Default-timeout, 1 second typically). Proc scpau.
; IF condtion command - tests condition (SUCCESS or FAILURE just now) and
;       if the condition is met executes the main-line Kermit command.
; GOTO label - rewinds Take file or Macro, locates the :label, transfers
;       parsing control to the next line.
; Input [time] text - waits up to time seconds while scanning serial port
;	input for a match with text. Default value for time is Input
;	Default-timeout, 1 second typically). Spaces or tabs are separators
;	between the time and text fields. Proc scinp.
;	A carriage return typed by the local user simulates a match.
; Reinput [time] text - like INPUT but non-destructively rereads the 128 byte
;       script buffer. Buffer can be added to, until full, if necessary.
; Output text - sends the text to the serial output port. Proc scout.
; Transmit text [prompt] - raw file transfer to host. Proceeds from source
;	line to source line upon receipt of prompt from host or carriage
;	return from us. Default prompt is linefeed. A null prompt (\0)
;	causes the file to be sent with no pausing or handshaking. Note
;	that linefeeds are stripped from outgoing material. Proc scxmit.
; In the above commands "text" may be replaced by "@filespec" to cause the
;	one line of that file to be used instead. @CON obtains one line of
;	text from the keyboard. Such "indirect command files" may be nested
;	to a depth of 100. Control codes are written as decimal numbers
;	in the form "\ddd" where d is a digit between 0 and 9. Carriage
;	return is \13, linefeed is \10, bell is \7; the special code \255
;	is used to match (Input) either cr or lf or both.
; These commands can be given individually by hand or automatically
;	in a Kermit Take file; Take files may be nested.
;;;;;;;;
; These routines expect to be invoked by the Kermit command dispatcher
; and can have their default operations controlled by the Kermit Set Input
; command (implemented in file mssset.asm). They parse their own cmd lines.
; Set Input accepts arguments of
;   Case Ignore or Observe  (default is ignore case when matching strings)
;   Default-timeout seconds (default is 5 seconds)
;   Echo On or Off	controls echoing of Input cmd text (default is Off)
;   Timeout-action Quit or Proceed (default is Proceed)
; These conditions are passed via global variables script.incasv, .indfto,
;   .inecho, .inactv, respectively, stored here in structure script.
;;;;;;;;;

	public	script, scout, scinp, scpau, scecho, scclr, scxmit, scwait
    	public	sgoto, screinp, ifcmd, setalrm, inptim, chktmo, alrhms
	public	buflog, scpini, scpbuflen, decvar, incvar, scmpause, outpace
	public	scsleep, input_read, scapc, inpath_seg, sforward

linelen	 	equ	134		; length of working buffer line
maxtry		equ	5		; maximum number of output retries
stat_unk 	equ	0  		; status return codes.
stat_ok		equ	1		; have a port character
stat_cc		equ	2		; control-C typed
stat_tmo	equ	4		; timeout
stat_cr		equ	8		; carriage return typed

ifsuc		equ	0		; indicators for IF conditions
iffail		equ	1
ifext		equ	2
iferr		equ	3
ifnot		equ	4
ifctr		equ	5
ifmdf		equ	6
ifalarm		equ	7
ifequal		equ	8
ifless		equ	9
ifsame		equ	10
ifmore		equ	11
ifllt		equ	12
iflgt		equ	13
ifpath		equ	14
ifdir		equ	15
ifnewer		equ	16

data	segment
	extrn	taklev:byte, takadr:word, portval:word, flags:byte
	extrn	rxtable:byte, spause:byte, errlev:byte, fsta:word
	extrn	kstatus:word, mcctab:byte, comand:byte, ttyact:byte
	extrn	keyboard:word, rdbuf:byte, apctrap:byte, filtst:byte
	extrn	diskio:byte, buff:byte

					; global (public) variables     
script	scptinfo <>			; global structure, containing:
;;inactv	db	0		; input action value (default proceed)
;;incasv	db	0dfh		; input case  (default ignore)
;;indfto	dw	1		; input and pause timeout (def 1 sec)
;;inecho	db	1		; echo Input cmd text (0 = no)
;;xmitfill	db	0		; non-zero to TRANSMIT filler
;;xmitlf	db	0		; non-zero to TRANSMIT LF's
;;xmitpmt	db	lf		; prompt between lines

					; local variables
line	db	linelen+1 dup (0)	; line of output or input + terminator
		even
scpbuflen dw	128			; serial port local buffer def length
bufcnt	dw	0			; serial port buf byte cnt, must be 0
bufseg	dw	0			; segment of buffer
bufrdptr dw	0			; serial port buf read ptr
bufwtptr dw	0			; serial port buf write ptr
bufpkptr dw	0			; peek-read pointer
bufpkcnt dw	0			; peek-read byte count remaining
input_read dw	0			; count of bytes read by INPUT/OUTPUT
inplen	dw	0			; length of input match string
inpath_seg dw	0			; segment of inpath string (0=none)
reinflg	db	0			; 0 = INPUT, else REINPUT command
notflag	db	0			; IF NOT flag
slablen	dw	0			; label length, for GOTO
forward	db	0			; 0=goto, else use Forward form
status	dw	0			; general status word
fhandle	dw	0			; file handle storage place
temptr	dw	0			; temporary pointer
temptr2	dw	0			; ditto, points to end of INPUT string
tempd	dw	0			; temp
temp	dw	0			; a temp
temp1	dw	0			; high order part of temp
tempa	db	0			; another temp
wtemp	db	0			; temp for WAIT
ltype	db	0			; lex type for IF statements
retry	db	0			; number of output retries
parmsk	db	7fh			; 7/8 bit parity mask
lecho	db	0			; local echo of output (0 = no)
timout	dw	0			; work area (seconds before timeout)
timhms	db	4 dup (0)		; hhmmss.s time of day buffer
alrhms	db	4 dup (0)		; hhmmss.s time of day alarm buffer
outpace	dw	0			; OUTPUT pacing, millisec

crlf	db	cr,lf,'$'
xfrfnf	db	cr,lf,'?Transmit file not found$'
xfrrer	db	cr,lf,'?error reading Transmit file$'
xfrcan	db	cr,lf,'?Transmission canceled$'
indmis	db	'?Indirect file not found',cr,lf,'$'
inderr	db	'?error reading indirect file',cr,lf,'$'
laberr	db	cr,lf,'?Label ":$'
laberr2	db	'" was not found.',cr,lf,'$'
tmomsg	db	cr,lf,'?Timeout',cr,'$'
wtbad	db	cr,lf,'?Command not understood, improper syntax $'
mpbad	db	cr,lf,'?Bad number$'
ifnmsg	db	cr,lf,'?Number expected, ignoring "$'
ifnmsg2	db	'"$'

clrtable db	3
	mkeyw	"input-buffer",1
	mkeyw	"device-buffer",2
	mkeyw	"both",3

iftable	db	18			; IF command dispatch table
	mkeyw	'Not',ifnot
	mkeyw	'<',ifless
	mkeyw	'=',ifsame
	mkeyw	'>',ifmore
	mkeyw	'Alarm',ifalarm
	mkeyw	'Count',ifctr
	mkeyw	'Defined',ifmdf
	mkeyw	'Directory',ifdir
	mkeyw	'Errorlevel',iferr
	mkeyw	'Equal',ifequal
	mkeyw	'Exist',ifext
	mkeyw	'Inpath',ifpath
	mkeyw	'LGT',iflgt
	mkeyw	'LLT',ifllt
	mkeyw	'Newer',ifnewer
	mkeyw	'Numeric',ifnumeric
	mkeyw	'Failure',iffail
	mkeyw	'Success',ifsuc
data	ends

data1	segment
outhlp	db	'line of text to be sent to remote host$'
apchlp	db	'Applications Program Commands to send to remote host$'
inphlp	db	'time-limit and line of text expected from remote host'
	db	cr,lf,' Time is number of seconds or until a specific'
	db	' hh:mm:ss (24 hour clock)$'
echhlp	db	'line of text to be Echoed to screen$'
ptshlp	db	'amount of time to pause/wait'
	db	cr,lf,' Time is number of seconds or until a specific'
	db	' hh:mm:ss (24 hour clock)$'
wthlp	db	cr,lf,'Optional modem status signals CD, CTS, DSR, RI which'
	db	' if asserted',cr,lf,'  will terminate waiting$'
xmthlp	db	' Name of file to be Transmitted$'
pmthlp	db	cr,lf
	db     ' Prompt character expected as an ACK from host (\0 for none)$'
ifdfhlp	db	cr,lf,' Name of macro or variable  then a command$'
alrmhlp	db	cr,lf,' Seconds from now or time of day (HH:MM:SS) for alarm,'
	db	' < 12H from present$'
mphlp	db	cr,lf,' Number of milliseconds to pause (0..65535)$'
ifmhlp	db	cr,lf,' Number, ARGC (1+argument count), COUNT, ERRORLEVEL,'
	db	' KEYBOARD, VERSION$'
ifnhlp	db	cr,lf,' Number which errorlevel should match or exceed$'
ifehlp1	db	cr,lf,'?pair of words or variables to be compared$'
ifehlp2	db	cr,lf,'?second word or variable to be compared$'
ifnewhlp db	' filename$'
chgvarhlp db	'name of variable$'
ssizehlp db	'amount, default is 1$'
clrhelp	db	cr,lf,' INPUT-BUFFER (script string matching buffer), or'
	db	cr,lf,' DEVICE-BUFFER (comms receive), or BOTH$'
discard	db      ' Kermit command'
	db	cr,lf,' "IF" condition is false, command will be ignored.$'
sleephlp db	cr,lf,' Wait seconds or time of day (HH:MM:SS), does not'
	db	' touch comms port.$'
data1	ends     
code	segment
     
	extrn	comnd:near, clrbuf:near, prtchr:near, outchr:near, sendbr:near
	extrn	cptchr:near, serini:near, pcwait:near, katoi:near, spath:near
	extrn	cnvstr:near, getmodem:near, isdev:near, isfile:near
	extrn	takrd:near, takclos:near, tolowr:near, prtasz:near,strlen:near
	extrn	sendbl:near, dec2di:near, lnout:near, cnvlin:near, dopar:near
	extrn	malloc:far
extrn prtscr:near
	assume	cs:code, ds:data, es:nothing

; Initialize script routines before use, called as Kermit initializes.

SCPINI	PROC	NEAR
	mov	cx,scpbuflen		; (RE)INPUT buffer length
	mov	bx,cx			; string length, in bytes
	add	bx,15			; round up
	jnc	scpini1			; nc = under max size
scpini3:mov	bx,0ffffh		; 64KB-16 bytes, max buffer
scpini1:mov	cl,4
	shr	bx,cl			; convert to paragraphs (divide by 16)
scpini2:mov	cx,bx			; remember desired paragraphs
	mov	ah,alloc		; allocate a memory block
	int	dos
	jc	scpini4			; error, not enough memory
	mov	bufseg,ax		; store new segment
	mov	cl,4
	shl	bx,cl			; convert paragraphs to bytes
	mov	scpbuflen,bx		; new length
	push	es
	mov	es,ax			; buffer segment
	xor	di,di			; offset
	mov	cx,bx			; length in bytes
	shr	cx,1			; even, do words
	xor	ax,ax			; null filler
	cld
	rep	stosw			; clear the buffer
	pop	es
	clc				; return success
	ret
scpini4:mov	scpbuflen,0
	stc				; carry set for failure to initialize
	ret
SCPINI	ENDP

; Clear input buffer(s) of serial port
; Clear command
;     
SCCLR	PROC	NEAR
	mov	comand.cmcr,1		; permit empty line
	mov	ah,cmkey
	mov	dx,offset clrtable
	mov	bx,offset clrhelp
	call	comnd
	mov	comand.cmcr,0
	mov	kstatus,kssuc		; global status
	push	bx			; cmd return
	mov	ah,cmeol		; get a confirm
	call	comnd
	pop	bx
	jnc	scclr1			; nc = success
	ret				; failure
scclr1:	cmp	bx,1
	jne	scclr2
	call	bufclear
	clc
	ret
scclr2:	cmp	bx,2
	jne	scclr3			; default, gibberish bx
	call	clrbuf
	clc
	ret
scclr3:	call	bufclear		; clear our serial port circular buf
	call	clrbuf			; clear system serial port buffer too
	clc
	ret
SCCLR	ENDP

;
; Echo a line of text to our screen
; Echo text
;
SCECHO	PROC	NEAR
	mov	ah,cmline		; get a whole line of asciiz text
	mov	bx,offset rdbuf		; where to store in
	mov	word ptr [bx],0		; clear line
	mov	comand.cmblen,cmdblen	; set line capacity (length of rdbuf)
	mov	dx,offset echhlp	; help
	call	comnd
	jc	echo3
	mov	si,offset rdbuf		; start of line
	mov	di,si			; convert to the same place
	mov	ah,script.incasv	; save current case state
	push	ax
	mov	script.incasv,0ffh	; say no case conversion
	call	cnvlin			; convert \numbers to binary
	pop	ax
	mov	script.incasv,ah	; recover case state
	jc	echo3			; carry set means error
	mov	ah,prstr
	mov	dx,offset crlf
	int	dos
	jcxz	echo2			; z = nothing to show
	cld
echo1:	lodsb				; get a source char into al
	mov	dl,al
	mov	ah,conout
	int	dos
	loop	echo1			; get another
echo2:	clc				; return success
echo3:	ret				; error
SCECHO	ENDP

; Extract label from command line. Store in LINE, length in slablen.
; Jump to line in Take or Macro following that label.
SGOTO	PROC	NEAR
	mov	forward,0		; assume goto, not forward
goto0:	mov	kstatus,kssuc		; global status
	mov	ah,cmword		; get a word
	mov	dx,offset line		; buffer to hold label
	xor	bx,bx			; no help (non-interactive command)
	mov	slablen,bx		; clear label holding buffer
	call	comnd
	jnc	goto1			; nc = success
	ret				; failure
goto1:	mov	ax,ax			; byte count
	mov	slablen,ax		; save count here
	mov	ah,cmeol
	call	comnd
	jnc	goto2			; nc = success
	ret				; failure

goto2:	or	ax,ax			; need contents
	jz	goto3			; empty, fail
	cmp	flags.cxzflg,'C'	; check for Control-C breakout
	je	goto3			; e = yes, fail
	cmp	taklev,0		; in a Take file or Macro?
	jne	GETTO			; ne = yes, find the label
	clc				; ignore interactive command
	ret
goto3:	stc
	ret

; Like GOTO but searches only forward from the current point.
; Forward cascades upward during searches of surrounding Take files/macros.
SFORWARD proc	near
	mov	forward,1		; say search only forward
	jmp	goto0
SFORWARD endp

; Find line starting just after ":label". Label is in variable LINE
; (length in slablen). Readjust Take read pointer to start of that line.
; Performs file search from beginning of file, popping up levels if req'd.
; Exit carry clear if success, carry set otherwise. Local worker routine.
getto	proc	near
	push	bx			; global save of bx
gett0:	mov	comand.cmkeep,1		; keep Take file open after this call
	mov	bx,takadr
	cmp	[bx].taktyp,take_file	; get type of take (a file?)
	jne	gett2			; ne = no, a macro
	cmp	forward,0		; goto?
	jne	gett1			; ne = no, FORWARD, do not rewind
					; scan from start of Take file
	mov	word ptr [bx].takseek,0
	mov	word ptr [bx].takseek+2,0 ; seek distance, bytes
gett1:	call	takrd			; get a line
	mov	bx,takadr		; restore bx to working value
	jmp	short gett4
					; Take a Macro
gett2:	mov	cx,[bx].takbuf		; segment of macro definition
	push	es
	mov	es,cx
	mov	cx,es:[0]		; get string length byte
	pop	es
	mov	[bx].takcnt,cx		; set unread to full buffer (rewind)
	mov	[bx].takptr,2		; set read pointer to start of text

gett4:	call	getch			; get a character
	jc	gett14			; c = end of file, no char
	cmp	al,' '			; leading white space?
	je	gett4			; e = yes, read again
	cmp	al,TAB			; this kind of whitespace?
	je	gett4			; e = yes
	cmp	al,':'			; start of label?
	je	gett8			; e = yes
gett6:	cmp	al,CR			; end of line?
	je	gett4			; e = yes, seek colon for label
	call	getch			; get a character
	jc	gett14			; c = end of file, no char
	jmp	short gett6		; read until end of line

gett8:	mov	si,offset line		; label to search for
	mov	cx,slablen		; its length
	jcxz	gett12			; no chars to match
	cmp	byte ptr[si],':'	; user label starts with colon
	jne	gett10			; ne = no
	inc	si			; skip user's colon
	dec	cx
	jcxz	gett12			; no chars to match
gett10:	call	getch			; read file char into al
	jc	gett14			; c = end of file
	mov	ah,al
	cld
	lodsb
	call	tolowr			; convert al and ah to lower case
	cmp	al,ah			; match?
	jne	gett6			; ne = no, goto end of line
	loop	gett10			; continue matching
					; match obtained
	call	getch			; read next file character
	jc	gett13			; c = end of file, no char
	cmp	al,' '			; separator?
	je	gett12			; e = yes, unique label found
	cmp	al,TAB			; this kind of separator?
	je	gett12			; e = yes
	cmp	al,CR			; or end of line?
	je	gett13			; e = yes
	jmp	gett6			; not correct label, keep reading

gett12:	call	getch			; read past end of line
	jc	gett13			; c = end of file, no char
	cmp	al,CR			; end of line character?		
	jne	gett12			; ne = no, keep reading
gett13: pop	bx
	clc				; return carry clear
	ret				; Take pointers are ready to read line
					; failed to find label, pop a level
gett14:	call	takclos			; close this macro/take file
	cmp	taklev,0		; still in macro/take?
	je	gett15			; e = no, quit
	jmp	gett0			; try next level up
gett15:mov	ah,prstr		; say label not found
	mov	dx,offset laberr	; first part of error message
	int	dos
	mov	dx,offset line
	cmp	line,':'		; label starts with ":"?
	jne	gett16			; ne = no
	inc	dx			; yes, skip it
gett16:	call	prtasz			; print asciiz string
	mov	ah,prstr
	mov	dx,offset laberr2	; trailer of error message
	int	dos
gett20:	pop	bx
	mov	kstatus,ksgen		; command status, failure
	stc				; set carry for failure
	ret
getto	endp
SGOTO	ENDP

; Read char from Take buffer. Returns carry clear and char in al, or if end
; of file returns carry set. Enter with BX holding takadr. Local worker.
getch	proc	near
	cmp	[bx].takcnt,0		; buffer empty?
	jg	getch2			; g = no
	cmp	[bx].taktyp,take_file	; file?
	jne	getch1			; ne = no, a macro
	call	takrd			; read another buffer
	cmp	[bx].takcnt,0		; end of file?
	jne	getch2			; ne = no
getch1:	stc				; e = yes, exit error
	ret
getch2:	push	si
	push	es
	mov	es,[bx].takbuf		; segment of buffer
	mov	si,[bx].takptr		; read a char from Take buffer
	mov	al,es:[si]
	inc	si
	mov	[bx].takptr,si		; move buffer pointer
	dec	[bx].takcnt		; decrease number of bytes remaining
	pop	es
	pop	si
	clc				; return carry clear
	ret
getch	endp


; IF [NOT] {< }| = | > | ALARM | COUNT | FAILURE | SUCCESS | INPATH filespec
;	| ERRORLEVEL \number | EQUAL string string | EXIST filespec |
;	NUMBER } command

IFCMD	PROC	NEAR
	mov	notflag,0		; assume NOT keyword is absent
ifcmd1:	mov	ah,cmkey		; parse keyword
	mov	dx,offset iftable	; table of keywords
	xor	bx,bx			; help is the table
	call	comnd
	jnc	ifcmd1a			; nc = success
	ret				; failure
ifcmd1a:cmp	bx,ifnot		; NOT keyword?
	jne	ifcmd2			; ne = no
	xor	notflag,1		; toggle not flag
	jmp	short ifcmd1		; and get next keyword

ifcmd2:	cmp	bx,ifsuc		; IF SUCCESS?
	jne	ifcmd4			; ne = no
	cmp	kstatus,kssuc		; do we have success?
	je	ifcmd2a			; e = yes
	jmp	ifcmdf			; ne = no, no jump
ifcmd2a:jmp	ifcmdp			; yes

ifcmd4:	cmp	bx,iferr		; IF ERRORLEVEL?
	jne	ifcmd5			; ne = no
	jmp	ifnum			; parse number to binary in line

ifcmd5:	cmp	bx,ifext		; IF EXIST filespec?
	je	ifcmd5a			; e = yes
	cmp	bx,ifpath		; IF INPATH filespec?
	je	ifcmd5a			; e = yes
	cmp	bx,ifdir		; IF DIR filespec?
	jne	ifcmd6			; ne = no
ifcmd5a:mov	ah,cmword		; read a filespec
	mov	dx,offset rdbuf		; buffer for filespec
	push	bx			; save command kind
	xor	bx,bx
	call	comnd
	pop	bx
	jnc	ifcmd5b			; nc = success
	ret				; failure
ifcmd5b:mov	si,offset rdbuf
	push	cx			; save drive letter part
	mov	dx,si
	call	strlen
	add	si,cx			; look at last char
	or	cx,cx			; if any bytes
	pop	cx			; restore drive letter
	jz	ifcmd5d			; z = none
	cmp	byte ptr [si-1],':'	; ends on ":"?
	je	ifcmd5f			; e = yes, just a drive letter
	cmp	byte ptr [si-1],'\'	; ends on backslash?
	je	ifcmd5e			; e = yes
	cmp	byte ptr [si-1],'.'	; ends on dot?
	jne	ifcmd5d			; ne = no
ifcmd5e:mov	byte ptr [si-1],0	; trim trailing backslash
	jmp	short ifcmd5b		; keep trimming

ifcmd5f:cmp	bx,ifdir		; IF DIRECTORY?
	jne	ifcmd5d			; ne = no
	mov	ah,gcurdsk		; get current disk
	int	dos
	push	ax			; save current disk in al
	xor	dl,dl
	xchg	dl,rdbuf		; get drive letter, set rdbuf=0 fail
	or	dl,40h			; to lower
	sub	dl,'a'			; 'a' is 0
	mov	ah,seldsk		; select disk
	int	dos
	jc	ifcmd5g			; c = fail
	cmp	ah,0ffh			; so is 0ffh
	je	ifcmd5g			; e = failure
	inc	rdbuf			; 1 is success
ifcmd5g:pop	ax
	mov	dl,al			; current disk
	mov	ah,seldsk		; reset to current directory
	int	dos
	cmp	rdbuf,0			; fail?
	je	ifcmdf			; e = yes
	jmp	ifcmdp			; pass

ifcmd5d:mov	ax,offset rdbuf		; isfile wants pointer in ds:ax
	cmp	bx,ifpath		; INPATH?
	je	ifcmd5c			; e = yes
	push	bx
	call	isfile			; see if file EXISTS
	pop	bx
	jc	ifcmdf			; c = no
	cmp	bx,ifdir		; IF DIRECTORY?
	jne	ifcmdp			; ne = no, Exists so succeed
	test	byte ptr filtst.dta+21,10H ; subdirectory name?
	jnz	ifcmdp			; nz = yes
	cmp	filtst.fname,2eh	; directory name?
	je	ifcmdp			; e = yes
	jmp	ifcmdf			; else fail
ifcmd5c:push	es
	mov	ax,inpath_seg		; preexisting inpath_seg?
	or	ax,ax
	jz	ifcmd5h			; z = no buffer yet
	mov	es,ax			; free it now
	mov	ah,freemem
	int	dos
	mov	inpath_seg,0		; say no segment
ifcmd5h:pop	es
	mov	ax,offset rdbuf		; file pointer
	call	spath			; search path
	jc	ifcmdf			; c = no such file
	push	es
	push	ax
	mov	dx,ax			; offset of local copy of ds:string
	call	strlen
	mov	si,ax			; start of string
	add	si,cx			; end of string + 1
ifcmd5i:dec	si
	cmp	byte ptr [si],'\'	; path/filename separator?
	je	ifcmd5j			; e = yes
	cmp	byte ptr [si],':'	; drive separator?
	je	ifcmd5j			; e = yes
	loop	ifcmd5i
ifcmd5j:inc	si
	inc	cx
	mov	byte ptr [si],0		; stuff a terminator
	mov	ax,cx			; length needed
	add	ax,2			; plus length word
	call	malloc			; create a buffer
	mov	inpath_seg,ax		; segment of string
	mov	es,ax
	xor	di,di
	mov	ax,cx
	cld
	stosw				; store count
	pop	si			; ds:offset of path string (was AX)
	rep	movsb			; copy string to buffer
	pop	es
	jmp	ifcmdp			; succeed, do the succeed stuff
	
ifcmd6:	cmp	bx,iffail		; IF FAIL?
	jne	ifcmd7
	test	kstatus,not (kssuc)	; check all bits
	jz	ifcmdf			; z = not that condition, no jump 
	jmp	ifcmdp

ifcmd7:	cmp	bx,ifctr		; IF COUNT?
	jne	ifcmd8			; ne = no
	cmp	taklev,0		; in a Take file?
	je	ifcmdf			; e = no, fail
	push	bx
	mov	bx,takadr		; current Take structure
	cmp	[bx].takctr,0		; exhausted count?
	je	ifcmd7a			; e = yes, dec no more ye counter
	dec	[bx].takctr		; dec COUNT if non-zero
	cmp	[bx].takctr,0		; exhausted now?
	je	ifcmd7a			; e = yes
	pop	bx
	jmp	short ifcmdp		; COUNT > 0 at entry, execute command
ifcmd7a:pop	bx
	jmp	short ifcmdf		; do not execute command

ifcmd8:	cmp	bx,ifmdf		; IF DEF?
	jne	ifcmd9			; ne = no
	jmp	ifmdef			; do further parsing below

ifcmd9:	cmp	bx,ifalarm		; IF ALARM?
	jne	ifcmd10			; ne = no
	jmp	ifalrm			; do further parsing below

ifcmd10:cmp	bx,ifequal		; IF EQUAL?
	jne	ifcmd10a		; ne = no
	jmp	ifequ			; do further parsing below
ifcmd10a:cmp	bx,iflgt		; IF LGT?
	jne	ifcmd10b		; ne = no
	jmp	ifequ
ifcmd10b:cmp	bx,ifllt		; IF LLT?
	jne	ifcmd11			; ne = no
	jmp	ifequ

ifcmd11:cmp	bx,ifless		; IF <?
	je	ifcmd12			; e = yes
	cmp	bx,ifsame		; IF =?
	je	ifcmd12
	cmp	bx,ifmore		; IF > ?
	jne	ifcmd13			; ne = no
ifcmd12:jmp	ifmath

ifcmd13:cmp	bx,ifnumeric		; IF NUMBER?
	jne	ifcmd14			; ne = no
	jmp	ifnumeric
ifcmd14:cmp	bx,ifnewer		; IF NEWER?
	jne	ifcmdf
	jmp	tstnewer		; do file newer test

					; Jump points for worker routines
					; failure
ifcmdf:	cmp	notflag,0		; need to apply not condition?
	jne	ifcmdp2			; ne = yes, take other exit
ifcmdf2:mov	ah,cmline		; fail, read and discard rest of line
	mov	comand.cmblen,cmdblen
	mov	bx,offset rdbuf
	mov	dx,offset discard	; say not doing anything
	mov	comand.cmper,1		; don't expand variables at this time
	call	comnd
	clc				; force success on discard of line
	ret
					; success (pass)
ifcmdp:	cmp	notflag,0		; need to apply not condition?
	jne	ifcmdf2			; ne = yes, take other exit
ifcmdp2:clc				; do command
	ret
IFCMD	ENDP

; Compare errlev against user number. Jump successfully if errlev >= number.
; Worker for IF [NOT] ERRORLEVEL number <command>
ifnum	proc	near
	mov	ah,cmword		; get following number
	mov	dx,offset rdbuf+1
	mov	comand.cmblen,cmdblen
	mov	rdbuf,'\'		; in case user forgets backslash
	mov	word ptr rdbuf+1,0	; clear buffer
	mov	bx,offset ifnhlp	; help
	call	comnd
	jnc	ifnum1			; nc = success
	ret				; failure
ifnum1:	mov	si,offset rdbuf		; put text in compare buffer
	cmp	rdbuf+1,'\'		; did user include backslash?
	jne	ifnum2			; ne = no
	inc	si			; yes, skip our helpful backslash
ifnum2:	call	katoi			; convert number to binary in ax
	jc	ifnum4			; c = failed to convert a number
	cmp	errlev,al		; at or above this level?
	jae	ifnum3			; ae = yes, succeed
	jmp	ifcmdf			; else fail
ifnum3:	jmp	ifcmdp			; jump to main command Success exit

ifnum4:	mov	dx,offset rdbuf+1	; pointer to bad word
	mov	tempd,dx		; remember starting place for text
	call	strlen			; get its length
	add	dx,cx			; skip over current word
	mov	bx,dx
	mov	byte ptr [bx],' '	; space, chopped by parser
	inc	bx			; new text goes here
	xor	dx,dx			; help
	mov	ah,cmline		; read rest of line
	mov	comand.cmblen,cmdblen
	call	comnd
	jnc	ifnum4a			; nc = success
	ret				; failure
ifnum4a:or	ax,ax			; returned byte count
	jnz	ifnum5			; nz = got some
	mov	byte ptr[bx-1],0	; remove space separator from above
ifnum5:	mov	ah,prstr
	mov	dx,offset ifnmsg	; error message header
	int	dos
	mov	dx,offset rdbuf+1	; start of user text
	call	prtasz			; display asciiz string
	mov	ah,prstr
	mov	dx,offset ifnmsg2	; trailer of message
	int	dos
	jmp	ifcmdf			; jump to main command Failure exit
ifnum	endp

; Compare text as number. Jump successfully if number.
ifnumeric proc	near
	mov	ah,cmword		; get following number
	mov	dx,offset rdbuf+1
	mov	comand.cmblen,cmdblen
	mov	rdbuf,'\'		; in case user forgets backslash
	mov	word ptr rdbuf+1,0	; clear buffer
	mov	bx,offset ifnhlp	; help
	call	comnd
	jnc	ifnumer1		; nc = success
	ret				; failure
ifnumer1:mov	si,offset rdbuf		; put text in compare buffer
	cmp	rdbuf+1,'\'		; did user include backslash?
	jne	ifnumer2		; ne = no
	inc	si			; yes, skip our helpful backslash
ifnumer2:call	katoi			; convert number to binary in ax
	jc	ifnumer3		; c = failed to convert a number
	cmp	byte ptr [si],0		; considered all bytes?
	jne	ifnumer3		; ne = no, then not a number
	jmp	ifcmdp			; jump to main command Success exit

ifnumer3:jmp	ifcmdf			; jump to main command Failure exit
ifnumeric endp

; Process IF [NOT] DEF <macro name> <command>
ifmdef	proc	near
	mov	dx,offset rdbuf+2	; point to work buffer
	mov	bx,offset ifdfhlp	; help
	mov	comand.cmblen,cmdblen
	mov	ah,cmword		; get macro name
	mov	comand.cmper,1		; do not react to \%x
	call	comnd
	jnc	ifmde1			; nc = success
	ret				; failure
ifmde1:	mov	word ptr rdbuf,ax	; store length in buffer
	mov	bx,offset mcctab+1	; table of macro keywords
	mov	tempd,0			; tempd = current keyword
	cmp	byte ptr [bx-1],0	; any macros defined?
	je	ifmde9			; e = no, failure, exit now
					; match table keyword and user word
ifmde3:	mov	si,offset rdbuf		; pointer to user's cnt+name
	mov	cx,[si]			; length of user's macro name
	add	si,2			; point to macro name
	cmp	cx,[bx]			; compare length vs table keyword
	jne	ifmde7			; ne = not equal lengths, try another
	push	si			; lengths match, how about spelling?
	push	bx
	add	bx,2			; point at start of keyword
ifmde4:	mov	ah,[bx]			; keyword char
	mov	al,[si]			; new text char
	cmp	al,'a'			; map lower case to upper
	jb	ifmde5
	cmp	al,'z'
	ja	ifmde5
	sub	al,'a'-'A'
ifmde5:	cmp	al,ah			; test characters
	jne	ifmde6			; ne = no match
	inc 	si			; move to next char
	inc	bx
	loop	ifmde4			; loop through entire length
ifmde6:	pop	bx
	pop	si
	jcxz	ifmde10			; z: cx = 0, found the name
					; select next keyword
ifmde7:	inc	tempd			; number of keyword to test next
	mov	cx,tempd
	cmp	cl,mcctab		; all done? Recall, tempd starts at 0
	jae	ifmde9			; ae = yes, no match
	mov	ax,[bx]			; cnt (keyword length from macro)
	add	ax,4			; skip over '$' and two byte value
	add	bx,ax			; bx = start of next keyword slot
	jmp	short ifmde3		; do another comparison
ifmde9:	jmp	ifcmdf			; jump to main command Failure exit
ifmde10:jmp	ifcmdp			; jump to main command Success exit
ifmdef	endp

; IF [not] ALARM hh:mm:ss command
ifalrm	proc	near
	call	chkkbd			; check keyboard for override
	test	status,stat_cc		; Control-C?
	jz	ifalr1			; z = no
	stc
	ret				; yes, return failure now
ifalr1:	push	word ptr timhms
	push	word ptr timhms+2	; save working timeouts
	mov	ax,word ptr alrhms
	mov	word ptr timhms,ax
	mov	ax,word ptr alrhms+2
	mov	word ptr timhms+2,ax	; set alarm value
	call	chktmo			; check for timeout
	pop	word ptr timhms+2	; restore working timeouts
	pop	word ptr timhms
	test	status,stat_tmo		; tod past user time (alarm sounded)?
	jnz	ifalr4			; nz = yes, succeed
					; failure (not at alarm time yet)
	cmp	notflag,0		; need to apply NOT condition?
	jne	ifalr5			; ne = yes, take other exit
ifalr3:	mov	ah,cmline		; fail, read and discard rest of line
	mov	bx,offset rdbuf
	mov	comand.cmblen,cmdblen
	xor	dx,dx
	call	comnd
	clc				; set command parse success
	ret
					; success (at or past alarm time)
ifalr4:	cmp	notflag,0		; need to apply not condition?
	jne	ifalr3			; ne = yes, take other exit
ifalr5:	clc				; pass, do command
	ret
ifalrm	endp

; IF [NOT] {LLT, EQUAL, LGT} word word command
; Permits use of \number, {string}, @filespec
ifequ	proc	near
	mov	ltype,bl		; remember kind of lex test
	mov	comand.cmblen,cmdblen
	mov	ah,cmword		; get a word
	mov	dx,offset rdbuf		; where to store
	mov	rdbuf,0			; clear first entry
	mov	bx,offset ifehlp1	; help
	call	comnd			; ignore parse error if no text
	mov	si,offset rdbuf		; start of line
	mov	di,si			; convert to the same place
	call	cnvlin			; convert \numbers to binary
	jc	ifequ9			; carry set means error
	jcxz	ifequ3			; z = empty word
 	mov	tempd,cx
	add	si,cx
	inc	si			; skip null terminator
	mov	temptr,si		; place to start second part
	mov	dx,si
	mov	word ptr[si],0		; clear second part
	mov	ah,cmword		; get a word of text
	mov	bx,offset ifehlp2	; help
	mov	comand.cmblen,cmdblen
	call	comnd			; ignore parse error if no text
	mov	si,temptr		; start of second line
	mov	di,si			; convert to the same place
	call	cnvlin			; convert \numbers to binary
	jc	ifequ9			; c = failure
	cmp	tempd,cx		; first longer than second?
	jae	ifequ3			; ae = yes
	xchg	tempd,cx		; use length of shorter word
ifequ3:
	jcxz	ifequ9			; z = empty word
	inc	cx			; include null terminator in count
	mov	si,offset rdbuf		; first word
	mov	di,temptr		; second word
	push	es
	mov	ax,ds
	mov	es,ax
	cld
ifequ4:	lodsb
	cmp	al,'a'			; candidate for conversion?
	jb	ifequ5			; b = no
	cmp	al,'z'			; still in lower case set?
	ja	ifequ5			; a = no
	and	al,script.incasv	; else apply case conversion mask
ifequ5:	mov	ah,[di]
	inc	di
	cmp	ah,'a'			; candidate for conversion?
	jb	ifequ5a			; b = no
	cmp	ah,'z'			; still in lower case set?
	ja	ifequ5a			; a = no
	and	ah,script.incasv	; else apply case conversion mask
ifequ5a:cmp	al,ah
	loope	ifequ4
	pop	es
	jb	ifequ6			; exited on before condition
	ja	ifequ7			; exited on above condition

	cmp	ltype,ifequal		; wanted EQUAL condition?
	jne	ifequ9			; ne = no, fail
	jmp	ifcmdp			; else success

ifequ6:	cmp	ltype,ifllt		; LLT test?
	jne	ifequ9			; ne = no, failed
	jmp	ifcmdp			; do IF cmd success

ifequ7:	cmp	ltype,iflgt		; LGT test?
	jne	ifequ9			; ne = no, failed
	jmp	ifcmdp			; do IF cmd success

ifequ9:	jmp	ifcmdf			; do IF cmd failure
ifequ	endp

; Worker for IF [NOT] < = > var var <command>
; var is ARGC, COUNT, ERRORLEVEL, VERSION, or a 16 bit number
ifmath	proc	near
	mov	tempa,bl		; save kind of math test here
	mov	temp,0			; place to store first value
	mov	temp1,0			; place to store high word of value
	mov	tempd,0			; count times around this loop
ifmath1:mov	dx,offset rdbuf+1
	mov	rdbuf,'\'		; in case user forgets backslash
	mov	word ptr rdbuf+1,0	; clear buffer
	mov	bx,offset ifmhlp	; help
	mov	comand.cmblen,cmdblen
	mov	ah,cmword		; get following number
	call	comnd
	jnc	ifmath2			; nc = success
	ret				; failure
ifmath2:mov	si,offset rdbuf+1	; put text in compare buffer
	mov	ax,[si]			; get first two user chars
	or	ax,2020h		; lowercase both bytes
	xor	dx,dx			; high word of apparent value
	cmp	ax,'ra'			; ARGC?
	jne	ifmath3			; ne = no
	xor	ax,ax
	cmp	taklev,0		; in a Take/macro?
	je	ifmath8			; e = no, report ARGC as 0
	mov	bx,takadr		; current Take structure
	mov	ax,[bx].takargc		; get argument count
	jmp	short ifmath8
ifmath3:cmp	ax,'re'			; ERRORLEVEL?
	jne	ifmath4			; ne = no
	mov	al,errlev		; get errorlevel
	xor	ah,ah
	jmp	short ifmath8
ifmath4:cmp	ax,'oc'			; COUNT?
	jne	ifmath5			; ne = no
	xor	ax,ax
	cmp	taklev,0		; in a Take/macro?
	je	ifmath8			; e = no, report COUNT as 0
	mov	bx,takadr		; current Take structure
	mov	ax,[bx].takctr		; get COUNT
	jmp	short ifmath8
ifmath5:cmp	ax,'ev'			; VERSION?
	jne	ifmath5a		; ne = no
	mov	ax,version		; get version such as 300
	jmp	short ifmath8
ifmath5a:cmp	ax,'ek'			; KEYBOARD?
	jne	ifmath6			; ne = no
	mov	ax,keyboard		; get 88 or 101 for keys on keyboard
	jmp	short ifmath8
ifmath6:cmp	rdbuf+1,'\'		; did user include backslash?
	je	ifmath7			; e = yes
	dec	si			; no, employ our helpful backslash
ifmath7:call	katoi			; convert number to binary in dx:ax
	jc	ifmathb			; c = failed to convert a number
ifmath8:cmp	tempd,0			; have second value yet?
	ja	ifmath9			; a = yes, it is in ax
	mov	temp,ax			; save first value
	mov	temp1,dx		; high order part
	inc	tempd			; say we have been here
	jmp	ifmath1			; do second argument

ifmath9:mov	bl,tempa		; kind of math test
	cmp	bl,ifless		; "<"?
	jne	ifmath10		; ne = no
	cmp	temp1,dx		; val1 < val2?
	jb	ifmathp			; b = pass
	ja	ifmathf			; a = fail
	cmp	temp,ax			; val1 < val2?
	jb	ifmathp			; b = pass
	jmp	short ifmathf		; fail
ifmath10:cmp	bl,ifsame		; "="?
	jne	ifmath11		; ne = no
	cmp	temp1,dx		; val1 = val2?
	jne	ifmathf			; ne = no, fail
	cmp	temp,ax			; val1 = val2?
	je	ifmathp			; e = yes, pass
	jmp	short ifmathf		; fail
ifmath11:cmp	temp1,dx		; val2 > val1?
	ja	ifmathp			; a = yes, pass
	jb	ifcmdf			; b = no, fail
	cmp	temp,ax			; val2 > val1?
	ja	ifmathp			; a = yes, pass
ifmathf:jmp	ifcmdf			; else fail
ifmathp:jmp	ifcmdp			; jump to main command Success exit

ifmathb:jmp	ifnum4			; do common error complaint
ifmath	endp

; IF NEWER file1 file2 
tstnewer proc	near
	mov	ah,cmword		; get first filename
	mov	dx,offset rdbuf
	mov	comand.cmblen,cmdblen
	mov	word ptr rdbuf,0	; clear buffer
	mov	bx,offset ifnewhlp	; help
	call	comnd
	jnc	tstnew1			; nc = success
	jmp	tstnewf			; failure
tstnew1:mov	ah,cmword		; get following number
	mov	dx,offset rdbuf+65	; second file name
	mov	comand.cmblen,cmdblen
	mov	word ptr rdbuf+65,0	; clear buffer
	mov	bx,offset ifnewhlp	; help
	call	comnd
	jnc	tstnew2			; nc = success
	jmp	tstnewf				; failure
tstnew2:mov	bx,offset rdbuf		; first file
	call	getfdate		; filedate to dx:ax
	jc	tstnewf			; c = fail
	mov	word ptr rdbuf,ax	; save low part
	mov	word ptr rdbuf+2,dx	; higher
	mov	bx,offset rdbuf+65	; second file
	call	getfdate		; filedate to dx:ax
	jc	tstnewf			; c = fail
	cmp	word ptr rdbuf+2,dx	; first vs second, high word
	ja	tstnewp			; a = newer, pass
	jb	tstnewf			; b = older, fail
	cmp	word ptr rdbuf,ax	; first vs second, low word
	jbe	tstnewf			; be = older, fail
tstnewp:jmp	ifcmdp			; pass
tstnewf:jmp	ifcmdf			; fail
tstnewer endp

; Return file date/time stamp in dx:ax, given ASCIIZ filename in bx
getfdate proc	near
	push	di
	push	es
	mov	dx,offset diskio.dta	; data transfer address
	mov	ah,setdma		; set disk transfer address
	int	dos
	mov	dx,bx			; for file open
	xor	cx,cx			; attributes: find only normal files
	mov	ah,first2		; DOS 2.0 search for first
	int	dos			; get file's characteristics
	pushf				; save status
	mov	ah,setdma		; restore dta
	mov	dx,offset buff
	int	dos
	popf				; get status
	pop	es
	pop	di
	jnc	getfdat1		; nc = success
	ret				; fail
getfdat1:				; time/date stamp yyyymmdd hh:mm:ss
	mov	dl,diskio.dta+25	; yyyyyyym from DOS via file open
	xor	dh,dh
	shr	dx,1			; get year
	add	dx,1980			; add bias
	mov	ax,word ptr diskio.dta+24 ; yyyyyyyym mmmddddd  year+month+day
	clc
	ret
getfdate endp

; DECREMENT/INCREMENT variable size (default size 1)
; Permits variable to be \%<char> or a macro name. Non-negative results.
decvar	proc	near
	mov	temp,-1			; marker to say dec
	jmp	short incvar1
decvar	endp

incvar	proc	near
	mov	temp,1			; marker to say inc
incvar1:mov	kstatus,ksgen		; general command failure
	mov	ah,cmword		; Common code
	mov	word ptr rdbuf,0	; entry count, empty
	mov	dx,offset rdbuf+2	; reserve word 0 for entry count
	mov	word ptr rdbuf+2,0
	mov	bx,offset chgvarhlp
	mov	comand.cmper,1		; don't react to \%x variables
	call	comnd
	jnc	incvar2			; nc = success
	ret				; failure
incvar2:or	ax,ax			; necessary macro name?
	jnz	incvar3			; nz = yes
incvar2a:stc				; no, fail
	ret
incvar3:mov	word ptr rdbuf,ax	; save length of macro name
	mov	si,offset mcctab	; table of macro names
	cld
	lodsb
	mov	cl,al			; number of macro entries
	xor	ch,ch
	jcxz	incvar2a		; z = none
					; find variable
incvar4:push	cx			; save loop counter
	lodsw				; length of macro name to ax
	mov	cx,word ptr rdbuf	; length of user's string
	cmp	ax,cx			; variable name same as user spec?
	jne	incvar6			; ne = no, no match
	push	ax
	push	si			; save these around match test
	mov	di,offset rdbuf+2	; user's string
incvar5:mov	ah,[di]
	inc	di
	lodsb				; al = mac name char, ah = user char
	and	ax,not 2020h		; clear bits (uppercase chars)
	cmp	ah,al			; same?
	loope	incvar5			; while equal, do more
	pop	si			; restore regs
	pop	ax
	jne	incvar6			; ne = no match
	pop	cx			; remove loop counter
	jmp	short incvar7		; e = match
incvar6:add	si,ax			; point to next name, add name length
	add	si,2			;  and string pointer
	pop	cx			; recover loop counter
	loop	incvar4			; one less macro to examine
	xor	ax,ax
	mov	temp,ax			; indicate failure
	jmp	incvar13		; go do command confirmation

incvar7:mov	ax,[si-2]		; get length of variable string
	add	si,ax			; point to segment of definition
	mov	si,[si]			; seg of definition
	mov	word ptr rdbuf+4,si	; preserve seg for later storage
	push	es
	mov	es,si
	mov	cx,es:[0]		; length of definition
	cmp	cx,10+4			; "\x{4294967296}" is max length
	ja	incvar10		; a = too large to qualify
	mov	di,offset rdbuf+6	; copy string to regular data segment
	mov	si,2			; skip over string count word
	mov	al,es:[si]		; get leading byte
	cmp	al,'\'			; escaped number
	je	incvar9			; e = yes
	mov	byte ptr [di],'\'	; insert escape
	inc	di
incvar9:mov	al,es:[si]		; copy string to regular data segment
	mov	[di],al
	inc	si
	inc	di
	loop	incvar9
	mov	byte ptr [di],0		; asciiz
	mov	si,offset rdbuf+6
	call	katoi			; convert var value to binary in dx:ax
	jc	incvar10		; c = failed
	mov	word ptr rdbuf,ax	; save value here, seg in rdbuf+4
	mov	word ptr rdbuf+2,dx	; high order portion of value
	pop	es
	jmp	short incvar12		; now get step size, if any
incvar10:pop	es
	xor	ax,ax
	mov	temp,ax			; setup failure (vs inc or dec)

incvar12:mov	ah,cmword		; get step size, if any
	mov	comand.cmblen,20	; length of step size
	mov	dx,offset rdbuf+9	; where to put string
	mov	rdbuf+9,0
	mov	bx,offset ssizehlp
	call	comnd
	jnc	incvar13
	ret
incvar13:push	ax			; save step size string length
	mov	ah,cmeol
	call	comnd
	pop	ax
	jnc	incvar14		; nc = success
	ret
					; now convert step size, if any
incvar14:or	ax,ax			; is length zero?
	jnz	incvar15		; nz = no, convert number to binary
	inc	ax			; set default inc/dec to 1
	xor	dx,dx			; high part
	jmp	short incvar17		; go process new value

incvar15:mov	si,offset rdbuf+9	; step size string
	cmp	byte ptr [si],'\'	; has "\"?
	je	incvar16		; e = yes
	dec	si
	mov	byte ptr [si],'\'	; insert one
incvar16:call	katoi			; ds:si to number to dx:ax
	jnc	incvar17		; nc = a number
	ret				; fail
					; step size is in ax
incvar17:cmp	temp,0			; inc or dec or fail?
	jg	incvar19		; g = increment
	jl	incvar18		; l = decrement
	stc				; else fail
	ret
incvar18:sub	word ptr rdbuf,ax	; sub current value
	sbb	word ptr rdbuf+2,dx
	jnc	incvar18a		; nc = ok
	ret				; fail
incvar18a:mov	ax,word ptr rdbuf
	mov	dx,word ptr rdbuf+2
	jmp	incvar20

incvar19:add	ax,word ptr rdbuf	; increment
	adc	dx,word ptr rdbuf+2
	jnc	incvar20
	ret				; carry means fail

incvar20:mov	di,offset rdbuf+6	; place to store string
	call	lnout			; binary to ascii decimal in ds:di
	mov	si,offset rdbuf+6
	sub	di,si			; new string length
	mov	cx,di
	cld
	push	es
	mov	ax,word ptr rdbuf+4	; get segment of variable
	mov	es,ax
	mov	di,2
	mov	es:[di-2],cx		; store new count word
	rep	movsb			; copy string to variable's seg
	pop	es
	mov	kstatus,kssuc		; say success
	clc
	ret
incvar	endp

; SET ALARM <time, sec from now or HH:MM:SS>
SETALRM	PROC	NEAR
	mov	dx,offset line		; point to work buffer
	mov	word ptr line,0
	mov	word ptr line+2,0
	mov	bx,offset alrmhlp	; help
	mov	ah,cmword		; get macro name
	call	comnd
	jc	setal1			; c = failure
	mov	ah,cmeol		; get a confirm
	call	comnd
	jc	setal1			; c = failure
	push	word ptr timhms
	push	word ptr timhms+2	; save working timeouts
	mov	si,offset line		; source pointer
	call	inptim			; get the timeout time, sets si
	mov	ax,word ptr timhms	; save time in alarm area
	mov	word ptr alrhms,ax
	mov	ax,word ptr timhms+2
	mov	word ptr alrhms+2,ax
	pop	word ptr timhms+2	; restore working timeouts
	pop	word ptr timhms
	clc
setal1:	ret
SETALRM	ENDP

; REINPUT <timeout> <match text>
; Reread material in serial port buffer, seeking a match with user's text
; pattern. If user's pattern is longer than material in buffer then read
; additional characters from the serial port. Use SCINP to do the main work.

SCREINP	PROC	NEAR
	mov	reinflg,1		; say doing REINPUT, not INPUT
	jmp	short input10
SCREINP	ENDP

; Input from port command, match input with text pattern
; Input [timeout] text
;     
SCINP	PROC	NEAR
	mov	reinflg,0		; say doing INPUT, not REINPUT
	jmp	short input10

input10:mov	kstatus,kssuc
	mov	ah,cmline		; get a whole line of asciiz text
	mov	bx,offset line		; place to put text
	mov	dx,offset inphlp	; help message
	call	comnd			; get the pattern text
	jnc	input11			; nothing, complain
	ret				; failure
input11:mov	ah,cmeol
	call	comnd
	jnc	input12
	ret
input12:cmp	reinflg,0		; Input command?
	jne	input1			; ne = no, Reinput
	cmp	taklev,0		; are we in a Take file?
	je	input0			; e = no, display linefeed
	cmp	flags.takflg,0		; are Take commands being echoed?
	je	input1			; e = no, skip display
input0:	cmp	script.inecho,0		; Input echo off?
	je	input1			; e = yes
	mov	al,lf			; next line
	call	scdisp			; display the char
input1: call	serini			; initialize the system's serial port
	jc	input1a			; c = failure
	mov	status,stat_unk		; clear status flag
	mov	si,offset line		; source pointer
	call	inptim			; get the timeout time, sets si
	jnc	input1b			; nc = legal time value or none
input1a:jmp	input5			; else fail on error
input1b:mov	di,offset line		; put text in compare buffer
	call	in_cnvlin		; convert \numbers in buf line
	mov	inplen,cx		; cx = number of bytes in final string
	mov	parmsk,0ffh	  	; parity mask, assume 8 bit data
	mov	di,portval
	cmp	[di].parflg,parnon	; parity is none?
	je	input1c			; e = none
	mov	parmsk,07fh		; else strip parity (8th) bit
input1c:mov	di,offset line
	mov	temptr,di		; pointer to pattern char
	mov	temptr2,di		; and we need pointer to end of string
	add	temptr2,cx		; offset of end of string
	mov	input_read,0		; init count of bytes read
					; setup reinput read pointer & count
	mov	ax,bufwtptr		; where next new char goes
	mov	bufpkptr,ax		; set peek-read pointer at oldest char
	mov	ax,scpbuflen		; length of the buffer
	mov	bufpkcnt,ax		; always look back one full buffer
 					; see if a pattern needs matching
	cmp	inplen,0		; empty pattern? (cnvlin sets cx=cnt)
	jne	input4			; ne = not empty
	cmp	reinflg,0		; Input command?
	je	input3			; e = yes, read and discard chars
	jmp	input5			;  reinput, just exit timeout

					; empty. read, display, and discard
input3:	call	chkkbd			; check keyboard
	test	status,stat_cc		; did user type control-c?
	jnz	input3b			; nz = yes, quit
	test	status,stat_cr		; did user type cr? [js]
	jz	input3a			; z = no
	or	status,stat_tmo		; force timeout status too
	jmp	input5			; nz = yes, return timeout failure
input3a:call	chktmo			; check timeout
	test	status,stat_tmo
input3b:jnz	input5			; nz = timed out, quit
	call	bufread			; read from serial port buffer into al
	jc	input3			; c = nothing read
	inc	input_read		; count read characters
	jmp	short input3		; loop until timeout, exit timeout

					; start main read and compare loop
input4:	mov	di,temptr		; pointer to current pattern char
	cmp	di,temptr2		; at end of pattern?
	jae	inputx			; ae = yes, return success
	call	chkkbd			; check keyboard
	test	status,stat_cc		; did user type Control-C?
	jnz	input5			; nz = yes, quit
	test	status,stat_cr		; did user type cr? [js]
	jz	input4a			; z = no
	or	status,stat_tmo		; force timeout status too
	jmp	short input5		; nz = yes, return success [js]
input4a:test	status,stat_tmo+stat_ok ; user override/timeout on last read
	jnz	input5			; nz = timed out, quit
	cmp	reinflg,0		; Input command?
	jne	input4b			; ne = no, a reinput cmd
	call	bufread			; read from serial port buffer into al
	jc	input4			; c = nothing there, keep looking
	inc	input_read		; count read characters
	jmp	short input4c		; analyze character

input4b:call	peekbuf			; reinput: peek-read from buffer
	jnc	input4c			; nc = got a character into al
	test	status,stat_tmo		; timeout (or examined all chars)?
	jz	input4			; z = no, keep trying
	jnz	input5			; nz = timeout getting a character

					; got a char from buffer/port
input4c:cmp	al,'a'			; candidate for case conversion? [js]
	jb	input4d			; b = no [js]
	cmp	al,'z'			; in lower case set? [js]
	ja	input4d			; a = no [js]
	and	al,script.incasv	; apply case conversion mask
input4d:mov	di,temptr
	mov	ah,byte ptr [di]	; get current pattern char again
	call	matchr			; al=rcvd, ah=pattern, do they match?
	jc	inpm			; c = no match, try substring
	inc	temptr			; matched, point to next pattern char
	jmp	short input4

input5:	or	errlev,ksrecv		; set RECEIVE failure condition
	or	fsta.xstatus,ksrecv	; set status
	or	kstatus,ksrecv
	cmp	reinflg,0		; Input command?
	jne	input6			; ne = no
	jmp	squit			; exit failure: timeout or Control-C
input6:	jmp	squit1			; skip timeout message, if any
inputx:	clc				; return success
	ret
; See if a trailing-subset of the matched chars + new port char can match
; the beginning part of the pattern. That is, if we were to simply "forget"
; the oldest of the matched chars and slide left the apparent port string
; then could we eventually find a match? Example: "Input 10 memema"
; gives the pattern of "memema"; suppose the received chars were "mememema".
; Forgetting one left-most rcv'd char at a time (two in this case) finally
; yields a match, from which we should continue to compare fresh port chars 
; with successive pattern chars until either they match through all pattern
; chars or we encounter another break. If there is a later break, repeat this
; algorithm.
; Since we really have only the latest char from the port then pointers to
; the matched pattern chars are used to mimic the earlier received chars:
; they must have been identical to produce a match to date. The quick way
; to "forget" oldest received chars is to scan backward through the matched
; pattern chars looking for the current port char; if the first such find does
; not yield a matching substring then look back further.
				; no or partial match then break
				; di = temptr = pattern break char
				; al = port char causing break
				; di - offset line = # chars matched thus far
			; avoid cpu-brand side effects with "repne scasb"
inpm:	mov	tempa,al	; save port char here 
inpm1:	mov	tempd,di	; pattern break loc, where matching failed
	mov	cx,di		; char at di does not match current port char
	sub	cx,offset line	; compute count of matched bytes
	jcxz	inpm4		; z = 0 = mismatch on the initial pattern char

	mov	al,tempa	; port char to find (in case we looped here)
inpm2:	dec	di		; back up one pattern char
	mov	ah,byte ptr [di]; current pattern character to consider
	call	matchr		; is port char = earlier pattern char? [js]
	jnc	inpm3		; nc = equal values, go construct substring
	loop	inpm2		; do cx times, max. (length of match to date)
	jmp	short inpm4	; get here when there are no matches [js]

inpm3:	mov	bx,tempd	; get last break location
	sub	bx,di		; displacement = break - new find of port char
	mov	tempd,di	; remember new location of a port-like char 
				; cx has number of chars in test substring
	dec	cx		; matched one char already [jrs]
	jcxz	inpm3a		; is there anything left? [jrs]
	call	matstr		; does this substring match the pattern?
	jc	inpm1		; c = no match, try making substring smaller

inpm3a:	mov	di,tempd	; sub-string matched. Use this shorter match
	mov	temptr,di	; set di for exit (matstr messes up di)
	inc	temptr		; matched, point to next pattern char
	jmp	input4		; continue with fresh port info

inpm4:	mov	temptr,offset line; complete failure, restart scanning
	jmp	input4		; get something from the port

; worker for SCINP
; compare strings. One starts at offset line, the other starts bx bytes later.
; cx = # chars to compare. Return carry clear if match, else carry set.
matstr:	mov	si,offset line	; start of pattern string
matstr1:mov	ah,byte ptr [si] ; pattern char
	mov	al,byte ptr [si+bx] ; "old port char" (same as pattern char)
	call	matchr		; check match of these two characters
	jc	matstr2		; c = no match (exit with carry flag set)
	inc	si		; match, consider next pair
	loop	matstr1		; consider rest of substring (cx is counter)
	clc			; clear c bit (substrings do match)
matstr2:ret			; preserves flags (c set = no match)

; worker for SCINP
; compare single characters, one in ah and the other in al. Allow the 0ffh
; wild card to match CR and LF individually. Return carry clear if match,
; or carry set if they do not match. Registers preserved.
matchr:	cmp	ah,al		; do these match?
	je	matchr6		; e = yes
	cmp	ah,0ffh		; the match cr/lf indicator?
	je	matchr2		; e = yes	
	cmp	al,0ffh		; the match cr/lf indicator?
	jne	matchr5		; ne = no match at all.
matchr2:push	ax		; save both chars again
	and	ah,al		; make a common byte for testing
	cmp	ah,cr
	je	matchr4		; e = cr matches 0ffh
	cmp	ah,lf
	je	matchr4		; e = lf matches 0ffh
	pop	ax		; recover chars
matchr5:stc			; set carry (no match)
	ret
matchr4:pop	ax		; recover chars
matchr6:clc			; clear carry (match)
	ret
SCINP	ENDP

; Pause for the indicated number of milliseconds, do not access comms channel
SCMPAUSE PROC	NEAR
	mov	kstatus,kssuc
	mov	ah,cmword		; get a word (number)
	mov	dx,offset line+1	; where to store it
	mov	line,'\'		; optional number escape
	mov	line+1,0		; terminate line incase no text
	mov	bx,offset mphlp		; help msg
	call	comnd
	jnc	scmpau1			; nc = success
	ret
scmpau1:mov	si,offset line		; put text in compare buffer
	cmp	line+1,'\'		; did user include backslash?
	jne	scmpau2			; ne = no
	inc	si			; yes, skip our helpful backslash
scmpau2:call	katoi			; convert number to binary in ax
	jc	scmpau3			; c = failed to convert a number
	call	pcwait			; delay number of millisec in AX
	clc
	ret
scmpau3:mov	ah,prstr
	mov	dx,offset mpbad		; complain about bad number
	int	dos
	mov	kstatus,ksgen		; command status, failure
	stc
	ret
SCMPAUSE ENDP

; Pause for the specified number of seconds or until a time of day
; Pause [seconds or hh:mm:ss]
;
SCPAU	PROC	NEAR
	mov	kstatus,kssuc
	mov	ah,cmword		; get a word (number)
	mov	dx,offset line		; where to store it
	mov	byte ptr line,0		; terminate line incase no text
	mov	bx,offset ptshlp	; help msg
	call	comnd
	jc	scpau1			; c = failure
	mov	si,offset line		; source pointer
	call	inptim			; parse pause time (or force default)
	jc	scpau1			; c = bad time value
	mov	wtemp,0			; no modem status to detect
	jmp	swait4			; finish in common code
scpau1:	ret				; return command failure
SCPAU	ENDP

;
; Wait for the indicated signal for the specified number of seconds or tod
; WAIT [seconds] \signal   where \signal is \cd, \dsr, \ri modem status lines.
; Use INPUT-TIMEOUT ACTION for failures.
;
SCWAIT	PROC	NEAR
	mov	kstatus,kssuc
	mov	ah,cmword		; get a word (number)
	mov	dx,offset line		; where to store it
	mov	byte ptr line,0		; terminate line in case no text
	mov	bx,offset ptshlp	; time help msg
	call	comnd
	jnc	swait0			; nc = success
	ret
swait0:	mov	wtemp,0			; clear modem status test byte
	mov	si,offset line		; source pointer
	push	ax			; save length count
	call	inptim			; parse pause time (or force default)
	pop	ax
	jnc	swait0a			; nc = good time value
	ret
swait0a:cmp	si,offset line		; was a number parsed?
	je	swait1c			; e = no, reparse as modem signal

swait1:	mov	ah,cmword		; get optional modem signal word(s)
	mov	dx,offset line
	mov	bx,offset wthlp		; modem signal help
	call	comnd
	jnc	swait1c			; nc = success
	ret
swait1c:mov	si,offset line
	mov	cx,ax			; returned byte count
	or	cx,cx			; number of chars to examine
	jle	swait4			; le = none
	cld
swait1d:lodsb				; get a character
	dec	cx			; reduce count remaining
	cmp	al,'\'			; backslash signal introducer?
	je	swait1d			; e = yes, skip it
	cmp	cx,1			; at least two chars in signal?
	jl	swait3a			; l = no, bad syntax
	mov	ax,[si-1]		; get first two characters
	or	ax,2020h		; upper case to lower, two chars
	cmp	ax,'dc'			; carrier detect?
	jne	swait2			; ne = no, try next signal
	or	wtemp,modcd		; look for the CD bit
	inc	si			; skip this field and separator
	dec	cx			; two less chars left in the line
	jmp	short swait1		; continue the scan
swait2:	cmp	ax,'sd'			; data set ready?
	jne	swait3			; ne = no
	mov	al,[si+1]		; third letter
	or	al,20h			; to lower case
	cmp	al,'r'			; r for dsr?
	jne	swait3b			; ne = no
	or	wtemp,moddsr		; look for the DSR bit
	add	si,2			; skip this field and separator
	sub	cx,2			; three less chars left in the line
	jmp	short swait1
swait3:	cmp	ax,'tc'			; clear to send?
	jne	swait3a			; ne = no
	mov	al,[si+1]		; third letter
	or	al,20h			; to lower case
	cmp	al,'s'			; r for dsr?
	jne	swait3b			; ne = no
	or	wtemp,modcts		; look for the CTS bit
	add	si,2			; skip this field and separator
	sub	cx,2			; three less chars left in the line
	jmp	short swait1		; continue the scan
swait3a:cmp	ax,'ir'			; ring indicator
	jne	swait3b			; ne = no, try next signal
	or	wtemp,modri		; look for the RI bit
	inc	si			; skip this field and separator
	dec	cx			; two less chars left in the line
	jmp	short swait1		; continue the scan
swait3b:or	al,al			; null terminator?
	je	swait4			; e = yes, no more text
	mov	ah,prstr
	mov	dx,offset wtbad		; say bad syntax
	int	dos
	or	errlev,ksuser		; set user intervention error condx
	or	fsta.xstatus,ksuser	; set status
	or	kstatus,ksuser
	stc				; failure
	ret
					; SWAIT4 is used by PAUSE command
SWAIT4:	mov	ah,cmeol		; get command confirmation
	call	comnd
	jnc	swait4a
	ret				; c set is failure
swait4a:cmp	taklev,0		; are we in a Take file
	je	swait5			; e = no, print linefeed
	cmp	flags.takflg,0		; are commands being echoed
	je	swait6			; e = no, skip this
swait5:	cmp	script.inecho,0		; Input echoing off?
	je	swait6			; e = yes
	mov	al,lf			; next line
	call	scdisp			; display the char
swait6: call	serini			; initialize the system's serial port
	jc	swait9			; c = failure
	mov	status,stat_unk		; clear status flag
	push	si
	mov	parmsk,0ffh	  	; parity mask, assume 8 bit data
	mov	si,portval
	cmp	[si].parflg,parnon	; parity is none?
	pop	si
	je	swait7			; e = none
	mov	parmsk,07fh		; else strip parity (8th) bit
swait7:	cmp	wtemp,0			; anything to be tested?
	je	swait8			; e = no, just do the wait part
	call	getmodem		; modem handshake status to AL
	and	al,wtemp		; keep only bits to be tested
	cmp	al,wtemp		; check selected status bits
	jne	swait8			; ne = not all selected bits match	
	clc				; all match. take successful exit
	ret
swait8:	call	chkport			; get and show any new port char
	call	chkkbd			; check keyboard
	test	status,stat_cc		; control-c?
	jnz	swait9			; nz = yes, quit	
	call	chktmo			; check tod for timeout
	test	status,stat_tmo+stat_ok	; timeout or user override?
	jz	swait7			; z = no, continue to wait
	cmp	wtemp,0			; were we waiting on anything?
	jne	swait9			; ne = yes, timeout = failure
	test	status,stat_ok		; user intervention?
	jnz	swait9			; nz = yes, failure
	clc				;  else timeout = success
	ret
swait9:	or	errlev,ksuser		; set user intervention error condx
	or	fsta.xstatus,ksuser	; set status
	or	kstatus,ksuser
	jmp	squit			; take error exit
SCWAIT	ENDP

; SLEEP <number of sec or time of day>
; Does not access comms channels
SCSLEEP	PROC	NEAR
	mov	kstatus,kssuc
	mov	ah,cmword		; get a word (number)
	mov	dx,offset line		; where to store it
	mov	byte ptr line,0		; terminate line incase no text
	mov	bx,offset sleephlp	; help msg
	call	comnd
	jc	scslee1			; c = failure
	mov	si,offset line		; source pointer
	call	inptim			; parse pause time (or force default)
	jnc	scslee2			; nc = good time value
scslee1:ret				; return command failure
scslee2:call	chkkbd			; check keyboard
	test	status,stat_cc		; control-c?
	jnz	scslee3			; nz = yes, quit	
	call	chktmo			; check tod for timeout
	test	status,stat_tmo+stat_ok	; timeout or user override?
	jz	scslee2			; z = no, continue to wait
	test	status,stat_ok		; user intervention?
	jnz	scslee3			; nz = yes, failure
	clc				;  else timeout = success
	ret
scslee3:or	errlev,ksuser		; set user intervention error condx
	or	fsta.xstatus,ksuser	; set status
	or	kstatus,ksuser
	jmp	squit			; take error exit
SCSLEEP	ENDP

; Output line of text to port, detect \b and \B as commands to send a Break
;  and \l and \L as a Long Break on the serial port line.
; Output text, display up to 100 received chars while doing so.
     
SCOUT	PROC	NEAR
	mov	kstatus,kssuc
	mov	ah,cmline		; get a whole line of asciiz text
	mov	bx,offset line		; store text here
	mov	dx,offset outhlp	; help message
	call	comnd
	jnc	outp0d			; nc = success
	ret				; failure
outp0d:	cmp	apctrap,0		; disable from APC
	je	outp0e			; e = no
	stc				; fail
	ret
outp0e:	cmp	taklev,0		; is this being done in a Take file?
	je	outpu0			; e = no, display linefeed
	cmp	flags.takflg,0		; are commands being echoed?
	je	outp0a			; e = no, skip the display
outpu0:	cmp	script.inecho,0		; Input echoing off?
	je	outp0a			; e = yes
	mov	al,lf			; next line
	call	scdisp			; display the char
outp0a:	mov	al,spause		; wait three millisec or more
	add	al,3
	xor	ah,ah
	call	pcwait			; breathing space for HDX systems
	call	serini			; initialize the system's serial port
	jnc	outp0c			; nc = success
	or	errlev,kssend		; set SEND failure condition
	or	fsta.xstatus,kssend	; set status
	or	kstatus,kssend
	jmp	squit

outp0c:	mov	status,stat_unk		; clear status flag
	mov	parmsk,0ffh	  	; parity mask, assume 8 bit data
	mov	si,portval
	cmp	[si].parflg,parnon	; parity is none?
	je	outp0b			; e = none
	mov	parmsk,07fh		; else strip parity (8th) bit
outp0b:	mov	si,portval		; serial port structure
	mov	bl,[si].ecoflg		; Get the local echo flag
	mov	lecho,bl		; our copy
	mov	si,offset line		; get start of line
	mov	di,offset line		; put results in the same place
	mov	ah,script.incasv	; save current case state
	push	ax
	mov	script.incasv,0ffh	; say no case conversion
	call	in_cnvlin		; convert \numbers to binary
	pop	ax
	mov	script.incasv,ah	; recover case state
	jnc	outpu1			; nc = no error
	ret				; return on error
outpu1:	mov	temptr,offset line	; save pointer here
	mov	tempd,cx		; save byte count here
	mov	ttyact,1		; say interactive style output

outpu2:	cmp	tempd,0			; are we done?
	jg	outpu2a			; g = not done yet
	mov	ttyact,0		; reset interactive output flag
	clc				; return success
	ret
outpu2a:mov	si,temptr		; recover pointer
	cld
	lodsb				; get the character
	dec	tempd			; one less char to send
	mov	temptr,si		; save position on line
	mov	tempa,al		; save char here for outchr
	mov	retry,0			; number of output retries
	cmp	al,5ch			; backslash?
	jne	outpu4d			; ne = no
	mov	al,[si]
	cmp	al,5ch			; "\\"?
	jne	outpu4a			; ne = no
	inc	temptr
	dec	tempd			; skip second "\"
	jmp	outpu4g			; send one
outpu4a:and	al,not 20h		; to upper case
	cmp	al,'B'			; "\B" for BREAK?
	jne	outpu4l			; ne = no
outpu4c:inc	temptr			; move scan ptr beyond "\b"
	dec	tempd
	call	sendbr			; call msx send-a-break procedure
	jmp	short outpu5		; resume beyond echoing
outpu4l:cmp	al,'L'			; "\L" for Long BREAK?
	jne	outpu4g			; ne = no
	inc	temptr
	dec	tempd
	call	sendbl			; send a Long BREAK
	jmp	short outpu5		; resume beyond echoing

outpu4d:inc	retry			; count output attempts
	cmp	retry,maxtry		; too many retries?
	jle	outpu4g			; le = no
	or	errlev,kssend		; set SEND failure condition
	or	fsta.xstatus,kssend	; set status
	or	kstatus,kssend
	jmp	squit			; return failure
outpu4g:
	mov	ax,outpace		; millisecs pacing delay
	inc	ax			; at least 1 ms
	call	pcwait
	mov	ah,tempa		; outchr gets fed from ah
	call	outchr			; send the character to the port
	jc	outpu4d			; failure to send char
	cmp	lecho,0			; is Local echo active?
	je	outpu5			; e = no
	mov	al,tempa		;
	test	flags.capflg,logses	; is capturing active?
	jz	outp4b			; z = no
	push	ax			; save char
	call	cptchr			; give it captured character
	pop	ax			; restore character and keep going
outp4b:	cmp	script.inecho,0		; Input echo off?
	je	outpu5			; e = yes
	call	scdisp			; echo character to the screen
					;
outpu5:	mov	tempa,100+1		; wait for max 100 chars in/out [dan]
outpu5a:mov	cx,10			; reset retry counter
outpu5b:push	cx
	call	chkkbd			; check keyboard for interruption
	pop	cx
	test	status,stat_cc		; control c interrupt?
	jnz	outpu6			; nz = yes, quit now
	cmp	script.inecho,0		; Input echo off?
	je	outpu5c			; e = yes, skip port reading/display
	dec	tempa			; reached maximum chars in yet? [dan]
	jz	outpu5c			; z = yes, send character anyway [dan]
	push	cx
	call	chkport			; check for char at serial port
	pop	cx
	test	status,stat_ok		;   and put any in buffer
	jnz	outpu5a			; nz = have a char, look for another
	mov	ax,1			; wait 1 millisec between rereads
	push	cx			; protect counter
	call	pcwait
	pop	cx
	dec	cx			; count down retries
	jge	outpu5b			; ge = keep trying
outpu5c:jmp	outpu2			; no more input, resume command
outpu6:	or	errlev,kssend		; set SEND failure condition
	or	fsta.xstatus,kssend	; set status
	or	kstatus,kssend
	mov	ttyact,0		; reset interactive output flag
	jmp	squit			; quit on control c
SCOUT	ENDP

; OUTPUT  ESC _ usertext ESC \   as APC command
SCAPC	PROC	NEAR
	mov	kstatus,kssuc
	mov	ah,cmline		; get a whole line of asciiz text
	mov	bx,offset line+2	; store text here
	mov	byte ptr[bx-2],ESCAPE	; ESC _ (APC
	mov	byte ptr [bx-1],'_'
	mov	dx,offset apchlp	; help message
	call	comnd
	jnc	spapc1			; nc = success
	ret				; failure
spapc1:	mov	bx,ax			; user text
	add	bx,2			; prefix
	mov	line [bx],ESCAPE	; ESC \ (ST)
	mov	line [bx+1],'\'
	mov	line [bx+2],0		; terminator
	mov	al,script.inecho	; preserve echo status
	mov	ah,flags.takflg
	push	ax
	mov	script.inecho,0		; off
	mov	flags.takflg,0		; off
scapc2:	call	outp0a			; do work via OUTPUT
	pop	ax
	mov	script.inecho,al	; restore
	mov	flags.takflg,ah
	ret
SCAPC	ENDP
     
; Raw file transfer to host (strips linefeeds)
; Transmit filespec [prompt]
; Optional prompt is the single char expected from the host to ACK each line.
; Default prompt is a script.xmitpmt (linefeed) or a carriage return from us.
;     
SCXMIT	PROC	NEAR
	mov	kstatus,kssuc
	mov	ah,cmword		; get a filename, asciiz
	mov	dx,offset line		; where to store it
	mov	bx,offset xmthlp	; help message
	call	comnd
	jnc	xmit0c			; nc = success
	ret				; failure
xmit0c:	mov	ah,cmword		; get a prompt string, asciiz
	mov	dx,offset line+81	; where to keep it (end of "line")
	mov	bx,offset pmthlp	; Help in case user types "?".
	call	comnd
	jnc	xmit0d			; nc = success
	ret				; failure
xmit0d:	mov	line+80,al		; length of user's string
	mov	ah,cmeol		; confirm
	call	comnd
	jnc	xmit0e
	ret
xmit0e:	cmp	line,0			; filename given?
	je	xmit0a			; e = no
	mov	si,offset line+81	; convert possible numeric prompt
	cmp	byte ptr [si-1],0	; anything given?
	jz	xmit0			; z = no, use default
	call	katoi			; convert number to binary, if number
	jnc	xmit0b			; nc = got number
	mov	al,line+81		; get ascii char from user's prompt
xmit0b:	mov	script.xmitpmt,al	; set prompt
xmit0:	mov	dx,offset line		; point to filename
	mov	ah,open2		; DOS 2 open file
	xor	al,al			; open for reading
	int	dos
	mov	fhandle,ax		; store file handle here
	mov	temp,0			; counts chars/line
	jnc	xmit1			; nc = successful opening

xmit0a:	mov	ah,prstr		; give file not found error message
	mov	dx,offset xfrfnf
	int	dos
	or	errlev,kssend		; set SEND failure condition
	or	fsta.xstatus,kssend	; set status
	or	kstatus,kssend
	jmp	squit			; exit failure

xmitx:	mov	ah,prstr		; error during transfer
	mov	dx,offset xfrrer
	int	dos
xmitx2:	mov	bx,fhandle		; file handle
	mov	ah,close2		; close file
	int	dos
	call	bufclear		; clear script buffer
	call	clrbuf			; clear local serial port buffer
	or	errlev,kssend		; set SEND failure condition
	or	fsta.xstatus,kssend	; set status
	or	kstatus,kssend
	jmp	squit			; exit failure
					;
xmity:	mov	bx,fhandle		; file handle
	mov	ah,close2		; close file
	int	dos
	call	bufclear		; clear buffers
	call	clrbuf
	clc				; and return success
	ret
xmit1:	call	serini			; initialize serial port
	jnc	xmit1b			; nc = success
	or	errlev,kssend		; set SEND failure condition
	or	fsta.xstatus,kssend	; set status
	or	kstatus,kssend
	jmp	squit

xmit1b:	call	bufclear		; clear script input buffer
	call	clrbuf			; clear serial port buffer
	mov	status,stat_unk		; clear status flag
	mov	parmsk,0ffh	  	; parity mask, assume 8 bit data
	mov	si,portval
	cmp	[si].parflg,parnon	; parity is none?
	je	xmit1a			; e = none
	mov	parmsk,07fh		; else strip parity (8th) bit
xmit1a:	mov	bl,[si].ecoflg		; get the local echo flag
	mov	lecho,bl		; our copy
	mov	dx,offset crlf		; display cr/lf
	mov	ah,prstr
	int	dos

xmit2:	mov	dx,offset line		; buffer to read into
	mov	cx,linelen		; # of bytes to read
	mov	ah,readf2		; read bytes from file
	mov	bx,fhandle		; file handle is stored here
	int	dos
	jnc	xmit2a			; nc = success
	jmp	xmitx			; exit failure
xmit2a:	mov	cx,ax			; number of bytes read
	jcxz	xmity			; z = none, end of file
	mov	si,offset line		; buffer for file reads
	cld
xmit3:	lodsb				; get a byte
	cmp	al,ctlz			; is this a Control-Z?
	jne	xmit3a			; ne = no
	cmp	flags.eofcz,0		; ignore Control-Z as EOF?
	je	xmit3a			; e = yes
	jmp	xmity			; ne = no, we are at EOF
xmit3a:	push	si			; save position on line
	push	cx			; and byte count
	cmp	al,cr			; CR, end of line?
	jne	xmit3b			; ne = no
	cmp	temp,0			; chars sent in this line, any?
	ja	xmit3c			; a = sent some
	cmp	script.xmitfill,0	; fill empty lines?
	je	xmit3c			; e = no, send the cr
	mov	al,script.xmitfill	; empty line fill char
	pop	cx
	pop	si
	dec	si			; backup read pointer to CR again
	inc	cx			; count filler as line information
	push	si
	push	cx
	jmp	short xmit3c
xmit3b:	cmp	al,lf			; line feed?
	jne	xmit3c			; ne = no
	cmp	script.xmitlf,0		; send LF's?
	je	xmit7			; e = no, don't send it
	mov	temp,-1			; -1 so inc returns 0 after send
xmit3c:	push	ax			; save char around outchr call
	mov	retry,0			; clear retry counter
xmit4f:	pop	ax			; recover saved char
	push	ax			; and save it again
	mov	ah,al			; outchr wants char in ah
	inc	retry			; count number of attempts
	cmp	retry,maxtry		; too many retries?
	jle	xmit4g			; le = no
	or	status,stat_cc		; simulate control-c abort
	pop	ax			; clean stack
	xor	al,al			; clear char
	jmp	xmita			; and abort transfer
xmit4g:	push	ax			; save char
	call	outchr			; send the character to the port
	pop	ax			; recover char
	pushf				; save carry flag
	cmp	flags.comflg,'t'	; using internal TCP/IP stack?
	jne	xmit4j			; ne = no
	mov	al,ah			; ah is the output byte
	call	dopar			; apply parity to al, if any
	cmp	al,0ffh			; IAC now?
	jne	xmit4j			; ne = no
	call	outchr			; double the IAC byte
xmit4j:	inc	temp			; count chars sent in this line
	popf				; recover carry flag
	jc	xmit4f			; c failed, try again
xmit4h:	pop	ax			; recover saved char
	cmp	lecho,0			; is local echoing active?
	je	xmit5			; e = no
	test	flags.capflg,logses	; capturing active?
	jz	xmit4a			; z = no
	call	cptchr			; give it the character just sent
xmit4a:	call	scdisp			; display char on screen

xmit5:	cmp	al,cr			; did we send a carriage return?
	je	xmit8			; e = yes, time to check keyboard

xmit7:	pop	cx
	pop	si
	dec	cx
	or	cx,cx
	jle	xmit7a			; le = finished this line
	jmp	xmit3			; finish this buffer full
xmit7a:	jmp	xmit2			; read next buffer

xmit8:	test	status,stat_cc		; Control-C seen?
	jnz	xmita			; nz = yes
	mov	temp,0			; say starting new char/line count
	call	chkkbd			; check keyboard (returns char in al)
	test	status,stat_ok		; have a char?
	jnz	xmita			; nz = yes
	cmp	script.xmitpmt,0	; is prompt char a null?
	jne	xmit8b			; ne = no
	call	bufread			; check for char from serial port buf
	jnc	xmit8			; nc = a char, read til none
	jmp	short xmit8c		; continue transfer
xmit8b:	call	bufread			; check for char from serial port buf
	jc	xmit8			; c = none
	cmp	al,script.xmitpmt	; is port char the ack?
	jne	xmit8			; ne = no, just ignore the char
xmit8c:	mov	ax,script.xmitpause	; get millisecs to pause
	or	ax,ax			; any time?
	jz	xmit7			; z = none
	call	pcwait			; wait this long
	jmp	short xmit7		; yes, continue transfer

xmita:	test	status,stat_cc		; Control-C?
	jnz	xmitc			; nz = yes
	test	status,stat_cr		; a local ack?
	jz	xmit8			; z = no, ignore local char
	mov	dx,offset crlf		; display cr/lf
	mov	ah,prstr
	int	dos
	jmp	xmit8c			; continue transfer
xmitc:	pop	cx			; Control-C, clear stack
	pop	si			; ...
	mov	dx,offset xfrcan	; say canceling transfer
	mov	ah,prstr
	int	dos
	mov	flags.cxzflg,0		; clear Control-C flag
	jmp	xmitx2			; ctrl-c, quit

SCXMIT	ENDP

;;;;;;;;;;;;;;;;;; local support procedures ;;;;;;;;;;
;
;worker: copy line from si to di, converting \nnn strings to single chars
; returns carry set if error, else carry clear. Detects leading at-sign
; as an indicator to read command file for one line of text; command files
; may be nested to a depth of 100.
; Items of the form \chars which are not numbers are copied verbatium
; to the output string (ex: \a  is copied as \a). The string is first trimmed
; of trailing spaces, then the possible curly brace delimiter pair is
; removed, '\\' is stored as '\', and \numbers are converted to binary.
in_cnvlin proc	near
	push	tempd
	push	es
	push	si			; source ptr
	push	di			; destination ptr
	push	ds
	pop	es			; use data segment for es:di
	mov	tempd,0			; count indirection depth
cnvln0:	cmp	tempd,100		; limit to 100 deep
	jbe	cnvln0a			; be = not too deep yet
	jmp	cnvln8			; too deep, quit
cnvln0a:cld
	xor	cx,cx			; initialize returned byte count
	lodsb				; get the first character
	cmp	al,40h			; at-sign indirection?
	je	cnvln5			; e = yes, open the file
	dec	si			; no, push back char just read
	call	cnvstr			; convert string's curly braces
cnvln1:	mov	ax,[si]			; read two bytes
	cmp	ax,'\\'			; escaped backslash?
	jne	cnvln1a			; ne = no
	add	si,2			; formally read them
	jmp	short cnvln4a		; and write one copy to output
cnvln1a:call	katoi			; get a char into al, convert number
	jnc	cnvln4			; nc = binary number converted
	cmp	al,0ffh			; cr/lf wild card?
	je	cnvln4			; e = yes, store it
	or	al,al			; end of line?
	jnz	cnvln3			; nz = no
	jmp	cnvlnx			; yes, exit now
cnvln3:	cmp	al,'a'			; candidate for conversion? [js]
	jb	cnvln4			; b = no
	cmp	al,'z'			; still in lower case set? [js]
	ja	cnvln4			; a = no
	and	al,script.incasv	; else apply case conversion mask
cnvln4: stosb				; save the char
	inc	cx			; and count it
	or	ah,ah			; was number larger than one byte?
	jz	cnvln1			; z = no
	xchg	ah,al			; put high byte into al
cnvln4a:stosb				; store it too
	inc	cx			; count storage
	jmp	short cnvln1		; read more

cnvln5:	mov	dx,si			; get filename ptr from source line
	push	si
	inc	tempd			; count indirection depth
	mov	cx,64			; max length of a filename.
cnvln5a:cmp	byte ptr [si],' '	; whitespace or control code?
	jbe	cnvln5b			; be = yes, found termination
	inc	si			; else look at next char
	loop	cnvln5a			; limit search
cnvln5b:mov	byte ptr [si],0		; make asciiz
	pop	si	
	mov	ah,open2		; DOS 2 open file
	xor	al,al			; open for reading
	int	dos
	mov	word ptr fhandle,ax	; store file handle
	jnc	cnvln7			; nc = open ok, read from file

	mov	ah,prstr
	mov	dx,offset indmis	; file open error msg
	int	dos
	xor	cx,cx			; say zero bytes read
	pop	di			; destination ptr
	pop	si			; source ptr
	pop	es
	pop	tempd
	stc				; set c bit, failure
	ret

cnvln7:	mov	bx,word ptr fhandle	; file handle
	mov	cx,linelen		; # of bytes to read
	mov	ah,ioctl		; ioctl, is this the console device?
	xor	al,al			; get device info
	int	dos
	and	dl,81h			; ISDEV and ISCIN bits needed together
	cmp	dl,81h			; Console input device?
	jne	cnvln7d			; ne = no, use regular file i/o
	push	ds
	pop	es			; set es:di to data segment
	push	di			; save starting pointer
cnvln7b:mov	ah,coninq		; read console, no echo
	int	dos
	stosb
	cmp	al,cr			; end of the line yet?
	loopne	cnvln7b			; keep reading
	mov	byte ptr [di],0		; insert terminator
	pop	di			; recover starting pointer
	mov	dx,di			; simulate read file read
	mov	ax,linelen
	sub	ax,cx			; ax = number of chars read
	jmp	short cnvln7e		; close file, finish processing

cnvln7d:mov	dx,di			; destination ptr
	mov	byte ptr [di],0		; insert null terminator, clears line
	mov	ah,readf2		; DOS 2 read from file
	int	dos
cnvln7e:pushf				; save flags
	push	ax			; save byte count read
	mov	ah,close2		; close file (wanted just one line)
	int	dos
	pop	ax
	popf				; recover flags now
	jc	cnvln8			; c = error
	mov	cx,ax			; ax = number of bytes read
	jcxz	cnvln8a			; cx = z = no bytes read
	mov	al,cr			; look for cr as terminator
	cld
	repne	scasb			; scan while not a cr and cx not zero
	jne	cnvln7a			; ne = no cr found
	dec	di			; point at cr
cnvln7a:mov	byte ptr [di],0		; plant terminator on the cr
					;  or after last read char, if no cr.
	pop	di			; get original destination ptr
	push	di			; and save it again
	mov	si,dx			; new source = this line
					; go convert text, as necessary, and
	jmp	cnvln0			;  allow nested indirection

cnvln8: mov	ah,prstr
	mov	dx,offset inderr	; error reading file message
	int	dos
cnvln8a:xor	cx,cx			; say zero bytes read
	pop	di
	pop	si
	pop	es
	pop	tempd
	stc				; set carry for failure
	ret				; and do a real return

cnvlnx:	pop	di			; destination ptr
	pop	si			; source ptr
	pop	es
	pop	tempd
	clc				; clear c bit, success
	ret
in_cnvlin	endp
;
; worker: read the number of seconds to pause or timeout
;    returns time of day for timeout in timhms, and next non-space or
;    non-tab source char ptr in si. Time is either elapsed seconds or
;    a specific hh:mm:ss, determined from context of colons being present.
;    Last form can be abbreviated as hh:[mm[:ss]]. Returns carry set if
;    hh:mm:ss form has bad construction (invalid time).
inptim	proc	near
	push	ax
	push	bx
	push	cx
	push	dx
	push	di
	cld				; decode pure seconds construction
	mov	di,si			; remember source pointer
	mov	cx,10			; multiplier
	mov	bx,script.indfto	; no numbers yet, use default-timeout
	mov	al,byte ptr[si]
	cmp	al,':'			; stray hh:mm:ss separator?
	je	inptm8			; e = yes
	cmp	al,'9'			; start with numeric input?
	ja	inptm4			; a = no, use default time
	cmp	al,'0'			; ditto
	jb	inptm4
	xor	ah,ah			; source char holder
	xor	bx,bx			; accumulated sum
inptm1:	mov	al,byte ptr[si]		; get a byte into al
	cmp	al,':'			; hh:mm:ss construction?
	je	inptm8			; e = yes
	sub	al,'0'			; remove ascii bias
	cmp	al,9			; numeric?
	ja	inptm4			; a = non-numeric, exit loop, bx = sum
	xchg	ax,bx			; put sum into ax, char in bl
	mul	cx			; sum times ten 
	xchg	ax,bx			; put char into al, sum in bx
	add	bx,ax			; add to sum
	inc	si			; next char
	jmp	short inptm1		; loop thru all chars

inptm4:	cmp	bx,12*60*60		; half a day, in seconds
	jb	inptm5			; b = less than
	jmp	inptm13			; more than, error
inptm5:	push	si			; save ending scan position for return
	mov	timout,bx		; # seconds of timeout desired
	mov	ah,gettim		; read DOS tod clock
	int	dos
	mov	timhms[0],ch		; hours
	mov	timhms[1],cl		; minutes
	mov	timhms[2],dh		; seconds
	mov	timhms[3],dl		; hundredths of seconds
	mov	bx,2			; start with seconds field
inptm6: mov	ax,timout		; our desired timeout interval
	add	al,timhms[bx]		; add current tod digit to interval
	adc	ah,0
	xor	dx,dx			; clear high order part thereof
	mov	cx,60			; divide by 60
	div	cx			; compute number of minutes or hours
	mov	timout,ax		; quotient
	mov	timhms[bx],dl		; put remainder in timeout tod digit
	dec	bx			; look at next higher order time field
	or	bx,bx			; done all time fields?
	jge	inptm6			; ge = no
	cmp	timhms[0],24		; normalize hours
	jl	inptm7			; l = not 24 hours
	sub	timhms[0],24		; discard part over 24 hours
inptm7:	pop	si			; return ptr to next source char
	jmp	short inptm11		; trim trailing whitespace

inptm8:					; decode hh:[mm[:ss]] to timhms
	mov	si,di			; recall starting source pointer
	mov	word ptr timhms[0],0	; clear time out tod
	mov	word ptr timhms[2],0
	xor	bx,bx			; three groups possible
inptm9:	mov	dl,byte ptr[si]		; get a char
	cmp	dl,':'			; field separator?
	je	inptm10			; e = a separator, step fields
	sub	dl,'0'			; remove ascii bias
	cmp	dl,9
	ja	short inptm11		; a = failure to get expected digit
	mov	al,timhms[bx]		; get sum to al
	mov	ah,10
	mul	ah			; sum times ten
	add	al,dl			; sum = 10 * previous + current
	mov	timhms[bx],al		; current sum
	cmp	timhms[bx],60		; more than legal?
	jae	inptm13			; ae = illegal
	or	bx,bx			; doing hours?
	jnz	inptm9a			; nz = no, min or sec
	cmp	timhms[bx],24		; more than legal?
	jae	inptm13			; ae = illegal
inptm9a:inc	si			; next char
	jmp	short inptm9		; continue analysis
inptm10:inc	bx			; point to next field
	inc	si			; next char
	cmp	bx,2			; last subscript to use (secs)
	jbe	inptm9			; be = get more text

inptm11:cmp	byte ptr [si],spc	; examine break char, remove spaces
	jne	inptm12			; ne = no, stay at this char
	inc	si			; look at next char
	jmp	short inptm11		; continue scanning off white space
inptm12:clc				; carry clear for success	
	jnc	inptm14
inptm13:stc				; carry set for illegal value	
inptm14:pop	di			; return with si beyond our text
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret
inptim	endp

; worker: display the char in al on screen
; use caret-char notation for control codes
scdisp	proc	near
	push	dx
	push	ax
	mov	ah,conout		; our desired function
	test	flags.remflg,d8bit	; show all 8 bits?
	jnz	scdisp0			; nz = yes
	and	al,7fh			; apply 7 bit display mask
scdisp0:or	al,al			; null?
	jz	scdis2			; z = yes, ignore
	cmp	al,del			; delete code?
	je	scdis2			; e = yes, ignore
	cmp	al,spc			; control char?
	jae	scdis1			; ae = no, display as-is
	cmp	al,cr			; carriage return?
	je	scdis1			; e = yes, display as-is
	cmp	al,lf			; line feed?
	je	scdis1
	cmp	al,tab			; horizontal tab?
	je	scdis1
	cmp	al,bell			; bell?
	je	scdis1
	cmp	al,bs			; backspace?
	je	scdis1
	cmp	al,escape		; escape?
	je	scdis1
	or	al,40h			; control code to printable char
	push	ax
	mov	dl,5eh			; display caret first
	int	dos
	pop	ax
scdis1:	mov	dl,al			; the char to be displayed
	int	dos
scdis2:	pop	ax
	pop	dx
	ret
scdisp	endp

; workers
; Circular buffer for data from serial port. Written by Joe R. Doupnik
; Entry points -
;	bufread: read serial port for latest char (invokes bufwrite, sets
;			status), get a char into al, return carry set if none.
;	bufwrite: put a char from al into buf. If this overwrites an unread
;			character then: we lose the old char, the read pointer
;			is moved to the next oldest unread char, and the
;			number of chars in the buffer is decreased by one.
;	bufclear: empties the buffer.
; The buffer is prtbuf, of size scpbuflen bytes. Internally, integer bufcnt
; holds the number of buffer locations occupied, pointer bufrdptr is the
; offset of the char to be read, pointer bufwtptr is the offset of the
; place to store the next incoming char.
;
bufclear proc	near
	xor	ax,ax			; create a zero
	mov	bufcnt,ax		; clear count of bytes in buffer
	mov	bufrdptr,ax		; move read pointer to start of buf
	mov	bufwtptr,ax		; move write pointer to start of buf
	mov	cx,scpbuflen
	push	es			; physically clear the buffer
	push	di
	mov	ax,bufseg		; segment of port buffer
	mov	es,ax
	xor	al,al			; write scpbuflen nulls
	xor	di,di
	cld
	shr	cx,1			; do an odd byte now
	jnc	bufcle1			; nc = no odd byte
	stosb
bufcle1:rep	stosw			; do double bytes (words)
	pop	di
	pop	es
	ret
bufclear endp

bufread	proc	near
	call	chkport			; get any oldest char from port
	call	chktmo			; check tod for timeout
	cmp	bufcnt,0		; empty buffer?
	jne	bufrd1			; ne = no
	stc				; yes, set carry flag (no char)
	ret				; and quit (chkport sets status)
bufrd1: push	si
	push	es
	mov	si,bufseg		; get buffer segment
	mov	es,si
	mov	si,bufrdptr
	mov	al,es:[si]		; extract a char into al
	inc	bufrdptr		; move pointer to next byte
	dec	bufcnt			; say have extracted a char
	mov	si,scpbuflen
	cmp	bufrdptr,si		; beyond end?
	jb	bufrd2			; b = not yet, just return
	mov	bufrdptr,0		; reset to start of buf (wrapping)
bufrd2:	pop	es
	pop	si
	clc				; clear carry flag (have read a char)
	ret				; chkport sets status
bufread	endp

; Non-destructive read of serial port circular buffer. Requires external
; initialization of peek read pointer bufpkptr and count remaining bufpkcnt.
; Returns character in register al.
peekbuf	proc	near
	cmp	bufpkcnt,0		; peek counter, empty buffer?
	jne	peekbu2			; ne = no, so look in buffer
	or	status,stat_tmo		; force timeout status
	push	si
	mov	si,scpbuflen
	cmp	bufcnt,si		; is real buffer full?
	pop	si
	jae	peekbu1			; ae = yes, have examined everything
	call	chkport			; get a char from port
	and	status,not stat_tmo	; clear timeout
	call	chktmo			; and check for timeout
	cmp	bufcnt,0		; still nothing from port?
	je	peekbu1			; e = no char, report fact
	inc	bufpkcnt		; got one, increase peek counter
	jmp	short peekbu2		; go extract it
peekbu1:stc				; return nothing to see
	ret
peekbu2:push	si
	push	es
	mov	si,bufseg		; segment of port buffer
	mov	es,si
	mov	si,bufpkptr		; buffer peek pointer
	mov	al,es:[si]		; extract a char into al
	inc	bufpkptr		; move pointer to next byte
	dec	bufpkcnt		; say have extracted a char
	mov	si,scpbuflen
	cmp	bufpkptr,si		; beyond end?
	jb	peekbu3			; b = not yet, just return
	mov	bufpkptr,0		; reset to start of buf (wrapping)
peekbu3:pop	es
	pop	si
	clc				; clear carry flag (have read a char)
	ret
peekbuf	endp

bufwrite proc	near
	push	si
	push	es
	mov	si,bufseg		; segment of buffer
	mov	es,si
	mov	si,bufwtptr
	mov	es:[si],al		; store char held in al
	inc	bufwtptr		; move pointer to next byte
	mov	si,scpbuflen		; length of buffer
	cmp	bufwtptr,si		; beyond end?
	jb	bufwt1			; b = not yet
	mov	bufwtptr,0		; reset to start of buf (wrapping)
bufwt1:	inc	bufcnt			; say have added a char to the buf
	cmp	bufcnt,si		; more than we can hold?
	jbe	bufwt3			; be = not overflowing
	push	bufwtptr		; read ptr can't alias write ptr
	pop	bufrdptr		; move up read pointer
	mov	bufcnt,si		; limit count to max buffer length
bufwt3:	pop	es
	pop	si
	ret
bufwrite endp

; Report buffer status for dumping buffer to a log file.
; Yield ax, cx, es, si as indicated below.
buflog	proc	far
	mov	ax,bufcnt			; number of unread chars
	mov	cx,scpbuflen			; length of buffer
	mov	si,bufseg			; segment of buffer
	mov	es,si
	mov	si,bufrdptr			; where to read next char
	ret					; return these registers
buflog	endp

; worker: check for timeout, return status=stat_tmo if timeout, else bit
;  stat_tmo is cleared.
chktmo:	and	status,not stat_tmo
	mov	ah,gettim		; get the time of day
	int	dos
	sub	ch,timhms[0]		; hours difference, ch = (now-timeout)
	je	chktmo2			; e = same, check mmss.s
	jg	chktmo1			; g = past target hour
	add	ch,24			; we are early, see by how much
chktmo1:cmp	ch,12			; hours difference, large or small?
	jge	chktmox			; ge = not that time yet
	jl	chktmo3			; l = beyond that time
chktmo2:cmp	cl,timhms[1]		; minutes, hours match
	jb	chktmox			; b = early
	ja	chktmo3			; a = late
	cmp	dh,timhms[2]		; seconds, hhmm match
	jb	chktmox			; b = early
	ja	chktmo3			; a = late
	cmp	dl,timhms[3]		; fractions, hhmmss match
	jb	chktmox			; b = early
chktmo3:or	status,stat_tmo		; say timeout
	stc
	ret
chktmox:clc
	ret
;
; worker: check keyboard for char. Return status = stat_cc if control-C typed,
; stat_cr if carriage return, or stat_ok if any other char typed. Else return
; with these status bits cleared.
chkkbd:	and	status,not (stat_ok+stat_cc+stat_cr) ; clear status bits
	xor	al,al
	cmp	flags.cxzflg,'C'	; Control-C interrupt seen?
	je	chkkbd0			; e = yes
	call	isdev			; is stdin a device, not disk file?
	jnc	chkkbd2			; nc = not device so do not read here
	mov	ah,dconio		; keyboard char present?
	mov	dl,0ffH
	int	dos
	je	chkkbd1			; e = none
	or	status,stat_ok		; have a char, return it in al
	cmp	al,3			; control c?
	jne	chkkbd1			; ne = not control c
chkkbd0:or	status,stat_cc		; say control c		 
chkkbd1:cmp	al,cr			; carriage return? [js]
	jne	chkkbd2			; ne = no
	or	status,stat_cr		; say carriage return [js]
chkkbd2:ret
;
; worker: check serial port for received char. Return status = stat_ok if
;  char received, otherwise stat_ok cleared. Can echo char to screen. Will
;  write char to local circular buffer.
chkport:and	status,not stat_ok	; clear status bit
	call	prtchr			; char at port (in al)?
	jnc	chkpor1			; nc = yes, analyze it
	push	bx
	mov	bx,portval
	cmp	[bx].portrdy,0		; is port not-ready?
	pop	bx
	jne	chkpor5			; ne = no, port is ready, just no char
	or	status,stat_cc		; Control-C for port not ready
chkpor5:stc
	ret				; no, return
chkpor1:and	al,parmsk		; strip parity, if any
	cmp	rxtable+256,0		; is translation turned off?
	je	chkpor0			; e = yes, no translation
	push	bx			; translate incoming character
	mov	bx,offset rxtable	; the translation table
	xlatb
	pop	bx
chkpor0:test	flags.capflg,logses	; capturing active?
	jz	chkpor3			; z = no
	test	flags.remflg,d8bit	; keep 8 bits for displays?
	jnz	chkpo0a			; nz = yes, 8 bits if possible
	cmp	flags.debug,0		; is debug mode active?
	jne	chkpo0a			; ne = yes, record 8 bits
	and	al,7fh			; remove high bit
chkpo0a:push	ax			; save char
	call	cptchr			; give it captured character
	pop	ax			; restore character and keep going
chkpor3:test	flags.remflg,d8bit	; keep 8 bits for displays?
	jnz	chkpo3a			; nz = yes, 8 bits if possible
	and	al,7fh			; remove high bit
chkpo3a:cmp	script.inecho,0		; input echoing off?
	je	chkpor4			; e = yes
	call	scdisp			; display the char
chkpor4:call	bufwrite		; put char in buffer
	or	status,stat_ok		; say have a char (still in al)
	ret
;
; Squit is the script error exit pathway.
;
squit:	cmp	flags.cxzflg,'C'	; Control-C interrupt seen?
	je	squit5			; e = yes
	test	status,stat_tmo		; timeout?
	jz	squit2			; z = no, another kind of failure
	cmp	taklev,0		; in a Take/macro?
	jne	squit1			; ne = yes, skip timeout message
	push	dx
	mov	dx,offset tmomsg	; say timed out
	mov	ah,prstr
	int	dos			; display it
	pop	dx
squit1:	cmp	script.inactv,0		; action to do upon timeout
	je	squit4			; 0 = proceed, ne = non-zero = quit
squit5:	call	takclos			; close Take file or macro
squit2:	call	isdev			; stdin is a device (vs file)?
	jc	squit3			; c = device, not a file
	mov	flags.extflg,1		; set Kermit exit flag
squit3:	cmp	flags.cxzflg,'C'	; Control-C interrupt seen?
	jne	squit6			; ne = no
	or	kstatus,ksuser		; say user intervention
squit6:	stc
	ret				; return failure
squit4:	clc				; return success, ignore error
	ret
code	ends
	end
