;
; Program: IF
; Author: Richard Conn
; Modified By: Charles McManis
; Version: 1.2
; Date: 11 Feb 85
; Previous Versions:  1.1 (22 Apr 84)
;
version equ     12

;
;       IF is intended to be invoked from the IF routine in an FCP.
; This program implements the IF conditional tests and sets the next level
; of IF to be TRUE or FALSE.
;
; Modified on 02/11/85 to accept ambiguous file names and match them. This
; allows aliases to add file extensions if they are needed, for instance
; if there is an alias LDIR that gets a directory of an .LBR file, it
; previously had to be defined as an example :
;
;

;
; Equates for Key Values
;
z3env   SET     0f400h  ;address of ZCPR3 environment
noise   equ     0       ;set to 1 for noisey (message) operation
negchar equ     '~'     ;negation prefix char
bdos    equ     5
fcb1    equ     5ch
fcb2    equ     6ch
tbuff   equ     80h
cr      equ     0dh
lf      equ     0ah
bel     equ     07h

;
; External Z3LIB and SYSLIB Routines
;
       ext     z3init,strtzex,stopzex,geter1,getreg,ift,iff,getenv
       ext     eval10,print,capine,codend,sksp,sknsp,zfname,cout

;
; Environment Definition
;
       if      z3env ne 0
;
; External ZCPR3 Environment Descriptor
;
       jmp     start
       db      'Z3ENV' ;This is a ZCPR3 Utility
       db      1       ;External Environment Descriptor
z3eadr:
       dw      z3env
start:
       lhld    z3eadr  ;pt to ZCPR3 environment
;
       else
;
; Internal ZCPR3 Environment Descriptor
;
       MACLIB  Z3BASE.LIB
       MACLIB  SYSENV.LIB
z3eadr:
       jmp     start
       SYSENV
start:
       lxi     h,z3eadr        ;pt to ZCPR3 environment
       endif

;
; Start of Program -- Initialize ZCPR3 Environment
;
       call    z3init  ;initialize the ZCPR3 Environment
       jmp     ifstart
;
; Condition Table
;
condtab:
       db      'T '            ;TRUE
       dw      ifctrue
       db      'F '            ;FALSE
       dw      ifcfalse
       db      'EM'            ;file empty
       dw      ifcempty
       db      'ER'            ;error message
       dw      ifcerror
       db      'EX'            ;file exists
       dw      ifcex
       db      'IN'            ;user input
       dw      ifcinput
       db      'NU'            ;null argument
       dw      ifcnull
       db      'TC'            ;Z3TCAP Entry Loaded
       dw      ifctcap
       db      'WH'            ;Wheel Byte
       dw      ifcwheel
       db      0

;
; FCP Extension Command: IF
;
ifstart:
;
; Advance to Next Line if Noisey
;
       IF      NOISE
       mvi     a,lf
       call    cout
       ENDIF           ;NOISE
;
; Test for Equal Sign in Line and Process FCB1=FCB2 form if so
;
       lxi     h,tbuff+1       ;pt to buffer
ifteq:
       mov     a,m             ;look for =
       inx     h               ;pt to next
       ora     a               ;done if EOL
       jz      ifck0
       cpi     '='             ;equal?
       jnz     ifteq
       lxi     h,fcb1+1        ;= found, so compare FCB1 and FCB2
       lxi     d,fcb2+1
       mvi     b,11            ;11 chars
ifteq1:
       ldax    d               ;compare
; ** Such a small change really.
       cpi     '?'             ; see if an AFN was specified
       jz      okchar          ; always match a ?
       mov     c,a             ; save it in C temporarily
       mov     a,m             ; get the other character
       cpi     '?'             ; see if it is a ?
       jz      okchar          ; if so accept it as a match
       cmp     c
; ** This allows IF $1=* and IF $1=*.?q? etc
;       cmp     m               ; this guy is no longer needed.
       jnz     ifcf            ;FALSE if no match
okchar:
       inx     h               ;advance
       inx     d
       dcr     b               ;count down
       jnz     ifteq1
       jmp     ifct            ;TRUE if match
;
; Test Condition in FCB1 and file name in FCB2
;   Execute condition processing routine
;
ifck0:
       lxi     d,fcb1+1        ;pt to first char in FCB1
       ldax    d               ;get it
       cpi     '/'             ;help?
       jz      ifhelp
       cpi     ' '             ;also help
       jz      ifhelp
       sta     negflag         ;set negate flag
       cpi     negchar         ;is it a negate?
       jnz     ifck1
       inx     d               ;pt to char after negchar
ifck1:
       call    regtest         ;test for register value
       jnz     runreg
       call    condtest        ;test of condition match
       jnz     runcond         ;process condition
       IF      NOISE
       call    print
       db      ' No IF Condition Given',0
       ret
       ELSE            ;NOT NOISE
       mvi     a,bel
       jmp     cout
       ENDIF           ;NOISE
