Atari Tape Data Files: A Consumer Oriented Approach

Al Baker

This complements Larry Isaacs' article "Inside Atari BASIC." The technique presented here is very useful for cassette users.


This article is based on a major axiom of consumer computing:

     Easier is Better

The specific corollary when writing a program which saves data between program runs is:

     Use only one tape. Program and data should be on the same tape. They should, in fact, be the same thing.

A consumer should be able to load his program, run it to update his checkbook and balance his budget, and then save the program on tape when done. The next day, he can load his program and all data changes from the previous day should be there.

"Impossible," you say? Well, perhaps. It is certainly impossible on some of the computers on the market. But it is not impossible on the Atari. The trick is to fool Atari Basic into saving all dimensioned variables when a program is saved to tape. We won't try to save the simple variables. Since I am not a revered expert, I won't make the mistake of saying this is impossible. (But, I think it's impossible.) Saving the dimensioned variables with a program is relatively easy.

Write Your Program

Listing 1 is a simple program. Nothing tricky. But notice that I print the dimensioned variables in Lines 70-130 and then assign values to them in Lines 140-190. I am assuming the variables have valid contents before changing them! The only important restriction here is to type the line containing the DIM statement first. It doesn't have to be the first line in the program. Just make sure it is the first line typed.

The Atari Basic variable symbol table is constructed when each line is typed in, not when the program is run. Later we will need to find the locations of the string variables in the table. This is easier if they are the first variables present. For a more complex discussion of the symbol table, see the text in the box.

Program 1. Dimensioning variables

Download (Saved BASIC)
Download / View (Listed BASIC)

Suppose the program is already written and you didn't type the DIM statement first. Write your program to tape using the command LIST"C". Type NEW. Now type the DIM statement from your program with the string variables first. Finally, reload the program from tape with the command ENTER "C". Now the string variables are at the beginning of the variable tables.

Protect The Dimensioned Variables

The next step is to fool Basic into treating the dimensioned variables as part of the program. Also, you have to add the code to let the program save itself to tape. In an application, saving the program to tape will be the final program option selected by the user. In Listing 2 this is added to the program in lines 200 through 230.

Program 2. Protecting Dimensioned Variables

Download (Saved BASIC)
Download / View (Listed BASIC)

Locations 140 and 141 contain the address of the end of the computer program. Program line 200 places this address in the variable A. In line 210 we add the size of the dimensioned variables. Each string variable contains as many bytes as its dimension. Each numeric array contains 6 times the number of members of the array. The B array is 6x(2+ 1)x(3+ 1)=6x3x4=72 bytes. Thus we had to add 10+ 72 or 82 to the end of the program in the example.

Now run the program and let the internal CSAVE create a tape. Turn the computer off and then on. Now reload the newly created program from tape. For some reason this step is important. (I don't know why.) If you do not use the new tape, this procedure won't work.

Finish The Program

We now have a program in memory which has an invalid program-end pointer. See the third listing. Add lines 10 through 40 to your program. Make sure that you use the correct number instead of "-82" in line 10. Remember that this number is the size of your dimensioned variables.

Refer to Table 1. Locations 140 and 141 form the program-end address. Locations 142 and 143 form the stack address and locations 144 and 145 form the pointer to the end of memory used by the program. The RUN command sets all of them equal to the incorrect end-of-program pointer. Lines 10 through 40 correct them.

Here comes the only hard part. You are going to have to PEEK around in memory. The RUN command sets the length of all strings to zero. You must repair their lengths if you want to save string data.

Table 1.

These two byte addresses point to important areas used by Atari Basic.

Use this To get the location of this PEEK(130) + PEEK(131)*256 Variable name table PEEK(134) + PEEK(135)*256 Variable value table PEEK(136) + PEEK(137)*256 Beginning of program

Use these only when program running

PEEK(140) + PEEK(141)*256
End of program and beginning of dimensioned variables
PEEK(142) + PEEK(143)*256
End of dimensioned variables and beginning of stack
PEEK(144) + PEEK(145)*256
End of memory used by program

Table 2.

The variable name table: Entry lengths are different. Red symbolizes that 128 is added to ACSII value of last character to show the name's end.

Variable   Variable name
AB1        AB1               3 character number name
AR(3,4)    AR(               2 character array name
CDOG(l7)   CDOG(             4 character array name
ALPHA$(10) ALPHA$            6 character string name
E          E                 1 character number name
FIG        FIG               3 character number name

Note: Variable names can be up to 120 characters long and are completely unique. Variable ABC is different from variable ABCD. Variable names DO NOT appear in the program in memory. Only a I byte pointer to the variable name in the variable name table appears.

Look at Table 3. The third entry in the variable value table is the string ALPHA$. Its current length is 5+0*256 or 5. These two bytes must be set to the correct length of the string. Type command: PRINT PEEK(134)+PEEK(135)*256. Now you know where the variable value table is. If you have been writing the program in the listings, you should get the answer 2056. Assume the string is the first entry in the table. The location of the length is 2060 and 2061. Since the length of the string of data being saved in the example is 10, I set location 2060 to a 10 in line 60 of the program.

Table 3.

The variable value table: Each entry is eight bytes.

VariableContentsTable Entries 1-8Meaning
ABI50/0/64/5,0,0,0,0First byte is 0: this is a number. Second byte is 0: this is the first entry. 64 is the exponent. 5 is the binary coded decimal value.
AR(3,4)doesn't matter64+1/1/0,0/4,0/5,064 makes this an array. +1 means that it has been dimensioned. 2+0*256 is the displacement into the array area. 4+0*256 is the size of the first dimension and 5+0*256 is the size of the second dimension.
CDOG(17)doesn't matter64+1/2/120,0/18,0/1,0This array is displaced 120 bytes into the array area, and it is dimensioned 18+0*256 by 1+0*256.
ALPHA$(10)"12345"128+1/3/228,0/5,0/10,0128 makes this a string. +1 means that it has been dimensioned. It starts 228+0*256 bytes into the array area. The current length of the string is 5+0*256. The maximum size of the string is 10+0*256.
E.050/4/63/5,0,0,0,0This is a number. The exponent is now 63 so the number is only 1/100 of its integer value, or .05.
FIG-50/5/64+128/5,0,0,0This is a minus number (+128 on exponent).

Try it out

The program is complete. SAVE it. Now RUN it. You will probably get garbage in the printout. Put a 10 character string in the string variable. Now put numbers in various entries in the B array. Typing a 9 for the I subscript will end the program with a CSAVE. Do this CSAVE onto a new tape. Turn the computer off and on. Now load this new copy of the program and RUN it. Viola! The data is still there! Now just imagine that this was your budget information, address book or other files. You have a no-hassle, one-tape system.


I have provided more information about the internals of the Atari than is really necessary to solve this problem. If you are interested in this kind of information, study it. If not, skip it. If you have any questions, I would be glad to answer them. One warning. Do not press break while the program is running and then type RUN. Always use the CONT command after pressing BREAK. Otherwise the statements in lines 10-40 will destroy the program data. This can be prevented if you know what the correct value of A should be in line 10. Replace line 10 with 10 A=n, where n is this number. Do this for your finished product.

Program 3. Protecting Dimensioned Variables

Download (Saved BASIC)
Download / View (Listed BASIC)

Changing Atari programs to save the dimensioned variables:

The ATARI BASIC Symbol Table

Most BASIC interpreters assign values to the symbol table as the program is run. Not true with the Atari. New variables are placed in the symbol table when the program line they are contained in is first typed.

If you later change variable names, the old variable names are not removed from the table. They stay forever! Even the CLR command does not remove them. They continue to take up room. How much room? Eight bytes plus the length of the name. Add another byte if the variable is an array.

Fortunately, it is possible to clean up the variable table. Write the program to cassette using the command LIST "C", type NEW, and then reload the program from tape with the command ENTER "C".

A program can often be made to run faster by placing selected variables at the beginning of the variable table. This decreases the time it takes to find variables which are used in time-critical routines.

To place these variables at the beginning of the variable table, write the program to cassette using the command LIST "C" and then type NEW. Now use those variables. For example, if the variable A must be the first variable in the table, type A=O. If the string B$ must be used, type DIM B$(1). You are "ordering" the variable table. When you have finished placing as many variables in their correct order as you want, load the program you saved to tape with the command ENTER "C". This does not interfere with the contents of the variable table.

Figure 1: BASIC Program Memory Layout

System and
Basic overhead
Variable name
Value table

Return to Table of Contents | Previous Section | Next Section