	NAME	mssscp
; File MSSSCP.ASM
	include mssdef.h
;	Copyright (C) 1982, 1997, 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
;       
; 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, scapc, inpath_seg, sforward, scminput, xecho
	public	input_status, xifcmd, whilecmd, minpcnt, switch

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+0		; indicators for IF conditions
iffail		equ	1+0100h		; plus high byte = number of args
ifext		equ	2+0100h
iferr		equ	3+0100h
ifnot		equ	4+0
ifctr		equ	5+0
ifmdf		equ	6+0100h
ifalarm		equ	7+0100h
ifequal		equ	8+0200h
ifless		equ	9+0200h
ifsame		equ	10+0200h
ifmore		equ	11+0200h
ifllt		equ	12+0200h
iflgt		equ	13+0200h
ifpath		equ	14+0100h
ifdir		equ	15+0100h
ifnewer		equ	16+0100h
iftrue		equ	17+0
ifemulation	equ	18+0

braceop	equ	7bh		; opening curly brace
bracecl	equ	7dh		; closing curly brace

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, domath_ptr:word, domath_cnt:word
	extrn	domath_msg:word, pardone:word, parfail:word, ifelse:byte
	extrn	domacptr:word

					; global (public) variables     
script	scptinfo <>			; global structure, containing:
;;inactv	db	0		; input action value (default proceed)
;;incasv	db	0		; input case  (default ignore)
;;indfto	dw	1		; input and pause timeout (def 1 sec)
;;inecho	db	1		; echo Input cmd text (0 = no)
;;infilter	db	1	; filer control sequences from screen (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
minpcnt dw	0			; minput successful match pattern
bufcnt	dw	0			; serial port buf byte cnt, must be 0
bufseg	dw	0			; segment of buffer
bufrptr dw	0			; input buffer read-next pointer
inpath_seg dw	0			; segment of inpath string (0=none)
reinflg	db	0			; 0=INPUT, 1=REINPUT, 2=MINPUT
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
disp_state dw	0			; scdisp state machine state
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	dw	0			; another temp
wtemp	db	0			; temp for WAIT
ltype	dw	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
input_status dw 0ffffh			; INPUT status for v(instatus)
deflabel db	'default',0		; label :default, asciiz

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$'

ifkind	db	0		; 0 = IF, 1 = XIF, 2 = WHILE

clrtable db	4
	mkeyw	"input-buffer",1
	mkeyw	"device-buffer",2
	mkeyw	"both",3
	mkeyw	'APC',4

iftable	db	20			; 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	'Emulation',ifemulation
	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
	mkeyw	'True',iftrue
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$'
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$'
ifehlp	db	cr,lf,' word or variable to be compared$'
ifnewhlp db	' filename$'
whilehlp db	cr,lf,' While if-condition {command, command,...}'
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 (buffers)'
	db	cr,lf,' APC (to not return to Connect mode after APC cmd)$'
mskcmd	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.$'
switchhlp1 db	cr,lf,' index'
switchhlp2 db	' {body of Switch statement}$'

; ifkind    0 1 2	 3	4 5 6	    7 8       9      10
ifhlplst dw 0,0,ifnewhlp,ifnhlp,0,0,ifdfhlp,0,ifehlp,ifmhlp,ifmhlp
;	   11	  12	 13	14	 15	  16
	dw ifmhlp,ifehlp,ifehlp,ifnewhlp,ifnewhlp,ifnewhlp
data1	ends

code1	segment
	extrn	isdev:far, tolowr:far, prtasz:far, strlen:far, domath:far
	extrn	isfile:far, malloc:far, atpclr:near, atparse:near
	extrn	strcpy:far, prtscr:far, dec2di:far, strcat:far, docnv:far
	extrn	takrd:far, toupr:far, poplevel:far
extrn decout:far,valout:far
	assume	cs:code1
code1	ends

code	segment
     
	extrn	comnd:near, clrbuf:near, prtchr:near, outchr:near
	extrn	cptchr:near, serini:near, pcwait:far, spath:near
	extrn	getmodem:near, sendbr:near, takclos:far
	extrn	sendbl:near, lnout:near, dopar:near, dodecom:near
	extrn	takopen_macro:far, cnvlin:near
	assume	cs:code, ds:data, es:nothing

fcptchr	proc	far
	call	cptchr
	ret
fcptchr	endp

fprtchr	proc	far
	call	prtchr
	ret
fprtchr	endp

; 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
	mov	bufcnt,0		; clear the buffer (say is empty)
	mov	bufrptr,0		; buffer read-next pointer
	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:	xor	ax,ax
	mov	disp_state,ax		; reinit escape parser
	cmp	bx,1
	jne	scclr2
	mov	bufcnt,ax		; clear INPUT buffer (say is empty)
	mov	bufrptr,ax		; buffer read-next pointer
	clc
	ret
scclr2:	cmp	bx,2
	jne	scclr3			; default, gibberish bx
	call	clrbuf
	clc
	ret
scclr3:	cmp	bx,3			; BOTH?
	jne	scclr4			; ne = no
	mov	bufcnt,ax		; clear INPUT buffer (say is empty)
	mov	bufrptr,ax		; buffer read-next pointer
	call	clrbuf			; clear system serial port buffer too
	clc
	ret
scclr4:	cmp	bx,4			; APC?
	jne	scclr7
scclr5:	call	poplevel		; pop macro/take level
	ret
scclr7:	stc				; fail
	ret
SCCLR	ENDP

XECHO	proc	near
	mov	temp,1			; say want no leading cr/lf
	jmp	short echo1
XECHO	endp

;
; Echo a line of text to our screen
; Echo text
;
SCECHO	PROC	NEAR
	mov	temp,0			; say do cr/lf before text
echo1:	mov	ah,cmline		; get a whole line of asciiz text
	mov	bx,offset rdbuf		; where to store in
	mov	comand.cmdonum,1	; \number conversion allowed
	mov	comand.cmblen,cmdblen	; set line capacity (length of rdbuf)
	mov	dx,offset echhlp	; help
	call	comnd
	jnc	echo2
	ret
echo2:	push	ax			; returned byte count
	cmp	temp,0			; perform leading cr/lf?
	jne	echo3			; ne = no
	mov	ah,prstr
	mov	dx,offset crlf
	int	dos
echo3:	pop	cx			; recover byte count
	mov	di,offset rdbuf		; start of line
	call	prtscr			; print all bytes in ds:di, cx = count
	clc				; return success
	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 label (leaves ending CR unread)
	mov	bx,offset line		; buffer to hold label
	xor	dx,dx			; no help (non-interactive command)
	mov	slablen,dx		; clear label holding buffer
	call	comnd
	jnc	goto1			; nc = success
	ret				; failure
goto1:	mov	slablen,ax		; save count here
	cmp	slablen,0		; need contents
	jz	goto2			; empty, fail
	cmp	flags.cxzflg,'C'	; check for Control-C breakout
	je	goto2			; e = yes, fail
	cmp	taklev,0		; in a Take file or Macro?
	jne	goto3			; ne = yes, find the label
	clc				; ignore interactive command
	ret
goto2:	stc
	ret
goto3:	call	getto			; far call the label finding worker
	mov	comand.cmkeep,0		; do not keep open the macro after EOF
	ret
SGOTO	ENDP

; 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

; SWITCH variable {:label,commands,:label,commands,...}
; Have label :default present for unmatched labels.
SWITCH	proc	near
	mov	ah,cmword		; get variable
	mov	bx,offset line		; where to store it
	mov	comand.cmdonum,0	; \number conversion NOT allowed
	mov	dx,offset switchhlp1	; help
	call	comnd
	jnc	swit1
	ret
swit1:	mov	slablen,ax		; length of label
	mov	ah,cmline
	mov	bx,offset rdbuf+2	; buffer for switch body
	mov	dx,offset switchhlp2	; help
	mov	comand.cmdonum,1	; \number conversion is allowed
	mov	comand.cmper,1		; do not react to '\%' in macro name
	mov	comand.cmcnvkind,cmcnv_crprot ; allow CR within {..}
	mov	comand.cmblen,cmdblen	; set line capacity (length of rdbuf)
	call	comnd
	jnc	swit2
	ret
swit2:	mov	word ptr rdbuf,ax	; length to start of string
	mov	si,ds
	mov	es,si
	mov	si,offset rdbuf		; es:si is source of string
	call	docnv			; convert macro string in-place
	call	takopen_macro		; open macro
	jnc	swit3			; nc = succeeded
	ret
swit3:	mov	ax,word ptr rdbuf	; string length
	add	ax,2			; plus count word
	call	malloc			; ax has size of buffer
	jnc	swit4			; nc = success
	ret				; fail

swit4:	mov	bx,takadr		; point to current macro structure
	mov	[bx].takbuf,ax		; segment of definition string struc
	mov	cx,word ptr rdbuf	; string length
	mov	[bx].takcnt,cx		; number of chars in definition
	mov	[bx].takptr,2		; where to read first string byte
	or	[bx].takattr,take_malloc; say have buffer to be removed
	or	[bx].takattr,take_while	; so Break/Continue work
	mov	si,offset rdbuf		; switch body string
	push	es
	mov	es,ax			; new memory segment
	xor	di,di			; follow with string
	add	cx,2			; copy count word too
	cld
	rep	movsb			; copy string to malloc'd buffer
	pop	es
	mov	forward,2		; identify SWITCH operation
	call	getto			; find line after label or fail
	jc	swit5			; c = failed, try label :default
	ret				; found, execute
swit5:	mov	si,offset deflabel	; label :default
	mov	di,offset line
	call	strcpy
	mov	dx,offset line
	call	strlen
	mov	slablen,cx
	mov	bx,takadr		; rewind the macro
	mov	[bx].takptr,2		; where to read next time
	mov	ax,[bx].takbuf		; segment of string
	mov	es,ax
	mov	ax,es:[0]		; get string length from string
	mov	[bx].takcnt,ax		; quantity unread
	call	getto			; dispatch to label :default
	jc	swit6			; failed to find :default
	ret				; success, do :default commands
swit6:	call	takclos			; close switch macro
	stc				; fail
	ret
SWITCH	endp

; IF [NOT] {< }| = | > | ALARM | COUNT | FAILURE | SUCCESS | INPATH filespec
;	| ERRORLEVEL \number | EQUAL string string | EXIST filespec |
;	NUMBER } command

