In the article "Hardcore Applesoft" in the March 2010 issue of Juiced.GS, there is a bug in the TRY structure (Figure 4 in that issue's page 21) which makes it unsafe to use GOSUB or FOR…NEXT from a TRY block inside a subroutine. To fix this problem, change the following lines as shown. Every TRY structure in your program will need its own unique variable name, not used elsewhere, in place of XX.
10101 XX = PEEK(248) : ONERR GOTO 10150 10151 POKE 216,0 : ER = PEEK(222) : POKE 223,XX : CALL -3288
This makes Applesoft forget about returning to any GOSUB or FOR which came after the start of the TRY block, so RETURN from your subroutine works correctly, rather than jumping back inside the TRY block. In technical terms, the PEEK gets the stack index at the outset of the TRY, and the POKE/CALL restores it, thus erasing anything put on the stack by GOSUB or FOR after the TRY started.
In the second installment of our series on Structured Applesoft, appearing in the March 2010 issue of Juiced.GS, we examined IF…ELSE IF…ELSE structures for decision making. Languages such as C and Java provide an additional decision making structure, SWITCH…CASE, which, after the first true "case" (evaluation), will "fall through" all remaining cases as through they were also true, executing their code blocks until a BREAK (if present) is reached.
The SWITCH structure can also include an optional DEFAULT block at the end which always executes unless there is a BREAK which precedes it. (Other languages permit DEFAULT anywhere in the SWITCH structure, where it will execute only if subsequent cases aren't true, but this style is rarely used and not easily accomplished in Applesoft.)
SWITCH…CASE…DEFAULT is far less commonly used than IF…ELSE IF…ELSE, but for those who wish to use it, it can be structured in Applesoft as shown in Figures 1 and 2. To BREAK out of the SWITCH structure from any CASE, GOTO the END SWITCH line and conclude with REM BREAK. Example: 10290 GOTO 10350 : REM BREAK
10100 REM SWITCH 10101 ZZ = 0 10110 IF (p1=1) THEN ? 1 : ZZ = 1 : REM CASE 10120 IF ( ZZ OR (p1=2) ) THEN ? 2 : ZZ = 1 : REM CASE 10130 IF ( ZZ OR (p1=3) ) THEN ? 3 : ZZ = 1 : REM CASE 10140 ? 0 : REM DEFAULT 10150 REM END SWITCH
Figure 1. A Structured Applesoft single-line (per case) SWITCH structure.
10100 REM SWITCH 10101 ZZ = 0 10150 REM CASE 10151 ON (condition) GOTO 10152 : GOTO 10200 10152 ZZ = 1 10160 statement 10170 etc 10200 REM CASE 10201 ON ( (condition) OR ZZ ) GOTO 10202 : GOTO 10250 10202 ZZ = 1 10210 statement 10220 etc 10250 REM CASE 10251 ON ( (condition) OR ZZ ) GOTO 10252 : GOTO 10300 10252 ZZ = 1 10260 statement 10270 etc 10300 REM DEFAULT 10310 statement 10320 etc 10350 REM END SWITCH
Figure 2. A Structured Applesoft multiple-line (per case) SWITCH structure.
TRY, TRY Again
Our last issue also included a look at how to handle errors in an Applesoft program with a TRY structure. With this method, your program "tries" to execute code in a TRY block, and if an error occurs, it is "thrown" to a subsequent CATCH block, which handles it. (This supplement assumes you've read the article.)
Here, we'll discuss more options for your TRY structure. In addition to TRY and CATCH, you can use one or more CATCH ERROR blocks to act on specific errors, a FINALLY block for concluding code which always executes whether or not an error occurs, and you can THROW an error if you want the user to see it.
To check for specific errors, start with a standard CATCH as discussed in the main article, shown in lines 10150-10151 of Figure 3. This is followed with one or more CATCH ERROR blocks which act on specific errors by evaluating ER. These are followed by a CATCH OTHERS block, which is executed if the error is not anticipated by the previous CATCH ERROR blocks. (This is essentially the same structure as IF…ELSE IF…ELSE.) As shown in Figure 3, each ON line should be preceded with a separate line which says REM CATCH ERROR: followed by the error name(s) being checked for, and each catch block should conclude with a GOTO to FINALLY (discussed next), if present, or END TRY. The full list of errors and their corresponding codes is shown in Figure 4.
If you wish to have concluding code that always executes regardless of whether or not the TRY block throws an error, create a FINALLY block after your CATCH or CATCH OTHERS block. A common use of FINALLY is to close an open file or perform some other "cleanup" operation you want to have happen no matter what. To make this happen, you need a REM FINALLY line, followed by another line which GOSUBs to the subsequent line, and then on the same line (after the GOSUB), GOTOs the END TRY line. Your code then follows, and must conclude with RETURN. You can see this in lines 10350-10399 of Figure 3. The GOTO which concludes the TRY block and any catch blocks should jump to the FINALLY line rather than END TRY. If you want to RETURN or THROW (discussed next) from a catch block, you must GOSUB to the first line of your FINALLY code and append REM DO FINALLY, as shown in lines 10260 and 10310. This ensures there's no way out of your TRY structure without FINALLY executing.
What happens if the code that you tried threw an error, and your CATCH block caught it, but you want the user to see the error, as though it hadn't been caught? No problem; you deliberately THROW it back to the offending statement, which executes it again, and this time, the error will be displayed and the program will stop. To do this, use the RESUME command, followed by REM THROW, as shown in line 10310. Don't use RESUME for any other purpose, because it is a kind of GOTO, which we don't want in our structured program. In fact, because we want to show the error and stop the program, you should only THROW in scenarios where you are reasonably sure that the offending statement will behave the same way it did when it originally threw the error. If you wish to throw a CTRL-C (BREAK), don't use RESUME; instead, use POKE 117,PEEK(218) : POKE 118,PEEK(219) : STOP : RUN, as shown in line 10270 of Figure 3, which will print a BREAK statement with the correct line number, and restart the program if the user types CONT.
10000 REM SUB^OpenFile:10000-10499 10010 LET D$ = CHR$(4) : REM D$->ctrl-D 10100 REM TRY 10101 XX = PEEK(248) : ONERR GOTO 10150 10110 PRINT D$;"VERIFY ";P1$ 10120 PRINT D$;"OPEN ";P1$ 10130 PRINT D$;"CLOSE" 10135 PRINT "opened and closed file successfully" 10140 POKE 216,0 : GOTO 10350 10150 REM CATCH 10151 POKE 216,0 : ER = PEEK(222) : POKE 223,XX : CALL -3288 10160 REM CATCH ERROR: FILE TYPE MISMATCH 10161 ON (ER=13) GOTO 10170 : GOTO 10200 10170 PRINT "ERROR: not a text file" 10180 GOTO 10350 10200 REM CATCH ERROR: FILE NOT FOUND, SYNTAX ERROR [DOS 3.3] 10201 ON ( (ER=6) OR (ER=11) ) GOTO 10210 : GOTO 10250 10210 PRINT "ERROR: invalid filename" 10220 GOTO 10350 10250 REM CATCH ERROR: BREAK (CTRL-C) 10251 ON (ER=255) GOTO 10260 : GOTO 10300 10260 GOSUB 10360 : REM DO FINALLY 10270 POKE 117,PEEK(218) : POKE 118,PEEK(219) : STOP : RUN : REM THROW 10280 GOTO 10350 10300 REM CATCH OTHERS 10310 GOSUB 10360 : REM DO FINALLY 10320 RESUME : REM THROW 10330 GOTO 10350 10350 REM FINALLY 10351 GOSUB 10360 : GOTO 10400 10360 PRINT D$;"CLOSE" 10399 RETURN 10400 REM END TRY 10499 RETURN
Figure 3. A Structured Applesoft subroutine containing a TRY structure with multiple CATCH ERROR blocks, a FINALLY block, and a THROW.
A 0 NEXT WITHOUT FOR DP 1 LANGUAGE NOT AVAILABLE DP 2 RANGE ERROR D 3 RANGE ERROR P 3 NO DEVICE CONNECTED DP 4 WRITE PROTECTED DP 5 END OF DATA D 6 FILE NOT FOUND P 6 PATH NOT FOUND D 7 VOLUME MISMATCH DP 8 I/O ERROR DP 9 DISK FULL DP 10 FILE LOCKED D 11 SYNTAX ERROR P 11 INVALID PARAMETER DP 12 NO BUFFERS AVAILABLE DP 13 FILE TYPE MISMATCH DP 14 PROGRAM TOO LARGE DP 15 NOT DIRECT COMMAND AP 16 SYNTAX ERROR P 17 DIRECTORY FULL P 18 FILE NOT OPEN P 19 DUPLICATE FILE NAME P 20 FILE BUSY P 21 FILE(S) STILL OPEN A 22 RETURN WITHOUT GOSUB A 42 OUT OF DATA A 53 ILLEGAL QUANTITY A 69 OVERFLOW A 77 OUT OF MEMORY A 90 UNDEFINED STATEMENT A 107 BAD SUBSCRIPT A 120 REDIMENSIONED ARRAY A 133 DIVIDE BY ZERO A 163 TYPE MISMATCH A 176 STRING TOO LONG A 191 FORMULA TOO COMPLEX A 224 UNDEFINED FUNCTION A 254 REENTER A 255 BREAK (CTRL-C)
Figure 4. Table of PEEK(222) codes and corresponding errors. A=Applesoft, D=DOS 3.3, P=ProDOS.