Analyze Your Program—An Atari BASIC Utility

Fred Pinho

This program allows you to study the effects of space allocation in Atari variable value and string/array tables. You'll discover the memory saving effects of various methods of handling heavily edited programs. It's also a useful debugging tool for more advanced programmers.

This program was inspired by Art McGraw's "Variable Name Utility" (COMPUTE!, Oct. 1981, #17). To do advanced programming in BASIC, one often needs information, not only on the variable name table, but also on the variable value and the string/array tables. These tables reside in memory as follows:

Variable Name Table Increasing
Variable Value Table Memory
BASIC Program Locations
String/Array Table

There are a set of zero-page pointers that point to these tables and enable BASIC (and the programmer) to keep track of their location.

MEMORY LOCATION OF POINTER MEMORY AREA POINTED TO
130, 131 Start of Variable Name Table.
132, 133 End of Variable Name Table.
Points to a zero dummy byte when there are less than 128 variables.
Otherwise points to the last byte of the last variable name.
134, 135 Start of Variable Value Table.
140, 141 Start of the String/Array Table.
143, 143 Start of Run Time Stack. Also defines the end of the String/Array Table.

As usual, these pointers point to the address in low-high format (low byte of 16 bit address is stored first). To find the complete address:

Address = PEEK(Lo Byte) + 256'PEEK(Hi Byte)

In order to be able to read the information displayed by the program, I've included a description of each of these tables.

VARIABLE NAME TABLE

