Appendix B - Error Handling
This appendix provides an overview of QED-Forth's error handling, describes each error message that may occur, and presents a quick-reference list of all of the error messages.
When an error condition is detected, the headerless routine ERROR is called. If the user variable CUSTOM.ERROR contains a true flag, ERROR executes the user-supplied routine whose extended code field address (xcfa) is stored in the user variable UERROR. If CUSTOM.ERROR is false, the default handler routine named (ERROR) is executed. (ERROR) prints a message describing the type of error that occurred, and then in most cases executes ABORT.
ABORT checks the user variable CUSTOM.ABORT. If it is true, the user-supplied routine whose xcfa is stored in UABORT is executed. If CUSTOM.ABORT is false, ABORT calls the default routine named (ABORT) which clears the data and return stacks and executes FORTH DEFINITIONS to install FORTH as the CURRENT and CONTEXT vocabulary. Finally, (ABORT) checks to see if a valid autostart pattern has been installed. If so, the user-specified startup routine is executed. If there is no autostart word, the standard interpreter named QUIT is entered, and QED-Forth waits for commands from the serial input stream. (ERROR) performs a COLD restart in response to some severe errors, while other detected conditions, called "warnings", do not even cause an abort. For example, the definition of a non-unique name causes a warning to be issued (if the user variable UNIQUE.MSG is true) but does not cause an abort.
All errors and warnings send the bell character to the terminal (if the user variable QUIET is OFF) to give an audible error indication.
Chapter 3 contains more information regarding recovery from crashes. Chapter 15 discusses the user variables that control the error and abort handlers, and the glossary contains detailed definitions of CUSTOM.ERROR, CUSTOM.ABORT, ABORT, (ABORT), (ERROR), and 1).
Posting a Customized Error Handler
To post a customized error handler, simply store the extended code field address of the custom handler routine into the UERROR user variable and set the user variable CUSTOM.ERROR to true. For example, a particular application may require certain actions to be taken (such as setting off alarms, turning off valves, etc.) when an error is detected. You can define a customized routine that performs these actions. If you also want to print the standard system error message, you can use the 2) word. For example, you could define a new error handler as
: NEW.ERROR.ACTION ( -- ) ((ERROR)) \ print system error message ... perform other error recovery actions HERE ... ABORT \ perform standard ABORT ;
This new error routine can be installed by storing its code field address in UERROR and turning the CUSTOM.ERROR flag on as
CFA.FOR NEW.ERROR.ACTION UERROR X!
CUSTOM.ERROR ON
Posting a Customized ABORT Handler
To post a customized abort routine, store the extended code field address of the custom routine into the UABORT user variable and set the user variable CUSTOM.ABORT to true.
It is sometimes advantageous to install a custom abort handler in turnkeyed multitasked systems where one of the tasks is QED-Forth itself. A "turnkeyed" application is one that has a designated autostart program that has been installed using AUTOSTART or PRIORITY.AUTOSTART. The "Turnkeyed Application Program" presented in Chapter 17 illustrates such a case. If an end user is interacting with the QED-Forth task on such an instrument, any misspelled command or other slight mistake will call the error handler which calls ABORT which invokes the autostart routine. Thus a misspelled command causes the instrument to re-initialize itself and start over, and this may not be appropriate.
This problem is easily solved by installing a customized abort handler in the QED-Forth task. You could define and install the new abort action as:
: MY.ABORT.ACTION SP! RP! \ clear data and return stacks FORTH DEFINITIONS \ set forth vocabulary QUIT \ enter QED-Forth interpreter ; CFA.FOR MY.ABORT.ACTION UABORT X! \ install new action CUSTOM.ABORT ON \ set flag so custom routine is called
Errors That Cause a Cold Restart
If more than 255 page changes are required during a search of the names in the dictionary, QED-Forth decides that the linked name list has been corrupted, and issues the message:
Corrupted name list
followed by a cold restart.
If QED-Forth detects that the data stack, return stack, or POCKET is not in common RAM, it prints the error message:
<name> isn't in common ram !
where <name> is the name of the routine being interpreted when the error is discovered. A cold restart ensues.
A cold restart resets the contents of the FORTH vocabulary handle so that only the original kernel words can be found. Any words that you have defined will not be found. As described in Chapter 3, you can write-protect a portion of the dictionary and use SAVE to preserve the link information of the write-protected dictionary. Then executing RESTORE after the cold restart will restore access to those words. Otherwise, a cold restart will force you to reload all of the words that you had compiled. The last routine executed during a COLD restart is ABORT, so if an autostart or priority autostart routine has been posted, it will be called.
Warnings
The following warning is issued by FORGET:
Warning: NP or DP not in RAM
where NP and DP are the name pointer and definitions pointer, respectively. After receiving this error execute NHERE and HERE to see which of these points to non-modifiable memory. The situation can be fixed by setting NP and/or DP to a valid RAM location so that more words can be compiled. Check the write-protection DIP switches to make sure that they are properly set.
Non-unique Warning
If a word is defined that has the same name as a previously defined name, QED-Forth prints
<name> isn't unique
and continues processing without aborting. To disable this warning, turn the UNIQUE.MSG user variable OFF. UNIQUE.MSG is described in Chapter 15. To resolve a conflict among names, you can increase the user variable WIDTH to instruct the compiler to save more of the letters of each name in the dictionary. For example, if WIDTH equals 4 (the default value), the names ACCOUNT#1 and ACCOUNT#2 conflict; if WIDTH is set to 9 or greater before they are compiled, the names are distinguished from one another.
How to Resolve Name Conflicts
If a name you want to use conflicts with a name in the kernel, set WIDTH to an adequately large value, redefine the kernel word using the new width (a non-unique message will be printed at this point), and then define the new routine. The "Creation of a Synonym" section in Chapter 16 explains how to redefine the kernel word without any performance penalty. For example, suppose you want to define a word called CURRENT.HOME which conflicts with the kernel word CURRENT.HEAP. Of course, the easiest solution is to pick a name other than CURRENT.HOME. But the problem can also be solved by setting WIDTH to 10, defining a new header for CURRENT.HEAP using the new value of WIDTH, and then entering the definition of CURRENT.HOME. The following code solves the problem:
10 WIDTH ! HERE ( -- xaddr | save current DP = xaddr ) CFA.FOR CURRENT.HEAP DP X! \ set DP to xcfa of CURRENT.HEAP CREATE CURRENT.HEAP \ create new header using larger WIDTH DP X! ( -- | restore original DP ) : CURRENT.HOME ... definition goes HERE .... ;
Compilation Errors
All of the remaining errors discussed in this chapter terminate with ABORT and return control to the QED-Forth interpreter. The reference to <name> is the name of the routine that is executing when the error is detected. <array> and <matrix> refer to the name of an array or matrix that is involved in the error.
Compilation errors are detected while QED-Forth is attempting to compile a definition into the dictionary.
The message
Error at <name> Page boundary was crossed!
occurs when either the name pointer or definitions pointer is incremented across the address 8000H, usually because the name or definitions area runs out of space on a page. The solution to the problem is to set NP and/or DP to point to available RAM so that compilation can continue, or to change the memory map so that the dictionary has more room and start the compilation process again.
The error message
Error at <name> Can't compile–Not RAM!
means that either NP, DP, or VP is not pointing to modifiable memory. Check the write-protection DIP switches to make sure that they are properly set. If they are, modify the contents of the offending pointers so that each of the name, definitions, and variables areas is in RAM.
Certain words such as LOCALS{ , conditional and looping control structures, and ; can only be used when QED-Forth is in compilation mode. For example, if the word ENDIF is executed outside of a colon definition, QED-Forth prints the message
Error at ENDIF Compile mode only!
This error message will be displayed when these restricted words are executed outside of a colon definition.
Some commands require that the following word in the input stream be a valid entry in the dictionary; an error will be issued if this is not true. For example, executing CFA.FOR FOO.BAR results in the message
Error at FOO.BAR Word not found!
If you execute CFA.FOR followed by a carriage return, the error message is
Input exhausted!
because there is no word after CFA.FOR in the input stream. In general, when the input stream comes from the serial input port, a command (CFA.FOR in this example) that extracts a word from the input stream must find the word on the same line. If the input stream is coming from a disk block, the arguments need not be on the same line, but they must be in the same block as the command itself.
Executing the command
" hi there⌋
results in the message
Error at " Missing delimiter!
To correct the error, make sure that the terminating " is on the same line as the beginning " .
When using the DIN command to enter a double number, the string following DIN must be a valid number. For example, the command
DECIMAL DIN ABCD⌋
results in the error message
Error at ABCD Not a number!
To correct the problem, make sure that a valid number follows DIN. Check the number base by examining the contents of BASE. Sometimes the problem is that the base is not what you expect. In the example above, ABCD is a valid number if the base is HEX, but not if the base is DECIMAL.
An error is detected if a word is defined that contains an incomplete conditional or looping control structure. For example, the definition:
: YES/NO ( flag -- ) IF ." yes" ELSE ." no" ;
results in the error message
Error at ; Definition incomplete!
because the ENDIF (or THEN) is missing from the conditional structure. This error message is also issued if an extra item is left on the data stack inside a definition. Depending on which part of the control structure is missing, a different error message may be printed. For example, forgetting the DO word in a definition that contains LOOP causes the error
Error at LOOP Unpaired control structures!
These types of errors often occur when there are several nested IF … ELSE … ENDIF statements, and one of the IF or ENDIF words is missing. This error message is also printed if the word LEAVE is used outside of a DO … LOOP or DO … +LOOP construct.
If a LOCALS{ command specifies fewer than 1 or more than 16 local variables, the message
Error at <name.of.last.local> Invalid number of locals!
is printed. Rewrite the definition to use between one and sixteen locals.
The word TO expects either a local variable name or self-fetching variable name to follow it in the input stream. Otherwise, the error message
Error at <name> Invalid operand for TO!
is printed, where <name> is the word that follows TO in the input stream.
The command
AXE <name>
removes the header of <name> from the dictionary, leaving its definition. AXE can only be used on contiguous uninterrupted sections of the name area. It fails if the name pointer NP has been reset by the programmer (using an NP X! command) since <name> was defined, or if you have been defining words into several vocabularies since the definition of <name>. AXE cannot detect all of the conditions that cause problems, so be careful when using it. It can detect if the linked vocabulary list changes pages between the LATEST definition and the definition of <name>. If so, the error message
Error at <name> Can't AXE–Name list changes page!
is issued, and the AXE operation is not performed.
If blocks files are being used, remember that block 0 is for comments only and cannot be LOADed. The following error message is issued if this rule is broken:
Error at <name> Can't load Block 0!
If a non-existent block number is specified, the following message is printed:
Error at <name> <block#> is an invalid block number!
Assembler Errors
The following three error conditions are detected by the in-line assembler. For example, the assembler command
4 IMM JMP
is invalid because the IMMediate address mode cannot be used with the JMP (jump) instruction. (If you want to try this instruction now, remember to execute ASSEMBLER first so that the mnemonics can be found). QED-Forth would abort and print
Error at JMP -2 is an invalid address mode!
The assembler's code for IMM is -2.
The 68HC11 allows branches of up to +127 or -128 bytes. If a conditional (IF, … ELSE, … ENDIF, ) or loop structure ( BEGIN, … UNTIL, etc.) is too large, the assembler prints
Error at <name> <number> is too large a branch offset!
where <number> is the requested branch offset printed in the current number base. If the problem is a loop or conditional that is too large, define a subroutine to execute a portion of the code, and call the subroutine from within the loop or conditional. If you are using the debugger, recall that 8 additional bytes are required for each compiled TRACE instruction, so you may be forced to define a subroutine as described above to avoid an illegal branch offset. Sometimes the invalid branch offset error results from an unpaired conditional or an extra or missing operand that corrupts the data stack. The assembler passes all parameters including branch offsets on the data stack. If an instruction in the control structure places an extra item on the stack, this can cause the wrong stack item to be interpreted as a branch offset which leads to the error message. Check the source code to make sure that all control structures are properly paired, and that each instruction has the proper number of operands.
Assembler commands like IF, and WHILE, and UNTIL, expect a condition code on the data stack. The numerical equivalents of these codes are left on the stack by mnemonics such as NE (not equal) or EQ (equal), etc. If an unrecognized condition code is encountered, the error message
Error at <name> <number> is an invalid condition code!
is issued. The cause of this problem is usually a stack error as discussed above.
Run Time Errors
Run time errors are detected when a word executes, not when it compiles. The error handler prints the name of the routine that detects the error, followed by a message and other information including the names of arrays and matrices (if any) involved in the error.
The word NEEDED is a useful debugging word that can be compiled into any definition. It expects on the stack a number that specifies the minimum stack depth required for proper operation of the calling word. If less than the specified number of stack items is present and if DEBUG is ON, the error message
Error at <name> Data stack underflow!
is printed, where <name> is the routine being interpreted when the error is detected. This message can also be issued by the QED-Forth interpreter which checks for data stack underflow after interpreting each line of input.
Some words require inputs that are within a range of allowed values. For example, the programmer can set the period of the multitasker's timeslice clock by specifying the period in increments of 100 usec and calling *100US=TIMESLICE.PERIOD . If the period is incompatible with the 16-bit main counter on the 68HC11 (if it is too long), the error message
Error at <name> <number> is an invalid parameter!
is issued, where <name> is the routine that is executing when the error is encountered, and <number> is the offending parameter. Check the glossary entry for the word in question, and adjust the source code to correct the problem.
When creating a task with the TASK:, BUILD.TASK, and BUILD.STANDARD.TASK words, QED-Forth checks to make sure that the specified user base address, POCKET, and stack locations are in common ram. If not it prints the message
.Error at <name> <number> isn't in common ram!
If one of the "fast vector operations" is called (see the chapter titled "Arrays, Matrices, and Fast Vector Operations"), the error message
.Error at <name> –incompatible vectors!
is issued if the vectors involved have unequal numbers of elements.
Array and Matrix Errors
The word ?DIM.MATRIX is useful both for determining the number of rows and columns in a matrix and for detecting errors. It can be edited into any word that operates on a dimensioned matrix. It expects the xpfa of the matrix on the stack. If the matrix is dimensioned, it leaves the number of rows and columns on the stack. If the matrix is not dimensioned and if DEBUG is on, it aborts with the message
Error at <name> <matrix.name> isn't dimensioned!
If the matrix associated with the offending xpfa cannot be found in the dictionary, the matrix name is reported as ?NAME?.
If DEBUG is ON, the indices of arrays and matrices are checked when the array or matrix is addressed. If the index is invalid, the message
Error at [] <array.name> <number> is an out-of-range index!
is printed. [] is the routine that calculates an array element's address.
Other matrix routines require matrix operands with constrained dimensions. For example, the routine LU.BACKSUBSTITUTION (see the chapter titled "Solving Simultaneous Equations, Curve Fitting, and Performing FFTs") operates on a square matrix and a column vector of residue elements. If the matrix isn't square the error
Error at <name> <matrix.name> isn't square!
is issued, and if the column vector has improper dimensions the error
Error at <name> <matrix.name> isn't a column vector!
is printed. The message
Error at <name> <matrix.name> isn't a matrix!
is printed if the specified array doesn't have 2 dimensions with 4 bytes per element.
During dimensioning of an array, if a number of dimensions less than 1 or greater than MAX#DIMENSIONS is specified, the error
Error at <name> <array.name> <number> is an invalid number of dimensions!
is printed. If you need to use more dimensions, store a greater value into the user variable MAX#DIMENSIONS (the default value is 4). See the glossary entry for MAX#DIMENSIONS for some precautions to take when changing this value.
When an array or matrix is being dimensioned, space for the data structure must be allocated in the heap. If there is insufficient heap space, the message
Error at <name> <array.name> is too large. Not enough memory!
is printed. To find out how many bytes are available in the heap, execute ROOM. To avoid the error, delete unneeded heap items or move them to other heaps, or initialize the heap to have more room using IS.HEAP (caution: this command causes the loss of any data that is in the heap). Note that some matrix words need to dimension temporary matrices in the heap; these are deleted before the word finishes executing. There must be enough room in the heap to accommodate such temporary matrices, which are typically the size of the destination matrix for the operation.
The operation REDIMMED re-arranges the dimensions of a matrix without modifying the contents. Thus a 3 x 4 matrix could be changed to a 6 x 2 matrix. The product of the old dimensions must equal the product of the new dimensions; if not, the following error is issued:
Error at <name> <1st.matrix> <2nd.matrix> –Incompatible sizes!
This error message is also issued by routines that require the dimensions of two matrices to be related in a specific way. For example, when multiplying matrices the number of columns in the first matrix must equal the number of rows in the second. If such relationships are not met, the error is announced.
In the ROW/COL matrix words, if the specified row or column is not present in the matrix, the following error message is printed:Error at <name> <matrix> doesn't have the specified row/column!
The data matrix passed to FFT or IFFT must have 2 rows; the row#0 holds the real part of each complex data point, and row#1 holds the imaginary part. If the data matrix does not have 2 rows, the following message is printed:Error at <name> <matrix> doesn't have 2 rows!
Forgetting a Defective Definition
When an error occurs inside a colon definition, the error message is printed and ABORT is executed. The header of the definition is left "smudged", meaning that the smudge bit in the count byte of the header is set so that the name cannot be found in the dictionary. The partially completed definition can be left in the dictionary until the next FORGET or ANEW operation removes it and the surrounding words; it will not cause any problems. If you want to FORGET <name> where <name> is the defective definition itself, you must first execute SMUDGE immediately after the definition is aborted, then execute FORGET <name>. SMUDGE toggles the smudge bit of the last word in the dictionary so that it can be found. Then FORGET removes the defective definition from the dictionary.
When Errors Occur During Downloading
If a compilation error aborts a definition of a word, QED-Forth enters the execution mode and is waiting for input from the serial line. This can cause problems during downloading of a source code file. After the abort, the host terminal continues to send the rest of the aborted definition, but instead of compiling the commands, QED-Forth is executing them. As the system may not be properly configured to execute the commands, a crash may result. The solution is to monitor the downloads and stop the file transfer as soon as the error is detected. Then the source code file can be edited and the remainder of the code can be downloaded.
Summary of Error Messages
\ the following are warnings that do not abort: | |
---|---|
0008 | " Warning: NP or DP not in RAM" |
0004 | " isn't unique" |
\ The following perform a COLD restart: | |
FE07 | " Corrupted name list" |
041D | " isn't in common ram" |
\ the following print the word name at POCKET and the specified message: | |
FF00 | " ?" |
0109 | " Page boundary was crossed" |
010A | " Can't compile–Not RAM" |
010B | " Compile mode only" |
010C | " Word not found" |
010D | " Data stack underflow" |
010E | " Missing delimiter" |
010F | " Not a number" |
0110 | " Unpaired control structures" |
0111 | " Invalid number of locals" |
0112 | " Invalid operand for TO" |
0113 | " Definition incomplete" |
0114 | reserved |
0115 | " Can't AXE–Name list changes page" |
0116 | " Can't load Block 0" |
\ the following is a stand-alone error message: | |
FF01 | " Input exhausted" |
\ the following print the word name at POCKET and a parameter: | |
0217 | " is an invalid address mode" |
0218 | " is too large a branch offset" |
0219 | " is an invalid condition code" |
021A | " is an invalid block number" |
\ The following print the name of the calling routine: | |
031B | " –incompatible vectors" |
\ The following print the name of the calling routine and a parameter: | |
051C | " is an invalid parameter" |
051D | " isn't in common ram" |
\ The following print the name of the calling routine and ARRAY'S.NAME: | |
081E | " isn't dimensioned" |
081F | " isn't a column vector" |
0820 | " isn't a matrix" |
0821 | " isn't square" |
0822 | " is too large. Not enough memory" |
0823 | " Doesn't have the specified row/column" |
0824 | " –Incompatible sizes" |
0825 | " doesn't have 2 rows" |
\ the following prints the calling routine's name and array's.name and index: | |
0626 | " is an invalid number of dimensions" |
0627 | " is an out-of-range index" |
\ the following prints the calling routine's name and 2 array names: | |
0724 | " –Incompatible sizes" |