	NAME	mssscp
; File MSSSCP.ASM
	include mssdef.h
;  Copyright (C) 1985, 1993, Trustees of Columbia University in the 
;  City of New York.  Permission is granted to any individual or institution
;  to use this software as long as it is not sold for profit.  This copyright
;  notice must be retained.  This software may not be included in commercial
;  products without written permission of Columbia University.
;
; Edit History
; 27 August 1992 version 3.13
; 6 Sept 1991 version 3.11
; Last edit 19 Jan 1993
; 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

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

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

					; 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
inplen	dw	0			; length of input match string
reinflg	db	0			; 0 = INPUT, else REINPUT command
notflag	db	0			; IF NOT flag
slablen	dw	0			; label length, for GOTO
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
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
eolchr	db	LF			; end of line character

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,'$'
outhlp	db	'line of text to be sent 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$'
wtbad	db	cr,lf,'?Command not understood, improper syntax$'
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)$'
mpbad	db	cr,lf,'?Bad number$'
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$'
ifnmsg	db	cr,lf,'?Number expected, ignoring "$'
ifnmsg2	db	'"$'
discard	db      ' Kermit command'
	db	cr,lf,' "IF" condition is false, command will be ignored.$'
ifehlp1	db	cr,lf,'?pair of words or variables to be compared$'
ifehlp2	db	cr,lf,'?second word or variable to be compared$'
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$'

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

iftable	db	15			; IF command dispatch table
	mkeyw	'Not',ifnot
	mkeyw	'<',ifless
	mkeyw	'=',ifsame
	mkeyw	'>',ifmore
	mkeyw	'Alarm',ifalarm
	mkeyw	'Count',ifctr
	mkeyw	'Defined',ifmdf
	mkeyw	'Errorlevel',iferr
	mkeyw	'Equal',ifequal
	mkeyw	'Exist',ifext
	mkeyw	'Inpath',ifpath
	mkeyw	'LGT',iflgt
	mkeyw	'LLT',ifllt
	mkeyw	'Failure',iffail
	mkeyw	'Success',ifsuc
data	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

	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	ah,cmeol
	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	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
	mov	comand.cmkeep,1		; keep Take/macro open after EOF
	call	comnd
	jnc	goto1			; nc = success
	ret				; failure
goto1:	mov	ax,ax			; byte count
	mov	slablen,ax		; save count here
	or	ax,ax			; need contents
	jz	goto3			; empty, fail
	mov	comand.cmkeep,1		; keep Take file open after this call
	mov	ah,cmeol
	call	comnd
	jnc	goto2			; nc = success
	ret				; failure
goto2:	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

; 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	bx,takadr
	cmp	[bx].taktyp,0feh	; get type of take (a file?)
	jne	gett2			; ne = no, a macro
					; scan from start of Take file
	mov	eolchr,LF		; file lines end on LF
	mov	bx,[bx].takhnd		; rewind the file
	xor	cx,cx
	xor	dx,dx
	xor	al,al			; zero displacement from start of file
	mov	ah,lseek
	int	dos
	jnc	gett1
	jmp	gett20			; c = failure
gett1:	call	takrd			; get a buffer of data
	mov	bx,takadr		; restore bx to working value
	jmp	short gett4
					; Take a Macro
gett2:	mov	eolchr,CR		; Macro lines end on CR
	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
	je	gett4
	cmp	al,':'			; start of label?
	je	gett8			; e = yes
gett6:	cmp	al,eolchr		; 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,eolchr		; or end of line?
	je	gett13			; e = yes
	cmp	al,CR			; macro eol, also file start eol pair
	jne	gett6			; ne = longer label than target

gett12:	call	getch			; read past end of line
	jc	gett13			; c = end of file, no char
	cmp	al,eolchr		; 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,0feh	; 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} 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?
	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	ax,offset rdbuf		; isfile wants pointer in ds:ax
	cmp	bx,ifpath		; INPATH?
	je	ifcmd5c			; e = yes
	call	isfile			; see if file exists
	jc	ifcmdf			; c = no
	jmp	ifcmdp			; yes, do following command