IFCMD	PROC	NEAR
	xor	ax,ax
	mov	ifkind,al		; say doing IF, not XIF or WHILE
	mov	notflag,al		; assume NOT keyword is absent
	mov	ifelse,al		; say ELSE if not yet permitted

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 ifnewhlp	; help
	mov	comand.cmblen,cmdblen	; long for long paths
	push	bx
	mov	bx,offset rdbuf		; buffer for filespec
	mov	word ptr [bx],0
	call	comnd
	pop	bx
	jnc	ifcmd5k			; nc = success
	ret				; failure
ifcmd5k:or	ax,ax			; any text?
	jnz	ifcmd5b			; nz = yes
	jmp	ifcmdf			; fail

ifcmd5b:mov	si,offset rdbuf		; file spec, top of loop
	mov	dx,si
	call	strlen
	add	si,cx			; look at last char
	or	cx,cx			; if any bytes
	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			; 0ffh is failure too
	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:cmp	bx,ifpath		; IF INPATH?
	je	ifcmd5c			; e = yes
	mov	ax,offset rdbuf		; isfile wants pointer in ds:ax
	push	bx
	call	isfile			; see if file EXISTS
	pop	bx
	jc	ifcmdf			; c = no, fail
	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
	jz	ifcmd7a			; z = exhausted
	pop	bx
	jmp	ifcmdp			; COUNT > 0 at entry, execute command
