home *** CD-ROM | disk | FTP | other *** search
/ ftp.barnyard.co.uk / 2015.02.ftp.barnyard.co.uk.tar / ftp.barnyard.co.uk / cpm / walnut-creek-CDROM / CPM / ZCPR33 / A-R / COMIF10.LBR / COMIF10.ZZ0 / COMIF10.Z80
Text File  |  2000-06-30  |  43KB  |  1,611 lines

  1. ; Program: COMIF
  2. ; Author: Jay Sage
  3. ; Version: 1.0
  4. ; Date: March 10, 1987
  5. ; Previous Versions:  Derived from IF (originated by Richard Conn)
  6.  
  7.  
  8. ;            * * *   IMPORTANT NOTE   * * *
  9. ;
  10. ; This program is copyrighted 1987 by Jay Sage.  It may be copied and modified
  11. ; freely for personal use but may not be sold or distributed for a fee.  It is
  12. ; being released through the NAOG/ZSIG organization, and modified versions must
  13. ; be submitted to and approved by NAOG/ZSIG before they may be distributed.
  14. ; See the file ZSIGPOL1.DOC on Z-Nodes for the ZSIG policy on signing out and
  15. ; modifying programs.
  16.  
  17.  
  18. ;=============================================================================
  19. ;
  20. ;            R E V I S I O N   H I S T O R Y
  21. ;
  22. ;=============================================================================
  23.  
  24. VERSION EQU    10
  25.  
  26. ; 03/10/87    Creation of COMIF program from IF.
  27. ;   1.0
  28. ;        Hex/binary/octal numerical inputs implemented for all number
  29. ;        entry.
  30. ;
  31. ;        "IF INPUT" prompt text has controls to generate control
  32. ;        character output and upper/lower case text.  A '^' converts
  33. ;        the following character to a control character.  The sequence
  34. ;        '%<' toggles output to upper case; '%>' toggles to lower case.
  35. ;
  36. ;        Added following tests:
  37. ;            IF AMBIG    tests for file ambiguity
  38. ;            IF ARCHIVE    tests for file archive attribute
  39. ;            IF BG        tests for presence of BackGrounder
  40. ;            IF COMPR    tests for squeezed or crunched file
  41. ;            IF DS        tests for presence of DateStamper
  42. ;            IF LIST        tests for items separated by commas
  43. ;            IF RO        test for file R/O attribute
  44. ;            IF SHELL    tests for shell on stack
  45. ;            IF SYS        tests for file SYS attribute
  46. ;            IF TAG        tests for tag attribute (tag specified
  47. ;                    following file names, i.e
  48. ;                    IF TAG FILE.EXT 3)
  49. ;            IF ZEX        tests for ZEX running
  50. ;
  51. ;        Added more general equality/inequality testing options for
  52. ;        strings.  If XCOMP1 is true, the following conditions are
  53. ;        recognized: EQ, NE, GT, GE, LT, LE.  All can be negated with ~.
  54. ;        If XCOMP2 is true, the following conditions are also allowed:
  55. ;        =   <>   ><   >   >=   <   <=.  All can be negated with '~'.
  56. ;
  57. ;        Added extended register and value testing options (if REGVALOPT
  58. ;        is true).  Syntax forms are:
  59. ;
  60. ;            IF [~]REG REG# OPERATOR VALUE
  61. ;            IF [~]VALUE ARG1 OPERATOR ARG2
  62. ;
  63. ;        VALUE compares two 16-bit numerical arguments; REG treats the
  64. ;        first argument as a register number rather than a value.
  65. ;        Spaces are optional surrounding the operator, and the following
  66. ;        operators are recognized:  =   <>   ><   >   >=   <   <=.
  67. ;        Here are some examples:
  68. ;
  69. ;            IF REG 3 > 1CH        IF REG 9= 0
  70. ;            IF REG 3 <>1101B    IF ~REG 011B=15Q
  71. ;            IF VAL C000H > 40000
  72. ;
  73. ;        Added many optional extensions to conditions.  IF TCAP can now
  74. ;        test for a specific TCAP using the syntax IF TCAP STRING.  The
  75. ;        string may have '?' wild cards in it.  The TCAP ID string is
  76. ;        compared to the given string up to the length of the latter.
  77. ;
  78. ;        Specific error conditions can be tested using the optional
  79. ;        syntax IF ERROR VALUE.
  80. ;
  81. ;        One can test for a particular shell program on the top of the
  82. ;        stack using IF SHELL NAME, where NAME may be ambiguous.
  83. ;
  84. ;        Modified EXIST and EMPTY tests to share code and meaning.  The
  85. ;        two tests are now the same except that (1) the EX test only
  86. ;        checks for the presence of a directory entry while EM also
  87. ;        checks for contents and (2) the senses of the tests are
  88. ;        reversed (EX is true if all files in list exist; EM is false
  89. ;        if all files exist and have contents.  Thus ~EM is equivalent
  90. ;        to EX but with a more stringent existence requirement).  Note
  91. ;        that when the file list has ambiguous filespecs, only the
  92. ;        first matching file is checked.
  93. ;
  94. ;        I would like to acknowlege extensive contributions to the
  95. ;        coding of COMIF by Howard Goldstein.
  96. ;
  97. ;                Jay Sage
  98.  
  99.  
  100. ; HISTORY OF IF.COM VERSIONS FROM WHICH THIS PROGRAM WAS DERIVED
  101.  
  102. ; 12/09/85    Fixed shortcoming in IF NULL test.  We now do it by checking
  103. ;   1.4        the command tail for characters.  IF NULL will now return false
  104. ;        if the second token is any kind of drive/user or named
  105. ;        directory specification as well as a file name.
  106. ;                Jay Sage
  107.  
  108. ; 09/06/85    Fixed mistake in IF ERROR code.  It was testing the error
  109. ;   1.3        handler flag instead of the program error flag.  Also enhanced
  110. ;        IF INPUT.  If text follows the INPUT token, then this text is
  111. ;        echoed to the console followed by ' (Y/N)? '.  If there is no
  112. ;        text, IF IN works as before.
  113. ;                Jay Sage
  114.  
  115. ; IF is intended to be invoked from the IF routine in an FCP.  This program
  116. ; implements the IF conditional tests and sets the next level of IF to be TRUE
  117. ; or FALSE.
  118. ; Modified on 02/11/85 to accept ambiguous file names and match them. This 
  119. ; allows aliases to add file extensions if they are needed, for instance 
  120. ; if there is an alias LDIR that gets a directory of an .LBR file, it 
  121. ; previously had to be defined as an example :
  122.  
  123.  
  124.  
  125. ; Configuration Equates
  126.  
  127. NO        EQU    0
  128. YES        EQU    NOT NO
  129.  
  130. USEDSEG        EQU    YES    ; Yes to put uninitialized data in a data
  131.                 ; segment (special linking required)
  132.  
  133. UPCASE        EQU    YES    ; Default to upper case output with IF IN
  134.                 ; ..prompt string
  135. UCASECH        EQU    '<'    ; Character to toggle to upper case output
  136. LCASECH        EQU    '>'    ; Character to toggle to lower case output
  137.  
  138. COMPROPT    EQU    YES    ; Include compressed file test
  139.  
  140. LISTOPT        EQU    YES    ; Include IF LIST test
  141.  
  142. TAGOPT        EQU    YES    ; Include IF TAG test
  143. ARCOPT        EQU    YES    ; Include IF ARCHIVE test
  144. ROOPT        EQU    YES    ; Include IF RO test
  145. SYSOPT        EQU    YES    ; Include IF SYS test
  146. ATTROPTS    EQU    TAGOPT OR ARCOPT OR SYSOPT OR ROOPT
  147.  
  148. SHELLOPT    EQU    YES    ; Include IF SHELL test
  149. ZEXOPT        EQU    YES    ; Include IF ZEX test
  150. REGVALOPT    EQU    YES    ; Include IF REG and IF VAL tests
  151.  
  152. PLUPERFOPT    EQU    YES    ; Include IF BG and IF DS tests
  153. IDOFF        EQU    5BH    ; Offset to BG ID in CCP
  154.  
  155. XERROPT        EQU    YES    ; Include extended ERROR option (IF ER VALUE)
  156. XTCAPOPT    EQU    YES    ; Include extended TCAP option (IF TC STRING)
  157. XSHELLOPT    EQU    YES    ; Include extended SHELL option (IF SHELL NAME)
  158. XCOMP1        EQU    YES    ; Include extended comparision tests EQ, NE,
  159.                 ; ..GT, GE, LT, LE
  160. XCOMP2        EQU    YES    ; Include extended comparision tests '=', '<>'
  161.                 ; '><', '>', '>=', '<', '<="
  162. XCOMP        EQU    XCOMP1 OR XCOMP2
  163.  
  164. NEGCHAR     EQU    '~'    ; Negation prefix char
  165.  
  166. Z3ENV        EQU    0FE00H    ; Address of ZCPR3 environment
  167.  
  168.  
  169. ; Miscellaneous Equates
  170.  
  171. BDOS    EQU    5
  172. FCB1    EQU    5CH
  173. FCB2    EQU    6CH
  174. TBUFF    EQU    80H
  175. CR    EQU    0DH
  176. LF    EQU    0AH
  177. TAB    EQU    09H
  178. BEL    EQU    07H
  179.  
  180.  
  181. ; External Z3LIB and SYSLIB Routines
  182.  
  183.     EXT    Z3INIT,STRTZEX,STOPZEX,GETZRUN,GETER2,GETREG
  184.     EXT    IFT,IFF,GETENV,GETWHL,GETSH1,ZFNAME
  185.  
  186.     EXT    EVAL,PSTR,PRINT,CAPINE,CODEND,SKSP,SKNSP,COUT,CAPS
  187.     EXT    COMPHD
  188.  
  189.  
  190. ; External ZCPR3 Environment Descriptor
  191.  
  192.     JP    START
  193.  
  194.     DEFB    'Z3ENV'        ; This is a ZCPR3 Utility
  195.     DEFB    1        ; External Environment Descriptor
  196. Z3EADR:
  197.     DEFW    Z3ENV
  198.  
  199.  
  200. CONDTAB:
  201.  
  202.     DEFB    'T '        ; Set state to TRUE
  203.     DEFW    IFCTRUE
  204.  
  205.     DEFB    'F '        ; Set state to FALSE
  206.     DEFW    IFCFALSE
  207.  
  208.     DEFB    'AM'        ; Test for ambiguous file specification
  209.     DEFW    IFCAMBIG
  210.  
  211.      IF    ARCOPT
  212.     DEFB    'AR'        ; Test for archive attribute
  213.     DEFW    IFCARC
  214.      ENDIF    ; ARCOPT
  215.  
  216.      IF    PLUPERFOPT
  217.     DEFB    'BG'        ; Test for BackGrounder loaded
  218.     DEFW    IFCBG
  219.      ENDIF    ; PLUPERFOPT
  220.  
  221.      IF    COMPROPT
  222.     DEFB    'CO'        ; Test for squeezed or crunched file
  223.     DEFW    IFCCOMPR
  224.      ENDIF    ; COMPROPT
  225.  
  226.      IF    PLUPERFOPT
  227.     DEFB    'DS'        ; Test for DateStamper loaded
  228.     DEFW    IFCDS
  229.      ENDIF    ; PLUPERFOPT
  230.  
  231.     DEFB    'EM'        ; Test for empty file(s)
  232.     DEFW    IFCEMPTY
  233.  
  234.     DEFB    'ER'        ; Test state of program error flag
  235.     DEFW    IFCERROR
  236.  
  237.     DEFB    'EX'        ; Test for existence of file(s)
  238.     DEFW    IFCEXIST
  239.  
  240.     DEFB    'IN'        ; Get user input
  241.     DEFW    IFCINPUT
  242.  
  243.      IF    LISTOPT
  244.     DEFB    'LI'        ; Test for multiple item token
  245.     DEFW    LIST
  246.      ENDIF    ; LISTOPT
  247.  
  248.     DEFB    'NU'        ; Test for null argument
  249.     DEFW    IFCNULL
  250.  
  251.      IF    REGVALOPT
  252.     DEFB    'RE'        ; Test register values
  253.     DEFW    IFCREG
  254.      ENDIF    ; REGVALOPT
  255.  
  256.      IF    ROOPT
  257.     DEFB    'RO'        ; Test for read-only attribute
  258.     DEFW    IFCRO
  259.      ENDIF    ; ROOPT
  260.  
  261.      IF    SHELLOPT
  262.     DEFB    'SH'        ; Test for shell name on shell stack
  263.     DEFW    IFCSHELL
  264.      ENDIF    ; SHELLOPT
  265.  
  266.      IF    SYSOPT
  267.     DEFB    'SY'        ; Test for sys file attribute
  268.     DEFW    IFCSYS
  269.      ENDIF    ; SYSOPT
  270.  
  271.      IF    TAGOPT
  272.     DEFB    'TA'        ; Test for tag attributes
  273.     DEFW    IFCTAG
  274.      ENDIF    ; TAGOPT
  275.  
  276.     DEFB    'TC'        ; Test for Z3TCAP entry loaded
  277.     DEFW    IFCTCAP
  278.  
  279.      IF    REGVALOPT
  280.     DEFB    'VA'        ; Compare numerical values
  281.     DEFW    IFCVAL
  282.      ENDIF    ; REGVALOPT
  283.  
  284.     DEFB    'WH'        ; Test if wheel byte set
  285.     DEFW    IFCWHEEL
  286.  
  287.      IF    ZEXOPT
  288.     DEFB    'ZE'        ; Test if ZEX running
  289.     DEFW    IFCZEX
  290.      ENDIF    ; ZEXOPT
  291.  
  292.      IF    XCOMP1
  293.     DEFB    'EQ'        ; Test for equality
  294.     DEFW    IFCEQUAL
  295.     DEFB    'NE'        ; Test for nonequality
  296.     DEFW    IFCNOTEQUAL
  297.     DEFB    'GE'        ; Test for ARG1 greater than or equal to ARG2
  298.     DEFW    IFCGTOREQ
  299.     DEFB    'GT'        ; Test for ARG1 greater than ARG2
  300.     DEFW    IFCGREATER
  301.     DEFB    'LT'        ; Test for ARG1 less than ARG2
  302.     DEFW    IFCLESS
  303.     DEFB    'LE'        ; Test for ARG1 less than or equal to ARG2
  304.     DEFW    IFCLTOREQ
  305.      ENDIF    ; XCOMP1
  306.  
  307.      IF    XCOMP2
  308.     DEFB    '= '        ; Test for equality
  309.     DEFW    IFCEQUAL
  310.     DEFB    '<>'        ; Test for nonequality
  311.     DEFW    IFCNOTEQUAL
  312.     DEFB    '><'        ; Test for nonequality
  313.     DEFW    IFCNOTEQUAL
  314.     DEFB    '>='        ; Test for ARG1 greater than or equal to ARG2
  315.     DEFW    IFCGTOREQ
  316.     DEFB    '> '        ; Test for ARG1 greater than ARG2
  317.     DEFW    IFCGREATER
  318.     DEFB    '< '        ; Test for ARG1 less than ARG2
  319.     DEFW    IFCLESS
  320.     DEFB    '<='        ; Test for ARG1 less than or equal to ARG2
  321.     DEFW    IFCLTOREQ
  322.      ENDIF    ; XCOMP2
  323.  
  324.     DEFB    0
  325.  
  326.  
  327. ; Start of program -- initialization
  328.  
  329. START:
  330.     LD    (STACK),SP    ; Save system stack pointer
  331.     LD    SP,STACK    ; Set up local stack
  332.     LD    HL,(Z3EADR)    ; Pt to ZCPR3 environment
  333.     CALL    Z3INIT        ; Initialize the ZCPR3 Environment
  334.     XOR    A        ; Clear negation flag
  335.     LD    (NEGFLAG),A
  336.  
  337. ; Test for ARG1=ARG2 syntax (as single token with no spaces)
  338.  
  339.     LD    HL,TBUFF+1    ; Point to command tail
  340.     CALL    SKSP        ; Start at first token
  341.     LD    D,H        ; Copy HL into DE for use at IFCK0
  342.     LD    E,L
  343.     LD    A,(HL)        ; Check for no tail
  344.     OR    A
  345.     JR    Z,IFHELP    ; Show help screen if no tail
  346.  
  347.      IF    XCOMP2        ; If conditions such as '<=' are allowed
  348.     CP    '<'        ; ..check for them and go to IFCK0 if found
  349.     JR    Z,IFCK0
  350.     CP    '>'
  351.     JR    Z,IFCK0
  352.     CP    NEGCHAR
  353.     JR    Z,IFCK0
  354.      ENDIF    ; XCOMP2
  355.  
  356. IFTEQ:                ; Scan for '=' starting with second character
  357.     INC    HL        ; Point to next character
  358.     LD    A,(HL)        ; Get it
  359.     CP    ' '+1        ; Test for end of token
  360.     JR    C,IFCK0        ; If end, we do not have ARG1=ARG2 syntax
  361.     CP    '='        ; Have we found '='?
  362.     JR    NZ,IFTEQ    ; If not, keep looping
  363.     LD    HL,FCB1+1    ; If so, compare FCB1 to FCB2
  364.     JP    IFCEQ
  365.  
  366. ; Test for help request or negation character
  367.  
  368. IFCK0:
  369.     LD    A,(DE)        ; Get first char of first token in tail
  370.     CP    '/'        ; If explicit help request
  371.     JR    Z,IFHELP    ; ..jump to help display
  372.     CP    NEGCHAR        ; If not negation character
  373.     JR    NZ,IFCK1    ; ..then leave negflag as is
  374.     CALL    NEGCOMPL    ; Else complement the flag setting
  375.     INC    DE        ; ..and point to char after negchar
  376.  
  377. ; Test for register syntax of form "IF REG# VALUE"
  378.  
  379. IFCK1:
  380.     PUSH    DE        ; Save pointer
  381.     CALL    REGTEST        ; Will not return if "IF REG# VALUE" syntax
  382.     POP    DE
  383.  
  384. ; Scan for condition option
  385.  
  386.     CALL    CONDTEST    ; Test of condition match
  387.     JR    Z,CONDERROR    ; Error message if condition not recognized
  388.     JP    (HL)        ; Process condition testing
  389.  
  390. ; Fall-through error code
  391.  
  392. CONDERROR:
  393.     CALL    PRINT
  394.     DEFB    ' Bad IF Condition',0
  395.     JP    ERRORMSG
  396.  
  397. ; Print help message
  398.  
  399. IFHELP:
  400.     CALL    PRINT
  401.     DEFB    CR,LF,LF
  402.     DEFB    TAB,TAB,'COMIF '
  403.     DEFB    (VERSION/10)+'0','.',(VERSION MOD 10)+'0'
  404.     DEFB    ' [ZSIG]'
  405.  
  406.     DEFB    CR,LF,LF,'SYNTAX:',TAB,'(1) IF ARG1=ARG2'
  407.     DEFB    CR,LF,TAB,'(2) IF REGISTER# [VALUE]'
  408.     DEFB    CR,LF,TAB,'(3) IF CONDITION ARGUMENTS'
  409.  
  410.     DEFB    CR,LF,LF,'CONDITIONS:'
  411.     DEFB    CR,LF,TAB,'T, F, AMBIG'
  412.  
  413.      IF    ARCOPT
  414.     DEFB    ', ARCHIVE'
  415.      ENDIF    ; ARCOPT
  416.  
  417.      IF    PLUPERFOPT
  418.     DEFB    ', BG'
  419.      ENDIF    ; PLUPERFOPT
  420.  
  421.      IF    COMPROPT
  422.     DEFB    ', COMPR'
  423.      ENDIF    ; COMPROPT
  424.  
  425.      IF    PLUPERFOPT
  426.     DEFB    ', DS'
  427.      ENDIF    ; PLUPERFOPT
  428.  
  429.     DEFB    ', EMPTY'
  430.     DEFB    ', ERROR'
  431.     DEFB    ', EXIST'
  432.     DEFB    ', INPUT'
  433.  
  434.     DEFB    CR,LF,TAB,'NULL'
  435.  
  436.      IF    LISTOPT
  437.     DEFB    ', LIST'
  438.      ENDIF    ; LISTOPT
  439.  
  440.      IF    REGVALOPT
  441.     DEFB    ', REG'
  442.      ENDIF    ; REGVALOPT
  443.  
  444.      IF    ROOPT
  445.     DEFB    ', RO'
  446.      ENDIF    ; ROOPT
  447.  
  448.      IF    SHELLOPT
  449.     DEFB    ', SHELL'
  450.      ENDIF    ; SHELLOPT
  451.  
  452.      IF    SYSOPT
  453.     DEFB    ', SYS'
  454.      ENDIF    ; SYSOPT
  455.  
  456.      IF    TAGOPT
  457.     DEFB    ', TAG'
  458.      ENDIF    ; TAGOPT
  459.  
  460.     DEFB    ', TCAP'
  461.  
  462.      IF    REGVALOPT
  463.     DEFB    ', VALUE'
  464.      ENDIF    ; REGVALOPT
  465.  
  466.     DEFB    ', WHEEL'
  467.  
  468.      IF    ZEXOPT
  469.     DEFB    ', ZEX'
  470.      ENDIF    ; ZEXOPT
  471.  
  472.      IF    XCOMP
  473.     DEFB    CR,LF,TAB
  474.      ENDIF    ; XCOMP
  475.  
  476.      IF    XCOMP1
  477.     DEFB    'EQ NE GT GE LT LE  '
  478.      ENDIF    ; XCOMP1
  479.  
  480.      IF    XCOMP2
  481.     DEFB    '=  <>  ><  >  >=  <  <='
  482.      ENDIF    ; XCOMP2
  483.  
  484.     DEFB    CR,LF,LF,'Only first 2 letters of condition are significant.'
  485.     DEFB    CR,LF,'A leading ''',NEGCHAR,''' negates all forms except (1).'
  486.     DEFB    CR,LF,'See COMIF.HLP for details.'
  487.     DEFB    CR,LF
  488.     DEFB    0
  489.  
  490.     JP    RETURN
  491.  
  492.  
  493. ;=============================================================================
  494. ;
  495. ;        C O N D I T I O N    T E S T I N G
  496. ;
  497. ;=============================================================================
  498.  
  499. ; Condition:  NULL
  500. ;
  501. ;    If any text other than spaces appears on the command line after
  502. ;    the 'NULL' option, then the IF state is set to false.  This differs
  503. ;    from the IF NULL test in the SYSFCP code, which returns false
  504. ;    only when a file name is given as a second token but not when a
  505. ;    directory specification is given.
  506.  
  507. IFCNULL:
  508.     LD    HL,TBUFF+1    ; Point to command tail
  509.     CALL    SKIP2        ; Skip to second token
  510.     JR    Z,TRUEREL
  511. FALSEREL:            ; Entry point for relative jump
  512.     JP    IFCFALSE
  513.  
  514.  
  515. ;=============================================================================
  516. ;
  517. ;    P L U P E R F E C T    E X T E N S I O N    T E S T I N G
  518. ;
  519. ;=============================================================================
  520.  
  521.      IF    PLUPERFOPT
  522.  
  523. ; Condition:  BG (BackGrounder)
  524. ;
  525. ;    This option tests for the presence of the 'BGii' ID string that
  526. ;    shows that BackGrounder ii is running.  The code looks for the
  527. ;    ID at an offset of IDOFF from the beginning of the CPR code.  The
  528. ;    value if IDOFF was determined by examination.
  529.  
  530. IFCBG:
  531.     LD    HL,(0001)        ; Get BIOS pointer
  532.     LD    DE,-1603H+IDOFF        ; Offset to 'BGii' ID string in BG CPR
  533.     ADD    HL,DE
  534.     LD    DE,IDSTR        ; Point to reference ID string
  535.     LD    B,IDLEN            ; Length of ID string
  536. BGCHK1:
  537.     LD    A,(DE)            ; Get reference character
  538.     CP    (HL)            ; Compare to actual character
  539.     JR    NZ,FALSEREL        ; Set false if mismatch
  540.     INC    HL            ; Move to next characters
  541.     INC    DE
  542.     DJNZ    BGCHK1            ; Loop through all characters
  543. BGTRUE:                    ; Entry point for relative jump
  544.     JR    TRUEREL            ; They match, so set true
  545.  
  546. IDSTR:    DEFB    'BGii'
  547. IDLEN    EQU    $ - IDSTR
  548.  
  549. ;-----------------------------------------------------------------------------
  550.  
  551. ; Condition: DS (DateStamper)
  552. ;
  553. ;    This option tests for the presence of DateStamper.
  554.  
  555. IFCDS:
  556.     LD    E,'D'            ; DateStamper ID character
  557.     LD    C,0CH            ; Return version function
  558.     CALL    BDOS
  559.     CP    22H            ; Must be CP/M 2.2
  560.     JR    NZ,FALSEREL        ; If not, set false IF state
  561.     LD    A,H            ; Check for return of ID
  562.     CP    'D'
  563.     JR    NZ,FALSEREL        ; If not, set false IF state
  564.     JR    BGTRUE            ; Otherwise set true IF state
  565.  
  566.      ENDIF    ; PLUPERFOPT
  567.  
  568.  
  569. ;=============================================================================
  570. ;
  571. ;    E X T E N D E D    C O M P A R I S O N    T E S T I N G
  572. ;
  573. ;=============================================================================
  574.  
  575.      IF    XCOMP        ; If extended compare options included
  576.  
  577.  
  578. ; Condition: NE (Not Equal)
  579. ;
  580. ;    This test is equivalent to ~EQ.
  581.  
  582. IFCNOTEQUAL:
  583.     CALL    NEGCOMPL    ; Complement the negation flag
  584.     JR    IFCEQUAL    ; Then perform EQ test
  585.  
  586. ;-----------------------------------------------------------------------------
  587.  
  588. ; Condition:  EQ (Equal)
  589. ;
  590. ;    This test compares the next two tokens for equality, with wildcards
  591. ;    ('?') always being taken as equality.  This code is used (at IFCEQ)
  592. ;    by the direct syntax version "IF ARG1=ARG2" and by the consistent
  593. ;    forms "IF EQ ARG1 ARG2" or "IF ~EQ ARG1 ARG2" or "IF = ARG1 ARG2"
  594. ;    and so on.
  595.  
  596. IFCEQUAL:
  597.     CALL    SETCOMPARE    ; Set up for comparison (ARG1 in FCB1,
  598.                 ; ..ARG2 in FCB2)
  599.  
  600.      ENDIF    ; XCOMP
  601.  
  602. ; Entry point for "IF ARG1=ARG2" syntax (THIS CODE IS USED EVEN IF XCOMP IS
  603. ; FALSE)
  604.  
  605. IFCEQ:
  606.     CALL    COMPARE        ; Perform comparison
  607.     JR    NZ,FALSEREL    ; False if not equal
  608. TRUEREL:            ; Entry point for relative jump
  609.     JP    IFCTRUE        ; Otherwise true
  610.  
  611. ;-----------------------------------------------------------------------------
  612.  
  613. ; Condition: LE (Less Than or Equal)
  614. ;
  615. ;    This test returns true if ARG1 is the same as or less than ARG2 in
  616. ;    the expression "IF LE ARG1 ARG2".  Wild cards are taken as equality.
  617.  
  618.      IF    XCOMP
  619.  
  620. IFCLTOREQ:
  621.     CALL    NEGCOMPL    ; Complement the negation flag
  622.     JR    IFCGREATER    ; Then use GT test
  623.  
  624. ;-----------------------------------------------------------------------------
  625.  
  626. ; Condition:  GT (Greater Than)
  627. ;
  628. ;    This test returns true if ARG1 is greater than ARG2 in the expression
  629. ;    "IF GT ARG1 ARG2".  Wild card characters are taken as equality.
  630.  
  631. IFCGREATER:
  632.     CALL    SETCOMPARE    ; Set up for comparison
  633.     CALL    COMPARE        ; Perform comparison
  634.     JR    Z,FALSEREL    ; False if equal
  635.     JR    C,FALSEREL    ; False if ARG2 (in FCB1) greater than ARG2
  636.     JR    TRUEREL        ; Otherwise true
  637.  
  638. ;-----------------------------------------------------------------------------
  639.  
  640. ; Condition:  LT (Less Than)
  641. ;
  642. ;    This test returns true if ARG1 is less than ARG2 in the expression
  643. ;    "IF LT ARG1 ARG2".  Wild card characters are taken as equality.
  644.  
  645. IFCLESS:
  646.     CALL    NEGCOMPL    ; Complement negation flag
  647.     JR    IFCGTOREQ    ; Then use GE test
  648.  
  649. ;-----------------------------------------------------------------------------
  650.  
  651. ; Condition:  GE (Greater Than or Equal)
  652. ;
  653. ;    This test returns true if ARG1 is greater than or equal to ARG2 in the
  654. ;    expression "IF GE ARG1 ARG2".  Wild card characters are taken as
  655. ;    equality.
  656.  
  657. IFCGTOREQ:
  658.     CALL    SETCOMPARE    ; Set up for comparison
  659.     CALL    COMPARE        ; Perform comparison
  660.     JR    Z,TRUEREL    ; True if equal
  661.     JR    C,FALSEREL    ; False if ARG2 (in FCB1) greater than ARG2
  662.     JR    TRUEREL        ; Otherwise true
  663.  
  664.      ENDIF    ; XCOMP
  665.  
  666. ;=============================================================================
  667. ;
  668. ;    R E G I S T E R    A N D    V A L U E    T E S T I N G
  669. ;
  670. ;=============================================================================
  671.  
  672. ; Condition:  REG
  673. ;
  674. ;    This test uses the syntax "IF [~]REG REG# OPERATOR VALUE" to test
  675. ;    values stored in user registers R#=0..9.  The allowed operators
  676. ;    are:    =    <>    ><
  677. ;        >    >=    <    <=
  678. ;    Spaces around the operators are optional.  Values may be entered in
  679. ;    decimal, octal, binary, or hexadecimal format.
  680.  
  681.      IF    REGVALOPT
  682.  
  683. IFCREG:
  684.     CALL    SKIP2        ; Skip to REG# token
  685.     CALL    GETNUM        ; Convert to a number
  686.     LD    A,9        ; Test for value not larger than 9
  687.     CP    B
  688.     JR    NC,IFCREG1    ; Jump if value is OK
  689.  
  690.     CALL    PRINT
  691.     DEFB    ' Bad register number',0
  692.     JP    ERRORMSG    ; Return with false if state
  693.  
  694. IFCREG1:
  695.     CALL    GETREG        ; Get value of designated register
  696.     LD    B,A        ; Save it in B
  697.     CALL    SKSP        ; Skip to operator (if there are spaces)
  698.     CALL    READOPER    ; Read the operator (save in register C)
  699.     CALL    SKSP        ; Skip over spaces if any to value
  700.     PUSH    BC        ; Save register value and operator
  701.     CALL    GETNUM        ; Get value for comparison into B
  702.     POP    DE        ; Restore register value to D, operator to E
  703.     LD    A,D        ; Form (REGISTERVALUE - REFERENCEVALUE)
  704.     SUB    B
  705. IFCREG1A:
  706.     PUSH    AF        ; Save result
  707.     LD    A,E        ; Branch based on operator type
  708.     CP    '='
  709.     JR    Z,IFCREG2
  710.     CP    '>'
  711.     JR    Z,IFCREG3
  712.                 ; Less-than case
  713.     POP    AF
  714.     JR    C,REGTRUE
  715. REGFALSE:            ; Entry point for relative jump
  716.     JP    IFCFALSE
  717.  
  718. IFCREG2:            ; Equal case
  719.     POP    AF
  720.     JR    Z,REGTRUE
  721.     JR    REGFALSE
  722.  
  723. IFCREG3:            ; Greater-than case
  724.     POP    AF
  725.     JR    Z,REGFALSE
  726.     JR    C,REGFALSE
  727. REGTRUE:            ; Entry point for relative jump
  728.     JP    IFCTRUE
  729.  
  730. ;-----------------------------------------------------------------------------
  731.  
  732. ;  Condition:  VAL
  733. ;
  734. ;    This option compares two 16-bit numerical values using the syntax
  735. ;    IF VAL ARG1 ARG2.
  736.  
  737. IFCVAL:
  738.     CALL    SKIP2        ; Point to ARG1
  739.     CALL    EVAL        ; Get 16-bit value into DE
  740.     JP    C,NUMERROR    ; Error if carry flag set
  741.  
  742.     PUSH    DE        ; Save ARG1 value on stack
  743.     CALL    SKSP        ; Skip to operator (if there are spaces)
  744.     CALL    READOPER    ; Read the operator (save in register C)
  745.     CALL    SKSP        ; Skip over spaces if any to value
  746.     CALL    EVAL        ; Get its value into DE
  747.     POP    HL        ; Get ARG1 value back in HL
  748.     JP    C,NUMERROR    ; Error if carry flag set
  749.  
  750.     CALL    COMPHD        ; Compare DE-HL
  751.     LD    E,C        ; Put operator character in E
  752.     JR    IFCREG1A    ; Use REG testing code to complete
  753.  
  754. ;-------------------------
  755.  
  756. ; Subroutine to interpret a comparison operator string
  757. ;
  758. ;    This subroutine reads an operator string of one or two characters.
  759. ;    When called, HL points to the character string; on exit, HL points
  760. ;    to the character following the operator string, register C contains
  761. ;    an effective one-character operator (= or < or >), and the negation
  762. ;    flag has been complemented if the operators were not-equal, greater-
  763. ;    than-or-equal, or less-than-or-equal.  If an invalid operator string
  764. ;    is encountered, the routine displays an error message and returns
  765. ;    a false if state.
  766.  
  767. READOPER:
  768.     LD    A,(HL)        ; Get first operator character
  769.     LD    C,A        ; Save it in C
  770.     INC    HL        ; Point to next character
  771.     CP    '='        ; Equality?
  772.     RET    Z        ; If so, we have complete operator
  773.     CP    '>'        ; Greater than?
  774.     JR    Z,GTOPER    ; If so, jump
  775.     CP    '<'        ; Less than?
  776.     JR    Z,LTOPER    ; If so, jump
  777.  
  778.     CALL    PRINT        ; We must have a bad operator
  779.     DEFB    ' Bad operator',0
  780.     JP    ERRORMSG    ; Return with false if state
  781.  
  782. GEOPER:                ; Treat '>=' as 'not <'
  783.     LD    A,'<'        ; Equivalent negated operator
  784.     JR    SETOPER
  785.  
  786. GTOPER:
  787.     LD    A,(HL)        ; Check for second operator character
  788.     CP    '='        ; Greater than or equal?
  789.     JR    Z,GEOPER
  790.     CP    '<'        ; Not equal "><" ?
  791.     RET    NZ        ; If not, must be end of operator string
  792.                 ; If so, fall through to NEOPER
  793. NEOPER:
  794.     LD    A,'='        ; Equivalent negated operator
  795. SETOPER:
  796.     LD    C,A        ; Save operator in C
  797.     INC    HL        ; Point to character after operator string
  798.     JP    NEGCOMPL    ; Complement negation flag and return
  799.  
  800. LTOPER:
  801.     LD    A,(HL)        ; Check for second operator character
  802.     CP    '='        ; Less than or equal?
  803.     JR    Z,LEOPER
  804.     CP    '>'        ; Not equal?
  805.     JR    Z,NEOPER
  806.     RET            ; We have '<'
  807.  
  808. LEOPER:
  809.     LD    A,'>'        ; Equivalent negated operator
  810.     JR    SETOPER
  811.  
  812.      ENDIF    ; REGVALOPT
  813.  
  814. ;-----------------------------------------------------------------------------
  815.  
  816. ; Condition:  TCAP
  817. ;
  818. ;    This test returns true if any terminal capability descriptor is
  819. ;    loaded into the TCAP buffer.  If the XTCAPOPT equate is true, then
  820. ;    the following extended syntax is supported:
  821. ;
  822. ;            IF TCAP STRING
  823. ;
  824. ;    The name of the loaded terminal will be compared to the string, and
  825. ;    the if state will be set to true only if they match.  The comparison
  826. ;    is made only for the number of characters present in STRING, and
  827. ;    wild cards ('?') are allowed in STRING.
  828.  
  829. IFCTCAP:
  830.     CALL    GETENV        ; Get ptr to ZCPR3 environment descriptor
  831.     LD    DE,80H        ; Pt to TCAP entry
  832.     ADD    HL,DE
  833.     LD    A,(HL)        ; Get first char
  834.     CP    ' '+1        ; Space or less = none
  835.     JR    C,AMBFALSE
  836.  
  837.      IF    XTCAPOPT    ; Extended TCAP condition testing
  838.  
  839.     PUSH    HL        ; Save pointer to TCAP ID
  840.     CALL    SKIP2        ; Make HL point to second command-line token
  841.     POP    DE        ; Get TCAP ID pointer into DE
  842. IFCTCAP1:
  843.     LD    A,(HL)        ; Get character from test string
  844.     OR    A        ; Test for end of line
  845.     JR    Z,WHLTRUE    ; If end of string, ID matches
  846.     CP    '?'        ; If wild card, take it as a match
  847.     JR    Z,IFCTCAP2
  848.     LD    A,(DE)        ; Get character from TCAP ID
  849.     CALL    CAPS        ; Capitalize it
  850.     CP    (HL)        ; Compare to test string
  851.     JR    NZ,AMBFALSE    ; Mismatch found
  852. IFCTCAP2:
  853.     INC    HL        ; Advance pointers
  854.     INC    DE
  855.     JR    IFCTCAP1    ; Loop through string
  856.  
  857.      ELSE    ; NOT XTCAPOPT
  858.  
  859.     JR    WHLTRUE
  860.  
  861.      ENDIF    ; XTCAPOPT
  862.  
  863.  
  864. ;-----------------------------------------------------------------------------
  865.  
  866. ; Condition:  WHEEL
  867. ;
  868. ;    This test returns true if the wheel byte contains a value other
  869. ;    than zero.
  870.  
  871. IFCWHEEL:
  872.     CALL    GETWHL        ; Get current wheel setting
  873.     JR    Z,AMBFALSE
  874. WHLTRUE:            ; Entry point for relative jump
  875.     JP    IFCTRUE
  876.  
  877.  
  878. ;-----------------------------------------------------------------------------
  879.  
  880. ; Condition:  AMBIG
  881. ;
  882. ;    This test returns true if the file specification given as the second
  883. ;    token on the command line is ambiguous (contains '*' or '?').
  884.  
  885. IFCAMBIG:
  886.     LD    HL,FCB2+1    ; Scan FCP2 for '?' characters
  887.     LD    B,11        ; Characters to scan
  888.     LD    A,'?'        ; Target character
  889. AMBIG1:
  890.     CP    (HL)        ; Is character in file name is '?'
  891.     JR    Z,WHLTRUE    ; If so, test is true
  892.     INC    HL        ; Point to next character
  893.     DJNZ    AMBIG1        ; Loop back to test more
  894. AMBFALSE:            ; Entry point for relative jumps
  895.     JP    IFCFALSE    ; Must not be ambiguous
  896.  
  897.  
  898. ;-----------------------------------------------------------------------------
  899.  
  900. ; Condition:  COMPRESSED
  901.  
  902.      IF    COMPROPT
  903.  
  904. IFCCOMPR:
  905.     LD    A,(FCB2+10)    ; Get middle character of file type
  906.     CP    'Z'        ; Crunched
  907.     JR    Z,WHLTRUE
  908.     CP    'Q'        ; Squeezed
  909.     JR    Z,WHLTRUE
  910.     JR    AMBFALSE
  911.  
  912.      ENDIF    ; COMPROPT
  913.  
  914. ;-----------------------------------------------------------------------------
  915.  
  916. ;  Condition:  LIST
  917.  
  918. ;    this test returns true if the following token contains multiple
  919. ;    items separated by commas.
  920.  
  921.      IF    LISTOPT
  922.  
  923. LIST:
  924.     CALL    SKIP2        ; Skip to 2nd token
  925.  
  926. LIST1:
  927.     LD    A,(HL)        ; Get character
  928.     INC    HL        ; Point to next character
  929.     CP    ' '+1        ; End of token?
  930.     JR    C,AMBFALSE    ; Set false if so
  931.     CP    ','        ; A comma?
  932.     JR    NZ,LIST1    ; If not, keep looking
  933.     LD    A,(HL)        ; Get next character
  934.     CP    ' '+1        ; Something following comma?
  935.     JR    NC,WHLTRUE    ; If so, we have a list
  936.     JR    AMBFALSE
  937.  
  938.      ENDIF    ; LISTOPT
  939.  
  940. ;-----------------------------------------------------------------------------
  941.  
  942. ;  Condition:  SHELL
  943. ;
  944. ;    This test returns true if anything is on the shell stack.  It returns
  945. ;    a false condition if there is no shell stack.  If XSHELLOPT equate is
  946. ;    true, then the following optional syntax is supported:
  947. ;
  948. ;            IF SHELL NAME
  949. ;
  950. ;    With this form, the code will compare the given NAME with the program
  951. ;    name on the top of the shell stack.  Any leading DU: or DIR: will be
  952. ;    skipped, both in NAME and in the shell stack entry.  Wild cards are
  953. ;    allowed in NAME.
  954.  
  955.      IF    SHELLOPT
  956.  
  957. IFCSHELL:
  958.     CALL    GETSH1        ; Get shell stack info
  959.     JP    Z,IFCFALSE    ; False if no shell stack
  960.     LD    A,(HL)        ; See if anything on stack
  961.     OR    A
  962.     JR    Z,AMBFALSE    ; False if not
  963.  
  964.      IF    XSHELLOPT    ; Extended shell option
  965.  
  966.     LD    DE,FCB1        ; Parse shell stack entry into FCB1
  967.     XOR    A        ; Scan DIR: before DU:
  968.     CALL    ZFNAME
  969.     CALL    SKIP2        ; Point to second command-line token
  970.     JP    Z,IFCTRUE    ; If no second token, set state to true
  971.     LD    DE,FCB2        ; Else parse token into FCB2
  972.     XOR    A        ; Scan DIR: before DU:
  973.     CALL    ZFNAME
  974.     LD    HL,FCB1+9    ; Force match in file types
  975.     LD    B,3        ; ..by setting type to '???'
  976. IFCSHELL0:
  977.     LD    (HL),'?'
  978.     INC    HL
  979.     DJNZ    IFCSHELL0
  980.  
  981.     LD    HL,FCB1+1    ; Compare name in FCB1 to that in FCB2
  982.     JP    IFCEQ
  983.  
  984.      ENDIF    ; XSHELLOPT
  985.  
  986.     JR    WHLTRUE        ; Otherwise true
  987.  
  988.      ENDIF    ; SHELLOPT
  989.  
  990. ;-----------------------------------------------------------------------------
  991.  
  992. ;  Condition:  ZEX
  993. ;
  994. ;    This test returns true if ZEX is currently running.  If no message
  995. ;    buffer is implemented, it returns false.
  996.  
  997.      IF    ZEXOPT
  998.  
  999. IFCZEX:
  1000.     CALL    GETZRUN        ; See if ZEX running
  1001.     JR    C,AMBFALSE    ; If no message buffer, take as false
  1002.     JR    Z,AMBFALSE    ; If ZEX not running, set false
  1003.     JR    WHLTRUE        ; Otherwise, set true
  1004.  
  1005.      ENDIF    ; ZEXOPT
  1006.  
  1007. ;-----------------------------------------------------------------------------
  1008.  
  1009. ; Condition:  TAG <file list> <n>
  1010.  
  1011. ;    This test returns true if each file in the list exists and if
  1012. ;    byte n, (1 <= n >= 8), of the file's name in the directory has its
  1013. ;    msb set.
  1014.  
  1015.      IF    TAGOPT
  1016.  
  1017. IFCTAG:
  1018.     CALL    SKIP2        ; Skip to second token
  1019.     CALL    SKNSP        ; Skip over it
  1020.     CALL    SKSP        ; ..to third token
  1021.     CALL    GETNUM        ; Get byte to test
  1022.     LD    A,B        ; Byte number in A
  1023.     OR    A
  1024.     JR    Z,TAGERR    ; Error if number more than 255
  1025.     CP    8+1        ; Must not be > 8
  1026.     JR    C,TAG1
  1027.  
  1028. TAGERR:
  1029.     CALL    PRINT
  1030.     DEFB    ' Bad tag',0
  1031.     JP    ERRORMSG
  1032.  
  1033.      ENDIF    ; TAGOPT
  1034.  
  1035.      IF    ATTROPTS
  1036.  
  1037. TAG1:
  1038.     LD    (OFFSET),DE    ; Store offset to tag byte
  1039.     CALL    NEGCOMPL    ; Reverse sense of true/false if negated
  1040.     LD    HL,ATTRTST    ; Get return to call
  1041.     LD    (TSTCALL),HL    ; Modify call instruction
  1042.     JR    IFCEM0        ; Go to modified empty test
  1043.  
  1044. ; Return to test for file attribute set.  On entry, regiter D
  1045. ; points to the FCB.
  1046.  
  1047. ATTRTST:
  1048.     LD    HL,(OFFSET)    ; Get offset into HL
  1049.     ADD    HL,DE        ; Now pointing at desired byte
  1050.     LD    A,(HL)
  1051.     RLCA            ; Get msb into carry
  1052.     JP    NC,IFCTRUE    ; False if not set (sense reversed)
  1053.     RET
  1054.  
  1055.      ENDIF    ; ATTROPTS
  1056.  
  1057. ;-----------------------------------------------------------------------------
  1058.  
  1059. ; Condition:  RO <file list>
  1060.  
  1061. ;    This test returns true if all files in the list are set
  1062. ;    to read-only.
  1063.  
  1064.      IF ROOPT
  1065.  
  1066.      IFCRO:
  1067.     LD    DE,9        ; Offset to R/O flag
  1068.     JR    TAG1        ; Go perform function
  1069.  
  1070.      ENDIF    ; ROOPT
  1071.  
  1072. ;-----------------------------------------------------------------------------
  1073.  
  1074. ; Condition:  SYS <file list>
  1075.  
  1076. ;    This test returns true if all files in the list are set
  1077. ;    to system.
  1078.  
  1079.      IF    SYSOPT
  1080.  
  1081.      IFCSYS:
  1082.     LD    DE,10        ; Offset to SYS flag
  1083.     JR    TAG1        ; Go perform function
  1084.  
  1085.      ENDIF    ; SYSOPT
  1086.  
  1087. ;-----------------------------------------------------------------------------
  1088.  
  1089. ; Condition:  ARCHIVE <file list>
  1090.  
  1091. ;    This test returns true if all files in the list are set
  1092. ;    to archive.
  1093.  
  1094.      IF    ARCOPT
  1095.  
  1096.      IFCARC:
  1097.     LD    DE,11        ; Offset to ARC flag
  1098.     JR    TAG1        ; Go perform function
  1099.  
  1100.      ENDIF    ; ARCOPT
  1101.  
  1102. ;-----------------------------------------------------------------------------
  1103.  
  1104. ; Condition: EXIST <file list>
  1105. ;
  1106. ;    A list of ambiguous file names separated by commas (no spaces allowed)
  1107. ;    may be given.  If at least one file from each ambiguous file
  1108. ;    specification exists, then the if state will be set to true.  As soon
  1109. ;    as one ambiguous file specification fails to match an existing file
  1110. ;    the condition is set to false.
  1111.  
  1112. IFCEXIST:
  1113.     CALL    NEGCOMPL    ; Reverse the sense of testing (vs EM test)
  1114.     LD    HL,JUSTRET    ; Modify call in empty test
  1115.     LD    (TSTCALL),HL
  1116.     JR    IFCEM0        ; Go to modified empty test
  1117.  
  1118.  
  1119. ;-----------------------------------------------------------------------------
  1120.  
  1121. ; Condition: EMPTY filename.typ
  1122. ;
  1123. ;    This test is like a NOT EXIST test except that existence is taken to
  1124. ;    require not only a directory entry for a file but also some contents
  1125. ;    to the file.  If ANY tested filespec is nonexistent or empty, then the
  1126. ;    if state is set to true.  If ALL files do exist and contain data, then
  1127. ;    the state is set to false.
  1128. ;
  1129. ;    The stack is not always cleaned up here, but that is no problem.
  1130.  
  1131. IFCEMPTY:
  1132.     LD    HL,READREC    ; Addr of rtn to execute
  1133.     LD    (TSTCALL),HL    ; Modify call instruction
  1134.  
  1135. ifcem0:
  1136.     CALL    SKIP2        ; Skip to 2nd token
  1137.     JP    Z,IFCTRUE    ; TRUE if none
  1138.  
  1139. ; Loop through files in list
  1140.  
  1141. IFCEM1:
  1142.     LD    DE,FCB1        ; Point to FCB1
  1143.     CALL    ZFNAME        ; Convert string to filespec
  1144.     PUSH    HL        ; Save pointer to file list string
  1145.     CALL    TLOG        ; Log into FCB1's DU
  1146.     LD    DE,FCB1        ; Try to open file
  1147.     LD    C,15
  1148.     PUSH    DE        ; Save FCB pointer
  1149.     CALL    BDOS
  1150.     POP    DE
  1151.     INC    A        ; Z if not found
  1152.     JR    Z,IFCTRUE    ; If not found, set true if state
  1153.  
  1154. TSTCALL    EQU    $+1        ; Pointer for in-code modification
  1155.     CALL    0        ; Perform function
  1156.  
  1157. IFCEM2:                ; File exists (and has contents if EM test)
  1158.                 ; ... or has specified attribute
  1159.     POP    HL        ; Get back pointer to file list
  1160.     LD    A,(HL)        ; Check for additional files on list
  1161.     INC    HL
  1162.     CP    ','        ; More to come?
  1163.     JR    NZ,IFCFALSE    ; All files found, so set state to false
  1164.     LD    A,(HL)        ; Make sure not a terminal comma
  1165.     CP    ' '+1
  1166.     JR    C,IFCFALSE    ; End of list, all empty, so FALSE
  1167.     JR    IFCEM1        ; Process next item
  1168.  
  1169. READREC:
  1170.     LD    C,20        ; Try to read a record
  1171.     CALL    BDOS
  1172.     OR    A        ; Z if files has contents
  1173.     JR    NZ,IFCTRUE    ; If file empty, set true if state
  1174. JUSTRET:
  1175.     RET
  1176.  
  1177. ;-----------------------------------------------------------------------------
  1178.  
  1179. ; Condition: INPUT (from user)
  1180. ;
  1181. ;    If there is any text after the option, it is used as a prompt.  The
  1182. ;    string " (Y/N)? " is automatically appended.  Any of the following
  1183. ;    input from the user is taken as affirmative: CR, space, Y, or T.
  1184. ;    Any other input is taken as a negative answer.
  1185.  
  1186. IFCINPUT:
  1187.     LD    A,CR        ; Carriage return
  1188.     CALL    COUT
  1189.     LD    A,LF        ; New line
  1190.     CALL    COUT
  1191.     CALL    STOPZEX        ; Suspend ZEX input
  1192.     LD    HL,TBUFF+1    ; See if text is given in command tail
  1193.     CALL    SKIP2        ; Skip to second token
  1194.     JR    Z,IFCIN2    ; If end of line, use default 'IF True?'
  1195.  
  1196.     CALL    ECHO        ; Echo the rest of line with case and control
  1197.                 ; ..character interpretation
  1198. ;    CALL    PRINT        ; Append the following
  1199. ;    DEFB    ' (Y/N)? ',0
  1200.     JR    IFCIN3
  1201.  
  1202. IFCIN2:
  1203.     CALL    PRINT
  1204.     DEFB    ' IF True? ',0
  1205.  
  1206. IFCIN3:
  1207.     CALL    CAPINE
  1208.     CALL    STRTZEX        ; Resume ZEX input
  1209.     CP    'T'        ; True?
  1210.     JR    Z,IFCTRUE
  1211.     CP    'Y'        ; Yes?
  1212.     JR    Z,IFCTRUE
  1213.     CP    CR        ; New line?
  1214.     JR    Z,IFCTRUE
  1215.     CP    ' '        ; Space?
  1216.     JR    Z,IFCTRUE
  1217.     JR    IFCFALSE
  1218.  
  1219.  
  1220. ;-----------------------------------------------------------------------------
  1221.  
  1222. ; Condition: ERROR
  1223. ;
  1224. ;    This tests the program error flag.  If it has a value of zero, then
  1225. ;    the if state is set to false.  If the value is nonzero, then an error
  1226. ;    condition is assumed to exist, and the if state is set to true.
  1227. ;    If the equate XERROPT is true, then the following form will also be
  1228. ;    processed:
  1229. ;            IF ERROR VALUE
  1230. ;
  1231. ;    The if state will be set true only if the program error flag has that
  1232. ;    specified value.
  1233.  
  1234. IFCERROR:
  1235.  
  1236.      IF    XERROPT        ; Extended ERROR option
  1237.  
  1238.     CALL    SKIP2        ; Move to second token
  1239.     LD    B,0        ; Default reference value
  1240.     JR    Z,IFCERR1    ; If no second token, use default
  1241.     CALL    NEGCOMPL    ; Complement sense of testing
  1242.     CALL    GETNUM        ; Convert token to number in B
  1243. IFCERR1:
  1244.     CALL    GETER2        ; Get error flag value
  1245.     CP    B
  1246.     JR    NZ,IFCTRUE
  1247.     JR    IFCFALSE
  1248.     
  1249.      ELSE    ; NOT XERROPT
  1250.  
  1251.     CALL    GETER2        ; Get error byte
  1252.     JP    NZ,IFCTRUE
  1253.     JP    IFCFALSE
  1254.  
  1255.      ENDIF    ; XERROPT
  1256.  
  1257.  
  1258. ;-----------------------------------------------------------------------------
  1259.  
  1260. ; Condition:  TRUE
  1261. ;    IFCTRUE  enables an active IF
  1262. ;
  1263. ; Condition:  FALSE
  1264. ;    IFCFALSE enables an inactive IF
  1265.  
  1266. IFCTRUE:
  1267.     LD    A,(NEGFLAG)    ; Check for negation of test
  1268.     OR    A
  1269.     JR    NZ,IFCF        ; Make IF FALSE
  1270. IFCT:
  1271.     CALL    IFT        ; Make IF TRUE
  1272.     JR    NZ,RETURN
  1273.     JR    IFOVFL
  1274.  
  1275. IFCFALSE:
  1276.     LD    A,(NEGFLAG)    ; Check for negation of test
  1277.     OR    A
  1278.     JR    NZ,IFCT        ; Make IF TRUE
  1279. IFCF:
  1280.     CALL    IFF        ; Make IF FALSE
  1281.     JR    NZ,RETURN
  1282.  
  1283. IFOVFL:
  1284.     CALL    PRINT
  1285.     DEFB    BEL
  1286.     DEFB    ' IF Overflow',0
  1287.  
  1288. RETURN:
  1289.     LD    SP,(STACK)    ; Restore system stack
  1290.     RET            ; ..and return to operating system
  1291.  
  1292. ERRORMSG:            ; Return from errors with if state false
  1293.     CALL    PRINT
  1294.     DEFB    BEL,
  1295.     DEFB    ' --- Setting FALSE if State'
  1296.     DEFB    0
  1297.     JR    IFCF
  1298.  
  1299. ;=============================================================================
  1300. ;
  1301. ;        S U P P O R T    R O U T I N E S
  1302. ;
  1303. ;=============================================================================
  1304.  
  1305.  
  1306. ; Save TBUFF and skip to 2nd token
  1307.  
  1308. SKIP2:
  1309.     LD    DE,TBUFF+1    ; Pt to first char
  1310.     CALL    CODEND        ; Pt to free area
  1311.     PUSH    HL
  1312.  
  1313. SKIP2A:                ; Copy command line tail to buffer area
  1314.     LD    A,(DE)        ; Get next char
  1315.     LD    (HL),A        ; Save it
  1316.     INC    HL        ; Pt to next
  1317.     INC    DE
  1318.     OR    A        ; Done?
  1319.     JR    NZ,SKIP2A
  1320.  
  1321.     POP    HL        ; Point to command line tail again
  1322.     CALL    SKSP        ; Skip over spaces
  1323.     CALL    SKNSP        ; Skip over 1st token
  1324.     CALL    SKSP        ; Skip over spaces
  1325.  
  1326.     LD    A,(HL)        ; Get 1st char of 2nd
  1327.     OR    A        ; Return with Z if none
  1328.     RET
  1329.  
  1330. ;-------------------------
  1331.  
  1332. ; Convert chars pointed to by HL into a byte-length number in B.  Give an error
  1333. ; message if the value is not in byte range.
  1334.  
  1335. GETNUM:
  1336.     CALL    EVAL        ; Evaluate
  1337.     LD    B,E        ; Move low-byte value to B
  1338.     LD    A,D        ; Check high byte for zero
  1339.     OR    A
  1340.     RET    Z        ; Return if no overflow to high byte
  1341.  
  1342. NUMERROR:
  1343.     CALL    PRINT        ; Print error message and return with false
  1344.     DEFB    ' Bad number',0
  1345.     JR    ERRORMSG
  1346.  
  1347. ;-------------------------
  1348.  
  1349. ; Log into DU in FCB1
  1350.  
  1351. TLOG:
  1352.     LD    A,(FCB1)    ; Get disk
  1353.     OR    A        ; Current?
  1354.     JR    NZ,TLOG1
  1355.     LD    C,25        ; Get disk
  1356.     CALL    BDOS
  1357.     INC    A        ; Increment for following decrement
  1358. TLOG1:
  1359.     DEC    A        ; A=0
  1360.     LD    E,A        ; Disk in E
  1361.     LD    C,14
  1362.     CALL    BDOS
  1363.     LD    A,(FCB1+13)    ; Pt to user
  1364.     LD    E,A
  1365.     LD    C,32        ; Set user
  1366.     JP    BDOS
  1367.  
  1368. ;-------------------------
  1369.  
  1370. ; Try to evaluate the first token in the tail as a register number.  There
  1371. ; are some complications as a result of allowing nondecimal numbers.  We
  1372. ; check for condition 'EX' separately, and we also require that the entire
  1373. ; token be a number (no extra characters).  Otherwise EVAL can return a
  1374. ; zero value for miscellaneous strings.
  1375.  
  1376. REGTEST:
  1377.     LD    H,D        ; Move DE into HL for EVAL
  1378.     LD    L,E
  1379.     LD    A,(DE)        ; Check for special case of 'EX'
  1380.     CP    'E'        ; ..which can be mistaken for
  1381.     JR    NZ,REGTEST1    ; ..a hex number 0EH
  1382.     INC    DE
  1383.     LD    A,(DE)
  1384.     CP    'X'
  1385.     RET    Z        ; If we have 'EX' condition, return
  1386.  
  1387. REGTEST1:
  1388.     CALL    EVAL        ; Try to evaluate a number
  1389.     RET    C        ; Carry flag set if bad number
  1390.  
  1391.     LD    A,(HL)        ; If we are not at end of token
  1392.     CP    ' '+1        ; ..then we do not have a number
  1393.     RET    NC
  1394.  
  1395.     LD    A,E        ; Get low byte of number
  1396.     CP    10        ; If low byte >=10
  1397.     JR    NC,REGERROR    ; ..then it's out of range
  1398.  
  1399.     LD    A,D        ; Get high byte of number
  1400.     OR    A        ; If high byte  >0,
  1401.     JR    NZ,REGERROR    ; ..then it's out of range
  1402.  
  1403.     LD    B,E        ; Get register number into B
  1404.     CALL    GETREG        ; Get value of register into A
  1405.     PUSH    AF        ; Save value
  1406.     CALL    SKIP2        ; Point to second command line token
  1407.     CALL    GETNUM        ; Convert it to a number
  1408.     POP    AF        ; Get value
  1409.     CP    B        ; Compare against extracted value
  1410.     JP    Z,IFCTRUE    ; TRUE if match
  1411.     JP    IFCFALSE    ; FALSE if non-match
  1412.  
  1413. REGERROR:
  1414.     CALL    PRINT
  1415.     DEFB    ' Bad register number'
  1416.     DEFB    0
  1417.     JP    ERRORMSG    ; Return with false if
  1418.  
  1419. ;-------------------------
  1420.  
  1421. ; Test FCB1 against condition table (must have 2-char entries)
  1422. ;  Return with routine address in HL if match and NZ flag
  1423.  
  1424. CONDTEST:
  1425.     LD    HL,CONDTAB    ; Point to condition table
  1426. CONDT0:
  1427.     LD    A,(HL)        ; End of table?
  1428.     OR    A
  1429.     RET    Z
  1430.     LD    A,(DE)        ; Get first char of specified condition
  1431.     LD    B,(HL)        ; Get first char of table option into B
  1432.     INC    HL        ; Point to next characters
  1433.     INC    DE
  1434.     CP    B        ; Compare entries
  1435.     JR    NZ,CONDT2    ; Branch on mismatch
  1436.     LD    A,(DE)        ; Get 2nd char of given option
  1437.     OR    A        ; If not null
  1438.     JR    NZ,CONDT1    ; ..jump on
  1439.     LD    A,' '        ; Otherwise substitute a space
  1440. CONDT1:
  1441.     CP    (HL)        ; Compare
  1442.     JR    NZ,CONDT2
  1443.     INC    HL        ; Pt to address
  1444.     LD    A,(HL)        ; Get address in HL
  1445.     INC    HL
  1446.     LD    H,(HL)
  1447.     LD    L,A        ; HL = address
  1448.     XOR    A        ; Set NZ for OK
  1449.     DEC    A
  1450.     RET
  1451. CONDT2:
  1452.     LD    BC,3        ; Pt to next entry
  1453.     ADD    HL,BC        ; ... 1 byte for text + 2 bytes for address
  1454.     DEC    DE        ; Pt to 1st char of condition
  1455.     JR    CONDT0
  1456.  
  1457. ;-------------------------
  1458.  
  1459. ; This routine moves ARG1 into FCB1 and parses the third command line token
  1460. ; into FCB2.  On exit, HL is pointing to the name in FCB1.
  1461.  
  1462.      IF    XCOMP
  1463.  
  1464. SETCOMPARE:
  1465.     LD    HL,FCB2+1    ; Move name in FCB2 to FCB1
  1466.     LD    DE,FCB1+1
  1467.     LD    BC,11
  1468.     LDIR
  1469.     CALL    SKIP2        ; Find third token in command tail
  1470.     CALL    SKNSP        ; ..skip over second token
  1471.     CALL    SKSP        ; ..skip to beginning of third token
  1472.     LD    DE,FCB2        ; Parse token into FCB2
  1473.     XOR    A        ; ..using DIR form before DU
  1474.     CALL    ZFNAME
  1475.     LD    HL,FCB1+1    ; Compare first FCB to second
  1476.     RET
  1477.  
  1478.      ENDIF    ; XCOMP
  1479.  
  1480. ;-------------------------
  1481.  
  1482. ; FCB Comparison Subroutine
  1483. ;
  1484. ;    Returns with:
  1485. ;        Z     if FCBs are the same (wild cards are
  1486. ;            treated as equality)
  1487. ;        NZ    if FCBs are different
  1488. ;        C & NZ    if FCB1 is greater than FCB2
  1489. ;        NC & NZ    if FCB2 is greater than FCB1
  1490.  
  1491. COMPARE:
  1492.     LD    DE,FCB2+1
  1493.     LD    B,11        ; 11 chars
  1494. COMPARE1:
  1495.     LD    A,(DE)        ; Compare
  1496.     CP    '?'        ; See if an AFN was specified
  1497.     JR    Z,COMPARE2    ; Always match a ?
  1498.     LD    C,A        ; Save it in C temporarily
  1499.     LD    A,(HL)        ; Get the other character
  1500.     CP    '?'        ; See if it is a ?
  1501.     JR    Z,COMPARE2    ; If so accept it as a match
  1502.     CP    C
  1503.     RET    NZ        ; Return nonzero if no match
  1504. COMPARE2:
  1505.     INC    HL        ; Advance
  1506.     INC    DE
  1507.     DJNZ    COMPARE1    ; Count down
  1508.     RET            ; Return zero if match
  1509.  
  1510. ;-------------------------
  1511.  
  1512. ; This routine complements the negation flag to reverse the sense of testing.
  1513.  
  1514. NEGCOMPL:
  1515.     LD    A,(NEGFLAG)    ; Get current flag
  1516.     CPL            ; Complement it
  1517.     LD    (NEGFLAG),A    ; Save new value
  1518.     RET
  1519.     
  1520. ;--------------------------
  1521.  
  1522. ; This subroutine echoes the null-terminated string pointed to by HL to the
  1523. ; console.  The special symbol '^' in the string converts the following
  1524. ; character to a control character.  The special symbol '%' flags a special
  1525. ; function.  If followed by '<', output switches to upper case; if followed by
  1526. ; '>', output switches to lower case.  Other characters following the '%' are
  1527. ; echoed as is.
  1528.  
  1529. ECHO:
  1530.     XOR    A        ; Lower case flag setting
  1531.  
  1532.      IF    UPCASE        ; If upper case default
  1533.     DEC    A
  1534.      ENDIF
  1535.  
  1536.     LD    (CASEFL),A    ; Store flag in code below
  1537.  
  1538. ECHO1:
  1539.     CALL    GETCHAR        ; Get next character (returns if end of string)
  1540.     CP    '^'        ; Control character leadin?
  1541.     JR    NZ,ECHO2    ; Branch if not
  1542.  
  1543.     CALL    GETCHAR        ; Get next character
  1544.     AND    1FH        ; Convert to control character
  1545.     JR    ECHO4        ; Echo it
  1546.  
  1547. ECHO2:
  1548.     CP    '%'        ; Case shift prefix?
  1549.     JR    NZ,ECHO4    ; Branch if not
  1550.  
  1551.     CALL    GETCHAR        ; Get next character
  1552.     CP    UCASECH        ; Up-shift character?
  1553.     JR    Z,ECHO3        ; Store non-zero value in case flag
  1554.  
  1555.     CP    LCASECH        ; Lower-case character?
  1556.     JR    NZ,ECHO4    ; If not, echo the character as is
  1557.  
  1558.     XOR    A        ; Else, clear case flag
  1559. ECHO3:
  1560.     LD    (CASEFL),A
  1561.     JR    ECHO1        ; On to next character
  1562.  
  1563. ECHO4:
  1564.     LD    C,A        ; Save real character in C
  1565.     CP    'A'        ; Branch to ECHO5 if not in range A..Z
  1566.     JR    C,ECHO5
  1567.     CP    'Z'+1
  1568.     JR    NC,ECHO5
  1569.     ADD    20H        ; Make a lower case version
  1570. ECHO5:
  1571.     LD    D,A        ; Save lower case version in D
  1572. CASEFL    EQU    $+1        ; Pointer for in-the-code modification
  1573.     LD    A,0
  1574.     OR    A        ; Clear Z flag if upper case
  1575.     LD    A,C        ; Get upper case version of character
  1576.     JR    NZ,ECHO6    ; If upper case selected, go on as is
  1577.     LD    A,D        ; Else substitute lower case version
  1578. ECHO6:
  1579.     CALL    COUT        ; Output the character and return
  1580.     JR    ECHO1        ; Back to process next character
  1581.  
  1582. GETCHAR:
  1583.     LD    A,(HL)        ; Get character
  1584.     INC    HL        ; Point to next one
  1585.     OR    A        ; Check for end of string
  1586.     RET    NZ        ; If not end, return to caller
  1587.     POP    HL        ; Else, clean up stack
  1588.     RET            ; ..and exit from main routine
  1589.  
  1590. ;=============================================================================
  1591.  
  1592. ; Buffers
  1593.  
  1594.      IF    USEDSEG
  1595.     DSEG
  1596.      ENDIF    ; USEDSEG
  1597.  
  1598.      IF ATTROPTS
  1599. OFFSET:
  1600.     DEFS    2        ; Storage for attribute offset
  1601.      ENDIF            ; Attropts
  1602.  
  1603. NEGFLAG:
  1604.     DEFS    1        ; Negation flag
  1605.  
  1606.     DEFS    2*25        ; Space for local stack
  1607. STACK:    DEFS    2        ; Place to save system stack
  1608.  
  1609.     END
  1610.