; PROGRAM: Z33ERROR ; AUTHOR: Jay Sage ; DATE: May 21, 1987 ; VERSION: 0.7 ; This is a prototype ZCPR33 error handler designed to serve as an example ; of how some ZCPR33 features can be used in an advanced error handler. ; This code displays the bad command and gives the user the option of ; replacing it, skipping over it, or aborting the entire command line. ;============================================================================= ; ; R E V I S I O N H I S T O R Y ; ;============================================================================= ; 0.7 05/21/87 Jay Sage ; ; Modified code to work with Z33LIB. Added 'Q' option to turn bells off even ; if they are configured to be on. Added support for built-in help display. ;============================================================================= ; ; D E F I N I T I O N S S E C T I O N ; ;============================================================================= version equ 07h no equ 0 yes equ not no bell1 equ yes ; Beep at user on entering error handler bell2 equ yes ; Beep at user on extra prompts (ZEX/SUBMIT) cr equ 0dh lf equ 0ah bell equ 07h tab equ 09h bdos equ 0005h bios equ 0000h fcb1 equ 005ch ; Z33LIB references extrn qerror,getccp,getsrun,haltsub,z33chk extrn gcmddu,geter3 ; Standard library references extrn z3init extrn getefcb,getcst,putcst,geterc,getwhl,erradr,getcl1 extrn cout,print,pstr,crlf extrn mafdc,pafdc extrn getzrun,haltzex extrn capin,bline extrn acase1 extrn codend ;============================================================================= ; ; S T A N D A R D P R O G R A M H E A D E R ; ;============================================================================= entry: jp start defb 'Z3ENV' defb 3 ; Type-3 environment envaddr: dw 0 dw entry defb version ;============================================================================= ; ; C O N F I G U R A T I O N A R E A ; ;============================================================================= ; Allow bell to sound on error defb 'BEEPS' bell1fl: defb bell1 bell2fl: defb bell2 defb 'END' ;============================================================================= ; ; M A I N C O D E S E C T I O N ; ;============================================================================= start: ld hl,(envaddr) ; Get environment address call z3init ; Initialize library routines ; We really should set up our own local stack, but I am going to skip ; that for now. call print ; Display the program header defb cr,lf defb ' Z33ERROR Version ' defb version / 10 + '0' defb '.' defb version mod 10 + '0' defb cr,lf,0 ld a,(fcb1+1) ; Check for help request cp '/' jr z,help cp '?' jr nz,main help: call print defb cr,lf defb ' Syntax: ',0 call name ; Print name of program call print defb cr,lf defb ' ',0 call name call print defb ' Q',tab,'quiet option (no bells)',cr,lf,0 ret main: call qerror ; See if error handler invocation jp z,errorh ; If so, branch to error processing ;============================================================================= ; ; I N S T A L L A T I O N C O D E ; ;============================================================================= ; Program was invoked manually, so we need to set it up as the error handler. ;---------------------------------------- ; Subtask 1 -- determine whether to use a DU, a DIR, or no prefix ; ; The program can examine the ZCPR33 option bytes to determine what features ; are supported (DU and/or DIR forms, which one first, wheel control over DU ; use, etc.). For now I will just assume that a DU prefix will be used and ; will omit coding this block. job1: ;---------------------------------------- ; Subtask 2 -- build error handling command line including directory prefix ; using data from the external FCB. We use the fact that the drive and user ; where the program was actually found along the path are stored in the ; command file control block. The user number is kept in the usual place; ; the drive is kept in the following byte. The drive is in the range 1..16 ; (unless the command is resident, in which case the drive byte is 0). job2: call gcmddu ; See if we were invoked by GO or JUMP inc b ; Shift drive back to range 1..16 jr nz,job2a ; If we were not, branch ahead call print ; If drive = 0, we have a resident command defb bell,' Cannot be installed using GO or JUMP',cr,lf,0 ret job2a: call geterc ; Get address of error command line ex de,hl ; ..into DE call z33chk ; Make sure we have ZCPR33 jr nz,job2b ; Branch if not ; If ZCPR33, use information in external command FCB to determine drive and ; user number where the file was found. call gcmddu ; Set BC to directory where program was found ld a,b ; Work on drive first add a,'A' ; Convert it to a letter ld (de),a ; Save in error command line inc de ; Increment command line pointer ld a,c ; Work on user number next call mafdc ; Convert it to decimal number in command line ld a,':' ; Put in the colon ld (de),a inc de job2b: call getefcb ; Get pointer to name of this program inc hl ld bc,8 ; Copy 8 characters of name ldir ; ..into error command line ld a,' ' ; Add a space ld (de),a inc de ld a,(fcb1+1) ; Copy any option character on command line ld (de),a inc de xor a ; Store terminating null ld (de),a ;---------------------------------------- ; Subtask 3 -- Report installation to the user job3: call print defb ' Error handling command line set to: ',0 call geterc ; Get pointer to error command line call pstr ; Print the string there jp crlf ; One extra line and quit ;============================================================================= ; ; E R R O R H A N D L I N G C O D E ; ;============================================================================= ; This is the main entry point for error handling errorh: ;---------------------------------------- ; Subtask 1 -- Display program signon message and determine whether Z33 is ; running or not. Check for the 'Q' (quiet) command line option. Optionally ; beep the bell. task1: ld a,(fcb1+1) ; Get any option character cp 'Q' ; If not quiet option, skip ahead jr nz,task1a ld hl,0 ; Clear the bell flags ld (bell1fl),hl task1a: ld a,(bell1fl) call beep ;---------------------------------------- ; Subtask 2 -- Display system status ; This task checks to see if there is anything about the system status that ; might be useful to the user in determining the cause of the error. ; Specifically, if either ZEX or SUBMIT is running or if the wheel byte is off, ; the user is given this information. If not, there is no output. task2: call z33chk ; Check for ZCPR33 running jp nz,task4 ; If not, show only the bad command line call getzrun ; See if there is anything interesting jr nz,task2a ; ..to report (ZEX or SUBMIT running or call getsrun ; ..wheel byte off) jr nz,task2a call getwhl jr nz,task3 ; If none of above, on to task3 task2a: call print defb ' Status: ',0 call getzrun ; Find out if ZEX is running jr z,task2b ; Skip ahead if not call print defb ' ZEX running',0 task2b: call getsrun ; Find out if SUBMIT is running jr z,task2c ; Skip ahead if not call print defb ' SUBMIT running',0 task2c: call getwhl ; Find out if wheel is off jr nz,task2d ; If not, on to task3 call print defb ' WHEEL OFF',0 task2d: call crlf ; End the line ;---------------------------------------- ; Subtask 3 -- Display information about the type of error: error number, ; internal or external, and description. task3: call print defb ' Error Type:',tab,0 call getcst ; Get command status flag bit 3,a ; See if external command bit is set jr nz,task3a ; Branch if external error call print defb 'CPR/ECP: (#',0 jr task3b task3a: call print defb 'External: (#',0 task3b: call getcst ; Get the command status flag res 1,a ; ..and clear the error res 2,a ; ..and ECP bits call putcst ; Store the modified value call geter3 ; Get the command error code call pafdc ; Display the number call print defb ') ',0 ld hl,task3c ; Set up return address push hl ; ..on stack call geter3 ; Get command error code again call acase1 ; CASE function defb 11 ; Eleven cases defw unknown ; Default case defb 1 defw duchange ; Illegal attempt to change directory defb 2 defw baddu ; Invalid directory defb 3 defw badpw ; Incorrect password defb 5 defw badform ; Bad command form (wild or type given) defb 6 defw badecp ; Command not found by CPR or ECP defb 7 defw badcmd ; Command file not found by CPR defb 8 defw ambig ; Ambiguous file spec defb 9 defw badnum ; Bad numerical value defb 10 defw nofile ; Object file not found defb 11 defw diskfull ; Disk is full defb 12 defw tpafull ; TPA overflow duchange: call print defb 'Illegal attempt to change directory',0 ret baddu: call print defb 'Invalid directory specification',0 ret badpw: call print defb 'Incorrect password',0 ret badform: call print defb 'Bad command form (file type / wild card)',0 ret badecp: call print defb 'Command not found by CCP or ECP',0 ret badcmd: call print defb 'Requested load file not found',0 ret ambig: call print defb 'Ambiguous or missing file name',0 ret badnum: call print defb 'Bad numerical expression',0 ret nofile: call print defb 'Requested operand file not found',0 ret diskfull: call print defb 'Disk full',0 ret tpafull: call print defb 'TPA full (program too big)',0 ret unknown: call print defb 'Unknown error type',0 ret task3c: call crlf ; End the line ;---------------------------------------- ; Subtask 4 -- Display bad command line ; ; In the final code, much more elaborate error processing would be performed ; here (or more likely, the code here will be used as a framework for existing ; error handlers). task4: call print defb ' Bad Command:',tab,0 call erradr ; Get pointer to bad command line push hl ; Save for reuse below scan: ; Find end of this command ld a,(hl) or a ; See if end of command line buffer jr z,task4a cp ';' ; See if at command separator jr z,task4a inc hl ; Point to next character jr scan ; Continue scanning task4a: ld (hl),0 ; Mark end of string ld (delimptr),hl ; Save ptr to bad command's delimiter ld (delim),a ; Store delimiter pop hl ; Restore pointer to beginning of command push af ; Save delimiting character call pstr ; Display the bad command push hl ; Save pointer to rest of command line call print defb cr,lf defb ' Rest of Line:',tab,0 pop hl pop af ; Get back delimiter of bad command or a jr nz,task4b ; Branch if there are more commands call print defb 'none',0 jr task4c task4b: dec hl ; Pt back to bad command delimiter ld (hl),a ; Put semicolon back inc hl call pstr ; Print rest of command line task4c: call crlf ; End the line ;---------------------------------------- ; Subtask 5 -- Deal with the bad command ; This is where the real error handling is performed. Here we just flush ; the entire command line and abort any submit job, but in a real error ; handler, several other functions would be performed. With normal command ; lines (ZEX and SUBMIT not running), the user has the following three basic ; choices: fix the bad command, skip the bad command, or abort the entire ; command line. If ZEX is running, there is an additional choice that should ; be available: abort the entire ZEX script. Similarly, if SUBMIT is running, ; the user must be given the option to abort the entire submit job. ; This code implements all of the above with the additional feature ; that if the bad command is the last on the line, the option to skip ; to next command is not presented as it would be meaningless. Similarly, if ; neither ZEX nor SUBMIT is running and there are no pending commands, there ; is nothing the user can do, so the code just returns. task5: ld a,(delim) or a ; If other commands are pending jr nz,task5a ; ..then present options to user call getzrun ; If ZEX is running jr nz,task5a ; ..then present options to user call getsrun ; If SUBMIT is not running either ret z ; ..then just return task5a: call print defb cr,lf,' Your options:' defb tab,'(R)eplace bad command' defb cr,lf,tab,tab defb '(A)bort entire command line',0 ld a,(delim) ; Get bad command delimiter or a jr z,task5b ; No trailing commands; skip next option call print defb cr,lf,tab,tab defb '(C)ontinue with rest of command line',0 task5b: call print defb cr,lf,' Enter choice: ',0 call capin ; get response ld b,a ; Save for a moment ld a,(delim) ; Get command delimiter again or a ld a,b ; Response back in A jr z,task5c ; Don't allow 's' choice if no trailing command cp 'C' ; Continue? jr z,skip task5c: cp 'A' ; Abort? jp z,abort cp 'R' ; Replace? jr z,replace call print defb bell,0 jr task5b ;----------------------------------------------------------------------------- ; Skip over bad command and resume with next in line skip: call getcl1 ; Pt to command line buffer ld de,(delimptr) ; DE pts to bad command's delimiter inc de ; Now pointing to next command ld (hl),e ; Stuff address in inc hl ; ..first two bytes ld (hl),d ; ..of multiple command line buffer call print defb ' Continuing ...',cr,lf,0 ret ; Resume command execution with next command ;----------------------------------------------------------------------------- ; Prompt the user for a new command (or commands) to replace the one ; that caused the error. If the user enters only a carriage return, he ; is taken back to the "options" menu. This feature provides a convenient ; way of "backing out" of this option. replace: call print defb cr,lf,' Enter replacement command(s):' defb cr,lf,' ',0 call codend ; Pt to free memory for input buffer ld (hl),254 ; Store max buffer length for BDOS ld a,0ffh ; Set capitalize flag for BLINE call bline ; Get user input via BDOS ; At this point, HL contains a pointer to the first character input ; by the user, and A contains the length of the string. or a ; Did user input anything? jr nz,replace1 ; Branch if so call print defb lf,bell,tab,'No input',0 jp task5 ; Go back to menu if no input replace1: ld c,a ; Put string length in BC ld b,0 push hl ; Save address of first character add hl,bc ; Pt to end of string ; Now we concattenate the rest of the original command line to the ; command just entered. ld de,(delimptr) ; Pt to bad command's delimiter replace2: ld a,(de) ; Get character ld (hl),a ; ..and store in buffer or a ; End of commawd line? jr z,replace3 ; Finished with copy if so inc bc ; Bump char count inc de ; ..and pointers inc hl jr replace2 ; Go back for more ; Check length of the new command line. If it will fit, copy new ; command line to the Z3 multiple command line buffer and return ; to the CPR to execute it. Otherwise, display error message and ; branch back to options menu. replace3: call getcl1 ; Get Z3 command line addr in hl, ; ..length in a cp c ; Compare with length of new line jr c,replacerr ; Branch if new line too long ld a,b ; High order byte of length should be 0 or a jr nz,replacerr ; Branch if not push hl ; Save command line address ld de,4 ; Offset to first character in buffer add hl,de ex de,hl ; First char address in DE pop hl ; Z3CL address in HL ld (hl),e ; Store ptr to first command inc hl ; At Z3CL ld (hl),d pop hl ; Get back pointer to new command inc bc ; Adjust length to include trailing null ldir ; Copy to system command line ret ; Go back to CPR to execute it replacerr: pop hl ; Clear stack call print defb lf,bell,tab defb 'Command line too long',0 jp task5 ;----------------------------------------------------------------------------- ; Abort (flush) command line abort: call abortmsg call getzrun ; See if ZEX is running jr z,abort2 ; Branch if not ; Deal with running ZEX script ld a,(bell2fl) call beep call print defb ' Abort ZEX script (Y/N)? ' defb 0 call getyesno ; Get user's answer jr nz,abort1 ; Branch if negative response call haltzex ; Abort ZEX call abortmsg jr abort2 abort1: call print defb ' No' defb cr,lf,0 ; Deal with running SUBMIT job abort2: call getsrun ; Is a submit job running ret z ; If not, return to command processor ld a,(bell2fl) call beep call print defb ' Abort SUBMIT job (Y/N)? ' defb 0 call getyesno ; Get user's answer jr nz,abort3 ; Branch if negative response call haltsub ; Abort SUBMIT abortmsg: call print defb ' Aborted' defb cr,lf,0 ret abort3: call print defb ' No' defb cr,lf,0 ret ; Back to command processor ;----------------------------------------------------------------------------- ; BEEP -- sound bell if flag in A is nonzero beep: or a ret z call print defb bell,0 ret ;----------------------------------------------------------------------------- ; GETYESNO -- get yes/no answer from user ; Only 'Y' or 'y' accepted as affirmative answers. Routine returns Z if ; affirmative, NZ otherwise. getyesno: call capin ; Get user response cp 'Y' ret ;----------------------------------------------------------------------------- ; PONOFF -- Print ON or OFF in message ; ; If the Z flag is set on entry, 'OFF' is displayed; otherwize 'ON' is ; displayed. ponoff: jr z,poff call print defb 'ON',0 ret poff: call print defb 'OFF',0 ret ;----------------------------------------------------------------------------- ; NAME -- Display name of command name: call getefcb ld b,8 ; Maximum number of characters in name name1: inc hl ld a,(hl) cp ' ' ; If space, we are done ret z call cout ; Display the next character djnz name1 ; Work through them ret ;============================================================================= ; ; B U F F E R S ; ;============================================================================= dseg delimptr: defs 2 ; Pointer to bad command's delimiter delim: defs 1 ; Bad command's delimiting character end