ifcmd7a:pop	bx
	jmp	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	ifcmd17
	jmp	tstnewer		; do file newer test

ifcmd17:cmp	bx,iftrue		; IF TRUE?
	jne	ifcmd18			; ne = no
	jmp	ifcmdp			; jump to test passed code

ifcmd18:cmp	bx,ifemulation		; IF EMULATION?
	jne	ifcmdf			; ne = no
	jmp	ifcmdf			; always evaluates as False

					; Jump points for worker routines
					; failure
ifcmdf:	cmp	notflag,0		; need to apply not condition?
	jne	ifcmdp2			; ne = yes, take other exit
ifcmdf2:cmp	ifkind,1		; doing XIF command, and failed?
	jne	ifcmdf3			; ne = no
	mov	bx,offset rdbuf
	xor	dx,dx			; help
	mov	comand.cmper,1		; don't expand variables at this time
	mov	comand.cmblen,cmdblen
	mov	comand.cmcnvkind,cmcnv_crprot ; allow CR within {..}
	mov	ah,cmword		; read the success word
	call	comnd
	jc	ifcmdf3
	mov	bx,offset rdbuf		; now read ELSE
	xor	dx,dx			; help
	mov	comand.cmper,1		; don't expand variables at this time
	mov	comand.cmblen,cmdblen
	mov	ah,cmword
	call	comnd
	jc	ifcmdf3
	cmp	ax,4			; four bytes for ELSE?
	jne	ifcmdf3			; ne = no, fail outright
	mov	ax,word ptr rdbuf	; check spelling
	or	ax,2020h		; quick to lower case
	cmp	ax,'le'
	jne	ifcmdf3			; ne = spelling failure
	mov	ax,word ptr rdbuf+2
	or	ax,2020h
	cmp	ax,'es'
	jne	ifcmdf3			; ne = not present, use failure case
	jmp	ifcmdp2			; make macro of rest of line	

ifcmdf3:mov	ifelse,1		; say permit following ELSE command
	cmp	taklev,0		; in a macro/take file?
	je	ifcmdf4a		; e = not in macro/take file
	mov	bx,takadr
	test	[bx].takattr,take_while	; is this a for/while macro?
	jz	ifcmdf4a		; z = no
	mov	[bx].takcnt,0		; read nothing more from While macro
	mov	ifelse,0		; no following ELSE command

ifcmdf4a:mov	bx,offset rdbuf+2	; soak line with no echo
	xor	dx,dx			; no help
	mov	comand.cmblen,cmdblen
	mov	comand.cmkeep,0
	mov	comand.cmcnvkind,cmcnv_crprot ; allow CR within {..}
	mov	ah,cmline		; soak up rest of line
	call	comnd
ifcmdf5: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:mov	bx,offset rdbuf+2
	xor	dx,dx			; help
	mov	comand.cmper,1		; don't expand variables at this time
	mov	comand.cmblen,cmdblen
	mov	comand.cmcnvkind,cmcnv_crprot ; allow CR within {..}
	mov	ah,cmword		; read word into buffer
	cmp	ifkind,1		; xif?
	je	ifcmdp2a		; e = yes, read word
	mov	ah,cmline		; read line for non-XIF's
	cmp	taklev,0		; in a takefile/macro?
	je	ifcmdp2a		; e = no
	push	bx
	mov	bx,takadr
	test	[bx].takattr,take_while	; for/while?
	pop	bx
	jz	ifcmdp2a		; z = no
	mov	comand.cmkeep,1		; keep for/while open after read
ifcmdp2a:call	comnd
	jnc	ifcmdp3			; nc = success
	ret

ifcmdp3:mov	word ptr rdbuf,ax	; store response length in buffer
	cmp	ifkind,1		; xif
	je	ifcmdp3d		; e = yes, no rewinding
	cmp	taklev,0		; still in a take file?
	je	ifcmdp3d		; e = no
	push	bx
	mov	al,taklev
	mov	bx,takadr
	test	[bx].takattr,take_while	; is a for/while macro active?
	jz	ifcmdp3b		; z = no
	mov	ax,[bx].takbuf		; rewind the macro,  seg of buffer
	mov	es,ax
	mov	ax,es:[0]		; get original filling qty
	mov	[bx].takcnt,ax		; set as unread qty
	mov	[bx].takptr,2		; from offset 2
ifcmdp3b:pop	bx

ifcmdp3d:cmp	ifkind,1		; xif?
	jne	ifcmdp3a		; ne = no, have read line
	mov	ah,cmline		; read and discard rest of line
	mov	comand.cmblen,cmdblen	; discards the else <cmds> part
	inc	bx
	mov	dx,offset mskcmd	; enter any command
	mov	comand.cmcnvkind,cmcnv_crprot ; allow CR within {..}
	mov	comand.cmper,1		; don't expand variables at this time
	call	comnd

ifcmdp3a:mov	dx,offset rdbuf+2
	call	strlen			; docnv cmds which permit {,..,}
	mov	word ptr rdbuf,cx	; plant count word for docnv
	mov	ax,ds
	mov	es,ax
	mov	si,offset rdbuf		; need count word here
	cmp	ifkind,1		; XIF?
	je	ifcmdp3c		; e = yes
	mov	bx,takadr
	test	[bx].takattr,take_while	; is a for/while macro active?
	jz	ifcmdp3e		; z = no
