Ad: Click Here

Digital Antic ANTIC VOL. 2, NO. 1 / APRIL 1983 / PAGE 34

Trace Utility

by Alan Filipski

Alan Filipski responded to our challenge to develop a trace routine (see ANTIC #4, page 6) and is the winner of that contest. His prize is a copy of Basic A+, by Optimized Systems Software. His program and explanatory article is published for the benefit of all of us.

When debugging a BASIC program it is often very valuable to have some way to trace the order of execution of statements within the program. This is frequently done by inserting "debug" print statements within the program and later deleting them. We could also sit down with the program listing and "play computer", simulating execution of the program. Neither of these ways is completely satisfactory. It is a nuisance to insert print statements for debugging and later have to delete them. We all know that when trying to follow a program listing, we can be completely blind to an obvious error because we see a statement as it "should be" rather than as it actually is.

The BASIC Trace Utility program given here is intended to be an aid in situations like this. It monitors the execution of your program, displaying each line as it is executed, and can display values of variables when requested. At any time, you may halt the execution, modify or display other variables, and then resume execution where you left off. This utility may be used to determine where a variable takes on an erroneous value or at what point the program takes an execution path contrary to the programmer's intention. In addition, the beginning programmer can use the trace utility to better understand what happens when a BASIC program executes.

The BASIC Trace Utility is written in BASIC and may be run on any ATARI 400 or 800 system with the BASIC cartridge and at least 16K RAM. The principal limitation on the user program is that it may not use line numbers greater than 30999, since this upper range of line numbers is used for the trace utility program.

To use the program, it must first be loaded with the program you want to debug. In order to do this conveniently, the trace utility program should have been stored on disk in source form using the command "LIST D1: TRACE.LST" (rather than the SAVE command) and should be merged with your loaded program using the command "ENTER D1:TRACE.LST" (rather than the LOAD command ).

To start the trace, type "GOTO 31000". The trace utility program will then ask you for the following information: the line number at which to begin tracing, and the number of lines to trace. The trace utility will then begin to execute your program. As each line of your program is executed that line will be printed out. Any output from your program will be printed out interspersed with this trace listing. This will continue until the number of lines you have requested have been traced. (DATA statements, not being executable, are neither printed out nor counted.) At that point, the trace utility will ask you how many more lines you wish to trace.

After tracing these lines, the program will again ask you how many more lines you wish to trace. This cycle will continue until either your program ends or you enter a "0" in response to the query. At this point, control is returned to the immediate mode. If you wish, you may now print out or modify variables, GOTO 31000 and restart the trace, either at the beginning, or at the line where your previous trace left off. If you start at the beginning, all variables will be cleared, and arrays and strings will be deallocated.

If there are any variables which you want printed out automatically at every step of the trace, you may insert your own PRINT statements anywhere in the line-number range 31122-31126. These PRINT statements will not be traced, but will be executed before each line of the user program is executed. To produce a more compact display, end each PRINT statement with a semicolon.

There are a few cautions and limitations to be observed when using this program:

The user program should not contain any TRAP or CLR statements.

If the user program terminates by executing a STOP or END which is not the first statement of the line in which it appears, for example:

910 PRINT "NORMAL TERMINATION":END or

910 INPUT A:IF A=0 THEN STOP

then the user program will be seen to contain some garbage when it is listed. If this happens, re-enter the trace utility by typing GOTO 31000, and exit by requesting 0 lines to be traced. This minor nuisance cannot be reasonably repaired within the framework of the existing design of the trace utility.

It is wise to maintain a backup copy on disk of any program being traced (or just being run).

The trace utility program uses some BASIC variable names, all of which begin with "DBG". Avoid using variable names in your program which start with this sequence of characters.

As mentioned before, the program being traced should not use any line numbers greater than 30999.

Do not expect the traced program to run as fast as the original program.

How does the BASIC Trace Utility program work? Since BASIC is an interpreted language, the simplest and most straightforward way to produce a trace utility would be to modify the interpreter. In the case of ATARI BASIC however, this alternative is not available, since the interpreter is in a ROM cartridge. The approach taken here is more similar to the approach that might be taken to trace a compiled language and involves setting trappable errors in each line of the user program, and then listing the line when the error trap is taken. This is admittedly a kludge, but I could not think of a better way to do it.

This is what happens when the user types in "GOTO 31000": The program first sets a TRAP so that any execution error causes control to go to statement 31046. Then the first command token in each line of the user program (except DATA statements) is set to 55, meaning "syntax error" to the ATARI BASIC interpreter. The real command token is stored by adding it into the "end-of-line" token for that line so that it may be retrieved later. The program then transfers control to the line number input by the user. Since the first statement of this line contains an error, control passes to 31046. This portion of the Trace Utility program re-introduces any errors which were cleared on a previous cycle, clears the error in the line which caused the trap, and LISTs this line. The line containing the most recently executed FOR or GOSUB statement, if any, is also cleared of its error. This is necessary, because whenever the interpreter encounters a NEXT or RETURN, it checks to see whether the corresponding FOR or GOSUB is still there. Control is now transferred to the (now corrected) statement which caused the error trap and the statement is executed. When control passes from this line to any other line, however, an error trap is taken and the cycle repeats. When the user indicates that he is done by entering a "0", errors are removed from all lines and the program stops.

The fact that this program was written in BASIC has several advantages. First, it is compact, consisting of less than 90 lines of executable code with only 12 variables. The primary advantage, however, is that it may be readily modified by the user. For example, if it were desired to print only the line number of the statement being traced and not the entire statement, it is only necessary to change line 31130 to

31130 ? PEEK(DBGPTR)+256*PEEK(DBGPTR+1);" ";

This ability to easily modify the source gives the user quite a bit of flexibility once he understands the code. Understanding the source code may take some work because of the high density of PEEK$ and POKE$ and the lack of such niceties as WHILE loops and indentation in ATARI BASIC. To aid understanding, here is a description of variables used in the program:

DBGCOM-Variable used to store the command token of a BASIC statement.

DBGEOL-Variable used to store the end-of-line token of a BASIC statement.

DBGLN1/DBGLN2-used to hold two-byte line number of a statement which triggered trap.

DBGPTR -Pointer to beginning of current statement; used in a loop to search for a particular line number.

DBGSAVE - A temporary holder for the value of DBGPTR.

DBGSAV1 - Statement-table offset of most recently executed FOR or GOSUB statement.

DBGSAV2 - Statement-table offset of statement most recently cleared for execution.

DBGST-Address of beginning of BASIC statement table.

DBGSTART-Line number at which trace execution is to start .

DBGTC-Count of number of lines left to trace.

DBGTOP-Address of top of BASIC run-time stack.

A further explanation of these concepts may be found in the book De Re ATARI.

Writing this program was very instructive and required some experimentation to discover undocumented details of the BASIC interpreter. Given the limitations described above, it provides a useful utility for debugging programs written in ATARI BASIC.

RAM REQUIREMENTS
TRACE 3K + traced program

[code]