Classic Computer Magazine Archive COMPUTE! ISSUE 34 / MARCH 1983 / PAGE 46

An Introduction To Data Storage On The VIC, 64, PET/CBM, And TI-99/4A

Ron Gunn

Data storage can be the most perplexing aspect of programming for the novice. Here are some practical tips for VIC, 64, PET/CBM, and TI users which just might save you days of experimentation.

Types Of Data

Commodore Computers use three kinds of variables, and it is the values stored in variables that you will be dealing with when you save and recall data. The first of these is floating point, represented by a variable like A or A(X). The second is integer, represented by a variable like A% or A%(X).

The third is the string variable, represented by A$ or A$(X). Any of these varieties can be single: A; or may have subscripts: A(X); A(X,Y); or A(X, Y,Z). Part of your sense of power in computing comes when you realize just how much data you can pack and organize into those multiple-subscripted arrays.

When you are putting data out on tape or disk and expecting to read it back in, you must remember two things: 1. The three variable types are different and are not interchangeable. 2. They are put onto the recording medium in series without any identification and must therefore be read back in, in exactly the same sequence, to be recovered.

Only the data is recorded, not the variable names themselves. You can send it onto the tape as A, and can call it B when reading it back in. That is fair. But if you read data back as B% or B$, you will get an error message. Some error messages are really undeserved, as you know. This one is deserved. Don't mix your data types — integer to integer, string to string, and so on.

A Caution About String Variables

String variables, however, are a special case. Let's see why. In Commodore BASIC, unlike some other versions, there is a default value for variables. It is set when the machine is turned on, or when an array is dimensioned. The value is zero.

When you write string variables to tape, however, this default value of zero is not a legitimate representation of anything. A string "0" would be ASCII 48, but that is not what is there. What is there is a binary, octal, decimal, hex 0 — which, in the special language of strings, represents a null. Neither the cassette nor the disk will accept null strings. Result: input rejects it and the data isn't transferred.

The cure is logical, once it is pointed out: load all string variables, including string arrays, with a string variable that the tape or disk can recognize. Example: you have dimensioned a string array A$(20) that may not be filled from your program when you want to save it. Right after the DIMension statement, do the following:

11000 DIM A$(20)
11010 FOR I = 0 TO 20 : A$(I) = "X" : NEXT

The array has now been loaded with a recognizable string ("X") and can be saved. All unused parts of it will be saved as X and will not confuse things later.

Saving Simple Variables

When the sequence used in saving data is also followed in loading data, then the right variables get put back where they belong, and the transfer proceeds smoothly. You can safely use the following procedure, and it will work very well indeed on cassette:

12000 OPEN 2, 1, 1 : REM WRITE
12010 PRINT #2, A; "," ;B%; "," ;C$
12020 REM WHAT'S THIS?

You should be surprised by line 12010. First, variables are mixed, but that is OK as long as they are brought back in in the same order. A floating-point, an integer, and a string can be safely handled on the same line. You can't just have your other program trying to bring in a string when a number is next in line to come off the tape.

Second, what is all that between the variables? It is instructions to the computer about what to put on the tape record. Semicolons suppress "carriage returns," but "," is put in to allow the beginning and end of each separate item of information to be established. These are delimiters. They are like walls to make sure that two items are separated. (A "carriage return" is like moving the paper up one line when you hit the RETURN key on a normal typewriter. Each time you use a PRINT statement in BASIC, it is followed by a carriage return unless you put a ";" after it.)

Let's Put It On A Disk

So far we've zeroed in on cassette data operations. What about the same thing on disk? (Skip this section if you are concerned now just about cassette data.)

12000 DO$ = "1 : SCORES, S, W"
12010 OPEN 2, 8, 9, DO$
12020 PRINT #2, A ; "," ; B% ; ","; C$ ; CHR$ (13);

In line 12000, a record is defined as associated with disk unit 1: it is to be called SCORES and is identified as Sequential. This will be a Write operation. A later Read operation will be needed to bring it back in. In line 12010, file 2 is opened to unit 8 (the disk) with a secondary address of 9. Use 9 for a disk secondary address unless you specifically want something else. It works. The last part of the file opening statement is the DO$ that was defined in line 12000.