ifcmdp3c:call	docnv			; convert macro string in place
ifcmdp3e:mov	ax,cx
	add	ax,2			; plus count word
	call	malloc			; grab memory
	mov	es,ax			; segment
	mov	di,2			; start two bytes in
	mov	si,offset rdbuf+2
	push	cx
	cld
	rep	movsb			; copy to malloc'd area
	pop	cx
	call	takopen_macro		; open a macro
	mov	bx,takadr		; point to current macro structure
	mov	ax,es			; segment of definition
	mov	[bx].takbuf,ax		; segment of definition string struc
	mov	[bx].takcnt,cx		; number of chars in definition
	mov	es:[0],cx		; original amount in buffer
	mov	[bx].takargc,0		; our argument count
	mov	[bx].takptr,2		; offset to read next command char
	or	[bx].takattr,take_malloc ; free buffer when done
	cmp	taklev,1		; at least second macro level?
	jbe	ifcmd3f			; be = ne, skip caller-testing
	push	bx
	sub	bx,size takinfo		; backup to main macro
	test	[bx].takattr,take_while	; was it a for/while loop?
	pop	bx
	jz	ifcmd3f			; z = no
	or	[bx].takattr,take_subwhile ; mark this macro as part of it
ifcmd3f:clc
	ret
IFCMD	ENDP


XIFCMD	proc near
	mov	ifkind,1		; say doing XIF command
	mov	notflag,0		; assume NOT keyword is absent
	jmp	ifcmd1			; do common code
XIFCMD	endp

; While if-condition {command, command,...}
WHILECMD proc	near
	mov	di,offset rdbuf+2
	mov	word ptr [di],'FI'
	mov	byte ptr [di+2],' '	; 'IF ' prefix
	add	di,3			; append line to prefix

while1:	mov	ah,cmkey		; parse keyword
	mov	dx,offset iftable	; table of keywords
	push	di			; save built-up string
	xor	bx,bx			; help is the table
	call	comnd
	pop	di
	jnc	while2			; nc = success
whilex:	ret				; failure
while2:	mov	temp,bx			; save keyword value
	cmp	bx,ifnot		; NOT keyword?
	jne	while3			; ne = no
	mov	word ptr [di],'ON'	; insert 'NOT '
	mov	word ptr [di+2],' T'
	add	di,4
	jmp	short while1		; and get next keyword
while3:	mov	dx,offset iftable	; table of keywords
	call	revlookup		; get cx,si for keyword text
	jc	whilex			; c = fail, should never happen
	mov	ax,ds
	mov	es,ax
	cld
	rep	movsb			; copy keyword text
	mov	al,' '			; plus a space
	stosb
	mov	bx,di			; work with bx as destination now
	xor	ch,ch	
	mov	cl,byte ptr [si+1]	; number of following arguments
	jcxz	while6			; z = none, get command

while4:	mov	comand.cmper,1		; don't expand variables here
	mov	al,byte ptr temp	; keyword value
	xor	ah,ah
	mov	si,ax
	shl	si,1
	mov	ax,seg ifhlplst		; where help text lives
	mov	es,ax
	mov	dx,es:ifhlplst[si]	; get help msg pointer
	mov	ah,cmword		; read argument word
	push	cx			; save loop counter
	call	comnd
	pop	cx
	jnc	while5
	ret
while5:	mov	byte ptr [bx],' '	; space
	inc	bx			; ready for next phrase
	loop	while4

while6:	mov	comand.cmper,1		; don't expand variables here
	mov	comand.cmblen,cmdblen
	mov	comand.cmcnvkind,cmcnv_crprot ; allow CR within {..}
	mov	dx,offset mskcmd
	mov	ah,cmline		; read whole line as command
	call	comnd
	jnc	while7
	ret
while7:	mov	ifkind,2		; say doing WHILE command
	mov	si,offset rdbuf+2
	mov	dx,si
	call	strlen			; length of final string
	mov	ax,cx
	add	ax,2+1			; plus count word plus safety
	call	malloc
	mov	es,ax			; seg
	mov	di,2			; start two bytes in
	push	cx
	cld
	rep	movsb			; copy to malloc'd area
	pop	cx
	call	takopen_macro		; open a macro
	mov	bx,takadr		; point to current macro structure
	mov	ax,es			; segment of definition
	mov	[bx].takbuf,ax		; segment of definition string struc
	mov	[bx].takcnt,cx		; number of chars in definition
	mov	es:[0],cx		; count for goto's
	mov	[bx].takargc,0		; our argument count
	mov	[bx].takptr,2		; offset to read next command char
	or	[bx].takattr,take_malloc ; free buffer when done
	or	[bx].takattr,take_while	; mark as our While loop
	clc
	ret
WHILECMD 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	bx,offset rdbuf
	mov	comand.cmblen,cmdblen
	mov	dx,offset ifnhlp	; help
	call	comnd
	jnc	ifnum1			; nc = success
	ret				; failure
ifnum1:	mov	domath_ptr,offset rdbuf	; text in compare buffer
	mov	domath_cnt,ax
	mov	domath_msg,1		; no error message
	call	domath			; convert to number in dx:ax
	cmp	domath_cnt,0		; converted whole word?
	je	ifnum2			; e = yes
	mov	notflag,0		; fail completely
	jmp	ifcmdf
ifnum2:	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
ifnum	endp

; Compare text as number. Jump successfully if number.
ifnumeric proc	near
	mov	ah,cmword		; get following number
	mov	bx,offset rdbuf
	mov	comand.cmblen,cmdblen
	mov	dx,offset ifnhlp	; help
	call	comnd
	jnc	ifnumer1		; nc = success
	ret				; failure
ifnumer1:mov	cx,ax			; length of string
	mov	si,offset rdbuf		; where string starts
	cmp	cx,1			; more than one char?
	je	ifnumer2		; e = no
	jb	ifnumer3		; b = no chars
	lodsb				; could be +/- digits
	dec	cx
	cmp	al,'+'			; leading sign?
	je	ifnumer2		; e = acceptable
	cmp	al,'-'
	je	ifnumer2		; e = acceptable
	inc	cx			; back up to first byte
	dec	si
ifnumer2:lodsb
	cmp	al,'0'			; check for '0'..'9' as numeric
	jb	ifnumer3		; b = non-numeric
	cmp	al,'9'
	ja	ifnumer3		; a = non-numeric
	loop	ifnumer2
	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	bx,offset rdbuf+2	; point to work buffer
	mov	dx,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	ifalr2			; nz = yes, succeed
					; failure (not at alarm time yet)
	jmp	ifcmdf			; main fail exit
					; success (at or past alarm time)
