DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T m

⟦1449d4c9c⟧ TextFile

    Length: 95145 (0x173a9)
    Types: TextFile
    Names: »mssscp.asm«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦71044c191⟧ »EurOpenD3/misc/kermit.ms-2.32.tar.Z« 
        └─⟦31f2d420d⟧ 
            └─⟦this⟧ »mssscp.asm« 

TextFile

        NAME    mssscp
; File MSSSCP.ASM
; Edit History
; Last edit 21 Nov 1988
; 21 Nov 1988 Version 2.32
; 20 Nov 1988 Correct timeout calculations to enforce 12 hour rule.
; 14 Nov 1988 Let SET INPUT ECHO OFF stop OUTPUT cmd from reading seria port.
; 10 Nov 1988 Omit "?timeout" message while in Take file or Macro.
; 16 Sept Let TRANSMIT command utilize SET EOF state for Control-Z. Tnx
;  to Terry Kennedy.
; 25 August Fix label match test when CR seen from file.
; 14 August Correct uninitialized DI register in bufclear. Tnx to Hirofumi
;  Fujii. Allow Transmit command prompt to use first ascii char if not a
;  number. Tnx to Bert Tyler.
; 6 August Tweak label matching code again.
; 28 July 1988 use full label lengths when doing label search, add command
;  IF EQUAL word word command. [jrd]
; 1 July 1988 Version 2.31
; 12 June 1988 Add error recovery if serial port does not initialize.
; 22 May 1988 Add more IF commands.
; 15 May 1988 Add commands GOTO, IF, REINPUT, and :label. [jrd]
; 20 March 1988 Put global script variables in structure script. [jrd]
; 8 March 1988 Add form of hh:[mm:[ss]] to input/pause/wait time intervals.
;  This form means timeout at the specified 24 hour clock time. [jrd]
; 27 Feb 1988 Add capability of stdin being a file.
; Introduce command   WAIT [sec] signal (signal is \cd or \dsr)
;  to wait on modem status. No signal means just wait, like pause. [jrd]
; 27 Jan 1988 Remove serrst call, done now in mssker idle loop. [jrd]
; 6 Jan 1988 Fix pointer for out @con calls. [jrd]
; 1 Jan 1988 version 2.30
; 26 Dec 1987 Use no-echo reading of console for OUTPUT @CON, speedup
;  reading of host response for OUTPUT command. [jrd]
; 4 Dec 1987 Update global byte errlev when a Script command Fails
;  (timeout or output failure or manual interruption). [jrd]
; 9 Oct 1987 Allow curly braced strings, trim trailing whitespace too. [jrd]
; 4 Oct 1987 Apply Set Display 7/8 bit mask to bytes rcv'd from serial port
;  after Set Translation Input filter. Log 8-bit chars in Debug mode. [jrd]
; 26 Sept 1987 Add check for Control-C interrupt to chkkbd and ECHO,
;  make Control-C in TRANSMIT command use squit exit. [jrd]
; 18 Aug 1987 Change ESC to escape for MASM 4.5+ [jrd]
; 11 Aug 1987 Add Set Send Pause plus 3 millisec wait before doing OUTPUT.[jrd]
; 31 July 1987 Open port reading to null chars et al. Correct timeofday
;  routine, from Jack Bryans. [jrd]
; 22 July 1987 Rewrite time of day material for no ambiguities. [jrd]
; 15 July 1987 Terminate strings read as @filespec on first carriage return.
;  Change number parsing to use decimal as default and \bddd for other bases.
; 24 May 1987 Add error recovery for outchr calls. [jrd]
; 10 May 1987 Add translation of input characters, rxtable. [jrd]
; 4 April 1987 Clear Echo's old text line, from Eberhard Lisse. [jrd]
; 18 March 1987 Add requests for command confirmation. [jrd]
; 2 March 1987 Remove test of Set Input Echo from OUTPUT command. [jrd]
; 27 Oct 1986 preserve data char in al around call to outchr in Output [jrd]
; 19 Oct 1986 Add "\b" and "\B" to Output procedure as send-a-Break command
;  to serial port comms line. [jrd]
; 1 Oct 1986 Version 2.29a
; 1 Oct 1986 Add 7/8 bit display mask to displayed text. [jrd]
; 12 Sept 1986 Add changes from Frank da Cruz: one second default timeout,
;  echo chars if Local Echo is On, Input command without a pattern should
;  behave like a Pause command.
;
; 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.
;;;;;;;;;

        include mssdef.h
        public  script, scout, scinp, scpau, scecho, scclr, scxmit, scwait
        public  goto, screinp, ifcmd, setalrm, inptim, chktmo, alrhms