Lists all the variable names in the order entered by the program. Each element of the name is stored as ATASCII characters. There are three types of variables:

  1. Scalar variables – These contain a numeric value. The most significant bit is set on the last character of the name.
  2. String variables – The last character stored is a $ with the most significant bit set.
  3. Array variables – The last character stored is a (with the most significant bit set.

VARIABLE VALUE TABLE

This table reserves eight bytes for each variable in the program. The first byte of each entry defines the type of variable: zero for a scalar variable, 65 for a properly-dimensioned array variable and 129 for a properly-dimensioned string variable. The second byte is the variable number (0–127). The remaining 6 bytes vary with the type of variable:

  1. Scalar – The number stored in 6-byte BCD (Binary Coded Decimal) format.
  2. Array – Bytes 3 and 4 give the location of the array as an offset from the beginning of the String/Array Table. Bytes 5 and 6 give the size of the first dimension of the array plus one. Bytes 7 and 8 give the size of the second array DIMension plus one. All these byte-pairs give the number in low-byte, high-byte format. To get the desired value you must again calculate by: (value in lo byte) + 256 * (value in hi byte).
  3. String – Bytes 3 and 4 give the location of the string as an offset from the beginning of the String/Array Table. Bytes 5 and 6 give the current length of the string (i.e., the length of the string actually written to). Bytes 7 and 8 give the DIMensioned length of the string.

STRING/ARRAY TABLE

This block of memory stores all the actual string and array data. Each string character is stored as a one-byte ATASCII entry. Each element in an array is stored as a six-byte BCD number. BASIC allocates memory space within this table as dictated by the DIMension statements it encounters. As you can see, it is much more costly in memory usage to store arrays than strings. (See Program 1.) Ideally, this utility should be written with no declared variables to give a "pure" analysis of the variable tables. However, this would give a messy looking program and take a lot more coding. Therefore, I've used four variables in this program. I've chosen names that are unlikely to be used in normal programs. These are:

OPQ – FOR-NEXT counter
RST – Variable number
UVW – Location within Variable Value Table
XYZ – Location within Variable Name Table

If, for some reason, you are using these variables in your program, change the variable names in the utility program. The utility variables will be printed last and can be ignored.

A description of the program by line number is given.

18990 Vanity line
19010 Opens a file to the printer. It is best to do this rather than use LPRINT since LPRINT causes formatting difficulties.
19025-19040 Prints variable names. Note that since each name is ended with a character whose most significant bit is set (i.e., an inverse character), this bit is stripped out before printing. This would not be necessary if printing to the screen.
19050-19070 Checks for type of variable by inspecting first bit of entry in variable value table. Directs program to proper subroutine
19080 Checks for error in value table
19090 Increments variables
19100 Checks for end of variable name table
19110 Prints number of variables found which equals the total variables minus the four in the utility program.
19120 Prints memory size of string/array table.
19130 Closes printer file
19140 Error routine. Prints number found
19200-19240 Scalar variable subroutine. Converts six-byte BCD number to a decimal number multiplied by a power of 100
19300-19350 Array variable subroutine. Calculates location of array as an offset from the start of the string/array table. Also gives the first and second DIMensions of the array. Note that the Atari stores the chosen DIMension plus one. Therefore, the program subtracts one before printing. However, the actual DIMension is one higher than the chosen and printed value since the computer starts counting from zero rather than one.
19400-1940 String variable subroutine. Calculates the string storage location as an offset from the start of the string/array table. Also gives the DIMensioned length and the current length of the string. Note that the current length of the string is just the last location written to; there need not be anything in the previous locations. To show this, load the utility and type:
100 DIMA$(120), B$(120)
110 A$(103,103) = "F" : B$ = "FFFF"
Then RUN the utility program (i.e., let it analyze itself). See Figure 1 for the output of this run. Also, note that string position numbering starts from one, not zero, as in arrays.

The abbreviations used in the printout are:

VAR. NO. – Variable Number
VAR. NAME – Variable Name
OFS – Offset
DIMI – First DIMension of Array
DIM2 – Second DIMension of Array (zero if single-dimension array)
CURLTH – Current Length of String
DIMLTH – DIMensioned Length of String

Note that the location of the start of each table for the analyzed program should not be found using this utility. This is because the presence of the utility program will cause a shift of the string/array table location. If you need to know these locations, PEEK the appropriate locations before you load the utility program.

The utility program has been written with high line numbers so that it won't interfere with most programs to be analyzed. To use this utility, type it in and then save it to either disc or cassette using the LIST command. Don't use SAVE or CSAVE, as this will prevent you from merging the utility with the program to be analyzed (the program in the computer will be wiped out as you LOAD or CLOAD the utility program). Now LOAD in your program to be analyzed. To merge the two programs, LOAD in the utility using the ENTER command. Now turn on the printer, type in GOTO 19000 (RETURN).

If you did the above, you will get some unexpected results. All the variables will be listed, but they will have no entries for them. For example, all scalar variables will be zero regardless of their value in the program. Also, all strings will be unDIMensioned and will have zero for their length. Apparently, when a program is SAVEd to disc, the Atari saves the Variable Value Table with entries set to "zero" condition. Therefore, to get the proper analysis, do the following:

  1. LOAD your program.
  2. RUN it!
  3. ENTER the utility program.
  4. Type GOTO 19000 (RETURN).

This will give you a proper analysis. Note that loop variables will not always be caught at their initial value.

If your program has line numbers in the 18990-19450 area, it could interfere with the utility. Therefore, RUN your program and then delete the problem lines. This will not affect the Variable Name and Variable Value tables. Then ENTER the utility and proceed as before.

What can you use this program for? Well, first you can use it to gain a better understanding of how BASIC works. For example, analyze a heavily edited program which has had variables deleted. If you've only SAVEd this program, you'll find that these variables are still listed in the tables and continue to consume memory. LIST your program to disc or cassette and then ENTER it back into the computer. If you now re-analyze the program, you'll find that these "phantom" variables will have been eliminated. If you check "free" memory [FRE(0)] before and after, you'll find a gain in useable memory.

Many advanced programming techniques use string manipulations to take advantage of the high speed, string handling routines in the Atari. These often depend on changing the entries in the variable value table to relocate strings under program control. This utility is useful as a debugging tool for these applications.

One final note for those who do not have printers. If you wish to output to the screen, change line 19010 to:

OPEN #3, 8, 0, "S :"

You can stop the screen output at any time and then resume it by "Control-1."

Figure 1.

VAR. NO. = 0 VAR. NAME = A$
STRING DIMed
OFS = 0 : CUR LTH = 103 : DIM LTH = 120
VAR. NO. = 1 VAR. NAME = B$
STRING DIMed
OFS = 120 : CUR LTH = 4 : DIMLTH = 120
VAR. NO. = 2 VAR. NAME = XYZ
SCALAR—76.82000000 * 100
VAR. NO. = 3 VAR. NAME = UVW
SCALAR—77.17000000 * 100
VAR. NO. = 4 VAR. NAME = RST
SCALAR—04.00000000 * 0
VAR. NO. = 5 VAR. NAME = OPQ
SCALAR—08.00000000 * 0
END OF VARIABLE NAME AND VALUE TABLES.
NUMBER OF VARIABLES FOUND = 2
STRING/ARRAY AREA IS CURRENTLY 240 BYTES LONG.

PROGRAM. Analyze Your Program – An Atari BASIC Utility.

18990 REM VNT/VVT UTILITY BY F. PINHO 12/22/81
19000 XYZ = PEEK(130) + 256 * PEEK(131) : UVW = PEEK(134) + 256 * PEEK(135) : RST = 0
19010 OPEN #3, 8, 0, "P :"
19020 ? #3; "VAR. NO. = ";RST;" "; : ? #3; "VAR. NAME = ";
19025 IF PEEK(XYZ) < 128 THEN ? #3; CHR$(PEEK(XYZ));
19030 IF PEEK(XYZ) > 127 THEN ? #3; CHR$(PEEK(XYZ) - 128);
19040 IF PEEK(XYZ) < 128 THEN XYZ = XYZ + 1 : GOTO 19025
19050 IF PEEK(UVW) = 0 THEN GOSUB 19200
19060 IF PEEK(UVW) = 64 OR PEEK(UVW) = 65 THEN GOSUB 19300
19070 IF PEEK(UVW) = 128 OR PEEK(UVW) = 129 THEN GOSUB 19400
19080 IF PEEK(UVW)< >0 AND PEEK(UVW)< >64 AND PEEK(UVW)< >65 AND
      PEEK(UVW)< >128 AND PEEK(UVW)< >129 THEN GOTO 19140
19090 UVW = UVW + 8 : XYZ = XYZ + 1 : RST = RST + 1
19100 IF XYZ < (PEEK(132) + 256 * PEEK(133)) THEN 19020
19110 ? #3; "END OF VARIABLE NAME AND VALUE TABLES." : ? #3; "NUMBER OF VARIABLES
      FOUND = "; RST-4
19120 ? #3; "STRING/ARRAY AREA IS CURRENTLY "; ((PEEK(142) + 256 * PEEK(143)) -
      (PEEK(140) + 256 * PEEK(141))); " BYTES LONG."
