A comedy of errols
Sean McManus demystifies debugging in Amstrad CPC 464/6128 Basic
Picture the scene: You flick through the computer press, bash in the latest type-in and excitedly enter RUN. The title screen springs up, pictures flash and the machine grinds to a halt, flatly refusing to continue. "Syntax Error" reads the screen, devoid of sympathy, and you're heading for a night scanning screenfuls of code. It's rare that published listings contain errors in fact, and it's usually a case of having mistyped somewhere, the problem being finding where. There are some guidelines that can help debug programs (both other people's and your own creations) and we're going to take a stroll through them now. We'll kick off with some general tips on improving your debugging life:
First of all, use lower case when entering programs. When the program is LISTed, all command words (keywords) will be converted to upper case, springing up above the variables. This makes mistyped keywords ("pront" and friends) glaringly obvious, since they will still be cowering in non-caps. It is better still to define keys to contain whole commands. Try entering the following at the Ready prompt:
KEY 139,"data "
Pressing the small [ENTER] key hereafter will make the word DATA spring up, eliminating the possibility of mistyping it (unless you manage to do so when defining the key: very silly). There's a chart showing the numbers you need to redefine the whole numeric keypad in the back of the manual and you will find it worth the time writing a tiny program to set-up your most used commands.
It usually takes a particularly shocking powercut before anybody takes any notice of this advice, but you really should SAVE the program frequently and make sure that you have a backup. Sometimes a program won't be as kind as to return you to Basic. When machine code is used, for example, things can get very strange. CALL is one command that you cannot afford to get wrong. Granted, the screen might look like a video effect from DEFII. But it means retyping the whole lot unless you've saved it.
There are two commands that will trace a program's execution. Entering TRON at the Ready prompt will switch TRace ON and TROFF will disarm it. Each line number will be displayed as it is executed, which sounds more useful than it is. It makes an awful mess of the screen and is rarely helpful. That said, if the program doesn't do much screenwise anyway it could come in handy and there are public domain programs around that will divert the output to a printer.
You can create a special routine which your program will jump to when things go to the wall, by placing ON ERROR GOTO [line number] in your program. When an error occurs, Basic sets two system variables. System variables are variables which only the computer can change, like TIME. The first of these, ERL, contains the line in which the error was found. The second, ERR, contains the error number. Here's an example error trapping routine:
10 ON ERROR GOTO 500
20 FOR g= 1 TO 11
30 PRINT a$(g)
500 MODE 1:PAPER 0:PEN 1:CALL &BC02
510 REM That line sets the screen to something sensible.
520 PRINT "Error in line"ERL
530 PRINT "Error number"ERR
This program crashes because it tries to use an undimensioned array. Don't worry if you don't understand arrays, the program is only supposed to show the possible shape of a program with error trapping. You can use RESUME to continue the program after an error, but it's probably more useful to let it stop and begin your investigations.
Your first clues are the values of ERR and ERL. Each error number has a corresponding error message which the computer uses as it ejects you from your comfy proggy into the Ready prompt. The first thing about debugging is that the error line does not always point to where the error is. It points to where it was discovered, which is a good starting point nonetheless. This is a very important concept, since some errors might not be discovered for ages and when they are, the line reported will be perfect. We're going to discuss each of the errors in turn now, considering where the bug could really be lying.
ERROR 1 - Unexpected NEXT
A loop is a chunk of program that is to be repeated and its boundaries are marked by a FOR command at the beginning and a NEXT command at the end. The line given should contain a NEXT command. Check that the variable after it is correct if present and try removing it altogether (it is optional). Failing that, check the FOR statement most immediately before it. Make sure that it exists and that it has been entered correctly.
ERROR 2 - Syntax Error
Syntax means grammar (nothing to do with revenue from immoralities). This error occurs when you use a command incorrectly. Assuming that it looks okay, reach for the manual and check the correct format. It's possible that the parameters (data items) in the suspect command are in the wrong order. It's possible you've mistyped the command (although if you use lowercase to enter programs, this sticks out like a sore proverbial).
If you are using long multi-statement lines, tracking the problem command becomes less easy. You could try copying each command out out of the line (using shift with the cursor keys and the copy key) and running them one by one from the Ready prompt to try to locate the bugged one. There's no guarantee though that the command is not failing because it's running without the conditioning of the rest of the program.
ERL will show when the error was discovered and the line that Basic calls up for your attention may not be where the problem is. In particular, where user functions are concerned (variables that begin FN and are defined using a DEF FNvariablename command), the error is often in the DEF FN even though the first use of the function prompts the crash. In DATA statements, a Syntax Error can be caused by data getting out of step which can have as much to do with the READ commands as the data commands. If you use commas in strings contained in DATA statements, you must place speech marks around the whole string. For example:
10 DATA Good Afternoon, Sir,4
20 READ string$,number
Line ten contains two strings, because of the comma in the middle, and would cause a crash when trying to READ the number because a string would be next in the queue. This example shows how to correct this:
10 DATA "Good Afternoon, Sir",4
20 READ string$,number
If there is a loop in the suspect area, it's always a good idea to print the values of the looping variables (those immediately after the FOR commands). This will tell you whether the error is inherent in the code, or whether it only occurs when the loop reaches a certain point. If the variable values are the start values (usually one, otherwise the numbers after the equals sign in the FOR commands), then it occurs first time and is probably inherent. If not, it has already worked and is only stopping now the loop has reached a certain point. This could be because a number is going out of range for a command's use or could be because one of your data statements is wrong.
Syntax errors can also be triggered if you try to load programs for the CPC6128 on a CPC464. Basic keywords on the 6128 will appear as nonsense in the listing and trigger the alert, stopping the program loading. To get around this, save your program on a 6128 as an ascii file, like this:
You should now be able to load it in a CPC464 and edit out the CPC6128-specific commands.
ERROR 3 - Unexpected RETURN
This usually means that Basic has strayed into a subroutine. Assuming that you put your subroutines at the end of your program, it's always a good idea to put an END command just before the first one to prevent this happening. Apart from which, this might have been caused by using GOTO. Here is the GOTO sermon: Never use the command. It leads to listings like beetle bolognaise: spaghetti programs crawling with bugs. These programs are easy to write but painful to debug. There is always a better alternative to GOTO (except with ON ERROR) and when you know what it is, you would be well advised to use it. Here ends the first lesson.
ERROR 4 - DATA exhausted
The command DATA usually precedes a long list of words, numbers or both. When you get this error, it means that there isn't enough in the lists for the program to use. The command READ pulls items from the data list, so it's worth checking that you do not have too many of these. The READ doesn't have to be anywhere near the DATA and you should pay special attention to the values of FOR/NEXT loops near READ commands. ERL should point to the READ command where the program noticed it was datorially challenged. If the READ seems okay, it's a case of ploughing through the data. Look for patterns in it. Does it always have ten items per line? Is it word-number-word-number all the way through? If you spot the pattern, it will make it easier to spot omissions. Each item is separated by a comma and it's often the case that a full stop creeps in instead, which is not easy to spot. If a string is intended to contain a comma as part of it, the whole string must be placed within speech marks. Data which consists of lots of short letter and number combinations is usually hexadecimal machine code (eg 4A,1C,CO,66,7F or BC02,776E,1123). Only the letters A-F are valid for this and any other will cause an error (though probably ERRor 2 or 13). Thus if the typeface is unclear, I (ninth letter of the alphabet) is out of the range A-F, so it must be 1 (the first cardinal number). Similarly an O for orange is out of the question and must be a zero.
ERROR 5 - Improper Argument
This is usually difficult to trap. Assuming the command is syntactically correct, the problem usually lies in the values of the variables it is using. The best approach is to PRINT the variables concerned to screen to see if it is obvious which is causing the problem. Contrast the values given with any limits specified in the manual (e.g. LOCATE 1,72 causes an error because the second value may be no greater than 25). Almost certainly the bug is nowhere near where the error is found. You will have to scour the whole program to see where the variable starts misbehaving. Look for mistyped variable names, confused plus and minus signs and for using the wrong variable altogether. When choosing your own variable names, try to choose meaningful names that are long enough to remind you of their content without allowing too much room for mistyping (eg sc for score, gn for game number).
ERROR 6 - Overflow
An arithmetic operation has overflowed. If this is your own program, you are using numbers beyond the range that the CPC can cope with. If not, check the algebra (groan). The ERL will usually point to where the problem actually is. It could be, though, that the variables were corrupted earlier on and so the program should be checked in the same way as for ERROR 5.
ERROR 7 - Memory Full
It doesn't mean that you have literally typed in 64k (although it might feel like it); it means that the memory allocated to the Basic program is full up. The command MEMORY sets the top of Basic memory, so check this command. There should only be one of them. If you find that this command is being used with HIMEM (eg 10 MEMORY HIMEM-500), then it's likely that the program moves the top of Basic memory down each time it is run. RUNning the program several times, then, could bring this error on. To correct, try the following at the Ready prompt:
SYMBOL AFTER 256: MEMORY 43903
If you have any ROMs attached, this will fail because the default top of memory is no longer 43903. Try using PRINT HIMEM when you first switch on to see what value you should return the top of memory to in this case.
If your Basic program is anywhere near nineteen blocks long on the tape or 38k on disc, then it is beginning to stretch things. Try compressing the program by removing REMs and joining lines where possible. Be careful where REMs form the first line of a subroutine. To be on the safe side, it is best to remove the REMs one by one (only) and follow this with a RENUM. It might produce an error, but the program will still work. You can check how much memory is free by using PRINT FRE(0)"Bytes Free". Better still, look at making your program a multi-loader. The command CHAIN will load a program without clearing the variables set up. The "Road to Code" section includes a REM stripper and additional compression hints.
ERROR 8 -Line does not exist
ERL will show you where it's going from and this line will show where it's trying to get to. (Check GOSUB statements and the suchlike). You might have omitted a vital REM line or mistyped the line number (use AUTO as far as possible when entering listings to prevent this).
ERROR 9 - Subscript out of range
Arrays are variables with dimensions, which means that they use additional data items in brackets. The values in the brackets are often at fault here. If they are variables, then it could be time to scour the program again. If not, it's probably a typo. Before arrays can be used, the computer must allocate space and it is told to do so by using the DIM command. This is usually near the start of the program or in the initialisation subroutine and is well worth verifying.
ERROR 10 - Array already dimensioned
However, it should only be told to DIM each array once. The error must be in a DIM command somewhere, so it is usually relatively easy to track down.
ERROR 11 - Division by zero
Anything divided by zero is infinity, which is slightly beyond the CPC's capability (Ahem). It's almost certainly a case of a zero variable being used; probably because it's the wrong one. If it isn't you know the routine: a cup of coffee and scour the whole program.
ERROR 12 - Invalid direct command
By definition, you cannot get this while a program is running (that is not a challenge). It will occur if you type something in at the Ready prompt which can only be executed as part of a program. The solution is not to do it again.
ERROR 13 - Type mismatch
Usually found if a string occurs when a numeric value was expected, or vice versa. Check for $ signs and % signs and make sure that you have the correct number of speech marks in the right place. If the variable does not have any symbol after it to identify its type, check for DEFINT, DEFSTR (6128 only), DEFREAL statements at the beginning of the program or in the intialisation routine. These commands specify what type of variable a variable is to be throughout the whole program. Type mismatch is often caused by using brackets incorrectly, for example opening them and leaving the computer in hopeless anticipation of closure. If a command has brackets in it and it produces any error, it's always a good start to check there are the same number of open and closed brackets and that these are in sensible places.
ERROR 14 - String space full
Try to ERASE any redundant string arrays and try increasing the memory available by removing any ROMs from the machine and compressing the program (see error 7). In your own programs, try to recycle string variables as much as possible.
ERROR 15 - String too long
Strings may only be up to 255 characters long. This error is most likely to be caused by adding strings together, so check any pieces of code which do this. (eg a$=b$+c$)
ERROR 16 - String expression too complex
Basic only has a limited workspace (temporary memory) and if you use long multiple string operations, it will run out of room. The solution is to break the function down into smaller parts, using temporary strings for storage if necessary, then join them together. For example:
a$=b$+c$+d$+e$ might become
ERROR 17 - Cannot CONTinue
CONT is a command used at the Ready prompt to continue a program after it has been stopped by use of a STOP in the program. You must not alter the program inbetween STOPping and CONTinuing, otherwise this error will occur. You could pick it up by using GOTO followed by the number of the line you want, but don't tell anyone I told you so...
ERROR 18 - Unknown user function
Variables which begin with FN are user functions- they have a defined formula and the variable is automatically updated each time it is needed. Check the spelling of the variable (as ever) and check the DEF FN command which defines it. This is probably near the start of the program or in the initialisation subroutine.
ERROR 19 - RESUME missing
RESUME is the command used to carry on after an ON ERROR GOTO routine. This error will occur if the program finishes before it finds a RESUME after having been ON ERROR GOne TO. It's not too tragic. After all, the program was broken anyway.
ERROR 20 - Unexpected RESUME
This probably means the program has managed to stray into your error detection routine. Putting an END command before the start of it ought to help prevent this recurring. If not, check for GOSUBs (and GOTOs) that point to your error routine and shouldn't.
ERROR 21 - Direct command found
This occurs when trying to load a program. It means that a command has been found without a line number and is probably a sign that you are trying to load a file that is not a Basic program or a file that has been tampered with in some way. This is very unlikely to occur if it is possible to conventionally load the program concerned into Basic. Check the loading instructions (if any) or try loading it into a word processor or memory editor to see what's inside. Basic programs which have been editted as ASCII files could cause this if a line number is lost en route.
ERROR 22 - Operand missing
Missed a bit. It's similar to Syntax Error, but it's more specific. A missing comma between operands could be the problem. If not, reach for that manual to see what else the CPC needs to know...
ERROR 23 - Line too long
A Basic line is too long to be stored. Break the line down into many shorter lines until you have no problem. Assuming that the lines increment in steps of ten, there will be plenty of space inbetween anyway. If not, try RENUMbering and then breaking the line down. Use caution with RENUM, in case you renumber a routine held apart from the main program for your own convenience. Such a routine would be brought down to the end of the main body and would need to be RENUMbered back to a high line number range.
ERROR 24 - EOF met
The End of File has been encountered while still trying to read data in. Check references to #9. This is the cassette input stream, so any loops near a routine INPUTing from #9 might be the root of the evil. It might be the case that your program is saving its files incorrectly, so check the routine that PRINTs to #9 too.
ERROR 25 - File type error
This means that you are trying to load the wrong type of program. What you are trying to do (using the commands LOAD, RUN", OPENIN or similar) is not possible from normal Basic, so check filenames carefully.
ERROR 26 - NEXT missing
Every FOR must have a NEXT to mark the end of the repeat boundary. So check where it should be, and put it in. Don't be confused by multiple loops; each must have its own NEXT. If you are placing the variable name after the NEXT (eg NEXT g), then try checking the variable used or omitting it.
ERROR 27 - File already open
OPENIN and OPENOUT are used to open files for reading and writing. Only one may be open at a time, so make sure that OPENed files are being closed with a CLOSEIN or CLOSEOUT as appropriate.
ERROR 28 - Unknown command
Be very careful with typing the names of extended commands, i.e. those that begin with a bar character. If you make a mistake, the machine often crashes and you are lucky to see this error message. Check that you have all your CALLs in the correct place and with the correct values. Do not use the bar character in REM statements, either, because it used to cause strange happenings on the 464.
ERROR 29- WEND missing
ERROR 30- Unexpected WEND
Every WHILE command needs to have a WEND to mark the end of the section to be repeated - but just the one WEND. If either of these errors occurs, check both the WHILE and the WEND since an error in either can cause these messages.
Another frequently encountered basic error is Bad Command. This isn't a proper Basic error, but is instead usually caused by external commands, which begin with a bar character. These commands can take the form of machine code RSXs (extra commands) or ROM RSXs (including disc commands). In the event of this error, the best solution is to check in the ROM's manual or software documentation. The cause is often that parameters are being passed to the system incorrectly. If you are using strings with an external command on the 464, it is necessary to put an @ symbol before the string name.
Read error a / Read error b
Tape loading errors Read Error a and Read Error b require you to rewind the cassette and press [PLAY] so the machine can try to read that part again. Read Errors c and d exist, but it is believed they can only be caused by deliberately programming them or by playing a tape which has its own special loader (e.g. fast loading commercial releases). If the problem persists, try cleaning the pinch rollers and read/write heads of the tape deck using a cleaning kit. If you're using an external cassette deck, it's worth experimenting with the volume levels and ensuring that you don't have a graphic equilizer, noise reduction or similar feature selected. Problems are sometimes encountered trying to load data onto a different machine to that from which it was saved (these differences can also occur in audio tapes, but you can rarely hear the difference). Using a jeweller's screwdriver (i.e. the sort you're likely to find dropping out of a christmas cracker), it is possible to adjust the tape deck's head alignment. On the 464 the small square hole along the lower edge of the tape door is where the screwdriver should be inserted. When [PLAY] is pressed, a screw comes into line beneath this hole. By playing with a tape in and the volume up (use RUN" to get the tape to play), you should be able to hear the sound become crisper as you turn the screw. Don't turn it more than half a turn each way and be careful to remove the screwdriver again before releasing play.
Hopefully that's given a few clues on error tracking. Bugs are elusive beasts and some of them don't even produce error messages. If you've finished all the coffee and things still don't behave, it could be time to consider compatability. Sometimes programs written using a 6128 or 664 will not work on a 464. The next section tackles this problem.