Line 12020 contains all of the variables and delimiters used in the cassette statement, with one addition: a carriage return CHR$(13) has been added to the disk statement. Note that it is surrounded by semicolons so no line feeds will be slipped in. You want a CHR$(13), not a CHR$(13) CHR$(10), there to keep the records straight. There are simpler ways to do this with the new 4040 disk, but this works for all disks, both new and old.

Saving Array Variables

While it is clear that mixing variable types on a single line is OK as long as they are recovered in that same order, this does not seem to be true if an array is involved. The following is not recommended:

13000 FOR I = 0 TO 20
13010 PRINT #2, A(I)
13020 PRINT #2, B$(I)
13030 NEXT

For reliable records, just don't mix string and numerical variables in a FOR/NEXT loop when saving data. Use an entirely separate loop to handle the strings. Any potential savings by avoiding the use of another separate loop to handle the strings can be costly. This works reliably:

13000 FOR I = 0 TO 20
13010 PRINT #2, A(I)
13020 NEXT
13030 FOR I = 0 TO 20
13040 PRINT #2, B$(I)
13050 NEXT

(If this were a disk operation, each PRINT #2 statement would end with; CHR$(13);).

A Practical Application

Now let's define and then write a minor cassette or disk data tour-de-force program. Let's say you need to input two arrays that contain names and scores for a tournament. NT$ is the name of the tournament, TP the number of tournament players, N$(TP) their names, and S(TP) their scores. We are reading data:

15000 OPEN 1, 1
15010 INPUT #1, NT$, TP
15020 CLOSE 1
15030 DIM N$(TP), S(TP)
15040 OPEN 1, 1
15050 FOR I = 0 TO TP
15060 INPUT #1, N$ (I)
15070 NEXT
15080 FOR I = 0 TO TP
15090 INPUT #1, S(I)
15100 NEXT

At 15010 the name and size are brought in on the same line. That's OK. They were put on the record earlier using the necessary "delimiters." The file is then closed to bring all of the information in from the buffer.

At 15030, TP is used to dimension the necessary arrays to hold the data. Then, using loops, the data for names and then for scores is brought in separately. So, we have stuck to our principles. Single line data is mixed because it will mix. Array data is not mixed even though it seems compellingly simple to do so.

Note that we referred to both cassette and disk in this program. The only difference between input of cassette data and input of disk data is the opening statements. It is actually practical to have independent opening statements, but then GOSUB to the same input loop subroutine for both cassette and disk. When you are reading data back in, there are no forced delimiters and no fancy manipulation of the line feeds. You can easily make your program read either cassette or disk data with negligible extra programming or complexity.

The Commodore cassette and disk are amazingly reliable in handling data. I once tried saving and then reloading .5 megabytes (500,000 characters) in the same program, and no errors occurred.

TI Data Storage

C. Regena

Data handling is discussed in detail in the User's Reference Guide that is included with your TI computer. You may refer to the topic, "File Processing." In the TI-99/4 book, the pages are 144 to 162. In the TI-99/4A book, the pages are 11-118 to 11-136.

There are only two kinds of variables in TI BASIC, A or A(X) for numeric and A$ or A$(X) for string. You do not need to worry about integer or floating-point numbers.

Unlike the Commodore computers, the TI will accept null strings. You may specify a null string by setting the variable C$ = "" or reading in data;

200 DATA 3,4.5,,X
210 READ A,B,C$,D$

Saving Simple Variables

Only in DISPLAY mode do you need to specify delimiters in quotes. If you specify INTERNAL, it is easier to handle data. A sample program to save the variables A, B, and C$ is:

100 OPEN #2 : "CS1",INTERNAL, OUTPUT,FIXED
110 PRINT #2:A,B,C$

When you are using the program later and want to read the variables, use this procedure:

200 OPEN #3 : "CS1",INTERNAL, INPUT,FIXED
210 INPUT #3 : A,B,C$

By the way, you may number your devices anything you want, from #1 to #255, inclusive. You may even use OPEN #X + 5 if you have previously defined X.

On A Disk

The procedure is the same as with cassette except for the device name:

100 OPEN #2 : "DSK1.TEST", INTERNAL,OUTPUT,FIXED
110 PRINT #2 : A,B,C$

The Disk Memory System manual that comes with the Disk Controller describes "File Processing" on pages 29-41 and presents several sample programs.