linelen         equ     134             ; length of working buffer line
prtbuflen       equ     128             ; serial port local buffer length
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

datas   segment public 'datas'
        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

                                        ; 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)
                                        ; local variables
line    db      linelen+1 dup (?)       ; line of output or input + terminator
prtbuf  db      prtbuflen dup (?)       ; serial port storage buffer
bufcnt  dw      0                       ; serial port buf byte cnt, must be 0
bufrdptr dw     prtbuf                  ; serial port buf read ptr
bufwtptr dw     prtbuf                  ; serial port buf write ptr
bufpkptr dw     prtbuf                  ; peek-read pointer
bufpkcnt dw     0                       ; peek-read byte count remaining
reinflg db      0                       ; 0 = INPUT, else REINPUT command
inplen  dw      0                       ; length of input match string
notflag db      0                       ; IF NOT flag
slablen dw      0                       ; label length, for GOTO
temptr  dw      ?                       ; temporary pointer
temptr2 dw      ?                       ; ditto, points to end of INPUT string
tempd   dw      ?                       ; temp
tempa   db      ?                       ; another temp
retry   db      0                       ; number of output retries
status  dw      ?                       ; general status word
fhandle dw      ?                       ; file handle storage place
parmsk  db      7fh                     ; 7/8 bit parity mask
lecho   db      ?                       ; local echo of output (0 = no)
timout  dw      ?                       ; work area (seconds before timeout)
timhms  db      4 dup (?)               ; hhmmss.s time of day buffer
alrhms  db      4 dup (?)               ; hhmmss.s time of day alarm buffer
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'
        db      cr,lf,' Time is number of seconds or until a specific'
        db      ' hh:mm:ss (24 hour clock)$'
wthlp   db      'time-limit and modem status signals \CD, \CTS, and \DSR'
        db      cr,lf,' Time is number of seconds or until a specific'
        db      ' hh:mm:ss (24 hour clock)$'
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$'
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$'

iftable db      9                       ; IF command dispatch table
        mkeyw   'Not',ifnot
        mkeyw   'Alarm',ifalarm
        mkeyw   'Count',ifctr
        mkeyw   'Defined',ifmdf
        mkeyw   'Errorlevel',iferr
        mkeyw   'Equal',ifequal
        mkeyw   'Exist',ifext
        mkeyw   'Failure',iffail
        mkeyw   'Success',ifsuc
datas   ends

code    segment public 'code'

        extrn   comnd:near, clrbuf:near, prtchr:near, outchr:near, sendbr:near
        extrn   cptchr:near, serini:near, serrst:near, pcwait:near, katoi:near
        extrn   cnvstr:near, getmodem:near, isdev:near, isfile:near
        extrn   takrd:near, takclos:near, tolowr:near, prtasz:near,strlen:near

        assume  cs:code, ds:datas, es:nothing

; Clear input buffer(s) of serial port
; Clear command
;
SCCLR   PROC    NEAR
        mov     kstatus,0               ; global status
        mov     ah,cmcfm                ; get a confirm
        call    comnd
         jmp    r                       ; no confirm
         nop
        call    bufclear                ; clear our serial port circular buf
        call    clrbuf                  ; clear system serial port buffer too
        jmp     rskp                    ; return success
SCCLR   ENDP

;
; Echo a line of text to our screen
; Echo text
;
SCECHO  PROC    NEAR
        mov     ah,cmtxt                ; get a whole line of asciiz text
        mov     bx,offset line          ; where to store in
        mov     word ptr [bx],0         ; clear line
        mov     dx,offset echhlp        ; help
        call    comnd
         jmp    rskp                    ; ignore parse error if no text
         nop
        mov     si,offset line          ; 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     al,lf                   ; start with a linefeed
        call    scdisp                  ; show it
        jcxz    echo2                   ; z = nothing to show
