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 / Z3-33 / Z33IF14.LBR / IF14.ZZ0 / IF14.Z80
Text File  |  2000-06-30  |  46KB  |  1,726 lines

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