; ; Program: LUSH - Library Utility Shell for ZCPR 3.3 ; Author: Carson Wilson ; Version 1.0 ; Date: 1 December 1987 ; Assembly: Z80ASM, SLRNK, Z33LIB, Z3LIB, SLUDIR11, SYSLIB ; ; Notes: Does not test for shell stack, etc., as these are a ; required part of all Z33 systems. ; Important -- the Z33LIB routines PARSE2, REPARSE, and SCAN ; assume that the CCP has not been overwritten. If you alter ; LUSH to load higher in memory be sure to take this into ; account. ; LUSH flushes pending commands from the MCL, and will not ; run under ZEX or SUBMIT. Since I cannot think of an instance ; where this will cause a problem, I have omitted ZEX/SUBMIT/MCL ; testing as outlined in Z33PNOTE.002 by Jay Sage. ; The structure and much of the code of LUSH are derived from ; Terry Carroll's ZLBR.COM, which in turn is derived from John ; Poplett's ZLUX.COM. vers equ 10 ; ; ASCII characters and constants ; duoff equ 30 ; Offset to DU byte on shell stack ; = 'DUU:PROGNAME DUU:LIBRNAME.LBR',0 minstk equ duoff+2 ; Minimum shell stack entry size cmdlen equ 60 ; Internal command line length cr equ 0dh ; carriage return ctrlc equ 03h ; ctrl-c lf equ 0ah ; line feed del equ 7fh ; delete null equ 0 ; null bel equ 7 ; bell bs equ 8 ; backspace tab equ 9 ; tab ctrls equ 'S'-40h ; ctrl-s ctrlx equ 'X'-40h ; ctrl-x ctrly equ 'Y'-40h ; ; Base page address equates ; FCB1 equ 5ch ; ccp file control block DMAadr equ 80h ; default DMA buffer address (dbuf) parm equ DMAadr+2 ; location of parameter in DMA ; ; Z3LIB & SYSLIB external references ; extrn codend,condin,luinit,ludir ; internal extrn fname,pfn1,phldc,phl4hc ; ..directory routines ; extrn z33chk,fcb1chk,gdefdu,reparse ; Z33LIB references extrn scan,parse2,getefcb,pdefdu,gcmddu ; extrn z3init,z3log,clrcl,putcl,putcst extrn getsh1,qshell,shfull,shpush,shpop extrn cout,cin,eprint,crlf,sknsp,capstr extrn epstr,f$exist,retud,sksp ; ; Link sludir11.rel before syslib 3.6: ; ; .request z33lib,z3lib,sludir11,syslib entry: jp begin ; Jump z33 header db 'Z3ENV' ; Z3 program type db 3 ; Type-3 environment z3eadr: dw 0 ; Environment address dw entry ; Set up stack ; begin: ld (stack),sp ; save z3's stack pointer ld sp,stack ; set new stack pointer call z33chk ; see if CCP is ZCPR 3.x jp nz,notz3x ; abort if not ; ; Initialize z3 environment ; ld hl,(z3eadr) ; get env ptr call z3init ; inform z3lib ; ; Clear Z3 CL buffer and set pointer to LBR's name ; call clrcl ; check for & clear z3 command line buffer ; call getsh1 ; shell stack address in hl (de preserved) ; ld a,minstk-1 ; Minimum stack entry size - 1 cp b ; Subtract B from accu. jp nc,shsmal ; Shell entry too small ; push hl ld de,sheltail-shdu ; offset to lbr name from our shell name add hl,de ; hl --> libname on shell stack ld (stklf$ptr),hl ; store pointer for use in DIR command ; pop hl ld de,duoff ; offset to DU bytes in entry add hl,de ld (stkdu$ptr),hl ; save pointer ; Test if GO or JUMP used, leave shell name intact if so. ; ld a,(goflg) ; GO or JUMP used? or a jp nz,GetOrig ; Yes, preserve shell name or 0ffh ; No, set flag ld (goflg),a ; ..for future GO's ; Save invocation DU and name for 1st load and shell restarts, ; but not for GO or JUMP ; call gcmddu ; Get DU of command ld hl,shdu ; Point to DU of our buffer ld a,b add a,'A' ; ASCII the drive ld (hl),a ; Store drive inc hl ld a,c ; Get U of command call convert1 ; Store A as 2 digits + colon ; ..HL points to next byte ; Get invocation name ; ex de,hl ; DE --> shell name call getefcb ; Get address of External FCB in HL jp z,noefcb ; No ext. FCB inc hl ; Point to command in eFCB ; ; Save invocation name ; ld b,8 ; Copy file name only from eFCB nxtchr: ld a,(hl) ld (de),a inc hl inc de djnz nxtchr ld a,' ' ld (de),a ; trailing space before libname ; fall thru GetOrig: call qshell ; Invoked as shell by CCP? jr z,restrt ; ..then don't resave call retud ; Return current DU in BC ld (du$orig),bc ; Save to put on sh. stk. later ; ; Skip to here on GO or JUMP ; ; Check for filename parameter and append to shell name if found ; ; Give help and abort if '', '/' or '//' entered at command line ; restrt: ld a,(FCB1+1) ; 1st char. cp ' ' jr z,jphlp ; Give help for no parameters cp '/' ; Slash? jr nz,nohlp ; Not a help command ld a,(FCB1+2) ; 2nd char. cp ' ' jr z,jphlp ; No 2nd char, help cp '/' ; '//' or no help jr nz,nohlp jphlp: jp z3hlp nohlp: call fcb1chk ; Z33 test for bad dirspec jp nz,direrr ; ld a,(FCB1) ; FCB1 drive byte into a or a ; current disk? jr nz,gotdrv ; otherwise a has it curdir: call gdefdu ; Z33 get defaults, B = drive, C = user ld a,b ; disk into a inc a ; bump it to 1=a, etc. gotdrv: add a,'A'-1 ; ASCII the drive ld hl,sheltail ; point to tail storage ld (hl),a ; plant drivespec of library file ld a,(FCB1+13) ; get ZCPR user number from FCB1 inc hl ; bump to user spot in our buffer call convert ; Store A as ASCII # at HL ; ..HL points to last byte of ; ..stored number putnam: ; Add library name after DU: ld de,FCB1+1 ; DE points to name ld a,' ' ; Put a space at FCB1 type ld (FCB1+9),a ; ..so we won't copy it nloop: ld a,(de) ; Get character from FCB1 cp ' ' ; End of filename? jr z,clapnd ; Go append .LBR ld (hl),a ; Character to sheltail inc hl ; Bump hl inc de ; And de djnz nloop ; Count down ; Add ".LBR" plus hex 0 to complete library name ; clapnd: call apnd ; Append ".LBR" at HL ; Add "LBR" to FCB1 and test for existence ; ld hl,FCB1+9 ; point at FCB1 filetype call apnd1 ; Plug in L and B and R ld de,FCB1 ; point at FCB1 for the libs call z3log ; log in to DU of library via z3lib call f$exist ; check via syslib jp z,err4 ; not found call retud ; get requested DU into BC call pdefdu ; make default DU for all ; ..LUSH commands ; Install command on shell stack, unless invoked as a shell or ; rerunning for LUSH command: ; ld a,(rerun) ; Rerun for LUSH command? or a ; ..rerun byte = 0 if reloaded jr z,shtest ; No, go see if invoked by CCP xor a ld (rerun),a ; Yes, reset flag ld hl,(stkdu$ptr) ; ..save DU of origin ld b,(hl) inc hl ld c,(hl) ld (du$orig),bc ; ..into buffer call shpop ; ..remove previous shell entry jr nushel ; ..and process as new shell shtest: call qshell ; Invoked as shell by CCP? jp z,getcmd ; ..then don't push on stack again nushel: call shfull ; Shell stack full? jr nz,makesh ; No, put us on stack ld bc,(du$orig) ; Yes, restore origin DU call pdefdu jp shfulerr ; ..and abort ; Put command name, library name, and origin DU on shell stack ; makesh: ld hl,shdu ; not full call shpush ; install us on shell stack ld hl,(stkdu$ptr) ; Point to sh stk location ld bc,(du$orig) ; Get original DU ld (hl),b ; Save origin DU inc hl ; ..on shell stack ld (hl),c ; Tell internal directory to display full directory on restarts. ; xor a ld (cmdln),a jp dir ; show directory, display prompt ; ..and wait for command ; ------------------------------------------------------------------ ; Display routines ; ----------------------- ; Command line help ; z3hlp: call progid call eprint db 'Library Utility Shell for ZCPR 3.3' db cr,lf,lf db ' Syntax: LUSH [dir: or du:]ufn[.LBR]',0 jp CRexit ; Program name ; progid: call eprint db cr,lf,'LUSH ' db [vers/10]+'0','.',[vers mod 10]+'0' db ' - ',0 ret ; Print current library name ; nameprt: ld hl,(stklf$ptr) call epstr ret ; -------------------- ; Error routines ; ---------------- notz3x: call progid call eprint db 'Need ZCPR 3.3',0 jp CRexit ; noeFCB: call progid call eprint db 'No Ext FCB',0 jp CRexit ; shsmal: call progid call eprint db 'Sh Stk too small',0 jp CRexit ; shfulerr: call progid call eprint db 'Sh Stk Full',0 jr CRexit ; ; err4: call progid call eprint db 'File not found',0 jr CRexit ; direrr: call progid call eprint db 'Illegal ZCPR directory',0 jr CRexit ; tpaerr: call progid call eprint db 'Library missing',0 ; fall thru ; -------------------------------------------------------------- ; Exit routines ; ------------------- CRexit: call crlf exit: ld hl,(stack) ; get old z3 stack ld sp,hl ; restore it ret ; return to cpr ; --------------------------------------------------------------- ; Restore original DU from shell stack ; ------------------- restore: ld hl,(stkdu$ptr) ld b,(hl) inc hl ld c,(hl) call pdefdu ; Restore default DU ret ; --------------------------------------------------------------- ; Internal command line routines ; ------------------------------ ; Print shell prompt ; getcmd: call progid call eprint db '^C to exit, ? for help' db cr,lf,lf,0 ; fall thru ; Print command line prompt and get user command ; hlfprt: call nameprt ; Print current library name ld a,'>' call cout ; fall thru ; ..and get command ; ----------------------------------------------------------------------- ; Internal command line editor (like ZRDOS's, but no ctrl chars printed) ; ------------------------ ; ; Main inline (modified from syslib) entry point ; Original author: Richard Conn ; Inline restart loop ; ; The cmdlin buffer is cleared on each restart. ; inline: ld hl,cmdln ; get start of string ld b,cmdlen ; length of command line in b clrllp: ld (hl),0 ; put null in command line byte inc hl ; increment pointer djnz clrllp ; loop til command line initialized (nulled) ld hl,cmdln ; start of command line in hl ld c,0 ; set char count ; Main loop ; inl1: call cin ; get input char cp null ; do not permit jr z,inl1 cp ctrlc ; ^c? jp z,ccabrt cp bs ; backspace? jr z,inbs cp del ; delete? jr z,inbs cp ctrls ; ^s? jr z,inbs ; backspace -c.w. cp cr ; carriage return? jr z,incr cp ctrlx ; ctrl-x? jr z,rexstrt cp ctrly ; ^Y? jr z,rexstrt cp ' ' jr c,inl1 ; skip control chars. ld (hl),a ; store char inc hl ; pt to next call cout inc c ; incr char cnt ld a,cmdlen ; max char cnt cp c ; compare jr nc,inl1 ; no overrun, loop ld a,bel ; load ASCII bell into a call cout ; sound bell jr inbs ; print backspace & loop ; Inline modules ; ; Ctrl-X or Ctrl-Y ; Erase (and backspace) line and restart: ; rexstrt: call eralin ; erase line jp inline ; startover ; eralin: ld a,c ; check for empty line or a ; 0 chars? ret z call exbs ; jr eralin ; Backspace -- delete previous char and back up cursor ; inbs: call exbs ; execute jp inl1 ; Backspace routine ; exbs: call BOL ; beginning of line? ret z ; continue if so dec c ; decr count dec hl ; back up ld a,bs ; print call cout ld a,' ' ; call cout ld a,bs ; jp cout ; Carriage return -- done; store ending zero ; incr: call crlf ; echo cr call BOL ; test if was at begin. of line jp z,getcmd ; yes, go back ld (hl),0 ; no, store ending zero jp gotcmd ; Support routines for command line editor ; ; BOL -- returns w/zero flag set if user at beginning of line ; BOL: ex de,hl ; de=hl ld hl,cmdln ; get start adr ex de,hl ; hl restored ld a,d ; check for match cp h ; match? ret nz ; no match ld a,e ; check for complete match cp l ret ; -------------------------------------------------------------- ; Resident command scanner ; ------------------------ ; ; Cmdtbl (command table) scanner (adapted from ZCPR3.Z80). ; On exit, HL contains address of command if found, ; zero flag set means valid command. ; Original author: Richard Conn ; cmdser: ld hl,cmdtbl ; pt to command table ld b,6 ; get size of command text cms1: ld a,(hl) ; check for end of table or a jr z,cms5 ld de,FCB1+1 ; pt to stored command FCB1 push bc ; save size of command text cms2: ld a,(de) ; compare stored against table entry cp (hl) ; hl is command table pointer jr nz,cms3 ; no match cms2a: inc de ; pt to next char inc hl djnz cms2 ; count down ld a,(de) ; next char in input command must be cp ' ' jr nz,cms4 pop bc ; clear stack ld a,(hl) ; get address from table into hl inc hl ld h,(hl) ld l,a ; hl contains address xor a ; zero flag set for command found ret ; command is resident (zero flag set) cms3: inc hl ; skip to next command table entry djnz cms3 cms4: pop bc ; get size of command text inc hl ; skip address inc hl jr cms1 cms5: xor a ; set nz dec a ; command not found if nz ret ; ------------------------------------------------------------------ ; Resident command table ; ----------------------- CmdTbl: db 'HELP ' dw help ; Display internal help db 'DIR ' dw dir ; Display current file's directory db 'LUSH ' dw lush ; Attach to another library file db 'KMD ' dw kmd ; Send file with KMD db 'XMODEM' dw kmd ; Send with XMODEM db 0 ; Marks end of table ; -------------------------------------------------------------- ; Command processing routines ; ---------------------- ; Control-C entered -- ; ; 1. Display abort message ; 2. Restore original DU from shell stack ; 3. Pop shell stack ; 4. Return to CCP ; CCabrt: call eralin ; erase line call eprint ; print exit message db 'Control-C detected. Returning to ZCPR33.',cr,lf,0 call restore ; Restore origin DU call shpop jp exit ; Process other commands: ; gotcmd: ld hl,cmdln ld a,(hl) ; Get first byte into A cp ';' ; Is it a comment line? jp z,hlfprt ; Yes, go back cp '?' ; A request for help? jp z,help ; If help char, go get help cp ' ' ; If headed with space then send jr z,noscan ; ..to ZCPR as "command [parm ...]" call capstr ; Else capitalize input call parse2 ; CCP to parse to FCB1 call cmdser ; ..and scan for resident commands ; CMDSER returns (Z), HL pointing to routine if command found. ; ; Command found? jp nz,pass ; No, pass to ZCPR as ; .."command DU:filename.LBR [parm ...]" jp (hl) ; Yes, process command ; Pass command to ZCPR as "command [parm parm ...]" ; noscan: xor a call putcst ; Tell ZCPR3 next is a normal command ld hl,cmdln ; Addr of command in HL call putcl ; Put on Z3 command line jp exit ; Let ZCPR3 process the command ; -------------------------------------------------------------- ; Resident command routines ; ------------------------- ; ; 1. DIR - display library's directory ; dir: call codend ; get codend for library buffers ld (lubuff),hl ; stash it ld hl,(stklf$ptr) ; address of token into hl ld de,ludFCB ; address of lud FCB in de call scan ; Z33 parse library name with dir. check ld de,lud ; point at SYSLIB library structure call luinit ; init w/syslib routine ld hl,cmdln call sknsp ; Point to dir. spec. call sksp ; ..if any ld de,lmbrn ; Point to FCB call fname ; Parse spec. with wildcard fill ld hl,lmbrn+1 ld bc,(lubuff) ; start address in heap for dir buffers ld de,lud ; address of lu descriptor call ludir ; get directory (array of 17 byte elements) jp nz,tpaerr ; jump on error call crlf ld b,2 ldhdr: call eprint defb 'Member name Recs Size CHEK ',0 djnz ldhdr call crlf xor a ld (lbrflg),a ld hl,(lubuff) ; get start address of array prlibm: call condin ; see if user typed anything jr z,prlbm1 ; no, continue cp ctrlc ; yes, was it control-c? jp z,ldone ; if z, quit prlbm1: ld a,(hl) ; get first byte into a or a ; check for end of array delimiter (null) jp z,ldone ; if null, quit printing loop cp ' ' ; is it the first element in the library? jr nz,n1stel ; no, go print ld de,17 ; yes, offset in de add hl,de ; add offset to ptr jr prlibm ; go get next array element n1stel: push hl ; else, save hl on stack ex de,hl ; put filename ptr into de for call to pfn1 call pfn1 ; print the name of the library member ld a,' ' ; ASCII space into accumulator call cout ; print it pop hl ; restore address of array ld de,13 ; offset to rec size in de add hl,de ; add it ex de,hl ; put address of lib member rec size in de call getwd ; get word pointed to by de into hl push de ; save ptr call phldc ; print rec size ld a,' ' ; print space call cout ld de,8 ; div by 8 to get size in kbytes call divhd ; hl = hl / de jr z,nrmndr ; check for remainder inc hl ; if remainder, bump up a kbyte nrmndr: ld a,l ; put lsb into accumulator or h ; or it with msb jr nz,nozero ; is it zero? inc hl ; yes, increment nozero: call phldc ; print result ld a,'k' ; k into accumulator call cout ; print it ld a,' ' ; space into acc call cout ; print it call cout ; print it again pop de ; restore ptr into array element call getwd ; get word pointed to by de into hl push de ; save ptr call phl4hc ; print crc value in hex ld a,(lbrflg) or a jr nz,nxtlin call eprint defb ' ',0 ld a,0ffh jr uniret nxtlin: call crlf xor a uniret: ld (lbrflg),a pop hl jp prlibm getwd: ld a,(de) ; get lsb of rec size ld l,a ; put it in l inc de ; increment pointer into array element ld a,(de) ; get msb ld h,a ; put it in h inc de ; increment ptr into array element ret ; ; Divhd -> HL = HL / DE. On entry, HL = dividend, DE = divisor; on return, ; result in HL, remainder in DE. Zero flag reset if remainder ; divhd: ld a,d ; put lsb of divisor in a or e ; or w/msb ret z ; return if null ld a,h ; get dividend in ac ld c,l call div16 ; divide ex de,hl ; put remainder in de ld h,a ; put quotient in hl ld l,c ; ld a,d ; reset flag if remainder or e ret div16: ld hl,0 ld b,16 loop16: rl c rla adc hl,hl sbc hl,de jr nc,$+3 add hl,de ccf djnz loop16 rl c rla ret ldone: call crlf jp getcmd ; ; 2. HELP - display help message ; help: call eprint db cr,lf db '>DIR [afn]. . . . . . . . . . . Display this library''s ' db 'directory',cr,lf db '>KMD|XMODEM S[K] ufn. . . . . . ' db 'Send a member of this library',cr,lf db '>LUSH [du:|dir:]ufn[.LBR] . . . Attach to another library ' db 'file',cr,lf db '>command [parameters] . . . . . command ',0 call nameprt call eprint db ' [parameters]',cr,lf db '> command [parameters]. . . . . command [parameters]' db cr,lf,0 jp getcmd ; ; 3. LUSH - attach to another library file, if found ; lush: ld hl,cmdln ; point to command buffer call parse2 ; Use Z33 CCP to parse call reparse ; ..2nd token to FCB1 or 0ffh ; Set flag to push and pop ld (rerun),a ; ..shell stack with new name jp restrt ; restart lush ; ; 4. KMD - process KMD, XMODEM commands ; kmd: ld b,0ffh ; flag for PARMS ld hl,cmdln ; point to input command ld de,mcmdln ; point to our scratchpad buffer push de ; save addr for putcl call strcp ; move (hl) to (de) to 0 terminator ld hl,mcmdln call sknsp ; skip to 1st space call sksp ; skip over spaces ex de,hl ld a,'L' ; For "KMD L" or "XMODEM L" ld (de),a inc de ; Point to "K" or space inc de ; Add it to command jr parms ; Build rest of command line ; ------------------------------------------------------------- ; Resident command subroutines ; ---------------------------- ; Pass command as "command LIBNAME tail" ; pass: ld b,0 ; Not XMODEM/KMD command ld hl,cmdln ; point to input command ld de,mcmdln ; point to our scratchpad buffer push de ; save addr for putcl call strcp ; move (hl) to (de) to 0 terminator ld hl,mcmdln call sknsp ; skip to 1st ' ' in scratch buffer ex de,hl ; append ; fall thru ; ; Build ZCPR command line and execute ; parms: ld a,' ' ; put in space ld (de),a ; ..in case there wasn't one inc de ld hl,(stklf$ptr) ; the lbr's full name after command 1 call strcp ; move that ld hl,cmdln ; inline's buffer call sknsp ; skip to 1st space or null ld a,b ; B is flag or a ; ..KMD or XMODEM? jr z,parms1 ; No, just append tail call sksp ; Yes, call sknsp ; ..skip 2nd parm. parms1: call strcp ; add trailing parms w/null pop hl ; get back mcmdln call putcl ; give it to z3 xor a call putcst ; ensure that shell recovers jp exit ; and boogie ; ----------------------------------------------------------- ; General support routines ; ------------------------ ; Convert hexadecimal user area number to 1- or 2-digit ASCII ; plus colon and store beginning at (HL). ; Exit: HL points to byte after colon. ; Registers affected: HL, BC, A ; convert: ld b,'0'-1 ; preset for two-digit calculation later cp 10 ; see if single digit jr nc,twodig ; if not, print two digits add a,'0' ; else convert to ASCII ld (hl),a ; and plant it jr putcln ; then do colon convert1: ; make 2 digits ld b,'0'-1 ; preset for two-digit calculation later twodig: inc b ; count tens digit in b sub 10 ; keep subtracting 10 until carry is set jr nc,twodig add a,10 ; get remainder (units digit) back ld c,a ; save it in c ld (hl),b inc hl ld a,c add a,'0' ld (hl),a putcln: inc hl ; Place colon after DU ld (hl),':' inc hl ret ; Append .LBR extension to HL-pointed command string ; apnd: ld (hl),'.' inc hl apnd1: ld (hl),'L' inc hl ld (hl),'B' inc hl ld (hl),'R' inc hl ld (hl),0 ; In case we change libraries ret ; ; Copy HL to DE up to null terminator ; Registers affected: HL, DE, AF StrCp: ld a,(hl) ; get ld (de),a ; put or a ; have we copied null terminator ret z ; yes, return inc hl ; no, point to next byte in source str inc de ; point to next byte in destination str jr strcp ; loop til null terminator copied ; --------------------------------------------------------------------- ; Data areas ; ---------------------------- ; Initialized storage ; rerun: db 0 ; flag for "LUSH" command goflg: db 0 ; Flag set for GO or JUMP ; ----------------------- ; RAM area ; ----------------------- dseg ; ; LUSH installs shname in zcpr3's shell stack with du:lbrname.LBR as tail ; shdu: ds 13 ; Storage for shell duu: + name + " " sheltail: ds 17 ; Storage for 'duu:library .LBR',0 du$orig: ds 2 ; Buffers for DIR command ; lbrflg: ds 1 lubuff: ds 2 lmbrn: ds 36 ; Shell stack pointers ; stkdu$ptr: ds 2 ; Pointer to origin DU storage shellptr: ds 2 ; Pointer to shell stk. stklf$ptr: ds 2 ; pointer to libname string on shell stk. ; cmdln: ds cmdlen+2 ; Command line editor buffer ; mcmdln: ds cmdlen+2 ; Scratch buffer for processing commands ; ; SYSLIB LU routine buffers ; lud: ds 6 ; data for lu routines ds 11 ; name of current file ludFCB: ds 36 ; FCB of library file ; ds 64 ; room for 32 level stack stack: ds 2 ; old system stack saved here ; end ; END LUSH.Z80