; ; Program: SH ; Author: Richard Conn ; Version: 1.0 ; Date: 5 Mar 84 ; ; ; This program illustrates the design of a simple shell under ZCPR3 ; using Z3LIB. This program is transportable from one ZCPR3 system to another ; provided it is reassembled with the correct address for the ZCPR3 ; Environment Descriptor (Z3ENV) or DDT is used to patch this address ; (which is the first two bytes after the opening JMP). If an external ; ZCPR3 Environment Descriptor is not available, one will have to be ; provided by setting the Z3ENV equate to 0 and providing SYSENV.LIB in ; the current directory at assembly time. ; ; ; Equates for Key Values ; z3env SET 0f400h ;address of ZCPR3 environment lecnt equ 20 ;number of pointers on String Ptr Stack ctrlz equ 'Z'-'@' ;^Z for EOF cmdch equ '!' ;in comment mode, invoke following text as command cmtch equ ';' ;comment character subch equ '%' ;substitution flag fcb equ 5ch tbuff equ 80h cr equ 0dh lf equ 0ah ; ; External Z3LIB and SYSLIB Routines ; ext getshm,putshm ext getud,putud,logud,initfcb,f$open,f$read,f$close ext parser,codend,caps,fillb,hmovb,root,getfn1,pfn1 ext retud,print,pafdc,bbline,moveb,crlf,cout,sksp,dutdir ext z3init,getsh2,shpush,shpop,qshell,getefcb,putcl,putcst,putzex ; ; 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 ; ; Set Pointers ; call codend ;find scratch area shld intline ;set ptr to internal line buffer lxi d,200H ;reserve 200H bytes dad d shld varlist ;set ptr to variable list xra a sta loadfl ;say variables not loaded ; ; Check for Shell Stack ; call getsh2 ;get shell status jnz start0 ;skip over shell init call print db 'No Shell Stack',0 ret ; ; See if this program was invoked as a shell ; start0: call qshell ;find out from ZCPR3 environment jz shell ;do not push onto stack if invoked as a shell start1: ; ; Clear Shell Message for Comment Flag ; mvi b,0 ;message 0 xra a ;A=0 to clear call putshm ;set message ; ; Set Name of Shell Variable File if One is Given ; call getfn1 ;pt to buffer in env lxi d,fcb+1 ;pt to name xchg ;copy from name mvi b,11 ;11 bytes mov a,m ;check for name cpi ' ' ;no name if space jz defname call moveb ;copy if name present call setshdef ;set default file type jmp setshn ; ; Set Name of SH.VAR File ; defname: call getfn1 ;get name lxi d,shvfcb+1 ;pt to FCB xchg mvi b,11 ;11 chars call moveb ; ; Set Name of Shell from External FCB if Possible or From Default if Not ; setshn: call root ;get root address lxi h,shdisk ;pt to shell disk mov a,b ;get disk adi 'A' ;convert to letter mov m,a ;set disk letter inx h ;pt to user 10's mov a,c ;get user number mvi b,10 ;subtract 10's mvi d,'0' ;set char setshn1: sub b ;subtract jc setshn2 inr d ;increment digit jmp setshn1 setshn2: add b ;get 1's mov m,d ;set 10's digit for user inx h ;pt to 1's digit adi '0' ;compute 1's digit mov m,a ;set 1's digit call getefcb ;get ptr to external fcb jz start2 ;no external FCB, so use default name inx h ;pt to program name lxi d,shname ;pt to string mvi b,8 ;8 chars call moveb ;copy into buffer ; ; Push Name of Shell onto Stack ; start2: lxi h,shdisk ;pt to name of shell call shpush ;push shell onto stack jnz start3 ; ; Shell Successfully Installed ; call print db 'Shell Installed',0 ret ; ; Shell Stack Push Error ; start3: cpi 2 ;shell stack full? jnz start4 ; ; Shell Stack is Full ; call print db 'Shell Stack Full',0 ret ; ; Shell Stack Entry Size is too small for command line ; start4: call print db 'Shell Stack Entry Size',0 ret ; ; Restart on Empty Line ; shellr: call crlf ;new line ; ; Print Shell Prompt ; shell: call retud ;get current user and disk mov a,b ;save disk adi 'A' call cout ;print disk letter mov a,c ;get user call pafdc ;print A as floating decimal call dutdir ;convert into DIR reference if possible jz shell1 ;no match mvi a,':' ;print colon call cout mvi b,8 ;8 chars max shell0: mov a,m ;get char inx h ;pt to next cpi ' ' ;space? cnz cout jnz shell0 shell1: mvi b,0 ;get shell message 0 call getshm ani 1 ;test for prompt jz shell2 ;print normal prompt call print ;comment format db '; ',0 ;comment jmp shell3 shell2: call print ;normal format db '>> ',0 ;double prompt ; ; Accept User Input ; shell3: mvi a,1 ;tell ZEX that prompt is up call putzex xra a ;don't capitalize call bbline mvi a,0 ;say that normal processing is running now call putcst call putzex call sksp ;skip over leading spaces mvi b,0 ;get shell message 0 call getshm ani 1 ;test for comment jz shell4 ;process normally ; ; Process Shell Input as Comment ; mov a,m ;get char cpi cmdch ;command override? jnz shellr ;continue with next line if not inx h ;pt to command and fall thru to process ; ; Process Shell Command ; shell4: mov a,m ;get first char ora a ;no line? jz shellr cpi cmtch ;comment line? jz shellr ; ; Check for and Process Shell-Resident Command ; push h ;save HL call shcommand ;check for shell command pop h ;restore HL ; ; Expand Shell Command Line ; call expand ;expand line pted to by HL jz clovfl ;abort if overflow ; ; Load Multiple Command Line ; call putcl ;place command line pted to by HL into CL Buffer rnz ;resume ZCPR3 processing ; ; Input Line is Longer than Command Line Buffer ; clovfl: call print ;command line buffer has overflowed db cr,lf,'CL Ovfl ',0 jmp shellr ; ; Expand Shell Command Line (pted to by HL), performing variable ; Substitutions ; ; On exit, Z=command line overflow and Line Pted to by HL ; expand: push b ;save counter mvi b,0 ;get shell register 0 call getshm ani 2 ;test for echo cnz crlf ;new line if SHECHO is ON pop b ;get counter xchg ;DE pts to line ; ; Init String Pointer Stack ; mvi a,lecnt ;set local element count sta locelt lxi h,locstk ;set local stack shld locadr lxi h,0 ;set done code on stack call locpush ;push HL ; ; Set Ptrs ; lhld intline ;pt to internal line xchg ;DE pts to internal line, HL pt next char mvi b,0 ;256 chars max ; ; Analyze Next Char ; exp1: mov a,m ;get next char cpi subch ;substitution char? jnz exp2 ;handle normally ; ; Process Shell Variable ; call expvar ;resolve variable dcr c ;error? jz exp1 ;resume if none ; ; Store Next Char ; exp2: stax d ;store char ; ; Print Char if SHECHO is ON ; push b ;save counter mov c,a ;save char mvi b,0 ;get shell message 0 call getshm ;determines if display is on ani 2 ;test for echo jz exp3 mov a,c ;get char ani 7FH ;mask and don't output null cnz cout ;echo char ; ; Advance to Next Char ; exp3: mov a,c ;get char pop b ;get counter inx h ;pt to next inx d dcr b ;count down jz experr ;error if at 0 ora a ;done? jnz exp1 inr b ;increment count (not counting last 0) dcx d ;pt to 0 in case of abort ; ; Pop String Ptr Stack and Check for Analysis Complete ; call locpop ;get ptr to previous string mov a,h ;done? ora l jnz exp1 ;resume dcr a ;set NZ ; ; Expansion Complete ; On entry, Z Flag is Set Accordingly (Z=Error) ; experr: lhld intline ;pt to internal line ret ; ; Expand Variable ; Return with HL pting to next char, A=char, C=1 if OK, C=2 if error ; expvar: shld varptr ;save ptr to variable inx h ;get next char mvi c,2 ;prep for error return mov a,m ;get it ora a ;EOL? rz cpi subch ;double sub char? rz ;place one sub char in line if so ; ; Place Variable Into SHVAR ; push b ;save counter push d ;save ptr to next char push h ;save ptr to shell variable lxi h,shvar ;pt to shell variable buffer mvi b,8 ;8 chars max mvi a,' ' ;space fill call fillb xchg ;DE pts to shell variable buffer pop h ;pt to shell variable mvi b,8 ;8 chars max ; ; Place Shell Variable into Buffer ; expv1: mov a,m ;get char call delck ;check for delimiter jz expv3 ;done if delimiter stax d ;save char inx h ;pt to next inx d dcr b ;count down jnz expv1 ; ; Flush Overflow of Shell Variable ; expv2: mov a,m ;get char inx h ;pt to next call delck ;check for delimiter jnz expv2 dcx h ;pt to delimiter ; ; Shell Variable in buffer SHVAR ; HL pts to delimiter after variable in user line ; expv3: call locpush ;stack ptr to next char in current string jz expv4 ;error in stack call varload ;load shell variable list jz expv4 ;failure call namer ;resolve named variable reference mvi c,1 ;OK jz expv5 ;name found - resolve ; ; Shell Variable Not Resolved - Restore Ptr to It ; expv4: call locpop ;restore ptr mvi c,2 ;error lhld varptr ;pt to variable ; ; Entry Point for OK Return ; expv5: mov a,m ;get char pop d ;pt to target pop b ;get counter ret ; ; Push HL onto String Ptr Stack ; Return with Z if Stack Overflow ; locpush: lda locelt ;get count dcr a ;full? rz sta locelt ;set count push d ;save DE xchg ;DE pts to old string lhld locadr ;get ptr to top of stack mov m,e ;store low inx h mov m,d ;store high inx h ;pt to next shld locadr xchg ;restore HL pop d ;restore DE xra a ;return NZ dcr a ret ; ; Pop HL from String Ptr Stack ; locpop: push d lda locelt ;increment element count inr a sta locelt lhld locadr ;get address dcx h ;pt to high mov d,m ;get high dcx h ;pt to low mov e,m ;get low shld locadr ;set address xchg ;restore ptr pop d ret ; ; Load Shell Variable List ; varload: push h ;save regs push d push b lda loadfl ;already loaded? ora a ;NZ=yes jnz varl3 lhld varlist ;clear varlist in case of error mvi m,ctrlz ; ; Look for Variable File ; call getfn1 ;pt to file name of SH.VAR lxi d,shvfcb+1 mvi b,11 ;11 bytes call moveb call putud ;save current location call root ;determine DU of root call logud ;goto root lhld varlist ;pt to named variable list lxi d,shvfcb ;try to open file call initfcb ;init FCB call f$open jz varl1 ; ; Variable File Not Found ; call getud ;return home xra a ;set not found code pop b ;restore regs pop d pop h ret ; ; Read in Variable File ; varl1: lxi d,shvfcb ;read in file call f$read jnz varl2 lxi d,tbuff ;pt to data xchg ;copy into memory mvi b,128 ;128 bytes call hmovb xchg jmp varl1 varl2: lxi d,shvfcb ;close file call f$close call getud ;return home ; ; Say List is Already Loaded ; varl3: xra a ;return NZ for OK dcr a sta loadfl ;set loaded flag pop b ;restore regs pop d pop h ret ; ; Resolve Named Variable Reference ; On input, SHVAR contains the shell variable name and ; CODEND pts to the list of shell variables, terminated by ^Z; ; if found, return with HL pting to name and Z ; namer: lhld varlist ;pt to variable list namer1: mov a,m ;get char cpi ctrlz ;end of list? jz namex lxi d,shvar ;pt to shell variable name mvi b,8 ;8 chars namer2: ldax d ;get name cmp m ;match? jnz nomatch inx h ;pt to next inx d dcr b ;count down jnz namer2 ret ;found! nomatch: mov a,m ;flush to end of string inx h ;pt to next ora a jnz nomatch jmp namer1 ;resume search ; ; Search Failed ; namex: ora a ;return NZ (^Z in A) ret ; ; Check to see if char in A is a delimiter ; Return with Z if so ; delck: push h ;pt to table push b ;save BC call caps ;capitalize char mov b,a ;char in B lxi h,dtable ;pt to delimiter table delck1: mov a,m ;get delimiter ora a ;done? jz notdel cmp b ;compare jz yesdel inx h ;pt to next jmp delck1 notdel: mov a,b ;get char ora a ;set Z if null, else NZ yesdel: mov a,b ;restore char pop b ;restore regs pop h ret ; ; Delimiter Table ; dtable: db '<>;:,.=-_ ',0 ; ; Check for Shell Command and Process if Found ; HL pts to command line ; shcommand: xra a ;DIR before DU call parser ;parse command line pted to by HL inx d ;pt to name lxi h,ctable ;pt to command table shcmd: mov a,m ;get first char of next entry ora a ;done? rz mvi b,8 ;commands are 8 chars long push h ;save ptr to FCB push d ;save ptr to table entry shcmd1: ldax d ;compare cmp m jnz shcmd2 inx h ;pt to next inx d dcr b ;count down jnz shcmd1 ; ; Command Found - Get Address ; mov a,m inx h mov h,m mov l,a ;HL is address ; ; Clear Stack and Run ; pop psw ;clear stack pop psw pop psw ;clear return address pop psw ;clear pushed HL pchl ;"run" command ; ; Command Not Found Yet ; shcmd2: pop d ;restore ptrs pop h lxi b,10 ;advance to next command dad b jmp shcmd ;resume search ; ; If File Type not Specified, Set Default ; setshdef: call getfn1 ;check for file type lxi d,8 ;pt to file byte dad d xchg lxi h,shvtype ;default file type mvi b,3 ;3 chars ldax d ;get char cpi ' ' ;set if space cz moveb ;copy ret ; ; Pop Current Shell ; shexit: call print db cr,lf,'Exiting Shell',0 jmp shpop ;clear shell stack entry ; ; Toggle Shell Comment Mode ; shcomment: mvi b,0 ;access shell register 0 call getshm mov c,a ;save in C ani 0FEH ;all bits but comment bit mov d,a mov a,c ;get comment bit cma ;flip comment bit (other bits are 1) ani 1 ;select just comment bit ora d ;OR in other bits call putshm ;set new value jmp shellr ;resume ; ; Toggle Shell Echo Mode ; shecho: call print db cr,lf,' Echo of Shell Commands is O',0 mvi b,0 ;access shell register 0 call getshm mov c,a ;save in C ani 0FDH ;all bits but echo bit mov d,a mov a,c ;get comment bit cma ;flip comment bit (other bits are 1) ani 2 ;select just echo bit ora d ;OR in other bits call putshm ;set new value ani 2 ;test echo bit jz shecho1 call print db 'N',0 jmp shellr shecho1: call print db 'FF',0 jmp shellr ;resume ; ; Print Names of SH Commands ; shhelp: call print db cr,lf,'SH Commands --',cr,lf,0 lxi h,ctable ;pt to table mvi c,0 ;set count shh1: mov a,m ;done? ora a jz shellr call print db ' ',0 mvi b,8 ;8 chars shh2: mov a,m ;get char call cout inx h ;pt to next dcr b ;count down jnz shh2 inr c ;increment count mov a,c ani 3 ;new line? cz crlf inx h ;skip address inx h jmp shh1 ;next ; ; Command Table ; ctable: db '? ' ;help dw shhelp db 'SHCMT ' ;comment mode dw shcomment db 'SHECHO ' ;echo input dw shecho db 'SHEXIT ' ;exit shell dw shexit db 0 ;end of table ; ; Buffers ; shvfcb: db 0 db 'SH ' ;name of shell variable file shvtype: db 'VAR' ds 24 ;36 bytes total shdisk: db 'A' ;disk letter db '00' ;user number db ':' ;separator shname: db 'SH ',0 ;name of shell to go onto stack shvar: db ' ' ;shell variable locelt: ds 1 ;string stack element count locadr: ds 2 ;ptr to next entry on stack locstk: ds lecnt*2 ;string ptr stack varptr: ds 2 ;ptr to current variable in line varlist: ds 2 ;ptr to named variable list intline: ds 2 ;ptr internal expansion line loadfl: ds 1 ;variables loaded flag end