* SYSTEM SEGMENT: SYS.FCP * SYSTEM: ZCPR3 * CUSTOMIZED BY: RICHARD CONN * * PROGRAM: SYSFCP.ASM * AUTHOR: RICHARD CONN * VERSION: 1.0 * DATE: 22 FEB 84 * PREVIOUS VERSIONS: NONE * VERSION EQU 10 * * Global Library which Defines Addresses for SYSTEM * MACLIB Z3BASE ; USE BASE ADDRESSES MACLIB SYSFCP ; USE EQUATES FROM HEADER FILE ; LF EQU 0AH CR EQU 0DH BELL EQU 07H ; BASE EQU 0 WBOOT EQU BASE+0000H ;CP/M WARM BOOT ADDRESS UDFLAG EQU BASE+0004H ;USER NUM IN HIGH NYBBLE, DISK IN LOW BDOS EQU BASE+0005H ;BDOS FUNCTION CALL ENTRY PT TFCB EQU BASE+005CH ;DEFAULT FCB BUFFER FCB1 EQU TFCB ;1st and 2nd FCBs FCB2 EQU TFCB+16 TBUFF EQU BASE+0080H ;DEFAULT DISK I/O BUFFER TPA EQU BASE+0100H ;BASE OF TPA ; $-MACRO ;FIRST TURN OFF THE EXPANSIONS ; ; MACROS TO PROVIDE Z80 EXTENSIONS ; MACROS INCLUDE: ; ; JR - JUMP RELATIVE ; JRC - JUMP RELATIVE IF CARRY ; JRNC - JUMP RELATIVE IF NO CARRY ; JRZ - JUMP RELATIVE IF ZERO ; JRNZ - JUMP RELATIVE IF NO ZERO ; DJNZ - DECREMENT B AND JUMP RELATIVE IF NO ZERO ; ; @GENDD MACRO USED FOR CHECKING AND GENERATING ; 8-BIT JUMP RELATIVE DISPLACEMENTS ; @GENDD MACRO ?DD ;;USED FOR CHECKING RANGE OF 8-BIT DISPLACEMENTS IF (?DD GT 7FH) AND (?DD LT 0FF80H) DB 100H,?DD ;Displacement Range Error ELSE DB ?DD ENDIF ;;RANGE ERROR ENDM ; ; ; Z80 MACRO EXTENSIONS ; JR MACRO ?N ;;JUMP RELATIVE IF I8080 ;;8080/8085 JMP ?N ELSE ;;Z80 DB 18H @GENDD ?N-$-1 ENDIF ;;I8080 ENDM ; JRC MACRO ?N ;;JUMP RELATIVE ON CARRY IF I8080 ;;8080/8085 JC ?N ELSE ;;Z80 DB 38H @GENDD ?N-$-1 ENDIF ;;I8080 ENDM ; JRNC MACRO ?N ;;JUMP RELATIVE ON NO CARRY IF I8080 ;;8080/8085 JNC ?N ELSE ;;Z80 DB 30H @GENDD ?N-$-1 ENDIF ;;I8080 ENDM ; JRZ MACRO ?N ;;JUMP RELATIVE ON ZERO IF I8080 ;;8080/8085 JZ ?N ELSE ;;Z80 DB 28H @GENDD ?N-$-1 ENDIF ;;I8080 ENDM ; JRNZ MACRO ?N ;;JUMP RELATIVE ON NO ZERO IF I8080 ;;8080/8085 JNZ ?N ELSE ;;Z80 DB 20H @GENDD ?N-$-1 ENDIF ;;I8080 ENDM ; DJNZ MACRO ?N ;;DECREMENT B AND JUMP RELATIVE ON NO ZERO IF I8080 ;;8080/8085 DCR B JNZ ?N ELSE ;;Z80 DB 10H @GENDD ?N-$-1 ENDIF ;;I8080 ENDM * * SYSTEM Entry Point * org fcp ; passed for Z3BASE db 'Z3FCP' ; Flag for Package Loader * * **** Command Table for FCP **** * This table is FCP-dependent! * * The command name table is structured as follows: * * ctable: * DB 'CMNDNAME' ; Table Record Structure is * DW cmndaddress ; 8 Chars for Name and 2 Bytes for Adr * ... * DB 0 ; End of Table * cnsize equ 4 ; NUMBER OF CHARS IN COMMAND NAME db cnsize ; size of text entries ctab: db 'IF ' dw ifstart db 'ELSE' dw ifelse db 'FI ' dw ifend db 'XIF ' dw ifexit db 0 ; ; Condition Table ; condtab: ; IF IFOTRUE db 'T ' ;TRUE dw ifctrue db 'F ' ;FALSE dw ifcfalse ENDIF ; IF IFOEMPTY db 'EM' ;file empty dw ifcempty ENDIF ; IF IFOERROR db 'ER' ;error message dw ifcerror ENDIF ; IF IFOEXIST db 'EX' ;file exists dw ifcex ENDIF ; IF IFOINPUT db 'IN' ;user input dw ifcinput ENDIF ; IF IFONULL db 'NU' dw ifcnull ENDIF ; IF IFOTCAP ;Z3 TCAP available db 'TC' dw ifctcap ENDIF ; IF IFOWHEEL ;Wheel Byte db 'WH' dw ifcwheel ENDIF ; db 0 * * Print " IF" * prif: call print db 'IF',' '+80H ret * * Print String (terminated in 0 or MSB Set) at Return Address * print: IF NOISE mvi a,' ' ;print leading space call conout ENDIF ;NOISE xthl ; get address call print1 xthl ; put address ret * * Print String (terminated by MSB Set) pted to by HL * print1: mov a,m ; done? inx h ; pt to next call conout ; print char ora a ; set MSB flag (M) rm ; MSB terminator jr print1 * * **** FCP Routines **** * All code from here on is FCP-dependent! * ; ; FCP Command: XIF ; XIF terminates all IFs, restoring a basic TRUE state ; ifexit: IF NOISE call nl ;print new line ENDIF ;NOISE call iftest ;see if current IF is running and FALSE jrz ifstat ;abort with status message if so lxi h,z3msg+1 ;pt to IF flag xra a ;A=0 mov m,a ;zero IF flag jr ifendmsg ;print message ; ; FCP Command: FI ; FI decrements to the previous IF ; ; Algorithm: ; Rotate Current IF Bit (1st IF Message) Right 1 Bit Position ; ifend: IF NOISE call nl ;print new line ENDIF ;NOISE lxi h,z3msg+1 ;pt to IF flag mov a,m ;get it ora a ;no IF active? jrz ifnderr ifendmsg: IF NOISE push psw ;save A call print db 'T','o'+80H ;prefix to status display pop psw ;get A ENDIF ;NOISE rrc ;move right 1 bit ani 7fh ;mask msb 0 mov m,a ;store active bit jrnz ifstat ;print status if IF still active ifnderr: IF NOISE call print ;print message db 'N','o'+80H jmp prif ELSE ;NOT NOISE ret ENDIF ;NOISE ; ; FCP Command: ELSE ; ELSE complements the Active Bit for the Current IF ; ; Algorithm: ; If Current IF is 0 (no IF) or 1 (one IF), then toggle ; Active IF Bit associated with Current IF ; Else ; If Previous IF was Active then toggle ; Active IF Bit associated with Current IF ; Else do nothing ; ifelse: IF NOISE call nl ;print new line ENDIF ;NOISE lxi h,z3msg+1 ;pt to IF msgs mov a,m ;get current IF mov b,a ;save current IF in B inx h ;pt to active IF message rrc ;back up to previous IF level ani 7fh ;mask out possible carry jrz iftog ;toggle if IF level is 0 or 1 ana m ;determine previous IF status jrz ifstat ;don't toggle, and just print status iftog: mov a,m ;get active IF message cma ;flip bits ana b ;look at only interested bit mov c,a ;result in C mov a,b ;complement IF byte cma mov b,a mov a,m ;get active byte ana b ;mask in only uninterested bits ora c ;mask in complement of interested bit mov m,a ;save result and fall thru to print status ; ; Indicate if current IF is True or False ; ifstat: IF NOISE call prif mvi b,'F' ;assume False call iftest ;see if IF is FALSE (Z if so) jrz ifst1 ;Zero means IF F or No IF mvi b,'T' ;set True ifst1: mov a,b ;get T/F flag and fall thru to print it ELSE ;NOT NOISE ret ENDIF ;NOISE ; ; Console Output Routine ; conout: push h ; save regs push d push b push psw ani 7fh ; mask MSB mov e,a ; char in E mvi c,2 ; output call bdos pop psw ; get regs pop b pop d pop h ret ; ; Output LF (to go with CR from ZCPR3) ; nl: mvi a,lf ;output LF jr conout ; ; FCP Command: IF ; ifstart: IF NOISE call nl ;print new line ENDIF ;NOISE call iftest ;see if current IF is running and FALSE ; IF NOT COMIF jrz ifcfalse ;raise next IF level to FALSE if so ELSE jz ifcf ENDIF ;NOT COMIF ; ;**************************************************************** ;* * ;* IF.COM Processing * ;* * ;**************************************************************** ; ; If IF.COM to be processed, goto ROOT (base of path) and load it ; IF COMIF ; ; Get Current Disk and User in BC ; lda udflag ;get UD push psw ;save UD flag ani 0fh ;get disk sta cdisk ;set current disk mov b,a ;B=disk (A=0) pop psw ;get UD flag rlc ;get user in low 4 bits rlc rlc rlc ani 0fh ;get user sta cuser ;set current user mov c,a ;... in C ; ; Pt to Start of Path ; lxi h,expath ;pt to path ; ; Check for End of Path ; fndroot: mov a,m ;check for done ora a ;end of path? jrz froot2 ; ; Process Next Path Element ; cpi '$' ;current disk? jrnz froot0 lda cdisk ;get current disk inr a ;+1 for following -1 froot0: dcr a ;set A=0 mov b,a ;set disk inx h ;pt to user mov a,m ;get user cpi '$' ;current user? jrnz froot1 lda cuser ;get current user froot1: mov c,a ;set user inx h ;pt to next jr fndroot ; ; Done with Search - BC Contains ROOT DU ; froot2: ; ; Log Into ROOT ; call logbc ;log into root DU ; ; Set Address of Next Load and Set DMA for OPEN ; lxi h,100h ;pt to TPA shld nxtload ;set address for next load xchg ;DE=100H so don't wipe out buffers mvi c,26 ;set DMA call bdos ; ; Try to Open File IF.COM ; lxi d,extfcb ;pt to FCB mvi c,15 ;open file call bdos inr a ;check for found jz ifnotfnd ; ; Load File IF.COM ; ifload: ; ; Set Load Address ; lhld nxtload ;get address of next load push h ;save it lxi d,80h ;pt to following dad d shld nxtload pop d ;get load address mvi c,26 ;set DMA call bdos ; ; Read in Block (Sector) and Loop Back if Not Done ; lxi d,extfcb ;read file mvi c,20 push d ;save ptr in case of failure (done) call bdos pop d ora a ;OK? jz ifload ; ; Done - Close File ; mvi c,16 ;close file call bdos ; ; Reset Environment (DMA and DU) and Run IF.COM ; call reset ;reset DMA and directory jmp tpa ;run IF.COM ; ; Reset DMA Address and Current Disk (in CDISK) and User (in CUSER) ; reset: lxi d,80h ;reset DMA address mvi c,26 call bdos lda cdisk ;return home mov b,a lda cuser mov c,a ; ; Log Into DU in BC ; logbc: mov e,b ;set disk push b mvi c,14 ;select disk call bdos pop b mov e,c ;set user mvi c,32 ;select user jmp bdos ; ; IF.COM not found - Process as IF F ; ifnotfnd: call reset ;return home jr ifcf ; ; Buffers for COMIF ; nxtload: ds 2 ;address of next block (sector) to load cuser: ds 1 ;current user cdisk: ds 1 ;current disk (A=0) ; ENDIF ;COMIF ; IF NOT COMIF ;**************************************************************** ;* * ;* Non-IF.COM Processing * ;* * ;**************************************************************** ; ; Test for Equality if Enabled ; IF IFOEQ lxi h,tbuff+1 ;look for '=' in line tsteq: mov a,m ;get char inx h ;pt to next ora a ;EOL? jrz ifck0 ;continue if so cpi '=' ;'=' found? jrnz tsteq lxi h,fcb1+1 ;compare FCBs lxi d,fcb2+1 mvi b,11 ;11 bytes eqtest: ldax d ;compare cmp m jrnz ifcf inx h ;pt to next inx d djnz eqtest jr ifct ENDIF ;IFOEQ ; ; Test Condition in FCB1 and file name in FCB2 ; Execute condition processing routine ; ifck0: lxi d,fcb1+1 ;pt to first char in FCB1 ; IF IFONEG ldax d ;get it sta negflag ;set negate flag cpi negchar ;is it a negate? jrnz ifck1 inx d ;pt to char after negchar ifck1: ENDIF ;IFONEG ; IF IFOREG ;REGISTERS call regtest ;test for register value jrnz runreg ENDIF ;IFOREG ; call condtest ;test of condition match jrnz runcond ;process condition call print ;beep to indicate error db bell+80H jmp ifstat ;no condition, display current condition ; ; Process register - register value is in A ; IF IFOREG runreg: push psw ;save value call getnum ;extract value in FCB2 as a number pop psw ;get value cmp b ;compare against extracted value jrz ifctrue ;TRUE if match jr ifcfalse ;FALSE if non-match ENDIF ;IFOREG ; ; Process conditional test - address of conditional routine is in HL ; runcond: pchl ;"call" routine pted to by HL ; ENDIF ;NOT COMIF ; ; ; Condition: NULL (2nd file name) ; IF IFONULL ifcnull: lda fcb2+1 ;get first char of 2nd file name cpi ' ' ;space = null jrz ifctrue jr ifcfalse ENDIF ;IFONULL ; ; Condition: TCAP ; IF IFOTCAP ifctcap: lda z3env+80H ;get first char of Z3 TCAP Entry cpi ' '+1 ;space or less = none jrc ifcfalse jr ifctrue ENDIF ;IFOTCAP ; ; Condition: WHEEL ; IF IFOWHEEL ifcwheel: lhld z3env+29h ;get address of wheel byte mov a,m ;get byte ora a ;test for true jrz ifcfalse ;FALSE if 0 jr ifctrue ENDIF ;IFOWHEEL ; ; Condition: TRUE ; IFCTRUE enables an active IF ; Condition: FALSE ; IFCFALSE enables an inactive IF ; ifctrue: ; IF IFONEG call negtest ;test for negate jrz ifcf ENDIF ;IFONEG ; ifct: mvi b,0ffh ;active jmp ifset ifcfalse: ; IF IFONEG call negtest ;test for negate jrz ifct ENDIF ;IFONEG ; ifcf: mvi b,0 ;inactive jmp ifset ; ; Condition: INPUT (from user) ; IF IFOINPUT ifcinput: lxi h,z3msg+7 ;pt to ZEX message byte mvi m,10b ;suspend ZEX input push h ;save ptr to ZEX message byte IF NOT NOISE call nl ENDIF ;NOT NOISE call prif call print db 'True?',' '+80H mvi c,1 ;input from console call bdos pop h ;get ptr to ZEX message byte mvi m,0 ;return ZEX to normal processing cpi ' ' ;yes? jrz ifctrue ani 5fh ;mask and capitalize user input cpi 'T' ;true? jrz ifctrue cpi 'Y' ;yes? jrz ifctrue cpi CR ;yes? jrz ifctrue jr ifcfalse ENDIF ;IFOINPUT ; ; Condition: EXIST filename.typ ; IF IFOEXIST ifcex: call tlog ;log into DU lxi d,fcb2 ;pt to fcb mvi c,17 ;search for first call bdos inr a ;set zero if error jrz ifcfalse ;return FALSE jr ifctrue ;return TRUE ENDIF ;IFOEXIST ; ; Condition: EMPTY filename.typ ; IF IFOEMPTY ifcempty: call tlog ;log into FCB2's DU lxi d,fcb2 ;pt to fcb2 mvi c,15 ;open file push d ;save fcb ptr call bdos pop d inr a ;not found? jrz ifctrue mvi c,20 ;try to read a record call bdos ora a ;0=OK jrnz ifctrue ;NZ if no read jr ifcfalse ENDIF ;IFOEMPTY ; ; Condition: ERROR ; IF IFOERROR ifcerror: lda z3msg+6 ;get error byte ora a ;0=TRUE jrz ifctrue jr ifcfalse ENDIF ;IFOERROR ; ; **** Support Routines **** ; ; ; Convert chars in FCB2 into a number in B ; IF IFOREG getnum: mvi b,0 ;set number lxi h,fcb2+1 ;pt to first char getn1: mov a,m ;get char inx h ;pt to next sui '0' ;convert to binary rc ;done if error cpi 10 ;range? rnc ;done if out of range mov c,a ;value in C mov a,b ;A=old value add a ;*2 add a ;*4 add b ;*5 add a ;*10 add c ;add in new digit value mov b,a ;result in B jr getn1 ;continue processing ENDIF ;IFOREG ; ; Log into DU in FCB2 ; IF NOT COMIF tlog: lda fcb2 ;get disk ora a ;current? jrnz 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 fcb2+13 ;pt to user mov e,a mvi c,32 ;set user jmp bdos ; ENDIF ;NOT COMIF ; ; Test of Negate Flag = negchar ; IF IFONEG negtest: negflag equ $+1 ;pointer for in-the-code modification mvi a,0 ;2nd byte is filled in cpi negchar ;test for No ret ENDIF ;IFONEG ; ; Test FCB1 against a single digit (0-9) ; Return with register value in A and NZ if so ; IF IFOREG regtest: ldax d ;get digit sui '0' jrc zret ;Z flag for no digit cpi 10 ;range? jrnc zret ;Z flag for no digit lxi h,z3msg+30H ;pt to registers add l ;pt to register mov l,a mov a,h ;add in H aci 0 mov h,a xra a ;set NZ dcr a mov a,m ;get register value ret zret: xra a ;set Z ret ENDIF ;IFOREG ; ; Test to see if a current IF is running and if it is FALSE ; If so, return with Zero Flag Set (Z) ; If not, return with Zero Flag Clear (NZ) ; Affect only HL and PSW ; iftest: lxi h,z3msg+1 ;get IF flag mov a,m ;test for active IF ora a jrz ifok ;no active IF inx h ;pt to active flag ana m ;check active flag rz ;return Z since IF running and FALSE ifok: xra a ;return NZ for OK dcr a ret ; ; Test FCB1 against condition table (must have 2-char entries) ; Return with routine address in HL if match and NZ flag ; IF NOT COMIF 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 jrnz condt2 ldax d ;get 2nd char cmp m ;compare jrnz 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 jr condt1 ; ENDIF ;NOT COMIF ; ; Turn on next IF level ; B register is 0 if level is inactive, 0FFH is level is active ; Return with Z flag set if OK ; ifset: lxi h,z3msg+1 ;get IF flag mov a,m ora a ;if no if at all, start 1st one jrz ifset1 cpi 80h ;check for overflow (8 IFs max) jrz iferr inx h ;pt to active IF byte ana m ;check to see if current IF is TRUE jrnz ifset0 ;if TRUE, proceed mvi b,0 ;set False IF ifset0: dcx h ;pt to IF level mov a,m ;get it rlc ;advance to next level ani 0feh ;only 1 bit on mov m,a ;set IF byte jr ifset2 ifset1: inr a ;A=1 mov m,a ;set 1st IF inx h ;clear active IF byte mvi m,0 dcx h ifset2: mov d,a ;get IF byte ana b ;set interested bit mov b,a inx h ;pt to active flag mov a,d ;complement IF byte cma mov d,a mov a,m ;get active byte ana d ;mask in only uninterested bits ora b ;mask in complement of interested bit mov m,a ;save result call ifstat ;print status xra a ;return with Z ret iferr: call print ;beep to indicate overflow db bell+80H xra a ;set NZ dcr a ret ; ; Test for Size Error ; if ($ GT (FCP + FCPS*128)) sizerr equ novalue ;FCP is too large for buffer endif end