echo1:  push    cx                      ; save loop counter
        cld
        lodsb                           ; get a source char into al
        push    si
        call    scdisp                  ; display the char
        pop     si
        pop     cx
        loop    echo1                   ; get another
echo2:  jmp     rskp                    ; 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.
GOTO    PROC    NEAR
        mov     kstatus,0               ; global status
        mov     ah,cmfile               ; get a word
        mov     dx,offset line          ; buffer to hold label
        mov     bx,0                    ; no help (non-interactive command)
        mov     slablen,bx              ; clear label holding buffer
        mov     comand.cmkeep,1         ; keep Take/macro open after EOF
        call    comnd
         jmp    r
         nop
        mov     al,ah                   ; byte count
        mov     ah,0
        mov     slablen,ax              ; save count here
        mov     comand.cmkeep,1
        mov     ah,cmcfm
        call    comnd
         jmp    r
         nop
        cmp     taklev,0                ; in a Take file or Macro?
        jne     GETTO                   ; ne = yes, find the label
        jmp     rskp                    ; ignore interactive command

; 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.
; Exit carry clear if success, carry set otherwise. Local worker routine.
getto   proc    near
        push    bx                      ; global save of bx
        mov     bx,takadr
        cmp     [bx].taktyp,0ffh        ; get type of take (a Macro?)
        je      gett2                   ; e = yes, a macro
                                        ; scan from start of Take file
        mov     eolchr,LF               ; file lines end on LF
        mov     bx,[bx].takhnd          ; rewind the file
        mov     cx,0
        mov     dx,0
        mov     al,0                    ; 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     ax,[bx].takptr          ; offset of next char to be read
        sub     ax,[bx].takbuf          ; offset of start of buffer
        dec     ax                      ; omit count byte at start of buffer
        add     [bx].takcnt,ax          ; original length of text in buffer
        mov     ax,[bx].takbuf
        inc     ax                      ; omit count byte at start of buffer
        mov     [bx].takptr,ax          ; set read pointer to beginning

gett4:  call    getch                   ; get a character
        jc      gett14                  ; c = end of file, no char
        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,eolchr               ; or end of line?
        je      gett13                  ; e = yes
        cmp     al,CR                   ; macro eol, also file start eol pair
        je      gett12                  ; e = found match
        jmp     gett6                   ; 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
        jmp     rskp                    ; Take pointers are ready to read line

gett14: 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     gett15                  ; ne = no
        inc     dx                      ; yes, skip it
gett15: call    prtasz                  ; print asciiz string
        mov     ah,prstr
        mov     dx,offset laberr2       ; trailer of error message
        int     dos
gett20: pop     bx
        mov     kstatus,1               ; command status, failure
        stc                             ; set carry for failure
        ret
getto   endp
GOTO    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,0ffh        ; macro?
        je      getch1                  ; e = yes, that's the end
        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
        mov     si,[bx].takptr          ; read a char from Take buffer
        cld
        lodsb
        mov     [bx].takptr,si          ; move buffer pointer
        pop     si
        dec     [bx].takcnt             ; decrease number of bytes remaining
        clc                             ; return carry clear
        ret
getch   endp

