Classic Computer Magazine Archive COMPUTE! ISSUE 72 / MAY 1986 / PAGE 81

Debugging Tool

Gary W. Swanberg

Here's a powerful debugging utility that lets Atari BASIC programmers analyze and manipulate variables, search for BASIC keywords, produce printed documentation of important program information, and call up disk directories without leaving BASIC. It works on all Atari 400/800, XL, and XE computers with at least 40K RAM, a disk drive, and Atari DOS 2.0 or 2.5.

"DEBUT," a utility for debugging Atari BASIC programs, was inspired by the "Atari Wedge" (COMPUTE!, December 1982). It uses the Wedge technique to provide BASIC with direct-mode commands for listing and locating variables, dumping variable values, and replacing one variable with another. It also searches for BASIC keywords and includes a DIR command for listing the disk directory. Output is easily redirected from the screen to the printer, disk drive, or any other output device. And because DEBUT is written in machine language, it remains completely transparent-BASIC programs can be loaded, modified, tested, and saved without interference.

Preparing DEBUT
The program following this article creates DEBUT as an AUTORUN.SYS file that runs with two of the three revisions of Atari BASIC (revisions B and C). If PRINT PEEK(43234) returns 162, then you have revision A, in which case you must substitute the following lines for the corresponding lines in Program 1:

200 DATA 255,255,0,31,157,31,175,164,
202 DATA 76,98,164,76,144,164,76,92,181,
204 DATA 12,181,76,53,181,76,159,186,76,
206 DATA 171,76,188,175,76,155,171,68,69,

(Revision A is the original BASIC cartridge made for the 400/800 and 1200XL computers. Revision B is the BASIC built into the 600XL and 800XL. Revision C is built into the 65XE and 130XE and also is available as a cartridge from Atari.)
    Type in and save the BASIC program, then insert the disk on which you want to create DEBUT's AUTORUN.SYS file and type RUN. Make sure an AUTORUN.SYS file-such as Atari SpeedScript or SpeedCalc-doesn't already exist on the disk or it will be replaced. After the AUTORUN.SYS file is created, you won't need to run the BASIC program again, except to create copies of DEBUT on other disks. To start DEBUT, turn the computer off, make sure a disk with the DEBUT AUTORUN.SYS file is in the drive, then turn the computer on. DEBUT loads automatically and announces its presence with the message DEBUT. This message appears whenever you press SYSTEM RESET, reminding you that DEBUT is active.
    DEBUT protects itself by resetting MEMLO (the pointer to the bottom of free memory, locations 743 and 744) and by trapping the DOS command. As in the Atari Wedge, typing KILL gets rid of the utility and enables DOS.
    Note: DEBUT works with Atari DOS 2.0 and 2.5, but may not work with other types of DOS, especially after a SYSTEM RESET. Be sure that you have one of these versions before typing in the program.

Once DEBUT is up and running, you have four new commands at your disposal for debugging and documenting BASIC programsplus the DIR command. You type the command and press RETURN, just as you would with any directmode BASIC statement. Type DIR, for example, to list the disk directory, or use the form DIR D:filespec to list specific files. The ? and * wildcard characters are allowed, and drive numbers (D2:, D3:, etc.) can be specified as well. (D: defaults to drive 1.)
    Now you're ready to load one of your BASIC programs and try the four main commands:

FIND    (Find a variable or BASIC keyword.)
REP      (Replace one variable with another.)
XREF   (List variables and cross-reference line numbers.)
VIEW   (View the values of all variables.)

     These commands act on your entire BASIC program unless you specify a range of line numbers using the form starting line,last line. You may specify either or both. When specifying only the last number, precede it with a comma, or DEBUT thinks it's a starting number. The line range parameter is optional, so it follows the other arguments to the command, as we'll explain below.
    All commands can also use the output switch-a slash appended to the command followed by the name of the device to which you want to redirect output (P for Printer, D for Disk, C for Cassette, etc.). This sends the command's output to the device rather than to the screen. To send the disk directory listing to the printer, for example, type DIR/P. This option uses channel 1 for output, closing the channel when it's done. Files created on disk or cassette are ASCII files which can be viewed with most Atari word processors or ENTERed into memory with BASIC.
    We'll see more examples as we take a detailed look at each command.

