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 / Z33FCP10.LBR / Z33IF.ZZ0 / Z33IF.Z80
Text File  |  2000-06-30  |  43KB  |  1,619 lines

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