; IF [NOT] {ALARM | COUNT | FAILURE | SUCCESS | 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
        mov     bx,0                    ; help is the table
        call    comnd
         jmp    r
         nop
        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,0               ; do we have success?
        jne     ifcmdf                  ; ne = no, no jump
        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?
        jne     ifcmd6                  ; ne = no
        mov     ah,cmfile               ; read a filespec
        mov     dx,offset line          ; buffer for filespec
        mov     bx,0
        call    comnd
         jmp    r
         nop
        mov     ax,offset line          ; isfile wants pointer in ds:ax
        call    isfile                  ; see if file exists
        jc      ifcmdf                  ; c = no
        jmp     short ifcmdp            ; yes, do following command

ifcmd6: cmp     bx,iffail               ; IF FAIL?
        jne     ifcmd7
        test    kstatus,not (0)         ; 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     ifcmdf                  ; ne = no
        jmp     ifequ                   ; do further parsing below
                                        ; 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,cmtxt                ; fail, read and discard rest of line
        mov     bx,offset line
        mov     dx,offset discard       ; say not doing anything
        call    comnd
         nop
         nop
         nop
        jmp     rskp
                                        ; success (pass)
ifcmdp: cmp     notflag,0               ; need to apply not condition?
        jne     ifcmdf2                 ; ne = yes, take other exit
ifcmdp2:jmp     rskp                    ; do command
IFCMD   ENDP

; Compare errlev against user number. Jump successfully if errlev >= number.
; Worker for IF [NOT] ERRORLEVEL number <command>
ifnum   proc    near
        mov     ah,cmfile               ; get following number
        mov     dx,offset line+1
        mov     line,'\'                ; in case user forgets backslash
        mov     word ptr line+1,0       ; clear buffer
        mov     bx,offset ifnhlp        ; help
        call    comnd
         jmp    r
         nop
        mov     si,offset line          ; put text in compare buffer
        cmp     line+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 line+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
        mov     dx,0                    ; help
        mov     ah,cmtxt                ; read rest of line
        call    comnd
         jmp    r
         nop
        cmp     ah,0                    ; returned byte count
        ja      ifnum5                  ; a = 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 line+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 line+1        ; point to work buffer
        mov     bx,offset ifdfhlp       ; help
        mov     ah,cmfile               ; get macro name
        mov     comand.cmper,1          ; do not react to \%x
        call    comnd
         jmp    r
         nop
        mov     line,ah                 ; store length in buffer
ifmde2: 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 line          ; pointer to user's cnt+name
        mov     cl,[si]                 ; length of user's macro name
        xor     ch,ch
        inc     si                      ; point to macro name
        cmp     cl,[bx]                 ; compare length vs table keyword
        jne     ifmde7                  ; ne = not equal lengths, try another
        push    si                      ; lengths match, how about spelling?
        push    bx
        inc     bx                      ; 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
ifmde8: mov     al,[bx]                 ; cnt (keyword length from macro)
        xor     ah,ah
        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
        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)
ifalr2: cmp     notflag,0               ; need to apply not condition?
        jne     ifalr5                  ; ne = yes, take other exit
ifalr3: mov     ah,cmtxt                ; fail, read and discard rest of line
        mov     bx,offset line
        mov     dx,0
        call    comnd
         jmp    r
         nop
        jmp     rskp
                                        ; success (at or past alarm time)
ifalr4: cmp     notflag,0               ; need to apply not condition?
        jne     ifalr3                  ; ne = yes, take other exit
ifalr5: jmp     rskp                    ; pass, do command
ifalrm  endp

; IF [NOT] EQUAL word word command
; Permits use of \number, {string}, @filespec
ifequ   proc    near
        mov     ah,cmfile               ; get a word
        mov     dx,offset line          ; where to store
        mov     line,0                  ; clear first entry
        mov     bx,offset ifehlp1       ; help
        call    comnd
         jmp    rskp                    ; ignore parse error if no text
         nop
        mov     si,offset line          ; 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     tempa,0                 ; line length, so far
        cld
ifequ1: lodsb
        cmp     al,' '                  ; space or control code?
        jbe     ifequ2                  ; be = yes, end of word
        inc     tempa                   ; count char in word
        loop    ifequ1                  ; continue
ifequ2: mov     byte ptr[si],0          ; plant terminator
        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,cmfile               ; get a word of text
        mov     bx,offset ifehlp2       ; help
        call    comnd
         jmp    rskp                    ; ignore parse error if no text
         nop
        mov     si,temptr               ; start of second 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     si,temptr               ; point at second word
        mov     dx,0                    ; word length so far
        cld
ifequ3: lodsb
        cmp     al,' '                  ; space or control code?
        jbe     ifequ4                  ; be = yes, end of word
        inc     dx                      ; count char in word
        loop    ifequ3                  ; continue