;
; Print Help Message
;
ifhelp:
       IF      NOT NOISE
       mvi     a,lf    ;leading new line
       call    cout
       ENDIF           ;NOT NOISE
       call    print
       db      'IF, Version '
       db      (version/10)+'0','.',(version mod 10)+'0'
       db      ' - Conditional Test'
       db      cr,lf,'Syntax:'
       db      cr,lf,' IF condition arguments -or- IF ~condition arguments'
       db      cr,lf,'where a leading "~" negates the effect of the '
       db      'IF Condition'
       db      cr,lf,'Possible IF Conditions are:'
       db      cr,lf,' T                       Always TRUE'
       db      cr,lf,' F                       Always FALSE'
       db      cr,lf,' EMPTY <file list>       T if Files are Empty'
       db      cr,lf,' ERROR                   T if Error Flag Set'
       db      cr,lf,' EXIST <file list>       T if Files Exist'
       db      cr,lf,' INPUT                   T if User Hits T, Y, CR, or SP'
       db      cr,lf,' NULL arg                T if No Arg Follows'
       db      cr,lf,' TCAP                    T if ZCPR3 TCAP Available'
       db      cr,lf,' WHEEL                   T if Wheel Byte Set'
       db      cr,lf,' reg value               T if Register reg = value'
       db      cr,lf,' fcb1=fcb2               T if the Two FCB values are ='
       db      cr,lf,'Only first 2 letters of keywords are required'
       db      cr,lf,'The leading "~" is effective with all conditions except'
       db      ' fcb1=fcb2'
       db      0
       ret
;
; Process register - register value is in A
;
runreg:
       push    psw             ;save value
       call    getnum          ;extract value in FCB2 as a number
       pop     psw             ;get value
       cmp     b               ;compare against extracted value
       jz      ifctrue         ;TRUE if match
       jmp     ifcfalse        ;FALSE if non-match
;
; Process conditional test - address of conditional routine is in HL
;
runcond:
       pchl                    ;"call" routine pted to by HL

;
; Condition:  NULL (2nd file name)
;
ifcnull:
       lda     fcb2+1          ;get first char of 2nd file name
       cpi     ' '             ;space = null
       jz      ifctrue
       jmp     ifcfalse

;
; Condition:  TCAP
;
ifctcap:
       call    getenv          ;get ptr to ZCPR3 environment descriptor
       lxi     d,80h           ;pt to TCAP entry
       dad     d
       mov     a,m             ;get first char
       cpi     ' '+1           ;space or less = none
       jc      ifcfalse
       jmp     ifctrue

;
; Condition:  WHEEL
;
ifcwheel:
       call    getenv          ;get ptr to ZCPR3 environment descriptor
       lxi     d,29h           ;pt to Wheel Byte address
       dad     d
       mov     a,m             ;get low
       inx     h
       mov     h,m             ;get high
       mov     l,a             ;put low
       mov     a,m             ;get Wheel Byte
       ora     a               ;0=not wheel
       jz      ifcfalse
       jmp     ifctrue

;
; Condition:  TRUE
;       IFCTRUE  enables an active IF
; Condition:  FALSE
;       IFCFALSE enables an inactive IF
;
ifctrue:
       call    negtest ;test for negate
       jz      ifcf    ;make IF FALSE
ifct:
       IF      NOISE
       call    print
       db      ' IF T',0
       ENDIF           ;NOISE
       call    ift     ;make IF TRUE
       rnz
       jmp     ifovfl
ifcfalse:
       call    negtest ;test for negate
       jz      ifct    ;make IF TRUE
ifcf:
       IF      NOISE
       call    print
       db      ' IF F',0
       ENDIF           ;NOISE
       call    iff     ;make IF FALSE
       rnz
ifovfl:
       IF      NOISE
       call    print
       db      ' IF Overflow',0
       ret
       ELSE            ;NOT NOISE
       mvi     a,bel
       jmp     cout
       ENDIF           ;NOISE

;
; Condition: INPUT (from user)
;
ifcinput:
       IF      NOT NOISE
       mvi     a,lf            ;new line
       call    cout
       ENDIF           ;NOT NOISE
       call    stopzex         ;suspend ZEX input
       call    print
       db      ' IF True? ',0
       call    capine
       call    strtzex         ;resume ZEX input
       cpi     'T'             ;true?
       jz      ifctrue
       cpi     'Y'             ;yes?
       jz      ifctrue
       cpi     cr              ;new line?
       jz      ifctrue
       cpi     ' '             ;space?
       jz      ifctrue
       jmp     ifcfalse

;
; Condition: EXIST filename.typ
;       List of Files Permitted
;
ifcex:
       call    skip2   ;skip to 2nd token
       jz      ifctrue ;declare TRUE if none
;
; Extract Next File
;
ifcex1:
       lxi     d,fcb1  ;pt to FCB
       call    zfname  ;convert text
       push    h       ;save ptr to next char