ifalr2:	jmp	ifcmdp			; main pass exit
ifalrm	endp

; IF [NOT] {LLT, EQUAL, LGT} word word command
; Permits use of \number, {string}, @filespec
ifequ	proc	near
	mov	ltype,bx		; remember kind of lex test
	mov	comand.cmblen,cmdblen
	mov	comand.cmdonum,1	; \number conversion allowed
	mov	ah,cmword		; get a word
	mov	bx,offset rdbuf		; where to store
	mov	dx,offset ifehlp	; help
	call	comnd			; ignore parse error if no text
	jc	ifequ9			; carry set means error
	or	ax,ax			; byte count
	jz	ifequ3			; z = empty word
 	mov	tempd,ax
	mov	si,offset rdbuf		; start of line
	add	si,ax
	inc	si			; skip null terminator
	mov	temptr,si		; place to start second part
	mov	bx,si
	mov	ah,cmword		; get a word of text
	mov	dx,offset ifehlp	; help
	mov	comand.cmblen,cmdblen
	mov	comand.cmdonum,1	; \number conversion allowed
	call	comnd			; ignore parse error if no text
	jc	ifequ9			; c = failure
	mov	si,temptr		; start of second line
	cmp	tempd,ax		; first longer than second?
	jae	ifequ3			; ae = yes
	xchg	tempd,ax		; use length of shorter word
ifequ3:	or	ax,ax
	jz	ifequ9			; z = empty word
	inc	ax			; include null terminator in count
	mov	cx,ax
	mov	si,offset rdbuf		; first word
	mov	di,temptr		; second word
	push	es
	mov	ax,ds
	mov	es,ax
	cld
ifequ4:	lodsb
	mov	ah,[di]
	inc	di
	cmp	script.incasv,0		; case insensitive?
	jne	ifequ5			; ne = no
	call	toupr			; to upper case
ifequ5:	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,bx		; save kind of math test here
	xor	ax,ax
	mov	temp,ax			; place to store first value
	mov	temp1,ax		; place to store high word of value
	mov	tempd,ax		; count times around this loop
ifmath1:mov	bx,offset rdbuf
	mov	dx,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		; 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:mov	dx,offset rdbuf		; text in compare buffer
	call	strlen
	mov	domath_cnt,cx
	mov	domath_ptr,dx		; text in compare buffer
	call	domath			; convert to number in dx:ax
	cmp	domath_cnt,0		; converted whole word?
	je	ifmath8			; e = yes
	mov	notflag,0		; fail completely
	jmp	short ifmathf
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	bx,tempa		; kind of math test
	cmp	bx,ifless		; "<"?
	jne	ifmath10		; ne = no
	cmp	temp1,dx		; val1 < val2?
	jl	ifmathp			; b = pass
	jg	ifmathf			; a = fail
	cmp	temp,ax			; val1 < val2?
	jl	ifmathp			; b = pass
	jmp	short ifmathf		; fail
ifmath10:cmp	bx,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?
	jg	ifmathp			; a = yes, pass
	jl	ifcmdf			; b = no, fail
	cmp	temp,ax			; val2 > val1?
	jg	ifmathp			; a = yes, pass
ifmathf:jmp	ifcmdf			; else fail
ifmathp:jmp	ifcmdp			; jump to main command Success exit
ifmath	endp

; IF NEWER file1 file2 
tstnewer proc	near
	mov	ah,cmword		; get first filename
	mov	bx,offset rdbuf
	mov	comand.cmblen,cmdblen
	mov	dx,offset ifnewhlp	; help
	call	comnd
	jnc	tstnew1			; nc = success
	jmp	tstnewf			; failure
tstnew1:mov	ah,cmword		; get following number
	mov	bx,offset rdbuf+65	; second file name
	mov	comand.cmblen,cmdblen
	mov	dx,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,'--'		; marker to say dec
	jmp	short incvar1
decvar	endp

incvar	proc	near
	mov	temp,'++'		; marker to say inc
incvar1:mov	kstatus,ksgen		; general command failure
	mov	ah,cmword		; read variable name
	mov	bx,offset rdbuf+2	; reserve word 0 for entry count
	mov	word ptr rdbuf+2,0
	mov	dx,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
	push	es
	mov	es,si
	mov	cx,es:[0]		; length of definition
	mov	di,offset rdbuf+2	; variable name from user
	add	di,[di-2]		; plus length of variable name
	mov	byte ptr [di],' '	; <count><varname><space>
	inc	di
	mov	si,2			; skip over definition count word
incvar9:mov	al,es:[si]		; copy string to regular data segment
	mov	[di],al
	inc	si
	inc	di
	loop	incvar9
	pop	es
	mov	ax,temp			; get inc/dec +/- sign and a 1
	mov	[di],ax
	inc	di			; leave the '1' present
	push	di			; save place after '+/-'
	mov	bx,di			; optional step goes here
	mov	ah,cmword		; get step size, if any
	mov	dx,offset ssizehlp
	call	comnd
	pop	di
	jnc	incvar13
	ret
incvar13:push	di
	push	ax			; save step size string length
	mov	ah,cmeol
	call	comnd
	pop	ax
	pop	di
	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
	mov	word ptr [di],'1'	; put '1' where optional is missing

incvar15:mov	di,offset rdbuf+2	; user's variable name
	add	di,[di-2]		; its length
	inc	di			; skip space separator
	push	di			; save for place to write result
	mov	dx,di
	call	strlen			; get string length
	mov	domath_cnt,cx
	mov	domath_ptr,dx
	call	domath			; convert to number in dx:ax
	cmp	domath_cnt,0		; converted whole word?
	pop	di			; clean stack
	je	incvar17		; e = yes
	stc
	ret				; fail
					; step size is in ax
