home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / msvp98b1.lzh / MSSSCP.ASM < prev    next >
Assembly Source File  |  1993-05-14  |  77KB  |  2,265 lines

  1.     NAME    mssscp
  2. ; File MSSSCP.ASM
  3.     include mssdef.h
  4. ;  Copyright (C) 1985, 1992, Trustees of Columbia University in the 
  5. ;  City of New York.  Permission is granted to any individual or institution
  6. ;  to use this software as long as it is not sold for profit.  This copyright
  7. ;  notice must be retained.  This software may not be included in commercial
  8. ;  products without written permission of Columbia University.
  9. ;
  10. ; Edit History
  11. ; 6 Sept 1991 version 3.11
  12. ; Last edit 18 August 1992
  13. ; 4 June 1991 Make label searches be global so the next higher level is
  14. ;  searched if the present level does not contain the target.
  15. ; 6 June 1989 Limit echoing of received chars by OUTPUT to 100, to quench
  16. ;  verbose hosts. From Dan Norstedt.
  17. ; 4 May 1989 Make input/reinput scan buffer be allocated dynamically. Allow
  18. ;  for that size to be set via Environment as Kermit initializes.
  19. ;       
  20. ; MS Kermit Script routines, DEC-20 style.
  21. ; Extensively rewritten for MS Kermit 2.29a by Joe R. Doupnik 5 July 86
  22. ;;
  23. ;    Created June, 1986 a.d.    By James Sturdevant
  24. ;                      A. C. Nielsen Co. Mpls.
  25. ;                      8401 Wayzata Blvd.
  26. ;                      Minneapolis, Mn. 55426
  27. ;                      (612)546-0600
  28. ;;;;;;;;
  29. ; Kermit command level usages and this file's entry points:
  30. ; Clear - clears serial port buffers. Procedure scclr.
  31. ; Echo text - displays text on local screen. Proc scecho.
  32. ; Pause [time] - waits indicated number of seconds (default is Input
  33. ;    Default-timeout, 1 second typically). Proc scpau.
  34. ; IF condtion command - tests condition (SUCCESS or FAILURE just now) and
  35. ;       if the condition is met executes the main-line Kermit command.
  36. ; GOTO label - rewinds Take file or Macro, locates the :label, transfers
  37. ;       parsing control to the next line.
  38. ; Input [time] text - waits up to time seconds while scanning serial port
  39. ;    input for a match with text. Default value for time is Input
  40. ;    Default-timeout, 1 second typically). Spaces or tabs are separators
  41. ;    between the time and text fields. Proc scinp.
  42. ;    A carriage return typed by the local user simulates a match.
  43. ; Reinput [time] text - like INPUT but non-destructively rereads the 128 byte
  44. ;       script buffer. Buffer can be added to, until full, if necessary.
  45. ; Output text - sends the text to the serial output port. Proc scout.
  46. ; Transmit text [prompt] - raw file transfer to host. Proceeds from source
  47. ;    line to source line upon receipt of prompt from host or carriage
  48. ;    return from us. Default prompt is linefeed. A null prompt (\0)
  49. ;    causes the file to be sent with no pausing or handshaking. Note
  50. ;    that linefeeds are stripped from outgoing material. Proc scxmit.
  51. ; In the above commands "text" may be replaced by "@filespec" to cause the
  52. ;    one line of that file to be used instead. @CON obtains one line of
  53. ;    text from the keyboard. Such "indirect command files" may be nested
  54. ;    to a depth of 100. Control codes are written as decimal numbers
  55. ;    in the form "\ddd" where d is a digit between 0 and 9. Carriage
  56. ;    return is \13, linefeed is \10, bell is \7; the special code \255
  57. ;    is used to match (Input) either cr or lf or both.
  58. ; These commands can be given individually by hand or automatically
  59. ;    in a Kermit Take file; Take files may be nested.
  60. ;;;;;;;;
  61. ; These routines expect to be invoked by the Kermit command dispatcher
  62. ; and can have their default operations controlled by the Kermit Set Input
  63. ; command (implemented in file mssset.asm). They parse their own cmd lines.
  64. ; Set Input accepts arguments of
  65. ;   Case Ignore or Observe  (default is ignore case when matching strings)
  66. ;   Default-timeout seconds (default is 5 seconds)
  67. ;   Echo On or Off    controls echoing of Input cmd text (default is Off)
  68. ;   Timeout-action Quit or Proceed (default is Proceed)
  69. ; These conditions are passed via global variables script.incasv, .indfto,
  70. ;   .inecho, .inactv, respectively, stored here in structure script.
  71. ;;;;;;;;;
  72.  
  73.     public    script, scout, scinp, scpau, scecho, scclr, scxmit, scwait
  74.         public    sgoto, screinp, ifcmd, setalrm, inptim, chktmo, alrhms
  75.     public    buflog, scpini, scpbuflen, decvar, incvar
  76.  
  77. linelen         equ    134        ; length of working buffer line
  78. maxtry        equ    5        ; maximum number of output retries
  79. stat_unk     equ    0          ; status return codes.
  80. stat_ok        equ    1        ; have a port character
  81. stat_cc        equ    2        ; control-C typed
  82. stat_tmo    equ    4        ; timeout
  83. stat_cr        equ    8        ; carriage return typed
  84.  
  85. ifsuc        equ    0        ; indicators for IF conditions
  86. iffail        equ    1
  87. ifext        equ    2
  88. iferr        equ    3
  89. ifnot        equ    4
  90. ifctr        equ    5
  91. ifmdf        equ    6
  92. ifalarm        equ    7
  93. ifequal        equ    8
  94. ifless        equ    9
  95. ifsame        equ    10
  96. ifmore        equ    11
  97. ifllt        equ    12
  98. iflgt        equ    13
  99. ifpath        equ    14
  100.  
  101. data    segment
  102.     extrn    taklev:byte, takadr:word, portval:word, flags:byte
  103.     extrn    rxtable:byte, spause:byte, errlev:byte, fsta:word
  104.     extrn    kstatus:word, mcctab:byte, comand:byte, ttyact:byte
  105.     extrn    keyboard:word, rdbuf:byte
  106.  
  107.                     ; global (public) variables     
  108. script    scptinfo <>            ; global structure, containing:
  109. ;;inactv    db    0        ; input action value (default proceed)
  110. ;;incasv    db    0dfh        ; input case  (default ignore)
  111. ;;indfto    dw    1        ; input and pause timeout (def 1 sec)
  112. ;;inecho    db    1        ; echo Input cmd text (0 = no)
  113. ;;xmitfill    db    0        ; non-zero to TRANSMIT filler
  114. ;;xmitlf    db    0        ; non-zero to TRANSMIT LF's
  115. ;;xmitpmt    db    lf        ; prompt between lines
  116.  
  117.                     ; local variables
  118. line    db    linelen+1 dup (0)    ; line of output or input + terminator
  119.         even
  120. scpbuflen dw    128            ; serial port local buffer def length
  121. bufcnt    dw    0            ; serial port buf byte cnt, must be 0
  122. bufseg    dw    0            ; segment of buffer
  123. bufrdptr dw    0            ; serial port buf read ptr
  124. bufwtptr dw    0            ; serial port buf write ptr
  125. bufpkptr dw    0            ; peek-read pointer
  126. bufpkcnt dw    0            ; peek-read byte count remaining
  127. inplen    dw    0            ; length of input match string
  128. reinflg    db    0            ; 0 = INPUT, else REINPUT command
  129. notflag    db    0            ; IF NOT flag
  130. slablen    dw    0            ; label length, for GOTO
  131. status    dw    0            ; general status word
  132. fhandle    dw    0            ; file handle storage place
  133. temptr    dw    0            ; temporary pointer
  134. temptr2    dw    0            ; ditto, points to end of INPUT string
  135. tempd    dw    0            ; temp
  136. temp    dw    0            ; a temp
  137. tempa    db    0            ; another temp
  138. wtemp    db    0            ; temp for WAIT
  139. ltype    db    0            ; lex type for IF statements
  140. retry    db    0            ; number of output retries
  141. parmsk    db    7fh            ; 7/8 bit parity mask
  142. lecho    db    0            ; local echo of output (0 = no)
  143. timout    dw    0            ; work area (seconds before timeout)
  144. timhms    db    4 dup (0)        ; hhmmss.s time of day buffer
  145. alrhms    db    4 dup (0)        ; hhmmss.s time of day alarm buffer
  146. eolchr    db    LF            ; end of line character
  147.  
  148. crlf    db    cr,lf,'$'
  149. xfrfnf    db    cr,lf,'?Transmit file not found$'
  150. xfrrer    db    cr,lf,'?error reading Transmit file$'
  151. xfrcan    db    cr,lf,'?Transmission canceled$'
  152. indmis    db    '?Indirect file not found',cr,lf,'$'
  153. inderr    db    '?error reading indirect file',cr,lf,'$'
  154. laberr    db    cr,lf,'?Label ":$'
  155. laberr2    db    '" was not found.',cr,lf,'$'
  156. tmomsg    db    cr,lf,'?Timeout',cr,'$'
  157. outhlp    db    'line of text to be sent to remote host$'
  158. inphlp    db    'time-limit and line of text expected from remote host'
  159.     db    cr,lf,' Time is number of seconds or until a specific'
  160.     db    ' hh:mm:ss (24 hour clock)$'
  161. echhlp    db    'line of text to be Echoed to screen$'
  162. ptshlp    db    'amount of time to pause/wait'
  163.     db    cr,lf,' Time is number of seconds or until a specific'
  164.     db    ' hh:mm:ss (24 hour clock)$'
  165. wthlp    db    cr,lf,'Optional modem status signals CD, CTS, DSR which must'
  166.     db    ' be asserted while waiting$'
  167. xmthlp    db    ' Name of file to be Transmitted$'
  168. pmthlp    db    cr,lf
  169.     db     ' Prompt character expected as an ACK from host (\0 for none)$'
  170. ifdfhlp    db    cr,lf,' Name of macro or variable  then a command$'
  171. alrmhlp    db    cr,lf,' Seconds from now or time of day (HH:MM:SS) for alarm,'
  172.     db    ' < 12H from present$'
  173. ifmhlp    db    cr,lf,' Number, ARGC (1+argument count), COUNT, ERRORLEVEL,'
  174.     db    ' KEYBOARD, VERSION$'
  175. ifnhlp    db    cr,lf,' Number which errorlevel should match or exceed$'
  176. ifnmsg    db    cr,lf,'?Number expected, ignoring "$'
  177. ifnmsg2    db    '"$'
  178. discard    db      ' Kermit command'
  179.     db    cr,lf,' "IF" condition is false, command will be ignored.$'
  180. ifehlp1    db    cr,lf,'?pair of words or variables to be compared$'
  181. ifehlp2    db    cr,lf,'?second word or variable to be compared$'
  182. chgvarhlp db    'name of variable$'
  183. ssizehlp db    'amount, default is 1$'
  184.  
  185. iftable    db    15            ; IF command dispatch table
  186.     mkeyw    'Not',ifnot
  187.     mkeyw    '<',ifless
  188.     mkeyw    '=',ifsame
  189.     mkeyw    '>',ifmore
  190.     mkeyw    'Alarm',ifalarm
  191.     mkeyw    'Count',ifctr
  192.     mkeyw    'Defined',ifmdf
  193.     mkeyw    'Errorlevel',iferr
  194.     mkeyw    'Equal',ifequal
  195.     mkeyw    'Exist',ifext
  196.     mkeyw    'Inpath',ifpath
  197.     mkeyw    'LGT',iflgt
  198.     mkeyw    'LLT',ifllt
  199.     mkeyw    'Failure',iffail
  200.     mkeyw    'Success',ifsuc
  201. data    ends
  202.      
  203. code    segment
  204.      
  205.     extrn    comnd:near, clrbuf:near, prtchr:near, outchr:near, sendbr:near
  206.     extrn    cptchr:near, serini:near, pcwait:near, katoi:near, spath:near
  207.     extrn    cnvstr:near, getmodem:near, isdev:near, isfile:near
  208.     extrn    takrd:near, takclos:near, tolowr:near, prtasz:near,strlen:near
  209.     extrn    sendbl:near, dec2di:near
  210.  
  211.     assume    cs:code, ds:data, es:nothing
  212.  
  213. ; Initialize script routines before use, called as Kermit initializes.
  214.  
  215. SCPINI    PROC    NEAR
  216.     mov    cx,scpbuflen        ; (RE)INPUT buffer length
  217.     mov    bx,cx            ; string length, in bytes
  218.     add    bx,15            ; round up
  219.     jnc    scpini1            ; nc = under max size
  220. scpini3:mov    bx,0ffffh        ; 64KB-16 bytes, max buffer
  221. scpini1:mov    cl,4
  222.     shr    bx,cl            ; convert to paragraphs (divide by 16)
  223. scpini2:mov    cx,bx            ; remember desired paragraphs
  224.     mov    ah,alloc        ; allocate a memory block
  225.     int    dos
  226.     jc    scpini4            ; error, not enough memory
  227.     mov    bufseg,ax        ; store new segment
  228.     mov    cl,4
  229.     shl    bx,cl            ; convert paragraphs to bytes
  230.     mov    scpbuflen,bx        ; new length
  231.     push    es
  232.     mov    es,ax            ; buffer segment
  233.     xor    di,di            ; offset
  234.     mov    cx,bx            ; length in bytes
  235.     shr    cx,1            ; even, do words
  236.     xor    ax,ax            ; null filler
  237.     cld
  238.     rep    stosw            ; clear the buffer
  239.     pop    es
  240.     clc                ; return success
  241.     ret
  242. scpini4:mov    scpbuflen,0
  243.     stc                ; carry set for failure to initialize
  244.     ret
  245. SCPINI    ENDP
  246.  
  247. ; Clear input buffer(s) of serial port
  248. ; Clear command
  249. ;     
  250. SCCLR    PROC    NEAR
  251.     mov    kstatus,kssuc        ; global status
  252.     mov    ah,cmeol        ; get a confirm
  253.     call    comnd
  254.     jnc    scclr1            ; nc = success
  255.     ret                ; failure
  256. scclr1:    call    bufclear        ; clear our serial port circular buf
  257.     call    clrbuf            ; clear system serial port buffer too
  258.     clc
  259.     ret
  260. SCCLR    ENDP
  261.  
  262. ;
  263. ; Echo a line of text to our screen
  264. ; Echo text
  265. ;
  266. SCECHO    PROC    NEAR
  267.     mov    ah,cmline        ; get a whole line of asciiz text
  268.     mov    bx,offset rdbuf        ; where to store in
  269.     mov    word ptr [bx],0        ; clear line
  270.     mov    comand.cmblen,cmdblen    ; set line capacity (length of rdbuf)
  271.     mov    dx,offset echhlp    ; help
  272.     call    comnd
  273.     jc    echo3
  274.     mov    ah,cmeol
  275.     call    comnd
  276.     jc    echo3
  277.     mov    si,offset rdbuf        ; start of line
  278.     mov    di,si            ; convert to the same place
  279.     mov    ah,script.incasv    ; save current case state
  280.     push    ax
  281.     mov    script.incasv,0ffh    ; say no case conversion
  282.     call    cnvlin            ; convert \numbers to binary
  283.     pop    ax
  284.     mov    script.incasv,ah    ; recover case state
  285.     jc    echo3            ; carry set means error
  286.     mov    ah,prstr
  287.     mov    dx,offset crlf
  288.     int    dos
  289.     jcxz    echo2            ; z = nothing to show
  290.     cld
  291. echo1:    lodsb                ; get a source char into al
  292.     mov    dl,al
  293.     mov    ah,conout
  294.     int    dos
  295.     loop    echo1            ; get another
  296. echo2:    clc                ; return success
  297. echo3:    ret                ; error
  298. SCECHO    ENDP
  299.  
  300. ; Extract label from command line. Store in LINE, length in slablen.
  301. ; Jump to line in Take or Macro following that label.
  302. SGOTO    PROC    NEAR
  303.     mov    kstatus,kssuc        ; global status
  304.     mov    ah,cmword        ; get a word
  305.     mov    dx,offset line        ; buffer to hold label
  306.     xor    bx,bx            ; no help (non-interactive command)
  307.     mov    slablen,bx        ; clear label holding buffer
  308.     mov    comand.cmkeep,1        ; keep Take/macro open after EOF
  309.     call    comnd
  310.     jnc    goto1            ; nc = success
  311.     ret                ; failure
  312. goto1:    mov    ax,ax            ; byte count
  313.     mov    slablen,ax        ; save count here
  314.     or    ax,ax            ; need contents
  315.     jz    goto3            ; empty, fail
  316.     mov    comand.cmkeep,1        ; keep Take file open after this call
  317.     mov    ah,cmeol
  318.     call    comnd
  319.     jnc    goto2            ; nc = success
  320.     ret                ; failure
  321. goto2:    cmp    flags.cxzflg,'C'    ; check for Control-C breakout
  322.     je    goto3            ; e = yes, fail
  323.     cmp    taklev,0        ; in a Take file or Macro?
  324.     jne    GETTO            ; ne = yes, find the label
  325.     clc                ; ignore interactive command
  326.     ret
  327. goto3:    stc
  328.     ret
  329.  
  330. ; Find line starting just after ":label". Label is in variable LINE
  331. ; (length in slablen). Readjust Take read pointer to start of that line.
  332. ; Performs file search from beginning of file, popping up levels if req'd.
  333. ; Exit carry clear if success, carry set otherwise. Local worker routine.
  334. getto    proc    near
  335.     push    bx            ; global save of bx
  336. gett0:    mov    bx,takadr
  337.     cmp    [bx].taktyp,0feh    ; get type of take (a file?)
  338.     jne    gett2            ; ne = no, a macro
  339.                     ; scan from start of Take file
  340.     mov    eolchr,LF        ; file lines end on LF
  341.     mov    bx,[bx].takhnd        ; rewind the file
  342.     xor    cx,cx
  343.     xor    dx,dx
  344.     xor    al,al            ; zero displacement from start of file
  345.     mov    ah,lseek
  346.     int    dos
  347.     jnc    gett1
  348.     jmp    gett20            ; c = failure
  349. gett1:    call    takrd            ; get a buffer of data
  350.     mov    bx,takadr        ; restore bx to working value
  351.     jmp    short gett4
  352.                     ; Take a Macro
  353. gett2:    mov    eolchr,CR        ; Macro lines end on CR
  354.     mov    cx,[bx].takbuf        ; segment of macro definition
  355.     push    es
  356.     mov    es,cx
  357.     mov    cx,es:[0]        ; get string length byte
  358.     pop    es
  359.     mov    [bx].takcnt,cx        ; set unread to full buffer (rewind)
  360.     mov    [bx].takptr,2        ; set read pointer to start of text
  361.  
  362. gett4:    call    getch            ; get a character
  363.     jc    gett14            ; c = end of file, no char
  364.     cmp    al,' '            ; leading white space?
  365.     je    gett4            ; e = yes, read again
  366.     cmp    al,TAB
  367.     je    gett4
  368.     cmp    al,':'            ; start of label?
  369.     je    gett8            ; e = yes
  370. gett6:    cmp    al,eolchr        ; end of line?
  371.     je    gett4            ; e = yes, seek colon for label
  372.     call    getch            ; get a character
  373.     jc    gett14            ; c = end of file, no char
  374.     jmp    short gett6        ; read until end of line
  375.  
  376. gett8:    mov    si,offset line        ; label to search for
  377.     mov    cx,slablen        ; its length
  378.     jcxz    gett12            ; no chars to match
  379.     cmp    byte ptr[si],':'    ; user label starts with colon
  380.     jne    gett10            ; ne = no
  381.     inc    si            ; skip user's colon
  382.     dec    cx
  383.     jcxz    gett12            ; no chars to match
  384. gett10:    call    getch            ; read file char into al
  385.     jc    gett14            ; c = end of file
  386.     mov    ah,al
  387.     cld
  388.     lodsb
  389.     call    tolowr            ; convert al and ah to lower case
  390.     cmp    al,ah            ; match?
  391.     jne    gett6            ; ne = no, goto end of line
  392.     loop    gett10            ; continue matching
  393.                     ; match obtained
  394.     call    getch            ; read next file character
  395.     jc    gett13            ; c = end of file, no char
  396.     cmp    al,' '            ; separator?
  397.     je    gett12            ; e = yes, unique label found
  398.     cmp    al,TAB            ; this kind of separator?
  399.     je    gett12            ; e = yes
  400.     cmp    al,eolchr        ; or end of line?
  401.     je    gett13            ; e = yes
  402.     cmp    al,CR            ; macro eol, also file start eol pair
  403.     jne    gett6            ; ne = longer label than target
  404.  
  405. gett12:    call    getch            ; read past end of line
  406.     jc    gett13            ; c = end of file, no char
  407.     cmp    al,eolchr        ; end of line character?        
  408.     jne    gett12            ; ne = no, keep reading
  409. gett13: pop    bx
  410.     clc                ; return carry clear
  411.     ret                ; Take pointers are ready to read line
  412.                     ; failed to find label, pop a level
  413. gett14:    call    takclos            ; close this macro/take file
  414.     cmp    taklev,0        ; still in macro/take?
  415.     je    gett15            ; e = no, quit
  416.     jmp    gett0            ; try next level up
  417. gett15:mov    ah,prstr        ; say label not found
  418.     mov    dx,offset laberr    ; first part of error message
  419.     int    dos
  420.     mov    dx,offset line
  421.     cmp    line,':'        ; label starts with ":"?
  422.     jne    gett16            ; ne = no
  423.     inc    dx            ; yes, skip it
  424. gett16:    call    prtasz            ; print asciiz string
  425.     mov    ah,prstr
  426.     mov    dx,offset laberr2    ; trailer of error message
  427.     int    dos
  428. gett20:    pop    bx
  429.     mov    kstatus,ksgen        ; command status, failure
  430.     stc                ; set carry for failure
  431.     ret
  432. getto    endp
  433. SGOTO    ENDP
  434.  
  435. ; Read char from Take buffer. Returns carry clear and char in al, or if end
  436. ; of file returns carry set. Enter with BX holding takadr. Local worker.
  437. getch    proc    near
  438.     cmp    [bx].takcnt,0        ; buffer empty?
  439.     jg    getch2            ; g = no
  440.     cmp    [bx].taktyp,0feh    ; file?
  441.     jne    getch1            ; ne = no, a macro
  442.     call    takrd            ; read another buffer
  443.     cmp    [bx].takcnt,0        ; end of file?
  444.     jne    getch2            ; ne = no
  445. getch1:    stc                ; e = yes, exit error
  446.     ret
  447. getch2:    push    si
  448.     push    es
  449.     mov    es,[bx].takbuf        ; segment of buffer
  450.     mov    si,[bx].takptr        ; read a char from Take buffer
  451.     mov    al,es:[si]
  452.     inc    si
  453.     mov    [bx].takptr,si        ; move buffer pointer
  454.     dec    [bx].takcnt        ; decrease number of bytes remaining
  455.     pop    es
  456.     pop    si
  457.     clc                ; return carry clear
  458.     ret
  459. getch    endp
  460.  
  461.  
  462. ; IF [NOT] {< }| = | > | ALARM | COUNT | FAILURE | SUCCESS | INPATH filespec
  463. ;    | ERRORLEVEL \number | EQUAL string string | EXIST filespec} command
  464.  
  465. IFCMD    PROC    NEAR
  466.     mov    notflag,0        ; assume NOT keyword is absent
  467. ifcmd1:    mov    ah,cmkey        ; parse keyword
  468.     mov    dx,offset iftable    ; table of keywords
  469.     xor    bx,bx            ; help is the table
  470.     call    comnd
  471.     jnc    ifcmd1a            ; nc = success
  472.     ret                ; failure
  473. ifcmd1a:cmp    bx,ifnot        ; NOT keyword?
  474.     jne    ifcmd2            ; ne = no
  475.     xor    notflag,1        ; toggle not flag
  476.     jmp    short ifcmd1        ; and get next keyword
  477.  
  478. ifcmd2:    cmp    bx,ifsuc        ; IF SUCCESS?
  479.     jne    ifcmd4            ; ne = no
  480.     cmp    kstatus,kssuc        ; do we have success?
  481.     je    ifcmd2a            ; e = yes
  482.     jmp    ifcmdf            ; ne = no, no jump
  483. ifcmd2a:jmp    ifcmdp            ; yes
  484.  
  485. ifcmd4:    cmp    bx,iferr        ; IF ERRORLEVEL?
  486.     jne    ifcmd5            ; ne = no
  487.     jmp    ifnum            ; parse number to binary in line
  488.  
  489. ifcmd5:    cmp    bx,ifext        ; IF EXIST filespec?
  490.     je    ifcmd5a            ; e = yes
  491.     cmp    bx,ifpath        ; IF INPATH filespec?
  492.     jne    ifcmd6            ; ne = no
  493. ifcmd5a:mov    ah,cmword        ; read a filespec
  494.     mov    dx,offset rdbuf        ; buffer for filespec
  495.     push    bx            ; save command kind
  496.     xor    bx,bx
  497.     call    comnd
  498.     pop    bx
  499.     jnc    ifcmd5b            ; nc = success
  500.     ret                ; failure
  501. ifcmd5b:mov    ax,offset rdbuf        ; isfile wants pointer in ds:ax
  502.     cmp    bx,ifpath        ; INPATH?
  503.     je    ifcmd5c            ; e = yes
  504.     call    isfile            ; see if file exists
  505.     jc    ifcmdf            ; c = no
  506.     jmp    ifcmdp            ; yes, do following command
  507. ifcmd5c:call    spath            ; search path
  508.     jc    ifcmdf            ; c = no such file
  509.     jmp    ifcmdp            ; yes, do following command
  510.     
  511. ifcmd6:    cmp    bx,iffail        ; IF FAIL?
  512.     jne    ifcmd7
  513.     test    kstatus,not (kssuc)    ; check all bits
  514.     jz    ifcmdf            ; z = not that condition, no jump 
  515.     jmp    short ifcmdp
  516.  
  517. ifcmd7:    cmp    bx,ifctr        ; IF COUNT?
  518.     jne    ifcmd8            ; ne = no
  519.     cmp    taklev,0        ; in a Take file?
  520.     je    ifcmdf            ; e = no, fail
  521.     push    bx
  522.     mov    bx,takadr        ; current Take structure
  523.     cmp    [bx].takctr,0        ; exhausted count?
  524.     je    ifcmd7a            ; e = yes, dec no more ye counter
  525.     dec    [bx].takctr        ; dec COUNT if non-zero
  526.     cmp    [bx].takctr,0        ; exhausted now?
  527.     je    ifcmd7a            ; e = yes
  528.     pop    bx
  529.     jmp    short ifcmdp        ; COUNT > 0 at entry, execute command
  530. ifcmd7a:pop    bx
  531.     jmp    short ifcmdf        ; do not execute command
  532.  
  533. ifcmd8:    cmp    bx,ifmdf        ; IF DEF?
  534.     jne    ifcmd9            ; ne = no
  535.     jmp    ifmdef            ; do further parsing below
  536.  
  537. ifcmd9:    cmp    bx,ifalarm        ; IF ALARM?
  538.     jne    ifcmd10            ; ne = no
  539.     jmp    ifalrm            ; do further parsing below
  540.  
  541. ifcmd10:cmp    bx,ifequal        ; IF EQUAL?
  542.     jne    ifcmd10a        ; ne = no
  543.     jmp    ifequ            ; do further parsing below
  544. ifcmd10a:cmp    bx,iflgt        ; IF LGT?
  545.     jne    ifcmd10b        ; ne = no
  546.     jmp    ifequ
  547. ifcmd10b:cmp    bx,ifllt        ; IF LLT?
  548.     jne    ifcmd11            ; ne = no
  549.     jmp    ifequ
  550.  
  551. ifcmd11:cmp    bx,ifless        ; IF <?
  552.     je    ifcmd12            ; e = yes
  553.     cmp    bx,ifsame        ; IF =?
  554.     je    ifcmd12
  555.     cmp    bx,ifmore        ; IF > ?
  556.     jne    ifcmdf            ; ne = no
  557. ifcmd12:jmp    ifmath
  558.  
  559.                     ; Jump points for worker routines
  560.                     ; failure
  561. ifcmdf:    cmp    notflag,0        ; need to apply not condition?
  562.     jne    ifcmdp2            ; ne = yes, take other exit
  563. ifcmdf2:mov    ah,cmline        ; fail, read and discard rest of line
  564.     mov    comand.cmblen,cmdblen
  565.     mov    bx,offset rdbuf
  566.     mov    dx,offset discard    ; say not doing anything
  567.     mov    comand.cmper,1        ; don't expand variables at this time
  568.     call    comnd
  569.     clc                ; force success on discard of line
  570.     ret
  571.                     ; success (pass)
  572. ifcmdp:    cmp    notflag,0        ; need to apply not condition?
  573.     jne    ifcmdf2            ; ne = yes, take other exit
  574. ifcmdp2:clc                ; do command
  575.     ret
  576. IFCMD    ENDP
  577.  
  578. ; Compare errlev against user number. Jump successfully if errlev >= number.
  579. ; Worker for IF [NOT] ERRORLEVEL number <command>
  580. ifnum    proc    near
  581.     mov    ah,cmword        ; get following number
  582.     mov    dx,offset rdbuf+1
  583.     mov    comand.cmblen,cmdblen
  584.     mov    rdbuf,'\'        ; in case user forgets backslash
  585.     mov    word ptr rdbuf+1,0    ; clear buffer
  586.     mov    bx,offset ifnhlp    ; help
  587.     call    comnd
  588.     jnc    ifnum1            ; nc = success
  589.     ret                ; failure
  590. ifnum1:    mov    si,offset rdbuf        ; put text in compare buffer
  591.     cmp    rdbuf+1,'\'        ; did user include backslash?
  592.     jne    ifnum2            ; ne = no
  593.     inc    si            ; yes, skip our helpful backslash
  594. ifnum2:    call    katoi            ; convert number to binary in ax
  595.     jc    ifnum4            ; c = failed to convert a number
  596.     cmp    errlev,al        ; at or above this level?
  597.     jae    ifnum3            ; ae = yes, succeed
  598.     jmp    ifcmdf            ; else fail
  599. ifnum3:    jmp    ifcmdp            ; jump to main command Success exit
  600.  
  601. ifnum4:    mov    dx,offset rdbuf+1    ; pointer to bad word
  602.     mov    tempd,dx        ; remember starting place for text
  603.     call    strlen            ; get its length
  604.     add    dx,cx            ; skip over current word
  605.     mov    bx,dx
  606.     mov    byte ptr [bx],' '    ; space, chopped by parser
  607.     inc    bx            ; new text goes here
  608.     xor    dx,dx            ; help
  609.     mov    ah,cmline        ; read rest of line
  610.     mov    comand.cmblen,cmdblen
  611.     call    comnd
  612.     jnc    ifnum4a            ; nc = success
  613.     ret                ; failure
  614. ifnum4a:or    ax,ax            ; returned byte count
  615.     jnz    ifnum5            ; nz = got some
  616.     mov    byte ptr[bx-1],0    ; remove space separator from above
  617. ifnum5:    mov    ah,prstr
  618.     mov    dx,offset ifnmsg    ; error message header
  619.     int    dos
  620.     mov    dx,offset rdbuf+1    ; start of user text
  621.     call    prtasz            ; display asciiz string
  622.     mov    ah,prstr
  623.     mov    dx,offset ifnmsg2    ; trailer of message
  624.     int    dos
  625.     jmp    ifcmdf            ; jump to main command Failure exit
  626. ifnum    endp
  627.  
  628. ; Process IF [NOT] DEF <macro name> <command>
  629. ifmdef    proc    near
  630.     mov    dx,offset rdbuf+2    ; point to work buffer
  631.     mov    bx,offset ifdfhlp    ; help
  632.     mov    comand.cmblen,cmdblen
  633.     mov    ah,cmword        ; get macro name
  634.     mov    comand.cmper,1        ; do not react to \%x
  635.     call    comnd
  636.     jnc    ifmde1            ; nc = success
  637.     ret                ; failure
  638. ifmde1:    mov    word ptr rdbuf,ax    ; store length in buffer
  639.     mov    bx,offset mcctab+1    ; table of macro keywords
  640.     mov    tempd,0            ; tempd = current keyword
  641.     cmp    byte ptr [bx-1],0    ; any macros defined?
  642.     je    ifmde9            ; e = no, failure, exit now
  643.                     ; match table keyword and user word
  644. ifmde3:    mov    si,offset rdbuf        ; pointer to user's cnt+name
  645.     mov    cx,[si]            ; length of user's macro name
  646.     add    si,2            ; point to macro name
  647.     cmp    cx,[bx]            ; compare length vs table keyword
  648.     jne    ifmde7            ; ne = not equal lengths, try another
  649.     push    si            ; lengths match, how about spelling?
  650.     push    bx
  651.     add    bx,2            ; point at start of keyword
  652. ifmde4:    mov    ah,[bx]            ; keyword char
  653.     mov    al,[si]            ; new text char
  654.     cmp    al,'a'            ; map lower case to upper
  655.     jb    ifmde5
  656.     cmp    al,'z'
  657.     ja    ifmde5
  658.     sub    al,'a'-'A'
  659. ifmde5:    cmp    al,ah            ; test characters
  660.     jne    ifmde6            ; ne = no match
  661.     inc     si            ; move to next char
  662.     inc    bx
  663.     loop    ifmde4            ; loop through entire length
  664. ifmde6:    pop    bx
  665.     pop    si
  666.     jcxz    ifmde10            ; z: cx = 0, found the name
  667.                     ; select next keyword
  668. ifmde7:    inc    tempd            ; number of keyword to test next
  669.     mov    cx,tempd
  670.     cmp    cl,mcctab        ; all done? Recall, tempd starts at 0
  671.     jae    ifmde9            ; ae = yes, no match
  672.     mov    ax,[bx]            ; cnt (keyword length from macro)
  673.     add    ax,4            ; skip over '$' and two byte value
  674.     add    bx,ax            ; bx = start of next keyword slot
  675.     jmp    short ifmde3        ; do another comparison
  676. ifmde9:    jmp    ifcmdf            ; jump to main command Failure exit
  677. ifmde10:jmp    ifcmdp            ; jump to main command Success exit
  678. ifmdef    endp
  679.  
  680. ; IF [not] ALARM hh:mm:ss command
  681. ifalrm    proc    near
  682.     call    chkkbd            ; check keyboard for override
  683.     test    status,stat_cc        ; Control-C?
  684.     jz    ifalr1            ; z = no
  685.     stc
  686.     ret                ; yes, return failure now
  687. ifalr1:    push    word ptr timhms
  688.     push    word ptr timhms+2    ; save working timeouts
  689.     mov    ax,word ptr alrhms
  690.     mov    word ptr timhms,ax
  691.     mov    ax,word ptr alrhms+2
  692.     mov    word ptr timhms+2,ax    ; set alarm value
  693.     call    chktmo            ; check for timeout
  694.     pop    word ptr timhms+2    ; restore working timeouts
  695.     pop    word ptr timhms
  696.     test    status,stat_tmo        ; tod past user time (alarm sounded)?
  697.     jnz    ifalr4            ; nz = yes, succeed
  698.                     ; failure (not at alarm time yet)
  699.     cmp    notflag,0        ; need to apply NOT condition?
  700.     jne    ifalr5            ; ne = yes, take other exit
  701. ifalr3:    mov    ah,cmline        ; fail, read and discard rest of line
  702.     mov    bx,offset rdbuf
  703.     mov    comand.cmblen,cmdblen
  704.     xor    dx,dx
  705.     call    comnd
  706.     clc                ; set command parse success
  707.     ret
  708.                     ; success (at or past alarm time)
  709. ifalr4:    cmp    notflag,0        ; need to apply not condition?
  710.     jne    ifalr3            ; ne = yes, take other exit
  711. ifalr5:    clc                ; pass, do command
  712.     ret
  713. ifalrm    endp
  714.  
  715. ; IF [NOT] {LLT, EQUAL, LGT} word word command
  716. ; Permits use of \number, {string}, @filespec
  717. ifequ    proc    near
  718.     mov    ltype,bl        ; remember kind of lex test
  719.     mov    comand.cmblen,cmdblen
  720.     mov    ah,cmword        ; get a word
  721.     mov    dx,offset rdbuf        ; where to store
  722.     mov    rdbuf,0            ; clear first entry
  723.     mov    bx,offset ifehlp1    ; help
  724.     call    comnd            ; ignore parse error if no text
  725.     mov    si,offset rdbuf        ; start of line
  726.     mov    di,si            ; convert to the same place
  727.     call    cnvlin            ; convert \numbers to binary
  728.     jc    ifequ9            ; carry set means error
  729.     jcxz    ifequ9            ; z = empty word
  730.      mov    tempd,cx
  731.     add    si,cx
  732.     inc    si            ; skip null terminator
  733.     mov    temptr,si        ; place to start second part
  734.     mov    dx,si
  735.     mov    word ptr[si],0        ; clear second part
  736.     mov    ah,cmword        ; get a word of text
  737.     mov    bx,offset ifehlp2    ; help
  738.     mov    comand.cmblen,cmdblen
  739.     call    comnd            ; ignore parse error if no text
  740.     mov    si,temptr        ; start of second line
  741.     mov    di,si            ; convert to the same place
  742.     call    cnvlin            ; convert \numbers to binary
  743.     jc    ifequ9            ; c = failure
  744.     jcxz    ifequ9            ; z = empty word
  745.     cmp    tempd,cx        ; first longer than second?
  746.     jae    ifequ3            ; ae = yes
  747.     xchg    tempd,cx        ; use length of shorter word
  748. ifequ3:    inc    cx            ; include null terminator in count
  749.     mov    si,offset rdbuf        ; first word
  750.     mov    di,temptr        ; second word
  751.     push    es
  752.     mov    ax,ds
  753.     mov    es,ax
  754.     cld
  755.     repe    cmpsb            ; compare while equal
  756.     pop    es
  757.     jb    ifequ6            ; exited on before condition
  758.     ja    ifequ7            ; exited on above condition
  759.  
  760.     cmp    ltype,ifequal        ; wanted EQUAL condition?
  761.     jne    ifequ9            ; ne = no, fail
  762.     jmp    ifcmdp            ; else success
  763.  
  764. ifequ6:    cmp    ltype,ifllt        ; LLT test?
  765.     jne    ifequ9            ; ne = no, failed
  766.     jmp    ifcmdp            ; do IF cmd success
  767.  
  768. ifequ7:    cmp    ltype,iflgt        ; LGT test?
  769.     jne    ifequ9            ; ne = no, failed
  770.     jmp    ifcmdp            ; do IF cmd success
  771.  
  772. ifequ9:    jmp    ifcmdf            ; do IF cmd failure
  773. ifequ    endp
  774.  
  775. ; Worker for IF [NOT] < = > var var <command>
  776. ; var is ARGC, COUNT, ERRORLEVEL, VERSION, or a 16 bit number
  777. ifmath    proc    near
  778.     mov    tempa,bl        ; save kind of math test here
  779.     mov    temp,0            ; place to store first value
  780.     mov    tempd,0            ; count times around this loop
  781. ifmath1:mov    dx,offset rdbuf+1
  782.     mov    rdbuf,'\'        ; in case user forgets backslash
  783.     mov    word ptr rdbuf+1,0    ; clear buffer
  784.     mov    bx,offset ifmhlp    ; help
  785.     mov    comand.cmblen,cmdblen
  786.     mov    ah,cmword        ; get following number
  787.     call    comnd
  788.     jnc    ifmath2            ; nc = success
  789.     ret                ; failure
  790. ifmath2:mov    si,offset rdbuf+1    ; put text in compare buffer
  791.     mov    ax,[si]            ; get first two user chars
  792.     or    ax,2020h        ; lowercase both bytes
  793.     cmp    ax,'ra'            ; ARGC?
  794.     jne    ifmath3            ; ne = no
  795.     xor    ax,ax
  796.     cmp    taklev,0        ; in a Take/macro?
  797.     je    ifmath8            ; e = no, report ARGC as 0
  798.     mov    bx,takadr        ; current Take structure
  799.     mov    ax,[bx].takargc        ; get argument count
  800.     jmp    short ifmath8
  801. ifmath3:cmp    ax,'re'            ; ERRORLEVEL?
  802.     jne    ifmath4            ; ne = no
  803.     mov    al,errlev        ; get errorlevel
  804.     xor    ah,ah
  805.     jmp    short ifmath8
  806. ifmath4:cmp    ax,'oc'            ; COUNT?
  807.     jne    ifmath5            ; ne = no
  808.     xor    ax,ax
  809.     cmp    taklev,0        ; in a Take/macro?
  810.     je    ifmath8            ; e = no, report COUNT as 0
  811.     mov    bx,takadr        ; current Take structure
  812.     mov    ax,[bx].takctr        ; get COUNT
  813.     jmp    short ifmath8
  814. ifmath5:cmp    ax,'ev'            ; VERSION?
  815.     jne    ifmath5a        ; ne = no
  816.     mov    ax,version        ; get version such as 300
  817.     jmp    short ifmath8
  818. ifmath5a:cmp    ax,'ek'            ; KEYBOARD?
  819.     jne    ifmath6            ; ne = no
  820.     mov    ax,keyboard        ; get 88 or 101 for keys on keyboard
  821.     jmp    short ifmath8
  822. ifmath6:cmp    rdbuf+1,'\'        ; did user include backslash?
  823.     je    ifmath7            ; e = yes
  824.     dec    si            ; no, employ our helpful backslash
  825. ifmath7:call    katoi            ; convert number to binary in ax
  826.     jc    ifmathb            ; c = failed to convert a number
  827. ifmath8:cmp    tempd,0            ; gotten second value yet?
  828.     ja    ifmath9            ; a = yes, it is in ax
  829.     mov    temp,ax            ; save first value
  830.     inc    tempd            ; say we have been here
  831.     jmp    ifmath1            ; do second argument
  832.  
  833. ifmath9:mov    bl,tempa        ; kind of math test
  834.     cmp    bl,ifless        ; "<"?
  835.     jne    ifmath10        ; ne = no
  836.     cmp    temp,ax            ; val1 < val2?
  837.     jb    ifmathp            ; b = pass
  838.     jmp    short ifmathf        ; fail
  839. ifmath10:cmp    bl,ifsame        ; "="?
  840.     jne    ifmath11        ; ne = no
  841.     cmp    temp,ax            ; val1 = val2?
  842.     je    ifmathp            ; e = yes, pass
  843.     jmp    short ifmathf        ; fail
  844. ifmath11:cmp    temp,ax            ; val2 > val1?
  845.     ja    ifmathp            ; a = yes, pass
  846. ifmathf:jmp    ifcmdf            ; else fail
  847. ifmathp:jmp    ifcmdp            ; jump to main command Success exit
  848.  
  849. ifmathb:jmp    ifnum4            ; do common error complaint
  850. ifmath    endp
  851.  
  852. ; DECREMENT/INCREMENT variable size (default size 1)
  853. ; Permits variable to be \%<char> or a macro name. Non-negative results.
  854. decvar    proc    near
  855.     mov    temp,-1            ; marker to say dec
  856.     jmp    short incvar1
  857. decvar    endp
  858.  
  859. incvar    proc    near
  860.     mov    temp,1            ; marker to say inc
  861. incvar1:
  862.     mov    kstatus,ksgen        ; general command failure
  863.     mov    ah,cmword        ; Common code
  864.     mov    word ptr rdbuf,0    ; entry count, empty
  865.     mov    dx,offset rdbuf+2    ; reserve word 0 for entry count
  866.     mov    bx,offset chgvarhlp
  867.     mov    comand.cmper,1        ; don't react to \%x variables
  868.     call    comnd
  869.     jnc    incvar2            ; nc = success
  870.     ret                ; failure
  871. incvar2:or    ax,ax            ; necessary macro name?
  872.     jnz    incvar3            ; nz = yes
  873. incvar2a:stc                ; no, fail
  874.     ret
  875. incvar3:mov    word ptr rdbuf,ax    ; save length of macro name
  876.     mov    si,offset mcctab    ; table of macro names
  877.     cld
  878.     lodsb
  879.     mov    cl,al            ; number of macro entries
  880.     xor    ch,ch
  881.     jcxz    incvar2a        ; z = none
  882.                     ; find variable
  883. incvar4:push    cx            ; save loop counter
  884.     lodsw                ; length of macro name to ax
  885.     mov    cx,word ptr rdbuf    ; length of user's string
  886.     cmp    ax,cx            ; variable name same as user spec?
  887.     jne    incvar6            ; ne = no, no match
  888.     push    ax
  889.     push    si            ; save these around match test
  890.     mov    di,offset rdbuf+2    ; user's string
  891. incvar5:mov    ah,[di]
  892.     inc    di
  893.     lodsb                ; al = mac name char, ah = user char
  894.     and    ax,not 2020h        ; clear bits (uppercase chars)
  895.     cmp    ah,al            ; same?
  896.     loope    incvar5            ; while equal, do more
  897.     pop    si            ; restore regs
  898.     pop    ax
  899.     jne    incvar6            ; ne = no match
  900.     pop    cx            ; remove loop counter
  901.     jmp    short incvar7        ; e = match
  902. incvar6:add    si,ax            ; point to next name, add name length
  903.     add    si,2            ;  and string pointer
  904.     pop    cx            ; recover loop counter
  905.     loop    incvar4            ; one less macro to examine
  906.     xor    ax,ax
  907.     mov    temp,ax            ; indicate failure
  908.     jmp    incvar13        ; go do command confirmation
  909.  
  910. incvar7:mov    ax,[si-2]        ; get length of variable string
  911.     add    si,ax            ; point to segment of definition
  912.     mov    si,[si]            ; seg of definition
  913.     mov    word ptr rdbuf+2,si    ; preserve seg for later storage
  914.     push    es
  915.     mov    es,si
  916.     mov    cx,es:[0]        ; length of definition
  917.     cmp    cx,9            ; "\x{65384}" is max length
  918.     ja    incvar10        ; a = too large to qualify
  919.     mov    di,offset rdbuf+4    ; copy string to regular data segment
  920.     mov    si,2            ; skip over string count word
  921.     mov    al,es:[si]        ; get leading byte
  922.     cmp    al,'\'            ; escaped number
  923.     je    incvar9            ; e = yes
  924.     mov    byte ptr [di],'\'    ; insert escape
  925.     inc    di
  926. incvar9:mov    al,es:[si]        ; copy string to regular data segment
  927.     mov    [di],al
  928.     inc    si
  929.     inc    di
  930.     loop    incvar9
  931.     mov    byte ptr [di],0        ; asciiz
  932.     mov    si,offset rdbuf+4
  933.     call    katoi            ; convert var value to binary in ax
  934.     jc    incvar10        ; c = failed
  935.     mov    word ptr rdbuf,ax    ; save value here, seg in rdbuf+2
  936.     pop    es
  937.     jmp    short incvar12        ; now get step size, if any
  938. incvar10:pop    es
  939.     xor    ax,ax
  940.     mov    temp,ax            ; setup failure
  941.     jmp    short incvar13        ; do command confirmation
  942.  
  943. incvar12:mov    ah,cmword        ; get step size, if any
  944.     mov    comand.cmblen,7        ; length of step size
  945.     mov    dx,offset rdbuf+6    ; where to put string
  946.     mov    bx,offset ssizehlp
  947.     call    comnd
  948.     jnc    incvar13
  949.     ret
  950. incvar13:push    ax            ; save step size string length
  951.     mov    ah,cmeol
  952.     call    comnd
  953.     pop    ax
  954.     jnc    incvar14        ; nc = success
  955.     ret
  956.                     ; now convert step size, if any
  957. incvar14:or    ax,ax            ; is length zero?
  958.     jnz    incvar15        ; nz = no, convert number to binary
  959.     inc    ax            ; set default inc/dec to 1
  960.     jmp    incvar17        ; go process new value
  961.  
  962. incvar15:mov    si,offset rdbuf+6    ; step size string
  963.     cmp    byte ptr [si],'\'    ; has "\"?
  964.     je    incvar16        ; e = yes
  965.     dec    si
  966.     mov    byte ptr [si],'\'    ; insert one
  967. incvar16:call    katoi            ; ds:si to number to ax
  968.     jnc    incvar17        ; nc = a number
  969.     ret                ; fail
  970.                     ; step size is in ax
  971. incvar17:mov    cx,word ptr rdbuf    ; current value of variable
  972.     cmp    temp,0            ; inc or dec?
  973.     jg    incvar19        ; g = increment
  974.     jl    incvar18        ; l = decrement
  975.     stc                ; else fail
  976.     ret
  977. incvar18:cmp    ax,cx            ; if would subtract too much
  978.     jbe    incvar18a        ; be = ok
  979.     stc                ; a = would go below zero, fail
  980.     ret
  981. incvar18a:neg    ax            ; change increment to negative
  982.     add    ax,cx            ; add current value
  983.     jc    incvar20        ; c = ok
  984.     stc
  985.     ret                ; fail
  986. incvar19:add    ax,cx            ; increment
  987.     jnc    incvar20
  988.     ret                ; carry means fail
  989.  
  990. incvar20:mov    di,offset rdbuf+4    ; place to store string
  991.     call    dec2di            ; binary to ascii decimal in ds:di
  992.     mov    si,offset rdbuf+4
  993.     sub    di,si            ; new string length
  994.     mov    cx,di
  995.     cld
  996.     push    es
  997.     mov    ax,word ptr rdbuf+2    ; get segment of variable
  998.     mov    es,ax
  999.     mov    di,2
  1000.     mov    es:[di-2],cx        ; store new count word
  1001.     rep    movsb            ; copy string to variable's seg
  1002.     pop    es
  1003.     mov    kstatus,kssuc        ; say success
  1004.     clc
  1005.     ret
  1006. incvar    endp
  1007.  
  1008. ; SET ALARM <time, sec from now or HH:MM:SS>
  1009. SETALRM    PROC    NEAR
  1010.     mov    dx,offset line        ; point to work buffer
  1011.     mov    word ptr line,0
  1012.     mov    word ptr line+2,0
  1013.     mov    bx,offset alrmhlp    ; help
  1014.     mov    ah,cmword        ; get macro name
  1015.     call    comnd
  1016.     jc    setal1            ; c = failure
  1017.     mov    ah,cmeol        ; get a confirm
  1018.     call    comnd
  1019.     jc    setal1            ; c = failure
  1020.     push    word ptr timhms
  1021.     push    word ptr timhms+2    ; save working timeouts
  1022.     mov    si,offset line        ; source pointer
  1023.     call    inptim            ; get the timeout time, sets si
  1024.     mov    ax,word ptr timhms    ; save time in alarm area
  1025.     mov    word ptr alrhms,ax
  1026.     mov    ax,word ptr timhms+2
  1027.     mov    word ptr alrhms+2,ax
  1028.     pop    word ptr timhms+2    ; restore working timeouts
  1029.     pop    word ptr timhms
  1030.     clc
  1031. setal1:    ret
  1032. SETALRM    ENDP
  1033.  
  1034. ; REINPUT <timeout> <match text>
  1035. ; Reread material in serial port buffer, seeking a match with user's text
  1036. ; pattern. If user's pattern is longer than material in buffer then read
  1037. ; additional characters from the serial port. Use SCINP to do the main work.
  1038.  
  1039. SCREINP    PROC    NEAR
  1040.     mov    reinflg,1        ; say doing REINPUT, not INPUT
  1041.     jmp    short input10
  1042. SCREINP    ENDP
  1043.  
  1044. ; Input from port command, match input with text pattern
  1045. ; Input [timeout] text
  1046. ;     
  1047. SCINP    PROC    NEAR
  1048.     mov    reinflg,0        ; say doing INPUT, not REINPUT
  1049.     jmp    short input10
  1050.  
  1051. input10:mov    kstatus,kssuc
  1052.     mov    ah,cmline        ; get a whole line of asciiz text
  1053.     mov    bx,offset line        ; place to put text
  1054.     mov    dx,offset inphlp    ; help message
  1055.     call    comnd            ; get the pattern text
  1056.     jnc    input11            ; nothing, complain
  1057.     ret                ; failure
  1058. input11:mov    ah,cmeol
  1059.     call    comnd
  1060.     jnc    input12
  1061.     ret
  1062. input12:cmp    reinflg,0        ; Input command?
  1063.     jne    input1            ; ne = no, Reinput
  1064.     cmp    taklev,0        ; are we in a Take file?
  1065.     je    input0            ; e = no, display linefeed
  1066.     cmp    flags.takflg,0        ; are Take commands being echoed?
  1067.     je    input1            ; e = no, skip display
  1068. input0:    cmp    script.inecho,0        ; Input echo off?
  1069.     je    input1            ; e = yes
  1070.     mov    al,lf            ; next line
  1071.     call    scdisp            ; display the char
  1072. input1: call    serini            ; initialize the system's serial port
  1073.     jc    input1a            ; c = failure
  1074.     mov    status,stat_unk        ; clear status flag
  1075.     mov    si,offset line        ; source pointer
  1076.     call    inptim            ; get the timeout time, sets si
  1077.     jnc    input1b            ; nc = legal time value or none
  1078. input1a:jmp    input5            ; else fail on error
  1079. input1b:mov    di,offset line        ; put text in compare buffer
  1080.     call    cnvlin            ; convert \numbers in buf line
  1081.     mov    inplen,cx        ; cx = number of bytes in final string
  1082.     mov    parmsk,0ffh          ; parity mask, assume 8 bit data
  1083.     mov    di,portval
  1084.     cmp    [di].parflg,parnon    ; parity is none?
  1085.     je    input1c            ; e = none
  1086.     mov    parmsk,07fh        ; else strip parity (8th) bit
  1087. input1c:mov    di,offset line
  1088.     mov    temptr,di        ; pointer to pattern char
  1089.     mov    temptr2,di        ; and we need pointer to end of string
  1090.     add    temptr2,cx        ; offset of end of string
  1091.                     ; setup reinput read pointer & count
  1092.     mov    ax,bufwtptr        ; where next new char goes
  1093.     mov    bufpkptr,ax        ; set peek-read pointer at oldest char
  1094.     mov    ax,scpbuflen        ; length of the buffer
  1095.     mov    bufpkcnt,ax        ; always look back one full buffer
  1096.                      ; see if a pattern needs matching
  1097.     cmp    inplen,0        ; empty pattern? (cnvlin sets cx=cnt)
  1098.     jne    input4            ; ne = not empty
  1099.     cmp    reinflg,0        ; Input command?
  1100.     je    input3            ; e = yes, read and discard chars
  1101.     jmp    input5            ;  reinput, just exit timeout
  1102.  
  1103.                     ; empty. read, display, and discard
  1104. input3:    call    chkkbd            ; check keyboard
  1105.     test    status,stat_cc        ; did user type control-c?
  1106.     jnz    input3b            ; nz = yes, quit
  1107.     test    status,stat_cr        ; did user type cr? [js]
  1108.     jz    input3a            ; z = no
  1109.     or    status,stat_tmo        ; force timeout status too
  1110.     jmp    short input5        ; nz = yes, return timeout failure
  1111. input3a:call    chktmo            ; check timeout
  1112.     test    status,stat_tmo
  1113. input3b:jnz    input5            ; nz = timed out, quit
  1114.     call    bufread            ; read from serial port buffer into al
  1115.     jmp    short input3        ; loop until timeout, exit timeout
  1116.  
  1117.                     ; start main read and compare loop
  1118. input4:    mov    di,temptr        ; pointer to current pattern char
  1119.     cmp    di,temptr2        ; at end of pattern?
  1120.     jae    inputx            ; ae = yes, return success
  1121.     call    chkkbd            ; check keyboard
  1122.     test    status,stat_cc        ; did user type Control-C?
  1123.     jnz    input5            ; nz = yes, quit
  1124.     test    status,stat_cr        ; did user type cr? [js]
  1125.     jz    input4a            ; z = no
  1126.     or    status,stat_tmo        ; force timeout status too
  1127.     jmp    short input5        ; nz = yes, return success [js]
  1128. input4a:test    status,stat_tmo+stat_ok ; user override/timeout on last read
  1129.     jnz    input5            ; nz = timed out, quit
  1130.     cmp    reinflg,0        ; Input command?
  1131.     jne    input4b            ; ne = no, a reinput cmd
  1132.     call    bufread            ; read from serial port buffer into al
  1133.     jc    input4            ; c = nothing there, keep looking
  1134.     jmp    short input4c        ; analyze character
  1135.  
  1136. input4b:call    peekbuf            ; reinput: peek-read from buffer
  1137.     jnc    input4c            ; nc = got a character into al
  1138.     test    status,stat_tmo        ; timeout (or examined all chars)?
  1139.     jz    input4            ; z = no, keep trying
  1140.     jnz    input5            ; nz = timeout getting a character
  1141.  
  1142.                     ; got a char from buffer/port
  1143. input4c:cmp    al,'a'            ; candidate for case conversion? [js]
  1144.     jb    input4d            ; b = no [js]
  1145.     cmp    al,'z'            ; in lower case set? [js]
  1146.     ja    input4d            ; a = no [js]
  1147.     and    al,script.incasv    ; apply case conversion mask
  1148. input4d:mov    di,temptr
  1149.     mov    ah,byte ptr [di]    ; get current pattern char again
  1150.     call    matchr            ; al=rcvd, ah=pattern, do they match?
  1151.     jc    inpm            ; c = no match, try substring
  1152.     inc    temptr            ; matched, point to next pattern char
  1153.     jmp    short input4
  1154.  
  1155. input5:    or    errlev,ksrecv        ; set RECEIVE failure condition
  1156.     or    fsta.xstatus,ksrecv    ; set status
  1157.     or    kstatus,ksrecv
  1158.     cmp    reinflg,0        ; Input command?
  1159.     jne    input6            ; ne = no
  1160.     jmp    squit            ; exit failure: timeout or Control-C
  1161. input6:    jmp    squit1            ; skip timeout message, if any
  1162. inputx:    clc                ; return success
  1163.     ret
  1164. ; See if a trailing-subset of the matched chars + new port char can match
  1165. ; the beginning part of the pattern. That is, if we were to simply "forget"
  1166. ; the oldest of the matched chars and slide left the apparent port string
  1167. ; then could we eventually find a match? Example: "Input 10 memema"
  1168. ; gives the pattern of "memema"; suppose the received chars were "mememema".
  1169. ; Forgetting one left-most rcv'd char at a time (two in this case) finally
  1170. ; yields a match, from which we should continue to compare fresh port chars 
  1171. ; with successive pattern chars until either they match through all pattern
  1172. ; chars or we encounter another break. If there is a later break, repeat this
  1173. ; algorithm.
  1174. ; Since we really have only the latest char from the port then pointers to
  1175. ; the matched pattern chars are used to mimic the earlier received chars:
  1176. ; they must have been identical to produce a match to date. The quick way
  1177. ; to "forget" oldest received chars is to scan backward through the matched
  1178. ; pattern chars looking for the current port char; if the first such find does
  1179. ; not yield a matching substring then look back further.
  1180.                 ; no or partial match then break
  1181.                 ; di = temptr = pattern break char
  1182.                 ; al = port char causing break
  1183.                 ; di - offset line = # chars matched thus far
  1184.             ; avoid cpu-brand side effects with "repne scasb"
  1185. inpm:    mov    tempa,al    ; save port char here 
  1186. inpm1:    mov    tempd,di    ; pattern break loc, where matching failed
  1187.     mov    cx,di        ; char at di does not match current port char
  1188.     sub    cx,offset line    ; compute count of matched bytes
  1189.     jcxz    inpm4        ; z = 0 = mismatch on the initial pattern char
  1190.  
  1191.     mov    al,tempa    ; port char to find (in case we looped here)
  1192. inpm2:    dec    di        ; back up one pattern char
  1193.     mov    ah,byte ptr [di]; current pattern character to consider
  1194.     call    matchr        ; is port char = earlier pattern char? [js]
  1195.     jnc    inpm3        ; nc = equal values, go construct substring
  1196.     loop    inpm2        ; do cx times, max. (length of match to date)
  1197.     jmp    short inpm4    ; get here when there are no matches [js]
  1198.  
  1199. inpm3:    mov    bx,tempd    ; get last break location
  1200.     sub    bx,di        ; displacement = break - new find of port char
  1201.     mov    tempd,di    ; remember new location of a port-like char 
  1202.                 ; cx has number of chars in test substring
  1203.     dec    cx        ; matched one char already [jrs]
  1204.     jcxz    inpm3a        ; is there anything left? [jrs]
  1205.     call    matstr        ; does this substring match the pattern?
  1206.     jc    inpm1        ; c = no match, try making substring smaller
  1207.  
  1208. inpm3a:    mov    di,tempd    ; sub-string matched. Use this shorter match
  1209.     mov    temptr,di    ; set di for exit (matstr messes up di)
  1210.     inc    temptr        ; matched, point to next pattern char
  1211.     jmp    input4        ; continue with fresh port info
  1212.  
  1213. inpm4:    mov    temptr,offset line; complete failure, restart scanning
  1214.     jmp    input4        ; get something from the port
  1215.  
  1216. ; worker for SCINP
  1217. ; compare strings. One starts at offset line, the other starts bx bytes later.
  1218. ; cx = # chars to compare. Return carry clear if match, else carry set.
  1219. matstr:    mov    si,offset line    ; start of pattern string
  1220. matstr1:mov    ah,byte ptr [si] ; pattern char
  1221.     mov    al,byte ptr [si+bx] ; "old port char" (same as pattern char)
  1222.     call    matchr        ; check match of these two characters
  1223.     jc    matstr2        ; c = no match (exit with carry flag set)
  1224.     inc    si        ; match, consider next pair
  1225.     loop    matstr1        ; consider rest of substring (cx is counter)
  1226.     clc            ; clear c bit (substrings do match)
  1227. matstr2:ret            ; preserves flags (c set = no match)
  1228.  
  1229. ; worker for SCINP
  1230. ; compare single characters, one in ah and the other in al. Allow the 0ffh
  1231. ; wild card to match CR and LF individually. Return carry clear if match,
  1232. ; or carry set if they do not match. Registers preserved.
  1233. matchr:    cmp    ah,al        ; do these match?
  1234.     je    matchr6        ; e = yes
  1235.     cmp    ah,0ffh        ; the match cr/lf indicator?
  1236.     je    matchr2        ; e = yes    
  1237.     cmp    al,0ffh        ; the match cr/lf indicator?
  1238.     jne    matchr5        ; ne = no match at all.
  1239. matchr2:push    ax        ; save both chars again
  1240.     and    ah,al        ; make a common byte for testing
  1241.     cmp    ah,cr
  1242.     je    matchr4        ; e = cr matches 0ffh
  1243.     cmp    ah,lf
  1244.     je    matchr4        ; e = lf matches 0ffh
  1245.     pop    ax        ; recover chars
  1246. matchr5:stc            ; set carry (no match)
  1247.     ret
  1248. matchr4:pop    ax        ; recover chars
  1249. matchr6:clc            ; clear carry (match)
  1250.     ret
  1251. SCINP    ENDP
  1252. ;
  1253. ; Pause for the specified number of seconds or until a time of day
  1254. ; Pause [seconds or hh:mm:ss]
  1255. ;
  1256. SCPAU    PROC    NEAR
  1257.     mov    kstatus,kssuc
  1258.     mov    ah,cmword        ; get a word (number)
  1259.     mov    dx,offset line        ; where to store it
  1260.     mov    byte ptr line,0        ; terminate line incase no text
  1261.     mov    bx,offset ptshlp    ; help msg
  1262.     call    comnd
  1263.     jc    scpau1            ; c = failure
  1264.     mov    si,offset line        ; source pointer
  1265.     call    inptim            ; parse pause time (or force default)
  1266.     jc    scpau1            ; c = bad time value
  1267.     mov    wtemp,0            ; no modem status to detect
  1268.     jmp    swait4            ; finish in common code
  1269. scpau1:    ret                ; return command failure
  1270. SCPAU    ENDP
  1271.  
  1272. ;
  1273. ; Wait for the indicated signal for the specified number of seconds or tod
  1274. ; WAIT [seconds] \signal   where \signal is \cd, \dsr modem status lines.
  1275. ; Use INPUT-TIMEOUT ACTION for failures.
  1276. ;
  1277. SCWAIT    PROC    NEAR
  1278.     mov    kstatus,kssuc
  1279.     mov    ah,cmword        ; get a word (number)
  1280.     mov    dx,offset line        ; where to store it
  1281.     mov    byte ptr line,0        ; terminate line in case no text
  1282.     mov    bx,offset ptshlp    ; time help msg
  1283.     call    comnd
  1284.     jnc    swait1a            ; nc = success
  1285.     ret
  1286. swait1a:mov    wtemp,0            ; clear modem status test byte
  1287.     mov    si,offset line        ; source pointer
  1288.     call    inptim            ; parse pause time (or force default)
  1289.     jnc    swait1b            ; nc = good time value
  1290.     ret
  1291. swait1b:mov    cx,ax            ; returned byte count
  1292.     cmp    si,offset line        ; parsed a time?
  1293.     je    swait1c            ; e = no, reexamine line below
  1294.  
  1295. swait1:    mov    ah,cmword        ; get optional modem signal word(s)
  1296.     mov    dx,offset line
  1297.     mov    bx,offset wthlp        ; modem signal help
  1298.     call    comnd
  1299.     jnc    swait1c            ; nc = success
  1300.     ret
  1301. swait1c:mov    si,offset line
  1302.     mov    cx,ax            ; returned byte count
  1303. swait1d:or    cx,cx            ; number of chars to examine
  1304.     jle    swait4            ; le = none
  1305.     cld
  1306.     lodsb                ; get a character
  1307.     dec    cx            ; reduce count remaining
  1308.     cmp    al,'\'            ; backslash signal introducer?
  1309.     je    swait1d            ; e = yes, skip it
  1310.     cmp    cx,1            ; at least two chars in signal?
  1311.     jl    swait3            ; l = no
  1312.     mov    ax,[si-1]        ; get first two characters
  1313.     or    ax,2020h        ; upper case to lower, two chars
  1314.     cmp    ax,'dc'            ; carrier detect?
  1315.     jne    swait2            ; ne = no, try next signal
  1316.     or    wtemp,modcd        ; look for the CD bit
  1317.     inc    si            ; skip this field and separator
  1318.     dec    cx            ; two less chars left in the line
  1319.     jmp    short swait1d        ; continue the scan
  1320. swait2:    cmp    ax,'sd'            ; data set ready?
  1321.     jne    swait3            ; ne = no
  1322.     mov    al,[si+1]        ; third letter
  1323.     or    al,20h            ; to lower case
  1324.     cmp    al,'r'            ; r for dsr?
  1325.     jne    swait3            ; ne = no
  1326.     or    wtemp,moddsr        ; look for the DSR bit
  1327.     add    si,2            ; skip this field and separator
  1328.     sub    cx,2            ; three less chars left in the line
  1329. swait3:    cmp    ax,'tc'            ; clear to send?
  1330.     jne    swait3a            ; ne = no
  1331.     mov    al,[si+1]        ; third letter
  1332.     or    al,20h            ; to lower case
  1333.     cmp    al,'s'            ; r for dsr?
  1334.     jne    swait3a            ; ne = no
  1335.     or    wtemp,modcts        ; look for the CTS bit
  1336.     add    si,2            ; skip this field and separator
  1337.     sub    cx,2            ; three less chars left in the line
  1338. swait3a:jmp    short swait1b        ; continue the scan
  1339.                     ; SWAIT4 is used by PAUSE command
  1340. SWAIT4:    mov    ah,cmeol        ; get command confirmation
  1341.     call    comnd
  1342.     jnc    swait4a
  1343.     ret                ; c set is failure
  1344. swait4a:cmp    taklev,0        ; are we in a Take file
  1345.     je    swait5            ; e = no, print linefeed
  1346.     cmp    flags.takflg,0        ; are commands being echoed
  1347.     je    swait6            ; e = no, skip this
  1348. swait5:    cmp    script.inecho,0        ; Input echoing off?
  1349.     je    swait6            ; e = yes
  1350.     mov    al,lf            ; next line
  1351.     call    scdisp            ; display the char
  1352. swait6: call    serini            ; initialize the system's serial port
  1353.     jc    swait9            ; c = failure
  1354.     mov    status,stat_unk        ; clear status flag
  1355.     push    si
  1356.     mov    parmsk,0ffh          ; parity mask, assume 8 bit data
  1357.     mov    si,portval
  1358.     cmp    [si].parflg,parnon    ; parity is none?
  1359.     pop    si
  1360.     je    swait7            ; e = none
  1361.     mov    parmsk,07fh        ; else strip parity (8th) bit
  1362. swait7:    cmp    wtemp,0            ; anything to be tested?
  1363.     je    swait7a            ; e = no, just do the wait part
  1364.     call    getmodem        ; modem handshake status to AL
  1365.     and    al,wtemp        ; keep only bits to be tested
  1366.     cmp    al,wtemp        ; check selected status bits
  1367.     jne    swait7a            ; ne = not all selected bits match    
  1368.     clc                ; all match. take successful exit
  1369.     ret
  1370. swait7a:call    chkport            ; get and show any new port char
  1371.     call    chkkbd            ; check keyboard
  1372.     test    status,stat_cc        ; control-c?
  1373.     jnz    swait9            ; nz = yes, quit    
  1374.     call    chktmo            ; check tod for timeout
  1375.     test    status,stat_tmo+stat_ok    ; timeout or user override?
  1376.     jz    swait7            ; z = no, continue to wait
  1377.     cmp    wtemp,0            ; were we waiting on anything?
  1378.     jne    swait9            ; ne = yes, timeout = failure
  1379.     clc                ;  else timeout = success
  1380.     ret
  1381. swait9:    or    errlev,ksuser        ; set user intervention error condx
  1382.     or    fsta.xstatus,ksuser    ; set status
  1383.     or    kstatus,ksuser
  1384.     jmp    squit            ; take error exit
  1385. SCWAIT    ENDP
  1386.  
  1387.  
  1388. ; Output line of text to port, detect \b and \B as commands to send a Break
  1389. ;  and \l and \L as a Long Break on the serial port line.
  1390. ; Output text, display up to 100 received chars while doing so.
  1391.      
  1392. SCOUT    PROC    NEAR
  1393.     mov    kstatus,kssuc
  1394.     mov    ah,cmline        ; get a whole line of asciiz text
  1395.     mov    bx,offset line        ; store text here
  1396.     mov    dx,offset outhlp    ; help message
  1397.     call    comnd
  1398.     jnc    outp0d            ; nc = success
  1399.     ret                ; failure
  1400. outp0d:    cmp    taklev,0        ; is this being done in a Take file?
  1401.     je    outpu0            ; e = no, display linefeed
  1402.     cmp    flags.takflg,0        ; are commands being echoed?
  1403.     je    outp0a            ; e = no, skip the display
  1404. outpu0:    cmp    script.inecho,0        ; Input echoing off?
  1405.     je    outp0a            ; e = yes
  1406.     mov    al,lf            ; next line
  1407.     call    scdisp            ; display the char
  1408. outp0a:    mov    al,spause        ; wait three millisec or more
  1409.     add    al,3
  1410.     xor    ah,ah
  1411.     call    pcwait            ; breathing space for HDX systems
  1412.     call    serini            ; initialize the system's serial port
  1413.     jnc    outp0c            ; nc = success
  1414.     or    errlev,kssend        ; set SEND failure condition
  1415.     or    fsta.xstatus,kssend    ; set status
  1416.     or    kstatus,kssend
  1417.     jmp    squit
  1418.  
  1419. outp0c:    mov    status,stat_unk        ; clear status flag
  1420.     mov    parmsk,0ffh          ; parity mask, assume 8 bit data
  1421.     mov    si,portval
  1422.     cmp    [si].parflg,parnon    ; parity is none?
  1423.     je    outp0b            ; e = none
  1424.     mov    parmsk,07fh        ; else strip parity (8th) bit
  1425. outp0b:    mov    si,portval        ; serial port structure
  1426.     mov    bl,[si].ecoflg        ; Get the local echo flag
  1427.     mov    lecho,bl        ; our copy
  1428.     mov    si,offset line        ; get start of line
  1429.     mov    di,offset line        ; put results in the same place
  1430.     mov    ah,script.incasv    ; save current case state
  1431.     push    ax
  1432.     mov    script.incasv,0ffh    ; say no case conversion
  1433.     call    cnvlin            ; convert \numbers to binary
  1434.     pop    ax
  1435.     mov    script.incasv,ah    ; recover case state
  1436.     jnc    outpu1            ; nc = no error
  1437.     ret                ; return on error
  1438. outpu1:    mov    temptr,offset line    ; save pointer here
  1439.     mov    tempd,cx        ; save byte count here
  1440.     mov    ttyact,1        ; say interactive style output
  1441.  
  1442. outpu2:    cmp    tempd,0            ; are we done?
  1443.     jg    outpu2a            ; g = not done yet
  1444.     mov    ttyact,0        ; reset interactive output flag
  1445.     clc                ; return success
  1446.     ret
  1447. outpu2a:mov    si,temptr        ; recover pointer
  1448.     cld
  1449.     lodsb                ; get the character
  1450.     dec    tempd            ; one less char to send
  1451.     mov    temptr,si        ; save position on line
  1452.     mov    tempa,al        ; save char here for outchr
  1453.     mov    retry,0            ; number of output retries
  1454.     cmp    al,5ch            ; backslash?
  1455.     jne    outpu4d            ; ne = no
  1456.     mov    al,[si]
  1457.     and    al,not 20h        ; to upper case
  1458.     cmp    al,'B'            ; "\B" for BREAK?
  1459.     jne    outpu4l            ; ne = no
  1460. outpu4c:inc    temptr            ; move scan ptr beyond "\b"
  1461.     dec    tempd
  1462.     call    sendbr            ; call msx send-a-break procedure
  1463.     jmp    short outpu5        ; resume beyond echoing
  1464. outpu4l:cmp    al,'L'            ; "\L" for Long BREAK?
  1465.     jne    outpu4d            ; ne = no
  1466.     inc    temptr
  1467.     dec    tempd
  1468.     call    sendbl            ; send a Long BREAK
  1469.     jmp    short outpu5        ; resume beyond echoing
  1470.  
  1471. outpu4d:inc    retry            ; count output attempts
  1472.     cmp    retry,maxtry        ; too many retries?
  1473.     jle    outpu4g            ; le = no
  1474.     or    errlev,kssend        ; set SEND failure condition
  1475.     or    fsta.xstatus,kssend    ; set status
  1476.     or    kstatus,kssend
  1477.     jmp    squit            ; return failure
  1478. outpu4g:mov    ah,tempa        ; outchr gets fed from ah
  1479.     call    outchr            ; send the character to the port
  1480.     jc    outpu4d            ; failure to send char
  1481.     cmp    lecho,0            ; is Local echo active?
  1482.     je    outpu5            ; e = no
  1483.     mov    al,tempa        ;
  1484.     test    flags.capflg,logses    ; is capturing active?
  1485.     jz    outp4b            ; z = no
  1486.     push    ax            ; save char
  1487.     call    cptchr            ; give it captured character
  1488.     pop    ax            ; restore character and keep going
  1489. outp4b:    cmp    script.inecho,0        ; Input echo off?
  1490.     je    outpu5            ; e = yes
  1491.     call    scdisp            ; echo character to the screen
  1492.                     ;
  1493. outpu5:    mov    tempa,100+1        ; wait for max 100 chars in/out [dan]
  1494. outpu5a:mov    cx,10            ; reset retry counter
  1495. outpu5b:push    cx
  1496.     call    chkkbd            ; check keyboard for interruption
  1497.     pop    cx
  1498.     test    status,stat_cc        ; control c interrupt?
  1499.     jnz    outpu6            ; nz = yes, quit now
  1500.     cmp    script.inecho,0        ; Input echo off?
  1501.     je    outpu5c            ; e = yes, skip port reading/display
  1502.     dec    tempa            ; reached maximum chars in yet? [dan]
  1503.     jz    outpu5c            ; z = yes, send character anyway [dan]
  1504.     push    cx
  1505.     call    chkport            ; check for char at serial port
  1506.     pop    cx
  1507.     test    status,stat_ok        ;   and put any in buffer
  1508.     jnz    outpu5a            ; nz = have a char, look for another
  1509.     mov    ax,1            ; wait 1 millisec between rereads
  1510.     push    cx            ; protect counter
  1511.     call    pcwait
  1512.     pop    cx
  1513.     dec    cx            ; count down retries
  1514.     jge    outpu5b            ; ge = keep trying
  1515. outpu5c:jmp    outpu2            ; no more input, resume command
  1516. outpu6:    or    errlev,kssend        ; set SEND failure condition
  1517.     or    fsta.xstatus,kssend    ; set status
  1518.     or    kstatus,kssend
  1519.     mov    ttyact,0        ; reset interactive output flag
  1520.     jmp    squit            ; quit on control c
  1521. SCOUT    ENDP
  1522.  
  1523.      
  1524. ; Raw file transfer to host (strips linefeeds)
  1525. ; Transmit filespec [prompt]
  1526. ; Optional prompt is the single char expected from the host to ACK each line.
  1527. ; Default prompt is a script.xmitpmt (linefeed) or a carriage return from us.
  1528. ;     
  1529. SCXMIT    PROC    NEAR
  1530.     mov    kstatus,kssuc
  1531.     mov    ah,cmword        ; get a filename, asciiz
  1532.     mov    dx,offset line        ; where to store it
  1533.     mov    bx,offset xmthlp    ; help message
  1534.     call    comnd
  1535.     jnc    xmit0c            ; nc = success
  1536.     ret                ; failure
  1537. xmit0c:    mov    ah,cmword        ; get a prompt string, asciiz
  1538.     mov    dx,offset line+81    ; where to keep it (end of "line")
  1539.     mov    bx,offset pmthlp    ; Help in case user types "?".
  1540.     call    comnd
  1541.     jnc    xmit0d            ; nc = success
  1542.     ret                ; failure
  1543. xmit0d:    mov    line+80,al        ; length of user's string
  1544.     mov    ah,cmeol        ; confirm
  1545.     call    comnd
  1546.     jnc    xmit0e
  1547.     ret
  1548. xmit0e:    cmp    line,0            ; filename given?
  1549.     je    xmit0a            ; e = no
  1550.     mov    si,offset line+81    ; convert possible numeric prompt
  1551.     cmp    byte ptr [si-1],0    ; anything given?
  1552.     jz    xmit0            ; z = no, use default
  1553.     call    katoi            ; convert number to binary, if number
  1554.     jnc    xmit0b            ; nc = got number
  1555.     mov    al,line+81        ; get ascii char from user's prompt
  1556. xmit0b:    mov    script.xmitpmt,al    ; set prompt
  1557. xmit0:    mov    dx,offset line        ; point to filename
  1558.     mov    ah,open2        ; DOS 2 open file
  1559.     xor    al,al            ; open for reading
  1560.     int    dos
  1561.     mov    fhandle,ax        ; store file handle here
  1562.     mov    temp,0            ; counts chars/line
  1563.     jnc    xmit1            ; nc = successful opening
  1564.  
  1565. xmit0a:    mov    ah,prstr        ; give file not found error message
  1566.     mov    dx,offset xfrfnf
  1567.     int    dos
  1568.     or    errlev,kssend        ; set SEND failure condition
  1569.     or    fsta.xstatus,kssend    ; set status
  1570.     or    kstatus,kssend
  1571.     jmp    squit            ; exit failure
  1572.  
  1573. xmitx:    mov    ah,prstr        ; error during transfer
  1574.     mov    dx,offset xfrrer
  1575.     int    dos
  1576. xmitx2:    mov    bx,fhandle        ; file handle
  1577.     mov    ah,close2        ; close file
  1578.     int    dos
  1579.     call    bufclear        ; clear script buffer
  1580.     call    clrbuf            ; clear local serial port buffer
  1581.     or    errlev,kssend        ; set SEND failure condition
  1582.     or    fsta.xstatus,kssend    ; set status
  1583.     or    kstatus,kssend
  1584.     jmp    squit            ; exit failure
  1585.                     ;
  1586. xmity:    mov    bx,fhandle        ; file handle
  1587.     mov    ah,close2        ; close file
  1588.     int    dos
  1589.     call    bufclear        ; clear buffers
  1590.     call    clrbuf
  1591.     clc                ; and return success
  1592.     ret
  1593. xmit1:    call    serini            ; initialize serial port
  1594.     jnc    xmit1b            ; nc = success
  1595.     or    errlev,kssend        ; set SEND failure condition
  1596.     or    fsta.xstatus,kssend    ; set status
  1597.     or    kstatus,kssend
  1598.     jmp    squit
  1599.  
  1600. xmit1b:    call    bufclear        ; clear script input buffer
  1601.     call    clrbuf            ; clear serial port buffer
  1602.     mov    status,stat_unk        ; clear status flag
  1603.     mov    parmsk,0ffh          ; parity mask, assume 8 bit data
  1604.     mov    si,portval
  1605.     cmp    [si].parflg,parnon    ; parity is none?
  1606.     je    xmit1a            ; e = none
  1607.     mov    parmsk,07fh        ; else strip parity (8th) bit
  1608. xmit1a:    mov    bl,[si].ecoflg        ; get the local echo flag
  1609.     mov    lecho,bl        ; our copy
  1610.     mov    dx,offset crlf        ; display cr/lf
  1611.     mov    ah,prstr
  1612.     int    dos
  1613.  
  1614. xmit2:    mov    dx,offset line        ; buffer to read into
  1615.     mov    cx,linelen        ; # of bytes to read
  1616.     mov    ah,readf2        ; read bytes from file
  1617.     mov    bx,fhandle        ; file handle is stored here
  1618.     int    dos
  1619.     jnc    xmit2a            ; nc = success
  1620.     jmp    xmitx            ; exit failure
  1621. xmit2a:    mov    cx,ax            ; number of bytes read
  1622.     jcxz    xmity            ; z = none, end of file
  1623.     mov    si,offset line        ; buffer for file reads
  1624.     cld
  1625. xmit3:    lodsb                ; get a byte
  1626.     cmp    al,ctlz            ; is this a Control-Z?
  1627.     jne    xmit3a            ; ne = no
  1628.     cmp    flags.eofcz,0        ; ignore Control-Z as EOF?
  1629.     je    xmit3a            ; e = yes
  1630.     jmp    xmity            ; ne = no, we are at EOF
  1631. xmit3a:    push    si            ; save position on line
  1632.     push    cx            ; and byte count
  1633.     cmp    al,cr            ; CR, end of line?
  1634.     jne    xmit3b            ; ne = no
  1635.     cmp    temp,0            ; chars sent in this line, any?
  1636.     ja    xmit3c            ; a = sent some
  1637.     cmp    script.xmitfill,0    ; fill empty lines?
  1638.     je    xmit3c            ; e = no, send the cr
  1639.     mov    al,script.xmitfill    ; empty line fill char
  1640.     pop    cx
  1641.     pop    si
  1642.     dec    si            ; backup read pointer to CR again
  1643.     inc    cx            ; count filler as line information
  1644.     push    si
  1645.     push    cx
  1646.     jmp    short xmit3c
  1647. xmit3b:    cmp    al,lf            ; line feed?
  1648.     jne    xmit3c            ; ne = no
  1649.     cmp    script.xmitlf,0        ; send LF's?
  1650.     je    xmit7            ; e = no, don't send it
  1651.     mov    temp,-1            ; -1 so inc returns 0 after send
  1652. xmit3c:    push    ax            ; save char around outchr call
  1653.     mov    retry,0            ; clear retry counter
  1654. xmit4f:    pop    ax            ; recover saved char
  1655.     push    ax            ; and save it again
  1656.     mov    ah,al            ; outchr wants char in ah
  1657.     inc    retry            ; count number of attempts
  1658.     cmp    retry,maxtry        ; too many retries?
  1659.     jle    xmit4g            ; le = no
  1660.     or    status,stat_cc        ; simulate control-c abort
  1661.     pop    ax            ; clean stack
  1662.     xor    al,al            ; clear char
  1663.     jmp    short xmita        ; and abort transfer
  1664. xmit4g:    call    outchr            ; send the character to the port
  1665.     inc    temp            ; count chars sent in this line
  1666.     jc    xmit4f            ; c failed, try again
  1667. xmit4h:    pop    ax            ; recover saved char
  1668.     cmp    lecho,0            ; is local echoing active?
  1669.     je    xmit5            ; e = no
  1670.     test    flags.capflg,logses    ; capturing active?
  1671.     jz    xmit4a            ; z = no
  1672.     call    cptchr            ; give it the character just sent
  1673. xmit4a:    call    scdisp            ; display char on screen
  1674.  
  1675. xmit5:    cmp    al,cr            ; did we send a carriage return?
  1676.     je    xmit8            ; e = yes, time to check keyboard
  1677.  
  1678. xmit7:    pop    cx
  1679.     pop    si
  1680.     dec    cx
  1681.     or    cx,cx
  1682.     jle    xmit7a            ; le = finished this line
  1683.     jmp    xmit3            ; finish this buffer full
  1684. xmit7a:    jmp    xmit2            ; read next buffer
  1685.  
  1686. xmit8:    test    status,stat_cc        ; Control-C seen?
  1687.     jnz    xmita            ; nz = yes
  1688.     mov    temp,0            ; say starting new char/line count
  1689.     call    chkkbd            ; check keyboard (returns char in al)
  1690.     test    status,stat_ok        ; have a char?
  1691.     jnz    xmita            ; nz = yes
  1692.     cmp    script.xmitpmt,0    ; is prompt char a null?
  1693.     jne    xmit8b            ; ne = no
  1694.     call    bufread            ; check for char from serial port buf
  1695.     jnc    xmit8            ; nc = a char, read til none
  1696.     jmp    short xmit8c        ; continue transfer
  1697. xmit8b:    call    bufread            ; check for char from serial port buf
  1698.     jc    xmit8            ; c = none
  1699.     cmp    al,script.xmitpmt    ; is port char the ack?
  1700.     jne    xmit8            ; ne = no, just ignore the char
  1701. xmit8c:    mov    ax,script.xmitpause    ; get millisecs to pause
  1702.     or    ax,ax            ; any time?
  1703.     jz    xmit7            ; z = none
  1704.     call    pcwait            ; wait this long
  1705.     jmp    short xmit7        ; yes, continue transfer
  1706.  
  1707. xmita:    test    status,stat_cc        ; Control-C?
  1708.     jnz    xmitc            ; nz = yes
  1709.     test    status,stat_cr        ; a local ack?
  1710.     jz    xmit8            ; z = no, ignore local char
  1711.     mov    dx,offset crlf        ; display cr/lf
  1712.     mov    ah,prstr
  1713.     int    dos
  1714.     jmp    xmit8c            ; continue transfer
  1715. xmitc:    pop    cx            ; Control-C, clear stack
  1716.     pop    si            ; ...
  1717.     mov    dx,offset xfrcan    ; say canceling transfer
  1718.     mov    ah,prstr
  1719.     int    dos
  1720.     mov    flags.cxzflg,0        ; clear Control-C flag
  1721.     jmp    xmitx2            ; ctrl-c, quit
  1722.  
  1723. SCXMIT    ENDP
  1724.  
  1725. ;;;;;;;;;;;;;;;;;; local support procedures ;;;;;;;;;;
  1726. ;
  1727. ;worker: copy line from si to di, converting \nnn strings to single chars
  1728. ; returns carry set if error, else carry clear. Detects leading at-sign
  1729. ; as an indicator to read command file for one line of text; command files
  1730. ; may be nested to a depth of 100.
  1731. ; Items of the form \chars which are not numbers are copied verbatium
  1732. ; to the output string (ex: \a  is copied as \a). The string is first trimmed
  1733. ; of trailing spaces, then the possible curly brace delimiter pair is
  1734. ; removed, and finally \numbers are converted to binary. [jrd]
  1735. cnvlin    proc    near
  1736.     push    tempd
  1737.     push    es
  1738.     push    si            ; source ptr
  1739.     push    di            ; destination ptr
  1740.     push    ds
  1741.     pop    es            ; use data segment for es:di
  1742.     mov    tempd,0            ; count indirection depth
  1743. cnvln0:    cmp    tempd,100        ; limit to 100 deep
  1744.     jbe    cnvln0a            ; be = not too deep yet
  1745.     jmp    cnvln8            ; too deep, quit
  1746. cnvln0a:cld
  1747.     xor    cx,cx            ; initialize returned byte count
  1748.     lodsb                ; get the first character
  1749.     cmp    al,40h            ; at-sign indirection?
  1750.     je    cnvln5            ; e = yes, open the file
  1751.     dec    si            ; no, push back char just read
  1752.     call    cnvstr            ; convert string's curly braces
  1753. cnvln1:    xor    ah,ah            ; clear high byte of number
  1754.     call    katoi            ; get a char into al, convert number
  1755.     jnc    cnvln4            ; nc = binary number converted
  1756.     cmp    al,0ffh            ; cr/lf wild card?
  1757.     je    cnvln4            ; e = yes, store it
  1758.     or    al,al            ; end of line?
  1759.     jnz    cnvln3            ; nz = no
  1760.     jmp    cnvlnx            ; yes, exit now
  1761. cnvln3:    cmp    al,'a'            ; candidate for conversion? [js]
  1762.     jb    cnvln4            ; b = no
  1763.     cmp    al,'z'            ; still in lower case set? [js]
  1764.     ja    cnvln4            ; a = no
  1765.     and    al,script.incasv    ; else apply case conversion mask
  1766. cnvln4: stosb                ; save the char
  1767.     inc    cx            ; and count it
  1768.     or    ah,ah            ; was number larger than one byte?
  1769.     jz    cnvln1            ; z = no
  1770.     xchg    ah,al            ; put high byte into al
  1771.     stosb                ; store it too
  1772.     inc    cx            ; count storage
  1773.     jmp    short cnvln1        ; read more
  1774.  
  1775. cnvln5:    mov    dx,si            ; get filename ptr from source line
  1776.     push    si
  1777.     inc    tempd            ; count indirection depth
  1778.     mov    cx,64            ; max length of a filename.
  1779. cnvln5a:cmp    byte ptr [si],' '    ; whitespace or control code?
  1780.     jbe    cnvln5b            ; be = yes, found termination
  1781.     inc    si            ; else look at next char
  1782.     loop    cnvln5a            ; limit search
  1783. cnvln5b:mov    byte ptr [si],0        ; make asciiz
  1784.     pop    si    
  1785.     mov    ah,open2        ; DOS 2 open file
  1786.     xor    al,al            ; open for reading
  1787.     int    dos
  1788.     mov    word ptr fhandle,ax    ; store file handle
  1789.     jnc    cnvln7            ; nc = open ok, read from file
  1790.  
  1791.     mov    ah,prstr
  1792.     mov    dx,offset indmis    ; file open error msg
  1793.     int    dos
  1794.     xor    cx,cx            ; say zero bytes read
  1795.     pop    di            ; destination ptr
  1796.     pop    si            ; source ptr
  1797.     pop    es
  1798.     pop    tempd
  1799.     stc                ; set c bit, failure
  1800.     ret
  1801.  
  1802. cnvln7:    mov    bx,word ptr fhandle    ; file handle
  1803.     mov    cx,linelen        ; # of bytes to read
  1804.     mov    ah,ioctl        ; ioctl, is this the console device?
  1805.     xor    al,al            ; get device info
  1806.     int    dos
  1807.     and    dl,81h            ; ISDEV and ISCIN bits needed together
  1808.     cmp    dl,81h            ; Console input device?
  1809.     jne    cnvln7d            ; ne = no, use regular file i/o
  1810.     push    ds
  1811.     pop    es            ; set es:di to data segment
  1812.     push    di            ; save starting pointer
  1813. cnvln7b:mov    ah,coninq        ; read console, no echo
  1814.     int    dos
  1815.     stosb
  1816.     cmp    al,cr            ; end of the line yet?
  1817.     loopne    cnvln7b            ; keep reading
  1818.     mov    byte ptr [di],0        ; insert terminator
  1819.     pop    di            ; recover starting pointer
  1820.     mov    dx,di            ; simulate read file read
  1821.     mov    ax,linelen
  1822.     sub    ax,cx            ; ax = number of chars read
  1823.     jmp    short cnvln7e        ; close file, finish processing
  1824.  
  1825. cnvln7d:mov    dx,di            ; destination ptr
  1826.     mov    byte ptr [di],0        ; insert null terminator, clears line
  1827.     mov    ah,readf2        ; DOS 2 read from file
  1828.     int    dos
  1829. cnvln7e:pushf                ; save flags
  1830.     push    ax            ; save byte count read
  1831.     mov    ah,close2        ; close file (wanted just one line)
  1832.     int    dos
  1833.     pop    ax
  1834.     popf                ; recover flags now
  1835.     jc    cnvln8            ; c = error
  1836.     mov    cx,ax            ; ax = number of bytes read
  1837.     jcxz    cnvln8a            ; cx = z = no bytes read
  1838.     mov    al,cr            ; look for cr as terminator
  1839.     cld
  1840.     repne    scasb            ; scan while not a cr and cx not zero
  1841.     jne    cnvln7a            ; ne = no cr found
  1842.     dec    di            ; point at cr
  1843. cnvln7a:mov    byte ptr [di],0        ; plant terminator on the cr
  1844.                     ;  or after last read char, if no cr.
  1845.     pop    di            ; get original destination ptr
  1846.     push    di            ; and save it again
  1847.     mov    si,dx            ; new source = this line
  1848.                     ; go convert text, as necessary, and
  1849.     jmp    cnvln0            ;  allow nested indirection
  1850.  
  1851. cnvln8: mov    ah,prstr
  1852.     mov    dx,offset inderr    ; error reading file message
  1853.     int    dos
  1854. cnvln8a:xor    cx,cx            ; say zero bytes read
  1855.     pop    di
  1856.     pop    si
  1857.     pop    es
  1858.     pop    tempd
  1859.     stc                ; set carry for failure
  1860.     ret                ; and do a real return
  1861.  
  1862. cnvlnx:    pop    di            ; destination ptr
  1863.     pop    si            ; source ptr
  1864.     pop    es
  1865.     pop    tempd
  1866.     clc                ; clear c bit, success
  1867.     ret
  1868. cnvlin    endp
  1869. ;
  1870. ; worker: read the number of seconds to pause or timeout
  1871. ;    returns time of day for timeout in timhms, and next non-space or
  1872. ;    non-tab source char ptr in si. Time is either elapsed seconds or
  1873. ;    a specific hh:mm:ss, determined from context of colons being present.
  1874. ;    Last form can be abbreviated as hh:[mm[:ss]]. Returns carry set if
  1875. ;    hh:mm:ss form has bad construction (invalid time).
  1876. inptim    proc    near
  1877.     push    ax
  1878.     push    bx
  1879.     push    cx
  1880.     push    dx
  1881.     push    di
  1882.     cld                ; decode pure seconds construction
  1883.     mov    di,si            ; remember source pointer
  1884.     mov    cx,10            ; multiplier
  1885.     mov    bx,script.indfto    ; no numbers yet, use default-timeout
  1886.     mov    al,byte ptr[si]
  1887.     cmp    al,':'            ; stray hh:mm:ss separator?
  1888.     je    inptm8            ; e = yes
  1889.     cmp    al,'9'            ; start with numeric input?
  1890.     ja    inptm4            ; a = no, use default time
  1891.     cmp    al,'0'            ; ditto
  1892.     jb    inptm4
  1893.     xor    ah,ah            ; source char holder
  1894.     xor    bx,bx            ; accumulated sum
  1895. inptm1:    mov    al,byte ptr[si]        ; get a byte into al
  1896.     cmp    al,':'            ; hh:mm:ss construction?
  1897.     je    inptm8            ; e = yes
  1898.     sub    al,'0'            ; remove ascii bias
  1899.     cmp    al,9            ; numeric?
  1900.     ja    inptm4            ; a = non-numeric, exit loop, bx = sum
  1901.     xchg    ax,bx            ; put sum into ax, char in bl
  1902.     mul    cx            ; sum times ten 
  1903.     xchg    ax,bx            ; put char into al, sum in bx
  1904.     add    bx,ax            ; add to sum
  1905.     inc    si            ; next char
  1906.     jmp    short inptm1        ; loop thru all chars
  1907.  
  1908. inptm4:    cmp    bx,12*60*60        ; half a day, in seconds
  1909.     jb    inptm5            ; b = less than
  1910.     jmp    inptm13            ; more than, error
  1911. inptm5:    push    si            ; save ending scan position for return
  1912.     mov    timout,bx        ; # seconds of timeout desired
  1913.     mov    ah,gettim        ; read DOS tod clock
  1914.     int    dos
  1915.     mov    timhms[0],ch        ; hours
  1916.     mov    timhms[1],cl        ; minutes
  1917.     mov    timhms[2],dh        ; seconds
  1918.     mov    timhms[3],dl        ; hundredths of seconds
  1919.     mov    bx,2            ; start with seconds field
  1920. inptm6: mov    ax,timout        ; our desired timeout interval
  1921.     add    al,timhms[bx]        ; add current tod digit to interval
  1922.     adc    ah,0
  1923.     xor    dx,dx            ; clear high order part thereof
  1924.     mov    cx,60            ; divide by 60
  1925.     div    cx            ; compute number of minutes or hours
  1926.     mov    timout,ax        ; quotient
  1927.     mov    timhms[bx],dl        ; put remainder in timeout tod digit
  1928.     dec    bx            ; look at next higher order time field
  1929.     or    bx,bx            ; done all time fields?
  1930.     jge    inptm6            ; ge = no
  1931.     cmp    timhms[0],24        ; normalize hours
  1932.     jl    inptm7            ; l = not 24 hours
  1933.     sub    timhms[0],24        ; discard part over 24 hours
  1934. inptm7:    pop    si            ; return ptr to next source char
  1935.     jmp    short inptm11        ; trim trailing whitespace
  1936.  
  1937. inptm8:                    ; decode hh:[mm[:ss]] to timhms
  1938.     mov    si,di            ; recall starting source pointer
  1939.     mov    word ptr timhms[0],0    ; clear time out tod
  1940.     mov    word ptr timhms[2],0
  1941.     xor    bx,bx            ; three groups possible
  1942. inptm9:    mov    dl,byte ptr[si]        ; get a char
  1943.     cmp    dl,':'            ; field separator?
  1944.     je    inptm10            ; e = a separator, step fields
  1945.     sub    dl,'0'            ; remove ascii bias
  1946.     cmp    dl,9
  1947.     ja    short inptm11        ; a = failure to get expected digit
  1948.     mov    al,timhms[bx]        ; get sum to al
  1949.     mov    ah,10
  1950.     mul    ah            ; sum times ten
  1951.     add    al,dl            ; sum = 10 * previous + current
  1952.     mov    timhms[bx],al        ; current sum
  1953.     cmp    timhms[bx],60        ; more than legal?
  1954.     jae    inptm13            ; ae = illegal
  1955.     or    bx,bx            ; doing hours?
  1956.     jnz    inptm9a            ; nz = no, min or sec
  1957.     cmp    timhms[bx],24        ; more than legal?
  1958.     jae    inptm13            ; ae = illegal
  1959. inptm9a:inc    si            ; next char
  1960.     jmp    short inptm9        ; continue analysis
  1961. inptm10:inc    bx            ; point to next field
  1962.     inc    si            ; next char
  1963.     cmp    bx,2            ; last subscript to use (secs)
  1964.     jbe    inptm9            ; be = get more text
  1965.  
  1966. inptm11:cmp    byte ptr [si],spc    ; examine break char, remove spaces
  1967.     jne    inptm12            ; ne = no, stay at this char
  1968.     inc    si            ; look at next char
  1969.     jmp    short inptm11        ; continue scanning off white space
  1970. inptm12:clc                ; carry clear for success    
  1971.     jnc    inptm14
  1972. inptm13:stc                ; carry set for illegal value    
  1973. inptm14:pop    di            ; return with si beyond our text
  1974.     pop    dx
  1975.     pop    cx
  1976.     pop    bx
  1977.     pop    ax
  1978.     ret
  1979. inptim    endp
  1980.  
  1981. ; worker: display the char in al on screen
  1982. ; use caret-char notation for control codes
  1983. scdisp    proc    near
  1984.     push    dx
  1985.     push    ax
  1986.     mov    ah,conout        ; our desired function
  1987.     test    flags.remflg,d8bit    ; show all 8 bits?
  1988.     jnz    scdisp0            ; nz = yes
  1989.     and    al,7fh            ; apply 7 bit display mask
  1990. scdisp0:or    al,al            ; null?
  1991.     jz    scdis2            ; z = yes, ignore
  1992.     cmp    al,del            ; delete code?
  1993.     je    scdis2            ; e = yes, ignore
  1994.     cmp    al,spc            ; control char?
  1995.     jae    scdis1            ; ae = no, display as-is
  1996.     cmp    al,cr            ; carriage return?
  1997.     je    scdis1            ; e = yes, display as-is
  1998.     cmp    al,lf            ; line feed?
  1999.     je    scdis1
  2000.     cmp    al,tab            ; horizontal tab?
  2001.     je    scdis1
  2002.     cmp    al,bell            ; bell?
  2003.     je    scdis1
  2004.     cmp    al,bs            ; backspace?
  2005.     je    scdis1
  2006.     cmp    al,escape        ; escape?
  2007.     je    scdis1
  2008.     or    al,40h            ; control code to printable char
  2009.     push    ax
  2010.     mov    dl,5eh            ; display caret first
  2011.     int    dos
  2012.     pop    ax
  2013. scdis1:    mov    dl,al            ; the char to be displayed
  2014.     int    dos
  2015. scdis2:    pop    ax
  2016.     pop    dx
  2017.     ret
  2018. scdisp    endp
  2019.  
  2020. ; workers
  2021. ; Circular buffer for data from serial port. Written by Joe R. Doupnik
  2022. ; Entry points -
  2023. ;    bufread: read serial port for latest char (invokes bufwrite, sets
  2024. ;            status), get a char into al, return carry set if none.
  2025. ;    bufwrite: put a char from al into buf. If this overwrites an unread
  2026. ;            character then: we lose the old char, the read pointer
  2027. ;            is moved to the next oldest unread char, and the
  2028. ;            number of chars in the buffer is decreased by one.
  2029. ;    bufclear: empties the buffer.
  2030. ; The buffer is prtbuf, of size scpbuflen bytes. Internally, integer bufcnt
  2031. ; holds the number of buffer locations occupied, pointer bufrdptr is the
  2032. ; offset of the char to be read, pointer bufwtptr is the offset of the
  2033. ; place to store the next incoming char.
  2034. ;
  2035. bufclear proc    near
  2036.     xor    ax,ax            ; create a zero
  2037.     mov    bufcnt,ax        ; clear count of bytes in buffer
  2038.     mov    bufrdptr,ax        ; move read pointer to start of buf
  2039.     mov    bufwtptr,ax        ; move write pointer to start of buf
  2040.     mov    cx,scpbuflen
  2041.     push    es            ; physically clear the buffer
  2042.     push    di
  2043.     mov    ax,bufseg        ; segment of port buffer
  2044.     mov    es,ax
  2045.     xor    al,al            ; write scpbuflen nulls
  2046.     xor    di,di
  2047.     cld
  2048.     shr    cx,1            ; do an odd byte now
  2049.     jnc    bufcle1            ; nc = no odd byte
  2050.     stosb
  2051. bufcle1:rep    stosw            ; do double bytes (words)
  2052.     pop    di
  2053.     pop    es
  2054.     ret
  2055. bufclear endp
  2056.  
  2057. bufread    proc    near
  2058.     call    chkport            ; get any oldest char from port
  2059.     call    chktmo            ; check tod for timeout
  2060.     cmp    bufcnt,0        ; empty buffer?
  2061.     jne    bufrd1            ; ne = no
  2062.     stc                ; yes, set carry flag (no char)
  2063.     ret                ; and quit (chkport sets status)
  2064. bufrd1: push    si
  2065.     push    es
  2066.     mov    si,bufseg        ; get buffer segment
  2067.     mov    es,si
  2068.     mov    si,bufrdptr
  2069.     mov    al,es:[si]        ; extract a char into al
  2070.     inc    bufrdptr        ; move pointer to next byte
  2071.     dec    bufcnt            ; say have extracted a char
  2072.     mov    si,scpbuflen
  2073.     cmp    bufrdptr,si        ; beyond end?
  2074.     jb    bufrd2            ; b = not yet, just return
  2075.     mov    bufrdptr,0        ; reset to start of buf (wrapping)
  2076. bufrd2:    pop    es
  2077.     pop    si
  2078.     clc                ; clear carry flag (have read a char)
  2079.     ret                ; chkport sets status
  2080. bufread    endp
  2081.  
  2082. ; Non-destructive read of serial port circular buffer. Requires external
  2083. ; initialization of peek read pointer bufpkptr and count remaining bufpkcnt.
  2084. ; Returns character in register al.
  2085. peekbuf    proc    near
  2086.     cmp    bufpkcnt,0        ; peek counter, empty buffer?
  2087.     jne    peekbu2            ; ne = no, so look in buffer
  2088.     or    status,stat_tmo        ; force timeout status
  2089.     push    si
  2090.     mov    si,scpbuflen
  2091.     cmp    bufcnt,si        ; is real buffer full?
  2092.     pop    si
  2093.     jae    peekbu1            ; ae = yes, have examined everything
  2094.     call    chkport            ; get a char from port
  2095.     and    status,not stat_tmo    ; clear timeout
  2096.     call    chktmo            ; and check for timeout
  2097.     cmp    bufcnt,0        ; still nothing from port?
  2098.     je    peekbu1            ; e = no char, report fact
  2099.     inc    bufpkcnt        ; got one, increase peek counter
  2100.     jmp    short peekbu2        ; go extract it
  2101. peekbu1:stc                ; return nothing to see
  2102.     ret
  2103. peekbu2:push    si
  2104.     push    es
  2105.     mov    si,bufseg        ; segment of port buffer
  2106.     mov    es,si
  2107.     mov    si,bufpkptr        ; buffer peek pointer
  2108.     mov    al,es:[si]        ; extract a char into al
  2109.     inc    bufpkptr        ; move pointer to next byte
  2110.     dec    bufpkcnt        ; say have extracted a char
  2111.     mov    si,scpbuflen
  2112.     cmp    bufpkptr,si        ; beyond end?
  2113.     jb    peekbu3            ; b = not yet, just return
  2114.     mov    bufpkptr,0        ; reset to start of buf (wrapping)
  2115. peekbu3:pop    es
  2116.     pop    si
  2117.     clc                ; clear carry flag (have read a char)
  2118.     ret
  2119. peekbuf    endp
  2120.  
  2121. bufwrite proc    near
  2122.     push    si
  2123.     push    es
  2124.     mov    si,bufseg        ; segment of buffer
  2125.     mov    es,si
  2126.     mov    si,bufwtptr
  2127.     mov    es:[si],al        ; store char held in al
  2128.     inc    bufwtptr        ; move pointer to next byte
  2129.     mov    si,scpbuflen        ; length of buffer
  2130.     cmp    bufwtptr,si        ; beyond end?
  2131.     jb    bufwt1            ; b = not yet
  2132.     mov    bufwtptr,0        ; reset to start of buf (wrapping)
  2133. bufwt1:    inc    bufcnt            ; say have added a char to the buf
  2134.     cmp    bufcnt,si        ; more than we can hold?
  2135.     jbe    bufwt3            ; be = not overflowing
  2136.     push    bufwtptr        ; read ptr can't alias write ptr
  2137.     pop    bufrdptr        ; move up read pointer
  2138.     mov    bufcnt,si        ; limit count to max buffer length
  2139. bufwt3:    pop    es
  2140.     pop    si
  2141.     ret
  2142. bufwrite endp
  2143.  
  2144. ; Report buffer status for dumping buffer to a log file.
  2145. ; Yield ax, cx, es, si as indicated below.
  2146. buflog    proc    near
  2147.     mov    ax,bufcnt            ; number of unread chars
  2148.     mov    cx,scpbuflen            ; length of buffer
  2149.     mov    si,bufseg            ; segment of buffer
  2150.     mov    es,si
  2151.     mov    si,bufrdptr            ; where to read next char
  2152.     ret                    ; return these registers
  2153. buflog    endp
  2154.  
  2155. ; worker: check for timeout, return status=stat_tmo if timeout, else bit
  2156. ;  stat_tmo is cleared.
  2157. chktmo:    and    status,not stat_tmo
  2158.     mov    ah,gettim        ; get the time of day
  2159.     int    dos
  2160.     sub    ch,timhms[0]        ; hours difference, ch = (now-timeout)
  2161.     je    chktmo2            ; e = same, check mmss.s
  2162.     jg    chktmo1            ; g = past target hour
  2163.     add    ch,24            ; we are early, see by how much
  2164. chktmo1:cmp    ch,12            ; hours difference, large or small?
  2165.     jge    chktmox            ; ge = not that time yet
  2166.     jl    chktmo3            ; l = beyond that time
  2167. chktmo2:cmp    cl,timhms[1]        ; minutes, hours match
  2168.     jb    chktmox            ; b = early
  2169.     ja    chktmo3            ; a = late
  2170.     cmp    dh,timhms[2]        ; seconds, hhmm match
  2171.     jb    chktmox            ; b = early
  2172.     ja    chktmo3            ; a = late
  2173.     cmp    dl,timhms[3]        ; fractions, hhmmss match
  2174.     jb    chktmox            ; b = early
  2175. chktmo3:or    status,stat_tmo        ; say timeout
  2176.     stc
  2177.     ret
  2178. chktmox:clc
  2179.     ret
  2180. ;
  2181. ; worker: check keyboard for char. Return status = stat_cc if control-C typed,
  2182. ; stat_cr if carriage return, or stat_ok if any other char typed. Else return
  2183. ; with these status bits cleared.
  2184. chkkbd:    and    status,not (stat_ok+stat_cc+stat_cr) ; clear status bits
  2185.     xor    al,al
  2186.     cmp    flags.cxzflg,'C'    ; Control-C interrupt seen?
  2187.     je    chkkbd0            ; e = yes
  2188.     call    isdev            ; is stdin a device, not disk file?
  2189.     jnc    chkkbd2            ; nc = not device so do not read here
  2190.     mov    ah,dconio        ; keyboard char present?
  2191.     mov    dl,0ffH
  2192.     int    dos
  2193.     je    chkkbd1            ; e = none
  2194.     or    status,stat_ok        ; have a char, return it in al
  2195.     cmp    al,3            ; control c?
  2196.     jne    chkkbd1            ; ne = not control c
  2197. chkkbd0:or    status,stat_cc        ; say control c         
  2198. chkkbd1:cmp    al,cr            ; carriage return? [js]
  2199.     jne    chkkbd2            ; ne = no
  2200.     or    status,stat_cr        ; say carriage return [js]
  2201. chkkbd2:ret
  2202. ;
  2203. ; worker: check serial port for received char. Return status = stat_ok if
  2204. ;  char received, otherwise stat_ok cleared. Can echo char to screen. Will
  2205. ;  write char to local circular buffer.
  2206. chkport:and    status,not stat_ok    ; clear status bit
  2207.     call    prtchr            ; char at port (in al)?
  2208.     jnc    chkpor1            ; nc = yes, analyze it
  2209.     ret                ; no, return
  2210. chkpor1:and    al,parmsk        ; strip parity, if any
  2211.     cmp    rxtable+256,0        ; is translation turned off?
  2212.     je    chkpor0            ; e = yes, no translation
  2213.     push    bx            ; translate incoming character
  2214.     mov    bx,offset rxtable    ; the translation table
  2215.     xlatb
  2216.     pop    bx
  2217. chkpor0:test    flags.capflg,logses    ; capturing active?
  2218.     jz    chkpor3            ; z = no
  2219.     test    flags.remflg,d8bit    ; keep 8 bits for displays?
  2220.     jnz    chkpo0a            ; nz = yes, 8 bits if possible
  2221.     cmp    flags.debug,0        ; is debug mode active?
  2222.     jne    chkpo0a            ; ne = yes, record 8 bits
  2223.     and    al,7fh            ; remove high bit
  2224. chkpo0a:push    ax            ; save char
  2225.     call    cptchr            ; give it captured character
  2226.     pop    ax            ; restore character and keep going
  2227. chkpor3:test    flags.remflg,d8bit    ; keep 8 bits for displays?
  2228.     jnz    chkpo3a            ; nz = yes, 8 bits if possible
  2229.     and    al,7fh            ; remove high bit
  2230. chkpo3a:cmp    script.inecho,0        ; input echoing off?
  2231.     je    chkpor4            ; e = yes
  2232.     call    scdisp            ; display the char
  2233. chkpor4:call    bufwrite        ; put char in buffer
  2234.     or    status,stat_ok        ; say have a char (still in al)
  2235.     ret
  2236. ;
  2237. ; Squit is the script error exit pathway.
  2238. ;
  2239. squit:    cmp    flags.cxzflg,'C'    ; Control-C interrupt seen?
  2240.     je    squit5            ; e = yes
  2241.     test    status,stat_tmo        ; timeout?
  2242.     jz    squit2            ; z = no, another kind of failure
  2243.     cmp    taklev,0        ; in a Take/macro?
  2244.     jne    squit1            ; ne = yes, skip timeout message
  2245.     push    dx
  2246.     mov    dx,offset tmomsg    ; say timed out
  2247.     mov    ah,prstr
  2248.     int    dos            ; display it
  2249.     pop    dx
  2250. squit1:    cmp    script.inactv,0        ; action to do upon timeout
  2251.     je    squit4            ; 0 = proceed, ne = non-zero = quit
  2252. squit5:    call    takclos            ; close Take file or macro
  2253. squit2:    call    isdev            ; stdin is a device (vs file)?
  2254.     jc    squit3            ; c = device, not a file
  2255.     mov    flags.extflg,1        ; set Kermit exit flag
  2256. squit3:    cmp    flags.cxzflg,'C'    ; Control-C interrupt seen?
  2257.     jne    squit6            ; ne = no
  2258.     or    kstatus,ksuser        ; say user intervention
  2259. squit6:    stc
  2260.     ret                ; return failure
  2261. squit4:    clc                ; return success, ignore error
  2262.     ret
  2263. code    ends
  2264.     end
  2265.