19130 CLOSE #3 : END
19140 ? #3 : ? #3; "ERROR! VARIABLE TYPE NUMBER = ";PEEK(UVW) : END
19200 ? #3 : ? #3; "SCALAR--"; : IF PEEK(UVW + 2) = 0 THEN ? #3; "ZERO" :
      ? #3 : RETURN
19210 ? #3; INT(PEEK(UVW + 3) / 16); (PEEK(UVW + 3) - (INT(PEEK(UVW + 3) / 16)) * 16);
      ".";
19220 FOR OPQ = 4 TO 7 : ? #3; INT(PEEK(UVW + OPQ) / 16); (PEEK(UVW + OPQ) - (INT(PEEK(UVW +
      OPQ) / 16)) * 16);
19230 NEXT OPQ
19240 ? #3; "*"; : ? #3; ((PEEK(UVW + 2) - 64) * 100) : ? #3 : RETURN
19300 ? #3 : ? #3; "ARRAY ";
19310 IF PEEK(UVW) = 64 THEN ? #3; "unDIMed"; : ? #3
19320 IF PEEK(UVW) = 65 THEN ? #3; "DIMed"; : ? #3
19330 ? #3; "OFS = "; (PEEK(UVW + 2) + 256 * PEEK(UVW + 3)); " : ";
19340 ? #3; "DIM1 = "; ((PEEK(UVW + 4) + 256 * PEEK(UV W + 5)) - 1); " : ";
19350 ? #3; "DIM2 = "; ((PEEK(UVW + 6) + 256 * PEEK(UVW + 7)) - 1) : ? #3 : RETURN
19400 ? #3 : ? #3; "STRING ";
19410 IF PEEK(UVW) = 128 THEN ? #3; "unDIMed"; : ? #3
19420 IF PEEK(UVW) = 129 THEN ? #3; "DIMed"; : ? #3
19430 ? #3; "OFS = "; (PEEK(UVW + 2) + 256 * PEEK(UVW + 3)); " : ";
19440 ? #3; "CUR LTH = "; (PEEK(UVW + 4) + 256 * PEEK(UVW + 5)); " : ";
19450 ? #3; "DIM LTH = "; (PEEK(UVW + 6) + 256 * PEEK(UVW + 7)) : ? #3 : RETURN

Return to Table of Contents | Previous Section | Next Section