ifequ4: mov     byte ptr[si],0          ; plant terminator
        mov     cx,dx                   ; length of second word
        cmp     tempa,dl                ; same lengths?
        jne     ifequ9                  ; ne = no
        jcxz    ifequ9                  ; both are null
        push    es
        push    ds
        pop     es
        mov     si,offset line          ; first word
        mov     di,temptr               ; second word
        cld
        repe    cmpsb                   ; see if they are the same
        pop     es
        jne     ifequ9                  ; ne = not the same, fail
        jmp     ifcmdp                  ; do IF cmd success
ifequ9: jmp     ifcmdf                  ; do IF cmd failure
ifequ   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,cmfile               ; get macro name
        call    comnd
         jmp    r
         nop
        mov     ah,cmcfm                ; get a confirm
        call    comnd
         jmp    r
         nop
        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
        jmp     rskp
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,0
        mov     ah,cmtxt                ; 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
         jmp    r                       ; nothing, complain
         nop
        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     bufpkcnt,prtbuflen      ; 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     input5                  ; nz = yes, quit
        test    status,stat_cr          ; did user type cr? [js]
        jz      input3a                 ; z = no
        jmp     inputx                  ; nz = yes, return success [js]
input3a:call    chktmo                  ; check timeout
        test    status,stat_tmo
        jnz     input5                  ; nz = timed out, quit
        call    bufread                 ; read from serial port buffer into al
        jmp     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
        jmp     inputx                  ; nz = yes, return success [js]
input4a:call    chktmo                  ; check timeout
        test    status,stat_tmo+stat_ok ; timeout or user override
        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
        jc      input4                  ; c = failed to get 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     input4
input5: or      errlev,2                ; set RECEIVE failure condition
        or      fsta.xstatus,2          ; set status
        cmp     reinflg,0               ; Input command?
        jne     input6                  ; ne = no
        jmp     squit                   ; exit failure: timeout or control-c
input6: mov     kstatus,2               ; failure
        jmp     squit1                  ; skip timeout message, if any
inputx: jmp     rskp                    ; return success
; 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     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 specified number of seconds or until a time of day
; Pause [seconds or hh:mm:ss]
;
SCPAU   PROC    NEAR
        mov     kstatus,0
        mov     ah,cmfile               ; 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
         jmp    r
         nop                            ; must be at least 3 bytes
        mov     ah,cmcfm                ; get a confirm
        call    comnd
         jmp    r
         nop
        mov     si,offset line          ; source pointer
        call    inptim                  ; parse pause time (or force default)
        jc      scpau1                  ; c = bad time value
        mov     tempa,0                 ; no modem status to detect
        jmp     swait4                  ; finish in common code
scpau1: ret
SCPAU   ENDP

;
; Wait for the indicated signal for the specified number of seconds or tod
; WAIT [seconds] \signal   where \signal is \cd, \dsr modem status lines.
; Use INPUT-TIMEOUT ACTION for failures.
;
SCWAIT  PROC    NEAR
        mov     kstatus,0
        mov     ah,cmtxt                ; get a word (number)
        mov     bx,offset line          ; where to store it
        mov     byte ptr line,0         ; terminate line incase no text
        mov     dx,offset wthlp         ; help msg
        call    comnd
         jmp    r
         nop                            ; must be at least 3 bytes
        mov     tempa,0                 ; clear modem status test byte
        mov     si,offset line          ; source pointer
        call    inptim                  ; parse pause time (or force default)
        jc      swait1a                 ; c = bad time value
        mov     di,offset line          ; put text in compare buffer
        call    cnvlin                  ; convert \numbers in buf line
        mov     si,di                   ; code below uses si
        jnc     swait1                  ; nc = no error