ifcmd5c:call	spath			; search path
	jc	ifcmdf			; c = no such file
	jmp	ifcmdp			; yes, do following command
	
ifcmd6:	cmp	bx,iffail		; IF FAIL?
	jne	ifcmd7
	test	kstatus,not (kssuc)	; check all bits
	jz	ifcmdf			; z = not that condition, no jump 
	jmp	short 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	ifcmdf			; ne = no
ifcmd12:jmp	ifmath

					; 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

; 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	ifequ9			; 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
	jcxz	ifequ9			; z = empty word
	cmp	tempd,cx		; first longer than second?
	jae	ifequ3			; ae = yes
	xchg	tempd,cx		; use length of shorter word
ifequ3:	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
	repe	cmpsb			; compare while equal
	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	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
	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 ax
	jc	ifmathb			; c = failed to convert a number
ifmath8:cmp	tempd,0			; gotten second value yet?
	ja	ifmath9			; a = yes, it is in ax
	mov	temp,ax			; save first value
	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	temp,ax			; val1 < val2?
	jb	ifmathp			; b = pass
	jmp	short ifmathf		; fail
ifmath10:cmp	bl,ifsame		; "="?
	jne	ifmath11		; ne = no
	cmp	temp,ax			; val1 = val2?
	je	ifmathp			; e = yes, pass
	jmp	short ifmathf		; fail
ifmath11: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

; 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	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+2,si	; preserve seg for later storage
	push	es
	mov	es,si
	mov	cx,es:[0]		; length of definition
	cmp	cx,9			; "\x{65384}" is max length
	ja	incvar10		; a = too large to qualify
	mov	di,offset rdbuf+4	; 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+4
	call	katoi			; convert var value to binary in ax
	jc	incvar10		; c = failed
	mov	word ptr rdbuf,ax	; save value here, seg in rdbuf+2
	pop	es
	jmp	short incvar12		; now get step size, if any
incvar10:pop	es
	xor	ax,ax
	mov	temp,ax			; setup failure
	jmp	short incvar13		; do command confirmation

incvar12:mov	ah,cmword		; get step size, if any
	mov	comand.cmblen,7		; length of step size
	mov	dx,offset rdbuf+6	; where to put string
	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
	jmp	incvar17		; go process new value

incvar15:mov	si,offset rdbuf+6	; 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 ax
	jnc	incvar17		; nc = a number
	ret				; fail
					; step size is in ax
incvar17:mov	cx,word ptr rdbuf	; current value of variable
	cmp	temp,0			; inc or dec?
	jg	incvar19		; g = increment
	jl	incvar18		; l = decrement
	stc				; else fail
	ret
incvar18:cmp	ax,cx			; if would subtract too much
	jbe	incvar18a		; be = ok
	stc				; a = would go below zero, fail
	ret
incvar18a:neg	ax			; change increment to negative
	add	ax,cx			; add current value
	jc	incvar20		; c = ok
	stc
	ret				; fail
incvar19:add	ax,cx			; increment
	jnc	incvar20
	ret				; carry means fail

incvar20:mov	di,offset rdbuf+4	; place to store string
	call	dec2di			; binary to ascii decimal in ds:di
	mov	si,offset rdbuf+4
	sub	di,si			; new string length
	mov	cx,di
	cld
	push	es
	mov	ax,word ptr rdbuf+2	; 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	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
					; 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	short 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
	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
	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
	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


; 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	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]
	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	outpu4d			; 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

     
; 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	short xmita		; and abort transfer
xmit4g:	call	outchr			; send the character to the port
	inc	temp			; count chars sent in this line
	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, and finally \numbers are converted to binary. [jrd]
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:	xor	ah,ah			; clear high byte of number
	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
	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
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	near
	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
	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
