Error Trapping in Atari BASIC
Crashes that you can prevent.
Of the hundreds of program submissions Antic receives each year, many which are otherwise excellent must be rejected because they lack error trapping. No matter how thoroughly you test your program, errors can still crash it during operation simply because the user isn't as familiar with it as you are. This article will show you some error-trapping routines designed to make everybody's life easier.
Before sending your program out into the world, ask yourself, "Will something as simple as a user's misinterpretation of a prompt make the listing crash?
You've already done the hard part--making your program understandable to the machine. Now you just need to fine tune it for sensitivity to any off-the-wall keystroke combinations the user tries.
SET A TRAP
To make sure our program--not the operating system--takes control when an error occurs, we must first tell the computer, "If there's an error, let the program handle it." Predictably, this is called error trapping. The BASIC syntax we need to use is: TRAP linenum.
Here linenum is the program line for the computer to go to if an error occurs. At linenum we analyze the situation and act accordingly. Put this statement at the beginning of your program to enable error trapping before any problems crop up.
The trap must be reset each time an error occurs. Otherwise the operating system resumes control the next time there's an error. A good place to reset the trap is at the first line of your error-handling routine--but make sure it doesn't have any bugs. If you always put a TRAP statement there, you won't have to worry about the operating system taking over when you don't want it to.
You don't need to have an error to declare a new TRAP statement. When the computer encounters a TRAP statement, it remembers only the new line number and goes there if an error occurs. Thus we can have one central location for handling errors. Or we can reset the trap and go to different error-handling routines depending on where the error occurs. We'll use the second method in these examples, but you'll learn how to do it either way.
Of course, you won't always be able to deal effectively with an error. Unforeseen circumstances do tend to arise. The BASIC equivalent to throwing your hands up and telling the operating system, "I don't know what to do--you handle it" is to set a TRAP statement to an illegal line number. (BASIC allows line numbers between 0 and 32767.) Most programmers use 40000. This shuts off any previous TRAP statements.
Let's experiment, using the example of a user trying to write to a write-protected disk. We can handle this easily by asking the user to remove the write-protect tab and try again.
100 TRAP 30000
110 OPEN #1,8,0,"D:DUCKS.DAT"
120 ? #1,"Donald"
130 ? #1,"The, Howard"
140 ? #1,"Daffy"
145 ? #1,"Sitting"
150 CLOSE #1
160 ? "Names entered."
170 END
30000 TRAP 30000:REM Reset trap
30010 CLOSE #l:REM Close channel
30020 ? :? "Disk is write-protected.":?
30030 ? "Please remove write-protect tab and"
30040 ? "put disk back in drive.":?
30050 ? "Press any key to continue.":POKE 764,255
30060 IF PEEK(764)= 255 THEN 30060:REM Wait for keypress
30070 POKE 764,255:REM Clear last key pressed
30080 GOTO 110:REM Try again
This program works fine if it encounters the error we've anticipated. However, the key to error trapping is to consider every possible error. Here, the disk drive may be empty or disconnected, or the disk maybe full or even have a bad sector.
The computer stores the number of the last error encountered in memory location 195. We can use this information to modify our error-trapping routine.
100 TRAP 30000
110 OPEN #I,8,0,"D:DUCKS.DAT''
120 ? #1,"Donald"
130 ? #1,"The, Howard"
140 ? #1,"Daffy"
145 ? #1,"Sitting"
150 CLOSE #1
160 ? "Names entered."
170 END
30000 TRAP 30000:REM Reset trap
30010 CLOSE #1:? :REM Close channel
30015 ERR=PEEK(195)
30020 IF ERR=144 THEN 30150:REM Disk is write-protected
30030 IF ERR = 138 THEN 30200: REM Device does not respond
30040 IF ERR = 139 OR ERR = 164 THEN 30250:REM Having odd problems
30050 IF ERR = 140 OR ERR = 142 OR ERR = 143 THEN 30300:REM Disk may be damaged
30060 IF ERR = 162 THEN 30350: REM Disk full
30070 IF ERR = 167 THEN 30400: REM File locked
30080 IF ERR = 169 THEN 30450: REM Directory full
30090 ? "Error number ",ERR;" has occurred.":GOTO 30500:REM Don't know what's happened
30150 ? "Disk is write-protected.":?
30160 ? "Please remove write-protect tab and"
30170 ? "put disk back in drive."
30180 GOTO 30500
30200 ? "Device is not responding"
30210 ? "Check disk drive cables and try again."
30220 GOTO 30500
30250 ? "I'm having trouble openning file."
30260 ? "I suggest you check disk drive"
30270 ? "and try again."
30280 GOTO 30500
30300 ? "Your diskette may be damaged."
30310 ? "I suggest you try again or use"
30320 ? "another disk."
30330 GOTO 30500
30350 ? "Disk is full."
30360 ? "Please insert a new disk & try again."
30370 GOTO 30500
30400 ? "You already have a DUCKS.DAT file"
30410 ? "and it is locked. Insert new disk"
30420 ? "and try again or abort operation ."
30430 GOTO 30500
30450 ? "You can only have 64 files on a disk."
30460 ? "Please insert new disk and try again"
30470 ? "or abort operation."
30500 ? :? "Press R to retry; A to abort."
30510 OPEN #1,4,0,"K:":REM Open keyboard for input
30515 GET #1,OPT:CLOSE #1:REM Get option and close channel
30520 IF OPT=65 OR OPT=97 THEN 170:REM Abort
30530 IF OPT=82 OR OPT=114 THEN 110:REM Try again
30540 GOTO 30500:REM Wait for A or R
Granted, you normally wouldn't want such a complicated error-handling routine for so simple a program. But it's a good example of how error trapping can make a program easy for anyone to run. We could even make the routine more complicated by having the program offer to unlock a locked file or delete some files if the disk or directory is full. The main thing is to be familiar with these techniques.
PREVENTIVE MAINTENANCE
You can also use preventive programming techniques to make sure that some errors never occur. Consider this short program to divide two numbers:
100 ? "Numerator"; INPUT N
110 ? "Denominator"; INPUT D
120 ? "The answer is:"; N/D
130 GOTO 100
This program will crash if the user enters zero as a denominator. While we could add a TRAP statement and a complicated error-handling routine, it's much simpler to check the input before the division ever occurs:
115 IF D=0 THEN PRINT "Division by zero is a no-no.":GOTO 110
Another problem happens when the user tries to put too many numbers into an array. Rather than check for an array dimension error we can avoid a problem by counting the numbets as they are entered.
100 TRAP 40000:REM Let the operating system handle any errors
110 DIM NUMS(100):I=0
120 IF I=100 THEN ? "I can't accept any more numbers.":GOTO 200
130 I=I+1:? "What is number";I;"(or 999 if done)"
140 INPUT N
150 IF N = 999 THEN ? "Numbers entered.":GOTO 200
160 NUMS(I)=N
170 GOTO 120
200 END
ADVANCED TECHNIQUES
Believe it or not, errors aren't always bad. Error codes often show what's going on inside the machine. After we're comfortable with error-handling routines, we can use this information to make our programs more user-friendly.
Before we go on, we need to know how to find out where in the program an error has occurred. The line number where the error occurs is stored in memory locations 186 and 187. To get the value, we'll create a variable ERL and assign it the value PEEK(187)*256+ PEEK(186).
Atari BASIC returns a type mis-match error if it expects a number to be entered, but instead the user simply presses the [RETURN] key. This is because it interprets the [RETURN] as a single character string. Knowing this, let's set up a loop that accepts numbers until a [RETURN] is pressed.
100 TRAP 30000:REM Set error trap
Notice that we set a TRAP 40000 at the beginning of the error-handling routine. This is redundant because the error which caused you to hit that line would disable the error trapping anyway. However, it makes this program much easier to read and modify.
Another application is to use error trapping to see when a list of DATA statements is exhausted. The programmer usually knows how much data to expect, so you can put your READ statement in a loop or check each item READ against a number like 999 or a string such as "END" that signifies the end of the data. However, you might want to let the user customize the list, in which case having to remember how to end it is clumsy.
An example might be a list of names which can be modified depending on who's using the program. In the following program segment, imagine we're READing the names of people who are sharing pizza. Later, we'll print a list of who owes how much money. To do this, read the names into a string array until error 6--Data List Exhausted occurs.
100 TRAP 30000
The number of ways to incorporate error trapping is virtually unlimited. If you're getting data from a disk, you can use the above idea to read from the file until you get an error 136-- End of File. If your program is going to write a file, a quick way to tell if the file already exists is to try to read it first. If it's not there you'll get error number 170--File Not Found. Using error trapping to check for this is easy--and may save someone from overwriting an important file.
Like any aspect of programming, a good error-handling routine should be "invisible" to the user. If you have an idea of which kinds of errors to expect, you can troubleshoot potential problems without the user ever knowing something's gone wrong. On the other hand, if your error-handling routine can't figure out what to do, you can at least print out error messages in plain English and give the user a chance to recover from the problem without a crash. Whatever the situation, error trapping is an effective way to make your programs polished, professional, and easier to run.
Listing: TRAPPING.BAS Download
110 DIM NUMS(100):I=0
120 IF I = 100 THEN ? "I can't accept any more numbers.":GOTO 200
130 I=I+1: "What is number";I;"(or
140 INPUT N
150 NUMS(I)=N:GOTO 120
160 ? "Numbers entered."
200 END
30000 TRAP 40000:REM Trap turned off
30010 ERR= PEEK(195):ERL = : PEEK(186)+ PEEK(187)* 256
30020 IF ERR=8 AND ERL=140 THEN 160
30030 ? "Error";ERR; "at line" ;ERL;"has occurred.": GOTO 200:REM Give up.
110 DIM X$(10),EATER$(200):I =0:REM Allow for 20 names of length 10 each.
115 EATER$=" ":EATER$(200)=EATER$:EATER$(2)=EATER$:REM: Set array to spaces
120 IF I = 20 THEN ? "Sorry, list ignored after";X$:GOTO 200:REM Too many names
130 READ X$
135 EATER$(I*10+1,I*10+10)=X$
140 I=I+1:GOTO 120
160 ? "The names are in the array."v
200 REM Continue with program.
300 END
20000 DATA Charlie,Gregg,Nat,Carolyn,Marta
20010 DATA Andy,Heidi,Tom
30000 TRAP 40000
30010 ERR= PEEK(195):ERL=PEEK(187)*256 + PEEK(186)
30030 IF ERR=6 AND ERL = 130 THEN 160
30040 ? "Error ";ERR;" at line '';ERL;" has occurred.":GOTO 300: REM Give up