swait1a:ret                             ; else return on error
swait1: cmp     cx,0                    ; number of chars to examine
        jle     swait4                  ; le = none
        cld
        lodsb                           ; get a character
        dec     cx                      ; reduce count remaining
        cmp     al,' '                  ; white space?
        jbe     swait1                  ; be = yes, skip over it
        cmp     al,'\'                  ; backslash signal introducer?
        jne     swait1                  ; ne = no, keep searching
        cmp     cx,2                    ; at least two chars in signal?
        jl      swait3                  ; l = no
        mov     ax,[si]
        or      ax,2020h                ; upper case to lower, two chars
        cmp     ax,'dc'                 ; carrier detect?
        jne     swait2                  ; ne = no, try next signal
        or      tempa,modcd             ; look for the CD bit
        add     si,2                    ; skip this field
        sub     cx,2                    ; three 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+2]               ; third letter
        or      al,20h                  ; to lower case
        cmp     al,'r'                  ; r for dsr?
        jne     swait3                  ; ne = no
        or      tempa,moddsr            ; look for the DSR bit
        add     si,3                    ; skip this field
        sub     cx,3                    ; four less chars left in the line
swait3: cmp     ax,'tc'                 ; clear to send?
        jne     swait3a                 ; ne = no
        mov     al,[si+2]               ; third letter
        or      al,20h                  ; to lower case
        cmp     al,'s'                  ; r for dsr?
        jne     swait3a                 ; ne = no
        or      tempa,modcts            ; look for the CTS bit
        add     si,3                    ; skip this field
        sub     cx,3                    ; four less chars left in the line
swait3a:jmp     short swait1            ; continue the scan
                                        ; SWAIT4 is used by PAUSE command
SWAIT4: 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: call    getmodem                ; modem handshake status to AL
        and     al,tempa                ; keep only bits to be tested
        cmp     tempa,0                 ; anything to be tested?
        je      swait7a                 ; e = no, just do the wait part
        cmp     al,tempa                ; check selected status bits
        jne     swait7a                 ; ne = not all selected bits match
        jmp     rskp                    ; all match. take successful exit
swait7a: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     tempa,0                 ; were we waiting on anything?
        jne     swait9                  ; ne = yes, timeout = failure
        jmp     rskp                    ;  else timeout = success
swait9: or      errlev,2                ; set RECEIVE error condx
        or      fsta.xstatus,2          ; set status
        jmp     squit                   ; take error exit
SCWAIT  ENDP


; Output line of text to port, detect \b and \B as commands to send a Break
;  on the serial port line.
; Output text

SCOUT   PROC    NEAR
        mov     kstatus,0
        mov     ah,cmtxt                ; get a whole line of asciiz text
        mov     bx,offset line          ; store text here
        mov     dx,offset outhlp        ; help message
        call    comnd
         jmp    r
         nop
        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,1                ; set SEND failure condition
        or      fsta.xstatus,1          ; set status
        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
        jmp     rskp                    ; return success
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
        cmp     byte ptr [si],'b'       ; "\b" for Break?
        je      outpu4c                 ; e = yes
        cmp     byte ptr [si],'B'       ; "\B" ?
        jne     outpu4d                 ; ne = no
outpu4c:inc     temptr                  ; move scan ptr beyond "\b"
        dec     tempd
        call    sendbr                  ; call msx send-a-break procedure
        jmp     outpu5                  ; resume beyond echoing

outpu4d:inc     retry                   ; count output attempts
        cmp     retry,maxtry            ; too many retries?
        jle     outpu4g                 ; le = no
        or      errlev,1                ; set SEND failure condition
        or      fsta.xstatus,1          ; set status
        jmp     squit                   ; return failure
outpu4g:mov     ah,tempa                ; outchr gets fed from ah
        call    outchr                  ; send the character to the port
         jmp     outpu4d                ; failure to send char
         nop                            ; ensure 3 bytes for rskp of outchr
        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: push    cx
outpu5a:mov     cx,10                   ; reset retry counter
outpu5b:call    chkkbd                  ; check keyboard for interruption
        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
        call    chkport                 ; check for char at serial port
        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:pop     cx                      ; no more input, recover register
        jmp     outpu2                  ; resume command
outpu6: pop     cx                      ; recover register
        or      errlev,1                ; set SEND failure condition
        or      fsta.xstatus,1          ; set status
        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 linefeed (or a carriage return from us).