The FIND Command
With FIND, you can quickly identify any lines in your program that contain a certain variable or BASIC keyword. Here is the general format:

FIND [/output device] target [starting
   line,last line]

where /output device is the optional switch for redirecting output from the screen; target is the variable name or BASIC keyword you want to find; and starting line,last line is the optional line number range for the search.
    FIND lists every line in which the target appears. The target can be a statement name (keywords that begin a statement, such as PRINT, PLOT, and POKE), a function name (such as RND, COS, and PEEK), or a variable name. Include the right parenthesis as part of subscripted variable names, and the dollar sign as part of string variable names. (Remember: X, X(, and X$ are three distinct variables.)
    FIND does not locate keywords that are neither statements nor functions-THEN and TO, for instance. GOSUB and GOTO are missed when they appear as part of the ON-GOSUB or ON-GOTO statements. Also, FIND won't locate any operators, such as OR, AND, +, and /, to name a few.
    FIND does locate ERROR lines, however; use FIND * for this purpose.
    Directing output to the disk drive (FIND/D:filename) presents an interesting possibility-the file thus created can later be retrieved using BASIC's ENTER command, allowing you to sift lines out of one program to create another.
    Perhaps you've discovered that keywords can sometimes be used as variable names in Atari BASIC. FIND assumes that if it's spelled like a keyword, it is a keyword. You can force FIND to search for variables only by preceding the variable name with the greaterthan symbol (>). Thus, FIND LEN comes up with all occurrences of the LEN function, while FIND >LEN lists occurrences of a variable called LEN. This option is needed only if your target variable could be confused with a keyword. (Be careful when choosing variable names in Atari BASIC. LET NOTE=66 creates a legitimate variable, but if you try to access its value-for example, SOUND 0,NOTE,10,10 - BASIC treats it as NOT E, while FIND, without the > option, would go off looking for the keyword NOTE.)
    When you specify a line number range with the FIND command, only those lines within that range are listed. Here are some examples:


(List all lines in the program containing the array variable X.)


(List all lines containing the variable name TEMP, beginning at line 100.)


(List all lines through line 100 containing the function CHR$ and send the listing to the printer.)

FIND * 100,200

(List all lines from 100 through 200 that contain ERRORs.)


(Create a disk file named NEWFILE.ASC to hold the listing of all lines through line 20000 that contain the keyword PRINT.)

The XREF Command
As you write an Atari BASIC program, newly declared variables are added to an area of memory called the variable name table. XREF lists the contents of this table and cross-references the lines in which each variable appears. Here is the general format:

XREF [/output device] [starting line,last line]

where /output device is the optional switch for redirecting output from the screen; and starting line,last line is the optional line range parameter.
    Often, XREF finds variables in the variable name table that don't actually appear in your program. You can recognize these unused variables because XREF lists them with no line number references. Unused variables can happen when you delete all occurrences of a certain variable from a program, or when you misspell a variable or command and BASIC thinks you're creating a new variable. These unused variable names clutter up the variable name table, which might become a factor in a long program since the table is limited to 128 names.
    XREF can help you eliminate this deadwood. If a large number of unused variables show up, you can clear them out by LISTing the program to disk, typing NEW, and then re-ENTERing the program. (LIST and ENTER reconstruct the variable name table, whereas SAVE and LOAD preserve it.)
    When you specify a line number range with XREF (for instance, XREF 1000,2000), only those variables and line references within that range are listed. At the foot of the listing, both the count of listed variables and the total number of variables in the name table are displayed.
    XREF/P creates a hardcopy of the variable listing-useful documentation. XREF also makes it easy to spot misspelled variable names. No longer must you trace through a recalcitrant program line by line only to find that your mysterious bug is due to the slip of a finger. And if you're as poor a typist as I am, you'll be making frequent use of the REP command described below.
    Here are a few examples:


(List and cross-reference all variables in the program.)

XREF 2000,2999

(List and cross-reference all variables in the subroutine in lines 2000 through 2999.)

XREF ,10000

(List and cross-reference all variables up to line 10000.)


(List and cross-reference all variables in the program, and send the list to the disk file VARLIST.)

The REP Command
Now it's easy to replace those cryptic variable names with new names of crystal clarity-or, depending on your motives, vice versa. REP acts in a flash, replacing old variable names with new variable names. It's handy for making corrections and tightening up code-but be careful. With or without such a command, the more ambitious your program, the wiser it is to save backup copies and document changes as you make them.
    Here is the general format:

REP [/output device] oldvar newvar
   [starting line,last line]

where /output device is the optional switch for redirecting output from the screen; oldvar is the old variable name; newvar is the new variable name (separated by a space, not a comma, from oldvar); and starting line,last line is the optional line number range. (The only output produced by the output switch is a message telling you how many variables were changed.)
    REP does not let you replace keywords or mismatched variable types (for instance, you can't change A$ to A). Also, REP requires that the new variable already exists in the variable name table. To add a new variable to the table, simply use it in any BASIC statement, even in direct mode. (If you want to create a new variable SUM, for instance, you can just type SUM=0 and press RETURN.)
    If you don't include the optional line number range, REP works on the entire program. If you specify a range of lines, it replaces the old variable name only within those lines. Watch out for this, since it may cause a conflict elsewhere in your program.
    Here are some examples:


(Replace all occurrences of the variable CB with the new variable name CHECKBALANCE.)


(Replace all occurrences of the variable CLIENTNAME$ with the new variable name CN$.)


(Replace all occurrences of the array variable I() with the array variable INDEX().)

REP SUM TOTAL 3000,3999

(Replace all occurrences of the variable SUM with the new variable name TOTAL within the subroutine in lines 3000 to 3999.)
    Variables are stored as onebyte tokens in Atari BASIC. REP looks up the old and new variable names in the name table to determine their tokens, then replaces every old token (within range) with the new one. The old name remains in the name table (type XREF to see for yourself) and keeps the value it had at the time of the change.

The VIEW Command
BASIC's PRINT statement lets you examine variable values in direct mode as you test and debug a program, but it can be cumbersome when you're juggling lots of variables, especially subscripted variables. Of course, there's an easier way.
    DEBUT'S VIEW command lists all variables and their values, including strings and arrays, without requiring you to name each one. Type VIEW to quickly display the values, or use the optional output switch to generate hardcopy that you can analyze with the proverbial fine-toothed comb.
    Here is the format of the command:

VIEW [/output device] [starting line,last

where /output device is the optional switch for redirecting output from the screen; and starting line,last line is the optional range of line numbers.
    Here is the format of the VIEW command's output:

STRING$ current length,maximum length
"string contents"
10 20 30 40
2 4 6 8
3 5 7 11

    Scalar variable values are displayed in a pretty straightforward manner-the variable name is followed by its value. Strings are listed with additional information-the current and maximum length. String contents are printed in quotes below the name.
    For subscripted variables, VIEW lists the row and column dimensions corresponding to the indexes you assign with the DIM statement, plus one. (Because indexing starts at zero-ARRAY(0,0) is a valid array element-the actual size of each dimension is one greater than the value you give.) Values are listed by column and row; sample values are shown in the example above. A singly subscripted variable is really a onecolumn array; its values are listed vertically. If a string or array has not been dimensioned, only the name prints. Unused variables aren't listed at all.
    The output switch and range option work with this command as they do with all others. For example:


(Prints a hardcopy of all the variables and their values.)

VIEW/P 200,300

(Prints a hardcopy of the variables and values in lines 200 through 300.)

Additional Hints
The DEBUT commands are an aid to debugging, but they won't do the whole job for you. Use them along with other techniques (such as tracing the program's execution with temporary PRINT statements) to close the gap between what you're telling BASIC to do, and what you want it to do.
    Since the optional disk output produced by these commands is compatible with any word processor that handles ASCII text, you can easily produce program documentation with DEBUT. For example, you could save the XREF listing to disk, and use a word processor to add explanations of each variable. Every minute spent documenting a program pays off threefold should you ever have to go back and modify it.
    The DEBUT command formats are tolerant of extra spaces, and command words can be abbreviated-just type the first letter followed by a period. In fact, the period alone is all you need for DIR. The output switch is unaffected by abbreviations-DIR/P, D./P, and ./P all do the same thing. In addition, the FIND command accepts abbreviated keywords as its target. For example, FIND FOR, FIND F., and F.F. all seek out the keyword FOR. Sound confusing? Experiment. You'll find the shortcuts which are most useful to you.

Notes For ML Programmers
It would be nice if DEBUT had a renumbering command, program trace capabilities, or even a few more DOS commands. Machine language programmers can add such new commands by patching entries into DEBUT's command table, which reserves 25 bytes for just this purpose.
    A command table entry consists of your machine language routine's address minus one, followed by the command name. The address is stored in high byte/low byte order. The command name is in ASCII code exactly as you'd type it, except that the rightmost byte must have the most significant bit on (inverse video). Add entries starting at location 8149 (hex $1FD5), but don't use more than the 25 bytes or you'll overwrite DEBUT. Your code can be appended to the DEBUT AUTORUN.SYS file by using the DOS copy with append feature.
    DEBUT does some preprocessing before your routine gets control. As a result, you'll be able to abbreviate your commands, with DEBUT abbreviations taking precedence. The output switch works as well, opening channel 1 to the output device and storing the channel number at location 181 ($B5) before branching to your routine. If the command line requires further parsing, your routine will find it at location 1408 ($580); the offset to the first argument (if any) will be in location 242 ($F2). Your machine language routine should end with an RTS instruction if you wish it to return through DEBUT to BASIC.
    DEBUT calls on several routines in Atari BASIC and accesses various BASIC tables whose addresses have changed in the later releases, which accounts for the slightly different version of the program for BASIC revision A. DEBUT commands will not trigger the notorious lockup bug of revision A and (especially) revision B. For those with an interest in the inner workings of Atari BASIC, I highly recommend The Atari BASIC Source Book (COMPUTE! Books), as well as the aforementioned "Atari Wedge" article by Charles Brannon (reprinted in COMPUTE!'s Third Book of Atari as "The Wedge: Adding Commands to Atari BASIC").

Debugging Tool

For instructions on entering this listing, please
refer to "COMPUTE!'s Guide to Typing In
Programs" in this issue of COMPUTE!.

       WHEN READY"
HG 20 IF PEEK(764)<>12 THEN
PC 30 OPEN #1,8,0,"D:AUTORUN
KB 50 FOR I=1 TO 1304:READ B
BM 70 DATA 255,255,0,31,157,
CJ 80 DATA 76,84,164,76,130,
FK 90 DATA 62,181,76,103,181
6D 100 DATA 171,76,167,175,7
BK 110 DATA 85,84,155,53,46,
NK 120 DATA 86,31,165,13,141
DL 130 DATA 241,31,169,85,13
GO 140 DATA 160,31,169,31,32
AM 150 DATA 169,46,141,231,2
IB 160 DATA 96,32,88,31,76,5
PL 170 DATA 26,3,201,69,240,
EL 180 DATA 34,208,242,96,20
0G 190 DATA 169,31,153,27,3,
NF 200 DATA 157,158,31,202,1
AG 210 DATA 31,169,31,141,16
MG 220 DATA 105,1,141,243,31
D1 230 DATA 141,244,31,96,17
PB 240 DATA 68,73,210,32,178
NB 250 DATA 95,88,82,69,198,
MG 260 DATA 196,34,23,82,69,
BG 270 DATA 76,204,32,76,68,
NL 280 DATA 242,31,45,36,32,
BE 290 DATA 240,5,238,241,31
HH 300 DATA 729165,194,201,9
EH 310 DATA 240,87,169,155,1
DI 320 DATA 241,31,134,242,3
NF 330 DATA 174,162,2,32,4,3
LM 340 DATA 189,128,5,73,47,
AN 350 DATA 35,48,22,32,47,3
LN 360 DATA 140,254,2,200,13
MN 370 DATA 132,245,32,91,32
OL 380 DATA 36,169,0,141,254
FF 390 DATA 149,72,200,177,1
KK 400 DATA 170,104,168,169
NM 410 DATA 32,12,36,160,173
CM 420 DATA 104,201,155,240,
BE 430 DATA 169,6,32,227,35,
EE 440 DATA 128,157,73,3,133
CA 450 DATA 36,169,5,32,14,3
DG 460 DATA 31,32,222,34,208
LL 470 DATA 36,68,58,42,46,4
OD 480 DATA 32,195,35,240,10
LB 490 DATA 35,176,89,32,213
NF 500 DATA 107,34,240,70,14
GJ 510 DATA 182,221,32,217,3
BB 520 DATA 169,44,32,19,31,
AJ 530 DATA 38,210,144,60,32
OK 540 DATA 219,34,162,226,3
NC 550 DATA 0,177,224,32,19,
ED 560 DATA 30,32,118,34,208
1F 570 DATA 34,76,28,33,169,
DE 580 DATA 252,35,32,222,34
HF 590 DATA 145,76,213,34,32
NB 600 DATA 234,165,229,133
PM 610 DATA 32,25,31,240,223
MN 620 DATA 240,231,166,224
DH 630 DATA 252,35,32,217,34
GJ 640 DATA 160,6,32,118,34,
BA 650 DATA 208,2,133,245,32
LM 660 DATA 240,59,32,38,35,
EH 670 DATA 32,197,34,230,20
EF 680 DATA 209,34,162,237,3
HM 690 DATA 230,175,165,175,
BE 700 DATA 35,144,228,176,7
GM 710 DATA 197,34,32,222,34
AK 720 DATA 192,32,213,34,32
AM 730 DATA 169,33,160,201,3
FA 740 DATA 12E3,168,169,0,32
IJ 750 DATA 205,76,3,36,96,3
PO 760 DATA 76,73,83,84,69,6
HA 770 DATA 242,201,62,240,3
BH 780 DATA 240,7,198,242,32
KO 790 DATA 204,76,2,34,32,1
NJ 800 DATA 61,133,203,16,7,
AN 810 DATA 133,203,32,246,3
BF 820 DATA 32,10,31,32,222,
FC 830 DATA 35,144,243,96,24
DN 840 DATA 44,133,203,32,10
EN 850 DATA 35,32,173,34,176
BL 860 DATA 34,197,245,208,2
HN 870 DATA 35,176,12,165,24
FA 880 DATA 32,170,35,144,24
GJ 890 DATA 160,85,76,3,36,3
CB 900 DATA 71,69,68,155,173
EF 910 DATA 87,31,133,13,76,
EC 920 DATA 127,32,22,31,165
KF 930 DATA 208,2,162,153,24
HF 940 DATA 96,173,1,31,172,
AM 950 DATA 8,173,3,31,172,2
N0 960 DATA 4,31,176,12,189,
BI 970 DATA 6,201,155,240,2,
EH 980 DATA 175,24,96,165,13
LE 990 DATA 4,31,176,238,32,
NL 1000 DATA 7,31,144,246,9,
PM 1010 DATA 32,213,34,32,21
EF 1020 DATA 5,133,205,169,1
BI 1030 DATA 19,31,164,17,20
DA 1040 DATA 32,0,216,176,7,
AN 1050 DATA 165,213,96,56,1
GB 1060 DATA 32,34,35,200,13
DD 1070 DATA 35,201,44,240,1
JL 1080 DATA 132,162,133,163
NG 1090 DATA 13,230,242,32,2
GM 1100 DATA 133,165,132,164
NO 1110 DATA 133,139,96,166,
LO 1120 DATA 201,32,240,4,20
GO 1130 DATA 166,242,189,126
AK 1140 DATA 208,246,134,242
JC 1150 DATA 101,138,133,138
EI 1160 DATA 177,138,133,237
CB 1170 DATA 165,164,229,237
BM 1180 DATA 56,96,200,177,1
HI 1190 DATA 162,165,238,229
DA 1200 DATA 133,156,200,177
MD 1210 DATA 2,144,190,201,5
JA 1220 DATA 197,203,240,19,
PN 1230 DATA 240,17,201,22,2
DJ 1240 DATA 233,240,211,24,
DC 1250 DATA 177,138,24,200,
DF 1260 DATA 213,165,203,41,
PO 1270 DATA 130,162,0,32,13
NI 1280 DATA 96,162,1,134,18
NH 1290 DATA 36,169,8,157,74
AE 1300 DATA 3,169,3,208,31,
IE 1310 DATA 0,133,213,132,2
DN 1320 DATA 216,165,244,164
NM 1330 DATA 16,31,162,16,16
HE 1340 DATA 86,228,152,16,2
HE 1350 DATA 165,242,24,105,
LO 1360 DATA 157,69,3,152,15
        7,68,3,96,224 ,2
PN 1370 DATA 225,2,41,31