incvar17:or	dx,dx			; is result negative?
	jns	incvar18		; ns = no, positive or zero
	neg	dx			; flip sign
	neg	ax
	sbb	dx,0
	mov	byte ptr [di],'-'	; show minus sign
	inc	di
incvar18:call	lnout			; binary to ascii decimal in ds:di
	mov	dx,offset rdbuf+2	; place of <var><space><value>
	call	strlen			; length to cx for dodecom
	call	dodecom			; re-define variable
	mov	kstatus,kssuc		; say success
	clc
	ret
incvar	endp

; SET ALARM <time, sec from now or HH:MM:SS>
SETALRM	PROC	NEAR
	mov	bx,offset line		; point to work buffer
	mov	word ptr line+2,0
	mov	dx,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

; MINPUT <timeout> <match text> <match text> ...<match text>
SCMINPUT PROC	NEAR
	mov	minpcnt,0		; matched pattern count
	mov	reinflg,2		; say doing MINPUT
	jmp	short input100
SCMINPUT 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 input100
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

input100:
	mov	kstatus,kssuc
	mov	bx,offset line+2	; place to put text
	mov	ah,cmword		; get pattern match word
	mov	dx,offset inphlp	; help message
	mov	comand.cmdonum,1	; \number conversion allowed
	cmp	reinflg,2		; MINPUT?
	je	input101		; e = yes, else INPUT or REINPUT
	mov	ah,cmline		; get time + pattern match string
input101:call	comnd			; get the pattern text
	jnc	input20			; nothing, complain
	ret				; failure
input20:mov	word ptr line,ax	; store length in preceeding word
	mov	bx,offset line+2+2	; leave a full word empty
	add	bx,ax			; skip current material
	mov	word ptr [bx-2],0	; clear for next count
	or	ax,ax			; any text?
	jnz	input21			; nz = yes
	stc				; fail
	ret
input21:mov	si,offset line+2	; source pointer
	call	inptim			; get the timeout time, sets si
	mov	bx,offset line+2	; basic line again, omitting count
	jnc	input22			; nc = got timeout, now get pattern
					; c = not legal time, must be pattern
	mov	ax,word ptr line	; get byte count
	add	bx,ax			; move bx to end + 1
	jmp	short input12		; simulate skipping inptim call

input22:cmp	reinflg,2		; MINPUT?
	je	input10			; e = yes, else INPUT or REINPUT
	push	di
	mov	di,offset line+2	; where results go
	mov	bx,si			; break char (text pattern)
	sub	bx,offset line+2	; length of time field
	mov	cx,word ptr line	; length of time + pattern
	sub	cx,bx			; cx = length of pattern
	call	cnvlin			; remove curly braces, cnv \numbers
	mov	word ptr line,cx	; length of final result
	add	di,cx			; step to end of line
	mov	word ptr [di],0		; zero count of next pattern
	pop	di
	jmp	short input13

input10:mov	kstatus,kssuc		; MINPUT, REINPUT
	mov	ah,cmword		; get pattern match word
	mov	dx,offset inphlp	; help message
	mov	comand.cmdonum,1	; \number conversion allowed
	call	comnd			; get the pattern text
	jnc	input12			; nothing, complain
	ret				; failure
input12:mov	si,bx			; terminating null
	sub	si,ax			; minus length
	mov	[si-2],ax		; store length in preceeding word
	add	bx,2			; leave a full word empty
	mov	word ptr [bx-2],0	; clear count of next element
	cmp	reinflg,2		; MINPUT?
	jne	input13			; ne = no
	or	ax,ax			; any text?
	jnz	input10			; nz = got text, get more
	mov	ah,cmeol
	call	comnd
	jnc	input13
	ret

input13:cmp	reinflg,1		; reinput command?
	je	input1a			; e = yes, don't reecho
	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
	jnc	input1a			; nc = success
	ret
input1a:mov	status,stat_unk		; clear status flag
					; start main read and compare loop
input4:	mov	si,offset line		; <count word><pattern> sequence
	cmp	reinflg,2		; MINPUT?
	jne	input4a			; ne = no
	mov	minpcnt,0		; minput pattern match number
input4a:mov	cx,[si]			; length of pattern
	jcxz	input4d			; z = no pattern
	inc	minpcnt			; count minput pattern number
input4g:add	si,2			; point at pattern
input4c:call	inmat			; match strings
	jnc	input6			; nc = success
	add	si,cx			; skip to next pattern
	jmp	short input4a		; try again

input4d:mov	minpcnt,0		; clear \v(minput) match counter
	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	input4e			; z = no
	or	status,stat_tmo		; force timeout status too
	mov	input_status,1		; \v(instatus), timed out
	jmp	short input5
input4e:test	status,stat_tmo+stat_ok ; user override/timeout on last read
	jnz	input5			; nz = timed out, quit
	cmp	reinflg,1		; Reinput command?
	jne	input4f			; ne = no
	mov	ax,scpbuflen		; total buffer length
	cmp	bufcnt,ax		; full?
	jae	input5			; ae = yes, reinput fails
input4f:call	bufread			; read from serial port to buffer
	jmp	input4			; analyze character

input5:	or	errlev,ksrecv		; set RECEIVE failure condition
	or	fsta.xstatus,ksrecv	; set status
	or	kstatus,ksrecv
input6:	mov	input_status,0		; INPUT \v(instatus), assume success
	cmp	reinflg,2		; MINPUT?
	je	input6a			; e = yes
	mov	minpcnt,0		; clear \v(minput) match counter
input6a:test	status,stat_tmo		; timeout?
	jz	input7			; z = no
	mov	input_status,1		; \v(instatus), timed out
input7:	test	status,stat_cr+stat_cc	; user CR or Control-C?
	jz	input7a			; z = no
	mov	input_status,2		; \v(instatus), user Control-C
input7a:cmp	reinflg,2		; MINPUT?
	je	input7c			; e = yes
input7b:push	bx
	mov	bx,portval
	cmp	[bx].portrdy,0		; is port not-ready?
	pop	bx
	jne	input7c			; ne = no, port is ready
	mov	input_status,4		; connection lost