;
SCXMIT  PROC    NEAR
        mov     kstatus,0
        mov     ah,cmfile               ; get a filename, asciiz
        mov     dx,offset line          ; where to store it
        mov     bx,offset xmthlp        ; help message
        call    comnd
         jmp    r                       ; exit on failure
         nop
        mov     ah,cmtxt                ; get a prompt string, asciiz
        mov     bx,offset line+80       ; where to keep it (end of "line")
        mov     word ptr [bx],0         ; clear and add terminator
        mov     dx,offset pmthlp        ; Help in case user types "?".
        call    comnd
         jmp    r
         nop
        cmp     line,0                  ; filename given?
        je      xmit0a                  ; e = no
        cmp     ah,0                    ; prompt given?
        je      xmit0b                  ; e = no, use line feed default
        mov     si,offset line+80       ; convert possible numeric prompt
        call    katoi                   ; convert number to binary, if number
        jnc     xmit0                   ; nc = got number
        mov     al,line+80              ; get ascii char from user's prompt
        jmp     short xmit0
xmit0b: mov     al,lf                   ; default prompt
xmit0:  mov     tempa,al                ; save the code here
        mov     dx,offset line          ; point to filename
        mov     ah,open2                ; DOS 2 open file
        mov     al,0                    ; open for reading
        int     dos
        mov     fhandle,ax              ; store file handle here
        jnc     xmit1                   ; nc = successful opening

xmit0a: mov     ah,prstr                ; give file not found error message
        mov     dx,offset xfrfnf
        int     dos
        or      errlev,1                ; set SEND failure condition
        or      fsta.xstatus,1          ; set status
        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    serrst                  ; reset serial port
        call    bufclear                ; clear script buffer
        call    clrbuf                  ; clear local serial port buffer
        or      errlev,1                ; set SEND failure condition
        or      fsta.xstatus,1          ; set status
        jmp     squit                   ; exit failure
                                        ;
xmity:  mov     bx,fhandle              ; file handle
        mov     ah,close2               ; close file
        int     dos
;;      call    serrst                  ; reset serial port
        call    bufclear                ; clear buffers
        call    clrbuf
        jmp     rskp                    ; and return success

xmit1:  call    serini                  ; initialize serial port
        jnc     xmit1b                  ; nc = success
        or      errlev,1                ; set SEND failure condition
        or      fsta.xstatus,1          ; set status
        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?
        jne     xmity                   ; ne = no, we are at EOF
xmit3a: push    si                      ; save position on line
        push    cx                      ; and byte count
        push    ax                      ; save char around outchr call
xmit4:  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: cmp     al,lf                   ; line feed?
        je      xmit4h                  ; e = yes, don't send it
        call    outchr                  ; send the character to the port
         jmp     xmit4f                 ; failed, try again
         nop
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
        push    ax                      ; save char
        call    cptchr                  ; give it the character just sent
        pop     ax                      ; restore character and keep going
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
        loop    xmit3                   ; finish this buffer full
        jmp     xmit2                   ; read next buffer

xmit8:  test    status,stat_cc          ; Control-C seen?
        jnz     xmita                   ; nz = yes
        call    chkkbd                  ; check keyboard (returns char in al)
        test    status,stat_ok          ; have a char?
        jnz     xmita                   ; nz = yes
        cmp     tempa,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     xmit7                   ; continue transfer
xmit8b: call    bufread                 ; check for char from serial port buf
        jc      xmit8                   ; c = none
        cmp     al,tempa                ; is port char the ack?
        jne     xmit8                   ; ne = no, just ignore the char
        jmp     xmit7                   ; yes, continue transfer

xmita:  test    status,stat_cc          ; control-c?
        jnz     xmitc                   ; nz = yes
        test    status,stat_cr          ; a local ack?
        jz      xmit8                   ; no, ignore local char
        mov     dx,offset crlf          ; display cr/lf
        mov     ah,prstr
        int     dos
        jmp     xmit7                   ; 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    si                      ; source ptr
        push    di                      ; destination ptr
        push    ax
        mov     ax,ds
        mov     es,ax                   ; use data segment for es:di
        pop     ax
        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
        cmp     al,0                    ; end of line?
        jne     cnvln3                  ; ne = 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
        cmp     ah,0                    ; was number larger than one byte?
        je      cnvln1                  ; e = 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
        mov     al,0                    ; 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
        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?
        mov     al,0                    ; 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 datas 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