;
; Log Into to DU and Search for File
;
       call    tlog    ;log into DU
       lxi     d,fcb1  ;pt to fcb
       mvi     c,17    ;search for first
       call    bdos
       inr     a       ;set zero if error
;
; Abort as FALSE if File Not Found
;
       pop     h       ;get ptr to next char
       jz      ifcfalse
;
; Advance to Next File, if Any
;
       mov     a,m     ;more to follow?
       inx     h
       cpi     ','
       jz      ifcex1
;
; All Files Exist if No More Files
;
       jmp     ifctrue ;all found, so TRUE

;
; Condition: EMPTY filename.typ
;
ifcempty:
       call    skip2   ;skip to 2nd token
       jz      ifctrue ;TRUE if none
;
; Select Next File
;
ifcem1:
       lxi     d,fcb1  ;pt to FCB1
       call    zfname  ;convert
       push    h       ;save ptr to next
;
; Log into DU and Try to Open File
;
       call    tlog            ;log into FCB1's DU
       lxi     d,fcb1          ;pt to fcb1
       mvi     c,15            ;open file
       push    d               ;save fcb ptr
       call    bdos
       pop     d
       inr     a               ;not found?
;
; File is Empty if Not Found
;
       jz      ifemt
;
; Try to Read one Record from File
;
       mvi     c,20            ;try to read a record
       call    bdos
       ora     a               ;0=OK
;
; File is Empty if Can't Read Record
;
       jnz     ifemt           ;NZ if no read
       pop     h               ;file not empty
;
; File Exists and Contains Something
;
       jmp     ifcfalse        ;so EMPTY condition is FALSE
;
; File is Empty - Advance
;
ifemt:
       pop     h               ;pt to next char
       mov     a,m             ;get next char
       inx     h
       cpi     ','             ;more to come?
       jz      ifcem1
;
; Done and True if No More Files - All are Empty
;
       jmp     ifctrue         ;all empty, so TRUE

;
; Condition: ERROR
;
ifcerror:
       call    geter1          ;get error byte
       jz      ifctrue
       jmp     ifcfalse

;
; **** Support Routines ****
;

;
; Save TBUFF and skip to 2nd token
;
skip2:
       lxi     d,tbuff+1       ;pt to first char
       call    codend          ;pt to free area
skip2a:
       ldax    d               ;get next char
       mov     d
       ora     a               ;done?
       jnz     skip2a
       call    codend          ;skip over spaces
       call    sksp
       call    sknsp           ;skip over 1st token
       call    sksp            ;skip over spaces
       mov     a,m             ;get 1st char of 2nd token
       ora     a               ;return with Z if none
       ret

;
; Convert chars in FCB2 into a number in B
;
getnum:
       lxi     h,fcb2+1        ;pt to first char
       call    eval10  ;evaluate
       mov     b,a     ;value in B
       ret

;
; Log into DU in FCB1
;
tlog:
       lda     fcb1    ;get disk
       ora     a       ;current?
       jnz     tlog1
       mvi     c,25    ;get disk
       call    bdos
       inr     a       ;increment for following decrement
tlog1:
       dcr     a       ;A=0
       mov     e,a     ;disk in E
       mvi     c,14
       call    bdos
       lda     fcb1+13 ;pt to user
       mov     e,a
       mvi     c,32    ;set user
       jmp     bdos

;
; Test of Negate Flag = negchar
;
negtest:
       lda     negflag         ;get flag
       cpi     negchar         ;test for No
       ret

;
; Test FCB1 against a single digit (0-9)
;  Return with register value in A and NZ if so
;
regtest:
       ldax    d               ;get digit
       sui     '0'
       jc      zret            ;Z flag for no digit
       cpi     10              ;range?
       jnc     zret            ;Z flag for no digit
       mov     b,a             ;register number in B
       call    getreg          ;get register value
       mov     b,a             ;save value
       xra     a               ;set NZ
       dcr     a
       mov     a,b             ;get register value
       ret
zret:
       xra     a               ;set Z
       ret

;
; Test FCB1 against condition table (must have 2-char entries)
;  Return with routine address in HL if match and NZ flag
;
condtest:
       lxi     h,condtab       ;pt to table
condt1:
       mov     a,m             ;end of table?
       ora     a
       rz
       ldax    d               ;get char
       mov     b,m             ;get other char in B
       inx     h               ;pt to next
       inx     d
       cmp     b               ;compare entries
       jnz     condt2
       ldax    d               ;get 2nd char
       cmp     m               ;compare
       jnz     condt2
       inx     h               ;pt to address
       mov     a,m             ;get address in HL
       inx     h
       mov     h,m
       mov     l,a             ;HL = address
       xra     a               ;set NZ for OK
       dcr     a
       ret
condt2:
       lxi     b,3             ;pt to next entry
       dad     b               ; ... 1 byte for text + 2 bytes for address
       dcx     d               ;pt to 1st char of condition
       jmp     condt1

;
; Buffers
;
negflag:
       ds      1               ;negation flag

       end