input7c:cmp	kstatus,kssuc		; success?
	je	input8			; e = yes
	mov	minpcnt,0		; clear \v(minput) match counter
	jmp	squit			; exit failure: timeout or Control-C
input8:	jmp	squit1			; skip timeout message, if any
inputx:	clc				; return success
	ret
SCINP	ENDP

; Match input buffer and cx string bytes at ds:si
; Strategy: scan INPUT buffer from beginning looking for match of first
; byte of pattern. If found try to match rest of pattern. Upon failure
; select next INPUT buffer byte and try to match with pattern.
inmat	proc	near
	push	cx
	push	es
	push	si
	push	di
	push	temp
	push	temp1			; buffer bytes to read
	mov	temp,cx			; retain count of pattern bytes
	mov	di,bufseg		; get buffer segment
	mov	es,di
	mov	di,bufcnt		; bytes in buffer
	cmp	reinflg,1		; Reinput command?
	je	inmat1			; e = yes, use start of buffer
	sub	di,bufrptr		; where to read-next, yields unread
	mov	temp1,di		; retain count of buffer bytes to read
	mov	di,bufrptr		; es:di is oldest unread byte
	jmp	short inmat2
inmat1:	mov	temp1,di		; retain count of bytes to read here
	xor	di,di			; reinput uses start of buffer
		; work on first pattern byte
inmat2:	cmp	temp1,cx		; unread bytes vs pattern bytes
	jb	inmatx			; b = insufficient, fail
	cld				; match first pattern byte
	mov	al,[si]			; read pattern byte into AL
	mov	cx,temp1		; buffer byte count
inmat10:mov	ah,es:[di]		; extract buffer char into AH
	inc	di
	cmp	script.incasv,0		; case ignore?
	jne	inmat13			; ne = no
	call	toupr			; upper case ax
inmat13:call	matchr			; chars match?
	jnc	inmat14			; nc = yes, first bytes match
	loop	inmat10			; continue matching
	jmp	inmatx			; failed

inmat14:	; have matched first pattern byte, now consider the rest
	push	di			; save position of to-be matched
	push	si			; save first pattern position
	inc	si			; point at second pattern byte
	mov	cx,temp			; count of pattern 
	dec	cx			; count of remaining pattern 
	jcxz	inmat17			; z = none, success
inmat15:lodsb				; pattern byte to AL
	mov	ah,es:[di]		; extract buffer char into AH
	inc	di			; point to next buffer byte
	cmp	script.incasv,0		; case ignore?
	jne	inmat16			; ne = no
	call	toupr			; upper case ax
inmat16:call	matchr			; chars match?
	jc	inmat18			; c = no
	loop	inmat15			; consider rest of pattern bytes
inmat17:cmp	reinflg,1		; Reinput command?
	je	inmat19			; e = yes, do not adjust bufrptr
	mov	bufrptr,di		; remember last read byte plus one
inmat19:pop	si
	pop	di			; success
	pop	temp1
	pop	temp
	pop	di
	pop	si
	pop	es
	pop	cx
	clc				; here match
	ret

inmat18:pop	si			; failed to match second etc bytes
	pop	di			; step along buffer stream
	mov	cx,bufcnt		; buffer count
	sub	cx,di			; minus bytes examined in buffer
	jle	inmatx			; le = none left, fail
	mov	temp1,cx		; bytes in buffer
	mov	cx,temp			; pattern bytes
	jmp	inmat2			; rescan buffer from last di

inmatx:	pop	temp1			; failure exit
	pop	temp
	pop	di
	pop	si
	pop	es
	pop	cx
	stc				; no match
	ret
inmat	endp

; 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	proc	near
	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
matchr	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	bx,offset line		; where to store it
	mov	dx,offset mphlp		; help msg
	call	comnd
	jnc	scmpau1			; nc = success
	ret
scmpau1:mov	dx,offset line		; text in compare buffer
	call	strlen
	mov	domath_cnt,cx
	mov	domath_ptr,dx
	call	domath			; convert to number in dx: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	bx,offset line		; where to store it
	mov	dx,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	bx,offset line		; where to store it
	mov	dx,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	bx,offset line
	mov	dx,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	bx,offset line		; where to store it
	mov	dx,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
	mov	comand.cmdonum,1	; \number conversion allowed
	call	comnd
	jnc	outp0d			; nc = success
	ret				; failure
outp0d:	mov	tempd,ax		; save byte count here
	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	temptr,offset line	; save pointer 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	byte ptr 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]
	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,byte ptr 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,byte ptr 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	bx,offset line		; where to store it
	mov	dx,offset xmthlp	; help message
	call	comnd
	jnc	xmit0c			; nc = success
	ret				; failure
xmit0c:	mov	ah,cmword		; get a prompt string, asciiz
	mov	bx,offset line+81	; where to keep it (end of "line")
	mov	dx,offset pmthlp	; Help in case user types "?".
	mov	comand.cmdonum,1	; convert \number
	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
	cmp	line+80,0		; anything given?
	jz	xmit0			; z = no, use default
	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
	mov	bufcnt,0		; clear INPUT buffer (say is empty)
	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
	mov	bufcnt,0		; clear INPUT buffer (say is empty)
	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:	mov	bufcnt,0		; clear INPUT buffer (say is empty)
	mov	bufrptr,0
	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
	mov	bufcnt,0		; clear serial port buf
	call	bufread			; check for char from serial port buf
	jnc	xmit8			; nc = a char, read til none
	jmp	short xmit8c		; continue transfer
xmit8b:	mov	bufcnt,0		; clear serial port buf
	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

;
; 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
code1	segment
	assume	cs:code1

;;;;;;;;;;;;;;;;;; local support procedures ;;;;;;;;;;
; 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.
; Leaves  command.cmkeep,1 persistent, so be careful.
getto	proc	FAR
	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:	cmp	forward,2		; SWITCH?
	jne	gett14a			; ne = no
	pop	bx
	stc				; set carry for failure
	ret