cnvln7c: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     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
        stc                             ; set carry for failure
        ret                             ; and do a real return

cnvlnx: pop     di                      ; destination ptr
        pop     si                      ; source ptr
        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
        cmp     bx,0                    ; 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     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
        mov     bx,0                    ; 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
        cmp     bx,0                    ; doing hours?
        ja      inptm9a                 ; a = 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:cmp     al,0                    ; null?
        je      scdis2                  ; e = 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 prtbuflen 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
        mov     bufcnt,0                ; clear count of bytes in buffer
        mov     bufrdptr,offset prtbuf  ; move read pointer to start of buf
        mov     bufwtptr,offset prtbuf  ; move write pointer to start of buf
        mov     cx,prtbuflen
        push    es                      ; physically clear the buffer
        push    di
        mov     ax,datas
        mov     es,ax
        mov     al,0                    ; write prtbuflen nulls
        mov     di,offset prtbuf
        cld
        rep     stosb
        pop     di
        pop     es
        ret
bufclear endp

bufread proc    near
        call    chkport                 ; get any oldest char from port
        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
        mov     si,bufrdptr
        mov     al,byte ptr [si]        ; extract a char into al
        pop     si
        inc     bufrdptr                ; move pointer to next byte
        dec     bufcnt                  ; say have extracted a char
        cmp     bufrdptr,offset prtbuf+prtbuflen        ; beyond end?
        jb      bufrd2                  ; b = not yet, just return
        mov     bufrdptr,offset prtbuf  ; reset to start of buf (wrapping)
bufrd2: 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
        cmp     bufcnt,prtbuflen        ; is real buffer full?
        jae     peekbu1                 ; ae = yes, have examined everything
        call    chkport                 ; get a char from port
        cmp     bufcnt,0                ; still nothing from port?
        je      peekbu1                 ; e = no char, report fact
        inc     bufpkcnt                ; got one, increase peek counter
        jmp     peekbu2                 ; go extract it
peekbu1:stc                             ; return nothing to see
        ret
peekbu2:push    si
        mov     si,bufpkptr             ; buffer peek pointer
        mov     al,byte ptr [si]        ; extract a char into al
        pop     si
        inc     bufpkptr                ; move pointer to next byte
        dec     bufpkcnt                ; say have extracted a char
        cmp     bufpkptr,offset prtbuf+prtbuflen        ; beyond end?
        jb      peekbu3                 ; b = not yet, just return
        mov     bufpkptr,offset prtbuf  ; reset to start of buf (wrapping)
peekbu3:clc                             ; clear carry flag (have read a char)
        ret
peekbuf endp

bufwrite proc   near
        push    si
        mov     si,bufwtptr
        mov     byte ptr [si],al        ; store char held in al
        pop     si
        inc     bufwtptr                ; move pointer to next byte
        cmp     bufwtptr,offset prtbuf+prtbuflen        ; beyond end?
        jb      bufwt1                  ; b = not yet
        mov     bufwtptr,offset prtbuf  ; reset to start of buf (wrapping)
bufwt1: inc     bufcnt                  ; say have added a char to the buf
        cmp     bufcnt,prtbuflen        ; 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,prtbuflen        ; limit count to max buffer length
bufwt3: ret
bufwrite 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
        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)?
         jmp    chkpor1                 ; yes, analyze it
         nop                            ; ensure 3 bytes for rskp of prtchr
        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:  mov     kstatus,2               ; general command status, failure
        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
        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: ret                             ; return failure
squit4: jmp     rskp                    ; return success, ignore error

;
; Jumping to this location is like retskp. It assumes the instruction
;     after the call is a jmp addr.

RSKP    PROC    NEAR
        pop     bp
        add     bp,3
        push    bp
        ret
RSKP    ENDP

; Jumping here is the same as a ret.

R       PROC    NEAR
        ret
R       ENDP

code    ends
        end