Computed GOTOs And GOSUBs For Commodore 64
William M. Wiese
This short, relocatable utility permits computed GOTO and GOSUB statements in Commodore 64 BASIC.
You're probably familiar with GOTO and GOSUB statements, which pass control to another line in a BASIC program. In Commodore BASIC, these keywords can only be followed by a line number, as in GOTO 100. Some other versions of BASIC let you replace the line number with a variable, such as GOTO X, or even a complex expression, such as GOSUB X + 100 * ABS (Y). Since the line number is computed from the expression, the term computed GOTO or GOSUB is used to describe this feature.
Computing the destination from an expression offers two advantages. You can make your programs easier to understand by using meaningful variable names for subroutines instead of line numbers—for instance, replacing GOSUB 1000 with GOSUB DRAW. And computed GOTO and GOSUB statements offer a more flexible and efficient means of controlling program flow. For example, say that you write a program with six subroutines: The first starts at line 1000, the second is at 2000, and so on up to line 6000. The usual way to direct the computer to the correct subroutine would be with an ONGOSUB statement:
ON A GOSUB 1000,2000,3000,4000, 5000,6000
With computed GOSUBs, the same thing can be accomplished with the more compact statement GOSUB A. If A=1000, the computer performs the subroutine at line 1000. If A = 2000, then GOSUB 2000 is performed, and so forth.
The program below adds both of these useful statements to Commodore 64 BASIC. Type in and save a copy before you run it. Enter line 130 exactly as shown (do not add an extra comma after the number 57812). The program automatically saves a machine language program named "CGO.ML" on disk. If you're using tape, change the, 8 to, 1 in line 130. Once the program has been created, load it with LOAD "CG0.ML", 8, l for disk or LOAD "CG0.ML", l, l for tape.
Once the routine is loaded into memory, you can perform a computed GOSUB with the statement SYS 49152, expression. Replace expression with any variable or expression that evaluates to a valid line number (from 0–63999). Use SYS 49179, expression to perform a computed GOTO. For example, if the variable DRAW equals 1000, then SYS 49152, DRAW does the same thing as GOSUB 1000, and SYS 49179, DRAW does the same thing as GOTO 1000.
It's usually advantageous to substitute variables for 49152 and 49179 in such SYS statements. For instance, your program might contain the following lines:
10 CG = 49152 90 SYS CG,DRAW
In Commodore BASIC, using variables in place of numbers speeds up a program. It takes the computer less time to find the value of the variable CG than it does to calculate the value of a constant such as 49152.
In some cases, you may want to use the memory locations starting at 49152 for a different machine language routine. If you use a disk drive, you can move the computed GOTO/GOSUB routine to the cassette buffer, which begins at location 828. Simply change lines 100, 140, 150, and 210 as shown here:
100 FOR I = 828 TO 878 : rem 234 140 POKE 193, 60 : POKE 194, 3 : rem 89 150 POKE 174, 110 : POKE 175, 3 : rem 132 210 DATA 76, 91, 3, 169, 255, 133 : rem 102
Before running the modified program, replace the name CG0.ML in line 130 with a new name (CGML/828 or whatever) that reflects the alteration. Then load the program as described above and use SYS 828, expression for computed GOSUB and SYS 855, expression for computed GOTO.
Occasionally, computed GOTOs and GOSUBs don't seem to work correctly. For example, suppose a program contains the statement SYS 49179, 5 * COS(X). If X has the value 0, then this statement should do the same thing as GOTO 5 (to confirm this, type PRINT 5 * COS(0) and press RETURN). Instead, the computer performs the equivalent of GOTO 4. Such effects are the result of slight rounding errors caused when the computer converts numbers from one format to another. The 64—like virtually every other computer—stores and manipulates numbers internally in a different format from the decimal numbers we ordinarily use. In this case, the computer evaluates 5 * COS(0) as 4.99999999, then throws away the fraction, ending up with the integer (whole) value of 4. To prevent such rounding errors, add a small number (.00001 is a good value) to the expression. For instance, SYS 49179, 5 * COS(0) + .00001 correctly performs GOTO 5.
How It Works
Computed GOTOs and GOSUBs are surprisingly easy to add to Commodore BASIC. When the computer performs an ordinary GOSUB, it "remembers" its current place in the program by storing an address and the current BASIC line number in a special memory area called the stack. An additional byte is stored on the stack to show that a GOSUB caused the stack entry. This makes it possible for the computer to find its way back to the right spot when the subroutine ends with RETURN.
From this point onward, GOSUB and GOTO share the same code and work exactly the same. The computer looks at the ASCII line number stored in the BASIC program text (if it finds anything other than ASCII numerals, it stops with an UNDEF'D STATEMENT error). Then it converts the line number to integer form and stores it in locations 20-21. Finally, the computer searches the program text for the matching line number and (if the line exists) continues forward.
To make computed GOTOs and GOSUBs possible, this utility duplicates the way a GOSUB statement stores return information on the stack. But it adds something new to the common routine that retrieves the line number from the program text. Instead of getting the line number in the old manner, we call BASIC'S main evaluation routine at memory address 44446. This routine, usually labeled FRMEVL, can evaluate any BASIC expression (unlike the normal routine, which accepts only numerals). After calling a second routine at 47095 to convert the number into a two-byte address, the utility stores the line number in locations 20–21. Since this is exactly where the GOTO routine expects to find the line number, we then jump into the computer's normal routine at address 43171.
Computed GOTOs And GOSUBs
For instructions on entering this listing, please refer to "COMPUTE!'s Guide to Typing In Programs" published bimonthly in compute!:
100 FOR 1 = 49152 TO 49202 : rem 167 110 READ A : POKE I, A : rem 14 120 NEXT : rem 210 130 SYS 57812 "CGO.ML", 8 : rem 226 140 POKE 193, 0 : POKE 194, 192 : rem 140 150 POKE 174, 51 : POKE 175, 192 : rem 193 160 SYS 62954 : rem 160 170 DATA 169, 0, 133, 2, 169, 3 : rem 250 180 DATA 32, 251, 163, 165, 123, 72 : rem 193 190 DATA 165, 122, 72, 165, 58, 72 : rem 156 200 DATA 165, 57, 72, 169, 141, 72 : rem 152 210 DATA 76, 31, 192, 169, 255, 133 : rem 201 220 DATA 2, 32, 253, 174, 32, 158 : rem 90 230 DATA 173, 32, 247, 183, 32, 163 : rem 195 240 DATA 168, 165, 2, 240, 1, 96 : rem 47 250 DATA 76, 174, 167, : rem 176