; Z2CP40-2.Z80 ; Revisiona to ZCPR Version 3.4 to make Version 4.0 (C) ; Copyright Harold F. Bower, 1992, all rights reserved. ;============================================================================= ; C O M M A N D L I N E P R O C E S S I N G C O D E ;============================================================================= ; MAIN ENTRY POINT TO CPR ; This is the main entry point to the command processor. On entry the C ; register must contain the value of the user/drive to be used as the current ; directory. ZCPR: LD HL,(Z3ENV+26H) ;4.0E Get ptr to Ext stack LD DE,XSTKSZ ;4.0E .set size of stack ADD HL,DE ;4.0E ..and compute starting address LD SP,HL ;4.0E LD (RESTART+1),HL ;4.0E Store inline for easy access later ;4.0E LD SP,STACK ; Reset stack IF BANKED CALL GO_SYS ;4.0E LD A,(TPABNK) ;4.0E Get TPA Bank # LD (PBANK+1),A ;4.0E ..and save for PEEK/POKE CALL GO_TPA ;4.0E PUSH BC ;4.0E Save DU in C LD A,(SYSBNK) ;4.0E Get SYS Bank # LD C,A ;4.0E .in position for Store LD HL,0006H ;4.0E Point to BDOS Vector LD E,(HL) ;4.0E INC HL ;4.0E LD D,(HL) ;4.0E .load Arg DEC HL ;4.0E ..back up to start CALL PUTWRD ;4.0E ...and Store in Bank in case RSX altered it POP BC ;4.0E Restore DU in C ENDIF ;4.0E banked IF pwnoecho LD A,0C3H ; Get a JP instruction LD (BIOS+12),A ; Reenable bios conout routine ENDIF ;pwnoecho ; If the HIGHUSER option is enabled, we compare the user/drive in the login ; byte in C to the values stored in the message buffer. If, ignoring bit 4 ; of the user number, they match, then we remain in the current area, which ; may be a user area above 15. LD HL,CURUSR ; Point to current user (and drive) IF highuser LD A,(HL) ; Get current user (can be 0..31) RLCA ; Ensures Cy and A0 = 0 RLA RLA RLA ; Move to high nibble, masking MSB INC HL ; Point to current drive # OR (HL) ; Construct User/Drive byte, to compare with C CP C ; ..and do compare JR Z,ZCPR1 ; Skip update if no change in DU DEC HL ; Point back to curusr ENDIF ;highuser LD (HL),C ; Save user/drive code temporarily XOR A ; High and low nibbles to zero RRD ; A(low) -> CURUSR(high) -> ; .. CURUSR(low) -> A(low) ; Now A has drive from CURUSR low nibble ; ..and CURUSR has user from high nibble INC HL ; Point to drive byte LD (HL),A ; Store drive byte ZCPR1: ; This block of code is executed when submit processing is enabled. We log ; into user area 0, where the submit file is kept, and we search the ; designated drive for the file. The result is kept in SUBFLAG. This code ; only has to be executed on reentry to the command processor at the main ; entry point. Commands that do not reboot but simply return to the CPR will ; execute without the disk reset and file search required here. Ron Fowler ; pointed out a shortcut based on the fact that after a disk reset, the A ; regiser contains a value of 0 if there is no file on drive A with a '$' in ; the file name and 0FFH if there is such a file. Thus if A = 0, there can ; be no '$$$.SUB' file on drive A. This trick is, unfortunately, not reliable ; under some versions of ZRDOS. Therefore, an option has been included to ; use or not use this shortcut. IF subon ; If submit facility enabled .. ; Removed call to defltdma as function 13 does it. jww ; call defltdma ; Set DMA address to 80H IF subzero ; If all submits through user 0 XOR A ; Log into user area 0 ELSE LD A,(CURUSR) ; Else use current user number ENDIF ;subzero CALL SETUSER LD C,B_RESET ; Reset disk system (returns 0FFH if a $$$.SUB CALL BDOSSAVE ; ..file might exist on drive A) LD DE,SUBFCB ; Point to submit file FCB with explicit drive IF subclue CALL NZ,SRCHFST ; Search only if flag says it could exist ELSE ;not subclue CALL SRCHFST ; Search for the file unconditionally ENDIF ;subclue LD (SUBFLAG),A ; Set flag for result (0 = no $$$.SUB) ELSE ;not subon LD C,B_RESET ; Reset disk system CALL BDOSSAVE ENDIF ; subon JR NEXTCMD ; Go to entry point for processing next command ;----------------------------------------------------------------------------- ; NEW COMMAND LINE ENTRY POINT ; This entry point is used when ZCPR35 finds the command line empty. A call to ; READBUF gets the next command line from the following possible sources in ; this order: ; 1) a running ZEX script ; 2) the submit file $$$.SUB (if enabled) ; 3) the shell stack ; 4) the user ; If the line comes from the shell stack, then the shell bit in the command ; status flag is set. RESTART: LD SP,$-$ ;4.0E Reset stack (Set inline from main entry) XOR A LD (CMDSTATFL),A ; Reset ZCPR3 command status flag INC A ; Set ZEX message byte to 1 to LD (ZEXINPFL),A ; ..indicate command prompt IF subon LD (XSUBFLAG),A ; Ditto for XSUB flag ENDIF ;subon LD HL,CMDLIN ; HL --> beginning of command line buffer LD (NXTCHR),HL ; Save as pointer to next character to process LD (HL),0 ; Zero out command line (in case of warm boot) PUSH HL ; Save pointer to command line CALL READBUF ; Input command line (ZEX, submit, shell, ; .. or user) POP HL ; Get back pointer to command line LD A,(HL) ; Check for comment line CP COMMENT ; Begins with comment character? JR Z,RESTART ; If so, go back for another line ; Otherwise, fall through ;----------------------------------------------------------------------------- ; COMMAND CONTINUATION PROCESSING ENTRY POINT ; This is the entry point for continuing the processing of an existing command ; line. The current drive and user values as known to the CPR are combined ; and made into the user/drive byte that CP/M keeps at location 0004. If the ; HIGHUSER option is enabled, the user number for this byte is forced to be ; in the range 0..15. Next the command status flag is processed. The error ; and ECP bits in the actual flag are reset, and the original flag is checked ; for an ECP error return (both ECP bit and error bit set). In that case, ; control is transferred to the error handler. NEXTCMD: LD HL,(CURUSR) ; Get currently logged drive and user LD A,L ; Work on user number RLCA ; Clears Cy and A0 RLA RLA RLA ; Move to high nibble, masking MSB OR H ; ..and drive into low nibble LD (UDFLAG),A ; Set user/disk flag in page 0 LD A,2 ; Turn ZEX input redirection off LD (ZEXINPFL),A IF subon LD (XSUBFLAG),A ; Turn off XSUB input redirection ENDIF ;subon LD HL,CMDSTATFL ; Point to the command status flag (CSF) LD A,(HL) ; Get a copy into register A RES 1,(HL) ; Reset the actual error bit RES 2,(HL) ; Reset the actual ECP bit AND 110B ; Select ECP and error bits in original flag CP 110B ; Test for an ECP error JP Z,ERROR ; Process ECP error with error handler RES 3,(HL) ; Clear the external program bit ; This is the entry point from the error handler. It bypasses the resetting ; of the command status flag bits so that the information will be available to ; the error handler. NEXTCMD1: LD SP,(RESTART+1) ;4.0E Reset Stack ;4.0E LD SP,STACK ; Reset stack CALL LOGCURRENT ; Return to default directory LD A,(TPABNK) ;4.0E CALL SETBNK ;4.0E Insure TPA Bank for DMA Transfers LD HL,(NXTCHR) ; Point to first character of next command PUSH HL ; Save pointer to next character to process ; We have to capitalize the command line each time because an alias or other ; command line generator may have stuck some new text in. The code is shorter ; if we simply capitalize the entire command rather than trying to capitalize ; only the one command we are about to execute. CAPBUF: LD A,(HL) ; Get character CALL UCASE ; Convert to upper case LD (HL),A ; Put it back INC HL ; Point to next one OR A ; See if end of line (marked with null) JR NZ,CAPBUF ; If not, loop back POP HL ; Restore pointer to next character to process ; ZCPR35 provides a convenience feature to make it easier to enter a leading ; colon to force the current directory to be scanned and to make the CPR skip ; resident commands. If ALTCOLON is active, an alternate character can be ; entered as the first character of a command. The default (and recommended) ; alternative character is the period (it could not have any other meaning ; here). If FASTECP (see below) is not enabled or if ALTONLY is enabled, ; leading spaces on the command line are skipped before looking for the ; alternate character for the colon NEXTCMD3: IF [ NOT fastecp ] OR [ fastecp AND altonly ] CALL SKSP ELSE ;fastecp and not altonly LD A,(HL) ; Get first character in new command line ENDIF ;[ not fastecp ] or [ fastecp and altonly ] IF altcolon ; If allowing alias character for leading colon CP ALTCHAR ; If first character is ALTCHAR, treat as ':' JR NZ,NEXTCMD3A ; Branch if not '.' LD A,':' ; Else replace with colon IF NOT fastecp LD (HL),A ; Otherwise do more massaging below first NEXTCMD3A: ENDIF ;not fastecp ENDIF ;altcolon ; ZCPR35 supports three new options that can speed up command processing. ; FASTECP allows commands with a leading space to bypass the search for ; resident commands or transient commands (COM files) along the path and go ; directly to the extended command processor. With SKIPPATH enabled, when ; a command is prefixed by an explicit directory specification (but not a ; lone colon), searching of the path and invocation of the ECP are disabled. ; If the command is not found in the specified directory, the error handler ; is invoked immediately. Finally, if BADDUECP is enabled, when an attempt ; is made to log into an invalid directory, the command is sent directly to ; the ECP, which can provide special handling. To implement these three ; features, the first actual character of the command line is saved as a ; flag in FIRSTCH. My apologies for the complexity of these nested ; conditionals. ; With FASTECP we store the first actual character ; ..and then skip over spaces (unless ALTONLY is ; ..enabled, in which case we skipped spaces above) IF fastecp IF altspace ; If allowing alias character for leading space NEXTCMD3A: CP ECPCHAR ; If first character is ECPCHAR treat as ' ' JR NZ,NEXTCMD3B ; Branch if not '/' (alternate character) LD A,' ' ; Else replace with space IF NOT altcolon LD (HL),A ENDIF ;not altcolon NEXTCMD3B: ENDIF ;altspace IF altcolon LD (HL),A ; A could have been modified above IF NOT altspace NEXTCMD3A: ENDIF ;not altspace ENDIF ;altcolon LD (FIRSTCH),A ; Save it in flag CALL SKSP ; Then skip leading spaces ELSE ;not fastecp ; With SKIPPATH but not FASTECP we store the first ; ..character of the command (spaces were skipped above) IF skippath ; and [ not fastecp ] LD (FIRSTCH),A ; Store first nonspace character ; With only BADDUECP (and neither SKIPPATH nor FASTECP) ; ..we store a null in the FIRSTCH flag ELSE ;not skippath IF badduecp ; ..and [ not fastecp ] and [ not skippath ] XOR A ; Store a null LD (FIRSTCH),A ; ..to indicate baddu condition LD A,(HL) ; Retrieve character ENDIF ;badduecp ...and [ not fastecp ] and [ not skippath ] ENDIF ;skippath ... and [ not fastecp ] ENDIF ;fastecp ; Resume processing of the command line OR A ; Now at end of line? JR Z,RESTART ; If so, get a new command line CP CTRLC ; Flush ^C to prevent error-handler JR Z,RESTART ; ..invocation on warm boots CP CMDSEP ; Is it a command separator? INC HL ; If so, skip over it JR Z,NEXTCMD3 ; ..and process next command DEC HL ; If not, back up again ; Unless we are now running the external error handler, the following code ; saves the address of the current command in Z3MSG+4 for use by programs ; to determine the command line with which they were invoked. LD A,(CMDSTATFL) ; Get command status flag BIT 1,A ; Test for error handler invocation JR NZ,NEXTCMD5 ; If so, skip over next instruction LD (CMDPTR),HL ; Find the end of this command and set up the pointer to the next command. NEXTCMD5: PUSH HL ; Save command line pointer DEC HL ; Adjust for preincrementing below NEXTCMD6: ; Find end of this command INC HL ; Point to next character CALL TSTEOL ; Test for end of command JR NZ,NEXTCMD6 ; Keep looping if not LD (NXTCHR),HL ; Set pointer to next command POP HL ; Get back pointer to current command tail CALL PARSER ; Parse entire command line, then look for ; ..the command ;============================================================================= ; C O M M A N D S E A R C H C O D E ;============================================================================= ; CODE FOR FINDING AND RUNNING THE COMMAND ; Here is the code for running a command. Commands are searched for and ; processed in the following order: ; 1) flow control package (FCP) commands and IF state testing ; 2) resident command package (RCP) ; 3) command processor (CPR) ; 4) transient (COM file or extended command processor) ; 5) external error handler ; 6) internal error message and processing ; ; Special notes: ; ; a) If the current command is a shell command, special handling of flow ; control is required. If SHELLIF is enabled so that flow commands are ; allowed in shell alias scripts, then we reset the flow state to its ; initial condition (none) with each shell invocation (and after each ; command is run, we reset the shell bit in the code after CALLPROG). ; In this case shells will run regardless of flow state, and residual ; conditionals from the last running of the shell are flushed. Each ; shell input sequence begins afresh. On the other hand, if SHELLIF is ; off, flow control commands inside a shell script must be flushed so ; that they do not interfere with user entered commands. ; b) Directory prefixes are ignored for flow commands, since all flow control ; processing must pass through the FCP (the command must run even when ; the current flow state is false). ; c) If the command is not found in the FCP, then the current flow state is ; tested. If it is false, the command is flushed and the code branches ; back to get the next command. ; d) If the command had a directory prefix (a colon alone is sufficient), ; then steps #2 and #3 are skipped over,and the command is processed ; immediately as a transient program. ; e) In ZCPR35, unlike ZCPR30, RCP commands are scanned before CPR commands. ; This has been done so that more powerful RCP commands can supercede ; CPR commands. ; f) If the SKIPPATH option is enabled, when an explicit directory is ; specified with a command (but not just a colon), searching of the path ; is bypassed. If the FASTECP option is enabled, commands with leading ; spaces are sent directly to the ECP for processing. ; g) If no external command can be found, ZCPR35 performs extensive error ; handling. If the command error occurred while looking for a shell ; program, then the shell stack is popped. Otherwise, ZCPR35 tries to ; invoke an external, user-specified error handling command line. If ; none was specified or if the error handler invoked by that command ; line cannot be found, the internal error message (step #6) is displayed. ;----------------------------------------------------------------------------- RUNCMD: IF shellif ; If shells reininitialize flow control... LD A,(CMDSTATFL) ; Get command status flag RRCA ; Shell bit set? JR NC,FCPCMD ; If not a shell, process command XOR A ; Otherwise, shell is running, so LD (IFPTRFL),A ; ..reinitialize the IF system and continue ENDIF ;shellif ; ---------- Module <<1>>: Flow Control Processing ; An option is supported here to allow the address of the FCP to be obtained ; from the environment descriptor. This is logically consistent with the ; pholosopy of the Z-System and is useful when one wants to have a single block ; of FCP/RCP memory that can be allocated dynamically between FCP and RCP ; functions. FCPCMD: IF fcps NE 0 ; Omit code if FCP not implemented IF fcpenv ; If getting FCP address from Z3ENV LD HL,(Z3ENV+12H) ; Offset in Z3ENV to FCP address CALL PKGOFF ; Set HL to FCP+5 JR Z,RUNCMD1 ; Skip if no FCP present ELSE ; using fixed FCP address LD HL,RCP+5 ; Get address from Z3BASE.LIB ENDIF ;fcpenv ; If flow control processing is not allowed in shell aliases (scripts running ; as shell commands), then we have to make sure that we flush any flow control ; commmands, otherwise the CPR will attempt to execute them as transients, ; with dire consequences. In the code below we check the shell bit. If it ; is not set, we proceed normally. If it is set, we scan for flow commands ; and then jump past the flow testing to RUNFCP2, where the code will flush ; the command if it was a flow command and execute it unconditionally if not. CALL CMDSCAN ; Scan command table in the module ; ..and set Z-flag true if command found IF NOT shellif LD A,(CMDSTATFL) ; Get command status flag RRCA ; Test shell bit -- Does not affect Z-flag JR C,RUNFCP2 ; If shell bit not set, flush using code below ENDIF ;not shellif JR Z,CALLPROG ; Run if found (with no leading CRLF) ; This is where we test the current IF state. If it is false, we skip this ; command. CALL IFTEST ; Check current IF status RUNFCP2: ; If false, skip this command and go on to next IF drvprefix ; If DRVPREFIX we can use code below JR Z,JPNEXTCMD ; ..to save a byte ELSE ; Otherwise, we have to do an JP Z,NEXTCMD ; ..absolute jump ENDIF ;drvprefix ENDIF ;fcp ne 0 RUNCMD1: IF fastecp OR badduecp LD A,(FIRSTCH) ; If FIRSTCH flag set for ECP invocation, CP ' ' ; ..then go straight to transient processing JR Z,COM ENDIF ;fastecp or badduecp LD A,(COLON) ; If command had a directory prefix (even just OR A ; ..a colon) then skip over resident commands JR NZ,COMDIR ; ---------- Module <<2>>: RCP Processing ; An option is supported here to allow the address of the RCP to be obtained ; from the environment descriptor. This is logically consistent with the ; philosophy of the Z-System and is useful when one wants to have a single ; block of FCP/RCP memory that can be allocated dynamically between FCP and ; RCP functions. IF rcps NE 0 ; Omit code if RCP not implemented RCPCMD: IF rcpenv ; If getting address of rcp from Z3ENV LD HL,(Z3ENV+0CH) ; Offset in Z3ENV to RCP address CALL PKGOFF ; Set HL to address of RCP+5 JR Z,CPRCMD ; Skip if no RCP ELSE ; using fixed RCP address LD HL,RCP+5 ; Get address from Z3BASE.LIB ENDIF ; rcpenv CALL CMDSCAN ; Check for command in RCP JR Z,CALLPROGLF ; If so, run it (with leading CRLF) ENDIF ;rcp ne 0 ; ---------- Module <<3>>: CPR-Resident Command Processing CPRCMD: CPRCMD1 DEFL diron OR eraon OR geton OR goon OR jumpon CPRCMD1 DEFL cprcmd1 OR lton OR noteon OR renon OR saveon IF cprcmd1 ; If any CPR commands LD HL,CMDTBL ; Point to CPR-resident command table CALL CMDSCAN ; ..and scan for the command JR Z,CALLPROG ; If found, run it (with no leading CRLF) ENDIF ;any CPR commands ; ---------- Module <<4>>: Transient Command Processing COMDIR: ; Test for DU: or DIR: only (directory change) IF drvprefix LD A,(CMDFCB+1) ; Any command name? CP ' ' JR NZ,COM ; If so, must be transient or error ; Entry point for change of directory only IF wdu ; If controlled by wheel.. CALL WHLCHK IF badduecp JR NZ,COMDIR1 LD (COLON),A ; Pretend there is no colon LD A,' ' ; Force invocation of ECP LD (FIRSTCH),A JR COM ELSE ; not badduecp LD A,ECDUCHG JR Z,ERROR ENDIF ; badduecp ENDIF ; wdu COMDIR1: LD HL,(TMPUSR) ; Get temporary drive and user bytes IF NOT highuser ; If only users 0..15 can be logged BIT 4,L JP NZ,BADDIR ; If out of range, invoke error handling ENDIF ;not highuser DEC H ; Shift drive to range 0..15 LD (CURUSR),HL ; Make the temporary DU into the current DU CALL LOGCURRENT ; Log into the new current directory JPNEXTCMD: JP NEXTCMD ; Resume command line processing ELSE ;not drvprefix IF badduecp XOR A ; Pretend there is no colon LD (COLON),A LD A,' ' ; Force invocation of ECP LD (FIRSTCH),A ELSE ;not badduecp LD A,ECDUCHG JR Z,ERROR ENDIF ;badduecp ENDIF ;drvprefix ; Process transient command COM: LD HL,TPA ; Set default execution/load address LD A,3 ; Dynamically load type-3 and above ENVs CALL MLOAD ; Load memory with file specified in cmd line LD A,(CMDSTATFL) ; Check command status flag to see if AND 100B ; ..ECP running (and suppress leading CRLF) ; CALLPROG is the entry point for the execution of the loaded program. At ; alternate entry point CALLPROGLF if the zero flag is set, a CRLF is sent ; to the console before running the program. CALLPROGLF: CALL Z,CRLF ; Leading new line CALLPROG: LD A,(CMDSTATFL) ; Check command status flag to see if AND 2 ; ..error handler is running LD (ZEXINPFL),A ; Store result in ZEX control flag ; 2 = ZEX off, 0 = ZEX on IF subon LD (XSUBFLAG),A ; Ditto for XSUB input redirection ENDIF ;subon ; Copy command tail into TBUFF LD HL,(TAILSV) ; Get Addr of first char of command tail LD DE,TBUFF ; Point to TBUFF PUSH DE ; Save pointer LD BC,7E00H ; C=0 (byte counter) and B=7E (max bytes) INC DE ; Point to first char TAIL: CALL TSTEOL ; Check for EOL JR Z,TAIL1 ; Jump if we are done LD (DE),A ; Put character into TBUFF INC HL ; Advance pointers INC DE INC C ; Increment character count DJNZ TAIL ; If room for more characters, continue CALL PRINT ; Display overflow message DEFC BELL,'Ovfl' ; ..ring bell, then continue anyway TAIL1: XOR A ; Store ending zero LD (DE),A POP HL ; Get back pointer to character count byte LD (HL),C ; Store the count ; Run loaded transient program CALL DEFLTDMA ; Set DMA to 0080h standard value ; Perform automatic installation of Z3 programs (unless type-2 environment) LD HL,(EXEADR) ; Get current execution address CALL Z3CHK ; See if file is a Z3 program LD DE,Z3ENV ; Get actual ENV address JR NZ,EXECUT ; Branch if not Z3 CP 2 ; If type-2 (internal) environment JR Z,EXECUT ; ..do not perform installation ; Install types 0, 1, 3 and 4 INC HL ; Advance to place to put Z3ENV address LD (HL),E ; Put in low byte of environment address INC HL LD (HL),D ; Put in high byte of environment address ; Test for RST 0 as first byte and change to JP if so LD HL,(EXEADR) ; Point to first byte of program code LD A,(HL) CP 0C7H ; Test for RST 0 JR NZ,EXECUT LD (HL),0C3H ; Replace with JP instruction ; Execution of the program occurs here by calling it as a subroutine EXECUT: EX DE,HL ; Pass Z3ENV address to program in HL EXEADR EQU $+1 ; Pointer for in-line code modification CALL TPA ; Call transient (or resident) ; Return from execution IF shellif ; If flow processing allowed in shells... LD HL,CMDSTATFL ; Reset the shell bit in the command status RES 0,(HL) ; ..flag so multiple-command shells will work ENDIF ;shellif ; Continue command processing IF drvprefix ; If DRVPREFIX we can save a byte by JR JPNEXTCMD ; ..doing a two-step relative jump ELSE ; Otherwise, we just have to do JP NEXTCMD ; ..the absolute jump ENDIF ;drvprefix ;..... ;4.0E Insert a couple of Error vectors here to consolidate Error Code IF lton OR saveon OR renon OR geton OR jumpon BADNUM: LD A,ECBADNUM ;4.0E Load Bad Number Error Code DEFB 21H ;4.0E ..fall thru next code w/"LD HL,xx3EH" ENDIF ;4.0E JPNOFIL: LD A,ECNOFILE ;4.0E Load No File Error Code DEFB 21H ;4.0E ..fall thru next code w/"LD HL,xx3EH" ; ---------- Module <<5>>: External Error Handler Processing BADDIR: LD A,ECBADDIR ; Error code for bad directory specification ;..fall thru.. ; If we are returning from an external command to process an error, we want ; to leave the error return code as it was set by the transient program. ERROR: LD HL,CMDSTATFL ; Point to command status flag BIT 3,(HL) ; Check transient error flag bit JR NZ,ERROR1 ; If set, leave error code as set externally LD (ECFLAG),A ; Otherwise, save error code from A register ERROR1: RES 2,(HL) ; Reset the ECP bit to prevent recursion of ; ..error handler by programs that don't ; ..clear the bit IF BANKED ;4.0E CALL GO_TPA ;4.0E Insure TPA in context in case entered fm Bnk ENDIF ;4.0E BIT 0,(HL) ; Was error in attempting to run a shell? JR NZ,ERRSH ; If so, pop shell stack ; The following code is included to avoid a catastrophic infinite loop when ; the external error handler cannot be found. After one unsuccessful try, ; the internal code is invoked. BIT 1,(HL) ; Was an error handler already called? JR NZ,ERRINTRNL ; If so, use internal error handler ; If the current IF state is false, we ignore the error and just go on with ; the next command. IF fcps NE 0 CALL IFTEST JP Z,NEXTCMD ENDIF ;fcp ne 0 SET 1,(HL) ; Set command status flag for error invocation LD HL,ERRCMD ; Point to error handler command line LD A,(HL) ; Check first byte for presence of an OR A ; ..error command line JR Z,ERRINTRNL ; If no error handler, use built-in one LD (NXTCHR),HL ; Else, use error command line as next command JP NEXTCMD1 ; Run command without resetting status flag ; ---------- Module <<6>>: Resident Error Handler Code ; If the error is with the invocation of a shell command, we pop the bad shell ; command off the stack to prevent recursion of the error. We then use the ; internal error handler to echo the bad shell command. ERRSH: LD HL,(Z3ENV+20H) ; SHSTKS to L, SHSIZE to H LD B,L LD E,H ; SHSIZE to E XOR A ; Your basic null LD HL,(Z3ENV+1EH) ; Get shell stack address LD (HL),A ; Clear current stack entry DEC B ; SHSTKS-1 in B JR Z,ERRINTRNL ; Done if SHSTKS=1 PUSH HL ; Save address of shell stack LD D,A ; Clear D LD H,A ; Clear H.. LD L,A ; ..and L ERRS0: ADD HL,DE ; Multiply SHSIZE*(SHSTKS-1) DJNZ ERRS0 LD B,H LD C,L ; Length to BC EX DE,HL ; SHSIZE to HL POP DE ; Destination (shell stack beginning) ADD HL,DE ; SHSTK+SHSIZE to HL (Source) LDIR LD (DE),A ; Clear last entry ERRINTRNL: IF subon CALL SUBKIL ; Terminate active submit file if any ENDIF ;subon CALL CRLF ; New line LD HL,(CMDPTR) ; Point to beginning of bad command CALL PRINTHL ; Echo it to console CALL PRINT ; Print '?' DEFC '?' JP RESTART ; Restart CPR ; End Z2CP40-2.Z80