gett14a: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

; 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

; 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	far
	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

; Read comms line, put most recent byte into buffer, 
; and into reg AL as a side effect used by scxmit.
bufread	proc	far
	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:	clc				; clear carry flag (have a char)
	ret				; chkport sets status
bufread	endp

; Write byte AL to buffer. Manipulates bufcnt and bufrptr. Buffer is linear.
bufwrite proc	far
	push	ax
	push	si
	push	di
	push	es
	mov	si,bufseg		; segment of buffer
	mov	es,si
	mov	si,bufcnt		; buffer count
	cmp	si,scpbuflen		; filled?
	jb	bufwt2			; b = no
	push	ds			; save DS
	mov	di,bufseg
	mov	ds,di
	mov	cx,si			; count of bytes to be moved
	dec	cx			; leave a hole
	xor	di,di			; destination is at beginning
	mov	si,di
	inc	si			; source is one forward
	cld				; copy forward
	rep	movsb			; copy down the buffer contents
	pop	ds
	dec	bufcnt			; one hole left
	cmp	bufrptr,0		; is read pointer at start?
	je	bufwt1			; e = yes
	dec	bufrptr			; move it down too
bufwt1:	mov	si,bufcnt
bufwt2:	mov	es:[si],al		; store char held in al
	inc	bufcnt			; say have added a char to the buf
	pop	es
	pop	di
	pop	si
	pop	ax
	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	si,bufseg			; segment of buffer
	mov	es,si
	xor	si,si				; start of buffer
	mov	cx,bufcnt			; number of chars in buffer
	cmp	cx,cmdblen-1			; longer than command buffer?
	ja	buflog1				; a = yes, trim
	ret					; return these registers
buflog1:mov	si,cx				; ending offset
	mov	cx,cmdblen-1
	sub	si,cx				; starting offset
	ret
buflog	endp

; worker: check for timeout, return status=stat_tmo if timeout, else bit
;  stat_tmo is cleared.
chktmo	proc	far
	push	ax
	push	cx
	push	dx
	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
	pop	dx
	pop	cx
	pop	ax
	stc
	ret
chktmox:pop	dx
	pop	cx
	pop	ax
	clc
	ret
chktmo	endp
;
; 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	proc	far
	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
chkkbd	endp

;
; 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	proc	far
	and	status,not stat_ok	; clear status bit
	call	fprtchr			; 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	fcptchr			; 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
chkport	endp

; Given a keyword table value in BX find the keyword text and length.
; Offset of table is in DX.
; Returns CX = length and SI pointing to text string, and carry clear.
; Failure returns carry set
revlookup proc	far
	push	ax
	push	di
	mov	si,dx			; offset of table
	xor	ch,ch
	mov	cl,byte ptr [si]	; number of table entires
	inc	si			; start of entries
	cld
	jcxz	revloo2			; z = no entries, fail
revloo1:lodsw				; length of this entry's keyword
	mov	di,si			; point at keyword
	add	di,ax			; point at value
	cmp	[di],bx			; a match?
	je	revloo3			; e = yes
	add	di,2			; step over value to next entry
	mov	si,di			; next entry
	loop	revloo1			; keep looking
revloo2:pop	di
	pop	ax
	stc				; no match, report failure
	ret
revloo3:mov	cx,ax			; length of keyword, si has ptr
	pop	di
	pop	ax
	clc				; success
	ret
revlookup endp

; worker: display the char in al on screen
; use caret-char notation for control codes
; parse out escape and control sequences
scdisp	proc	far
scdisp0:cmp	disp_state,0		; inited yet?
	jne	scdisp0a		; ne = yes
	mov	disp_state,offset scdisp1 ; init now
scdisp0a:push	ax
	push	bx
	push	cx			; used by atparse
	push	dx
	call	word ptr disp_state
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	ret				; keep near/far calls straight
scdisp	endp

scdisp1 proc	near			; worker for above
	mov	ah,conout		; our desired function
	test	flags.remflg,d8bit	; show all 8 bits?
	jnz	scdisp1a		; nz = yes
	and	al,7fh			; apply 7 bit display mask
scdisp1a:or	al,al			; null?
	jz	scdisp3			; z = yes, ignore
	cmp	al,del			; delete code?
	je	scdisp3			; e = yes, ignore
	cmp	script.infilter,0	; filter echos?
	je	scdisp2			; e = no, display as-is
	test	al,not 9fh		; C0/C1 control char?
	jnz	scdisp2			; ae = no, display as-is
	cmp	al,CSI			; CSI?
	je	scdisp6			; e = yes
	test	al,80h			; other C1?
	jnz	scdisp3			; nz = yes, ignore
	cmp	al,cr			; carriage return?
	je	scdisp2			; e = yes, display as-is
	cmp	al,lf			; line feed?
	je	scdisp2
	cmp	al,tab			; horizontal tab?
	je	scdisp2
	cmp	al,bell			; bell?
	je	scdisp2
	cmp	al,bs			; backspace?
	je	scdisp2
	cmp	al,escape		; escape?
	je	scdisp4
	or	al,40h			; control code to printable char
	push	ax
	mov	dl,5eh			; display caret first
	int	dos
	pop	ax
scdisp2:mov	dl,al			; the char to be displayed
	int	dos
scdisp3:ret

scdisp4:mov	disp_state,offset scdisp5	; here on ESC
	ret
scdisp5:cmp	al,'['			; ESC [?
	jne	scdisp7			; ne = no, consider to be terminator
scdisp6:call	atpclr			; clear parser
	mov	parfail,offset scdisp7	; next state upon failure
	mov	pardone,offset scdisp7	; next state upon success
	mov	disp_state,offset atparse ; parse sequences
	ret
scdisp7:mov	disp_state,offset scdisp1 ; normal display
	ret
scdisp1	endp

code1	ends
	end
