Classic Computer Magazine Archive COMPUTE! ISSUE 64 / SEPTEMBER 1985 / PAGE 76

Saving Time
And Memory:
An Atari Variable Utility

P. E. Thompson


Here's a utility-actually three separate programs-which can help programmers save time and conserve memory. With them, you can list, rename, and abbreviate all variable names in a BASIC program. A thorough explanation is included.


One valuable feature of Atari BASIC is its provision for long variable names-up to 128 characters, with every character significant. Naming variables for what they represent, such as AVERAGE, rather than using a cryptic code, like A, makes programs self-documenting and more readable.
    However, there are two disadvantages. First, if you want to rename a variable, it is time consuming to go back through an entire program to edit long variable names. Second, long names lengthen program lines and make it difficult to add statements to the lines later. (Long variable names, however, don't consume much more memory; the Atari stores every character of a name only for the first reference, and uses a lookup table for subsequent references.)
    The utility programs following this article solve both problems. In addition, the program steps are ex plained in detail so you can understand what's happening. If you wish, you can readily modify the programs or use some of the same techniques in your own programming.

The Variable Name Table
Changing variable names in Atari BASIC is actually very easy. Each name is stored in a lookup table called the Variable Name Table. When a program is being listed, BASIC references this table each time a variable appears. When you change a name in the table, every name in the program listing also changes.
    You can locate the Variable Name Table by examining memory locations 130 and 131 (decimal) for the start of the table, and locations 132 and 133 for the end of the table. Try this example. Load a BASIC program, type the following line in immediate mode (no line number), and press RETURN:

FOR X=PEEK(130)+PEEK(131)*256
 TO PEEK(132)+PEEK(133)*256:
 PRINT CHR$(PEEK(X));:NEXT X

    This line converts the bytes in those addresses to decimal locations by adding the least significant byte (LSB) to the product of the most significant byte (MSB) times 256. Then it displays the character representations of each memory position between those locations. These character representations are the Variable Name Table.
    The table does not look quite as you might expect. Sprinkled throughout are characters in inverse video. These characters are flags which signal the end of a variable name and indicate the variable type. If the type is a scalar variable (that is, a number), the last character of the name is in inverse video. For string variables, an inverse-video dollar sign is appended. For an array variable, an inverse-video left parenthesis is added.
    By scanning the table, you may see variable names that no longer appear in the program itself. This can happen for two reasons. First, mistyped commands entered in immediate mode while you're programming may be inadvertently interpreted by BASIC as variable names, and therefore added to the table. Second, variable names used in a program but later removed are not deleted from the name table.
    The only way to remove these unused names is to LIST the program to tape or disk, type NEW to erase the program in memory, and then re-ENTER the program. When you load a program with ENTER, BASIC reinterprets each line as if you were typing the program manually. (That's why ENTER takes longer than LOAD.)

Using The Utilities
Follow these steps to use each utility:
1. Type each one into the computer individually from the listings here. REM lines are included strictly for reference and can be eliminated to save typing.
2. Store each utility on tape or disk using the LIST command, not SAVE.
3. Type NEW to erase any program in memory. Load the program on which the utility will operate. Make sure the program has no line numbers greater than 31999.
4. Load the appropriate utility using the ENTER command. For example, ENTER:C:" for tape or ENTER"D: filename" for disk. This appends the utility to the end of the program. (If your program has line numbers greater than 31999, they will be replaced by the utility.)
5. Run the utility by typing GOTO 32000 and pressing RETURN.
6. Write down the two starting addresses of the Variable Name Table. If a utility has run but an error has been made or a change is required, these addresses must be restored before any computer operations can take place. To restore the addresses, POKE 130 with the location 130 value listed by the utility, and POKE 131 with the location 131 value listed by the utility.
7. Execute the utility by responding to the screen prompts.
8. Two of the utilities-"Changer" and "Squeezer"-require that you immediately save the newly modified version of your program on tape or disk. However, you can't use the SAVE command for this purpose because the utility is merged with your program, so both would be saved together. Nor can you save the program with an immediate mode command, because the Variable Name Table would become garbled. Therefore, line 32380 in Changer and Squeezer automatically LISTs the modified program to tape or disk, separating it from the utility in the process. The utilities currently are set up to LIST your program to disk with the filename D:XXXXXXXX. XXX. You can change this filename by modifying line 32380 in both Changer and Squeezer. Also, change line 32380 in both utilities to LIST"C:",0,31999 for cassette.
9. After Changer or Squeezer has automatically saved your program, clear the computer by turning it off, then on again. Then you can load your program with the ENTER command for a test run. This assures that all pointers and the Variable Name Table will be reset to proper values.

Lister
The first utility, "Lister," lists the variable names and types. It scans the Variable Name Table looking for inverse characters to determine the type of variable. Each variable and its type are listed in the order of appearance in the table. More specific descriptions of the utility's steps are included in the program listing.
    If you want hardcopy, change the PRINT statements in lines 32040, 32140, 32160, and 32180 to LPRINT.

Changer
The second utility, "Changer," displays each variable on the screen and gives you the opportunity to change it. Press RETURN to retain the variable name.
    Changer operates by adding either the existing name or the changed name to a string variable called VARNAME$. This string emulates the format of the Variable Name Table, including the inverse video flags. When you've been given a chance to change all the names, Changer makes VARNAME$ the new name table. It does this by finding the starting memory location of VARNAME$ with the ADR function, then computing revised values for locations 130 and 131 and POKEing them into place.
    Immediately after Changer has LISTed your program to disk or tape, reboot the computer as described in step 9.
    You may want to expand the size of the new Variable Name Table. A program using many variables or long names may have insufficient space dimensioned for the new name table. If all the space in the new table is used before the utility has completed, an Error 5, String Length Error, will result. To allocate more space, change the dimensioned value for VARNAME$ in line 32020 from 500 to a larger number. You'll have to use your judgment as to the size of the number based on the number of variables and the length of the names.

Squeezer
The third utility is "Squeezer." It is similar to Changer except that each variable name is automatically replaced by a unique one- or two-letter name. This shortens the Variable Name Table to its minimum length, yet preserves the ability to LIST or modify the program. It's intended for use after a program is completely developed and debugged, particularly when the program requires as much free memory as possible. It's also helpful for shortening long program lines so you can add more statements. During testing, Squeezer reduced the size of one program by 400 bytes - an impressive figure, especially if you're working on a 16K computer.
    Squeezer lists the variable type, original name, and revised name. If you want a hardcopy, add the following line:

32001 OPEN #1,8,0,"P:"

and change the PRINT statements in lines 32045, 32050, 32060, 32160, 32181, 32201, 32220, 32260, and 32300 to PRINT #1;.
    As with Changer, after Squeezer has LISTed your program on disk or tape, immediately reboot the computer as described in step 9.

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

Program 1: Lister

FD 32000 PRINT CHR$(125):? :
         ?
OF 32010 REM INITIALIZE VARI
         ABLES
ME 32011 REM NAME$=VARIABLE
         NAME
FB 32012 REM LOCATION-MEMORY
          ADDRESS
BN 32020 CLR :DIM NAME$(128)
NP 32030 GOSUB 32040:GOTO 32
         060
BJ 32040 NAME$ "":? "Type
          Variable Name":RET
         URN
NA 32050 REM BEGIN FOR-NEXT
         LOOP
NI 32051 REM FROM STARTING L
         OCATION
EE 32052 REM OF VARIABLE NAM
         E TABLE
JC 32053 REM TO ENDING LOCAT
         ION
IL 32060 FOR LOCATION=PEEK(1
         30)+PEEK(131)*256 T
         O PEEK(132)+PEEK(13
         3)*256-1
IN 32070 REM CHECK FOR INVER
         SE CHAR.
PB 32071 REM IF NOT,ADD TO N
         AME STRING
CN 32072 REM AND GET NEXT LO
         CATION
P1 32080 IF PEEK(LOCATION)<l
         28 THEN NAME$(LEN(N
         AME$)+1)=CHR$(PEEK(
         LOCATION))3NEXT LOC
         ATION
DR 32090 REM IF LOCATION IS
         NOT A {RVS}${OFF}
KB 32091 REM THEN JUMP AHEAD
JE 32100 IF PEEK(LOCATION)<>
         164 THEN 32160
LB 32110 REM IF VARIABLE IS
         "NAME"
CB 32111 REM VARIABLES IN TH
         E UTILITY
FN 32112 REM HAVE BEEN ENCOU
         NTERED
BK 32113 REM SO WE ARE DONE
HK 32120 REM IF NAME$"NAME"
         THEN 32220
KF 32130 REM SINCE LAST CHAR
         ACTER OF
OP 32131 REM THE NAME IS {RVS}${OFF} P
         RINT TYPE
PN 32132 REM "STRING" AND TH
         E NAME.
F1 32133 REM GET NEXT LOCATI
         ON
MG 32140 PRINT "STRING: ";NA
         ME$:GOTO 32200
BC 32150 REM SINCE LAST CHAR
         ACTER
LL 32151 REM OF THE NAME IS
         {RVS}<{OFF}
FD 32152 REM PRINT "ARRAY" A
         ND NAME.
FK 32153 REM GET NEXT LOCATI
         ON
IL 32160 IF PEEK(LOCATION)=1
         68 THEN ? "ARRAY: "
         ;NAME$:GOTO 32200
BE 32170 REM SINCE LAST CHAR
         ACTER
HM 32171 REM OF NAME IS INVE
         RSE,
CD 32172 REM CHANGE TO NORMA
         L.
IN 32173 REM PRINT "SCALAR"
         AND NAME.
IL 32174 REM GET NEXT LOCATI
         ON.
LI 32180 NAME$(LEN(NAME$)+1)
         =CHR$(PEEK(LOCATION
         )-128):? "SCALAR: "
         ;NAME$
CN 32190 REM IF SCREEN IS FU
         LL,
NF 32191 REM STOP AND WAIT F
         OR INPUT,
CI 32192 REM RESET SCREEN
KC 32193 REM FOR MORE NAMES.
AI 32200 IF PEEK(84)>20 THEN
          ? :? "PRESS {RVS}RETURN{OFF}
          TO CONTINUE";zINPU
         T NAME$:? CHR$(125)
         :GOSUB 32040
KE 32210 REM RESET NAME$
HH 32211 REM FOR NEXT VARIAB
         LE.
IE 32212 REM GET NEXT LOCATI
         ON.
JB 32220 NAME$="":NEXT LOCAT
         ION
NC 32240 END


Program 2: Changer

BA 32000 ? CHR$(125):? :?
BF 32010 REM INITIALIZE VARI
         ABLES
LH 32011 REM ZNAME$  =OLD NA
         ME
LK 32012 REM VARNAME$=NEW NA
         ME TABLE
AB 32013 REM RENAME$ =NEW NA
         ME
FD 32014 REM LOCATION=MEMORY
          ADDRESS
KL 32020 CLR :DIM ZNAME$(128
         ),VARNAME$(500),REN
         AME$(128)
LA 32022 ? "VALUE AT LOCATIO
         N 130: ";PEEK(130):
         ? "VALUE AT LOCATIO
         N 131: ";PEEK(131):
         ?
FI 32030 GOSUB 32040:? :GOTO
          32060
MD 32040 ZNAME$="":? "Type
         : Variable Name":RE
         TURN
NA 32050 REM BEGIN FOR-NEXT
         LOOP
N1 32051 REM FROM STARTING L
         OCATION
EE 32052 REM OF VARIABLE NAM
         E TABLE
JC 32053 REM TO ENDING LOCAT
         ION
IL 32060 FOR LOCATION=PEEK(1
         30)+PEEK(131)*256 T
         O PEEK(132)+PEEK(13
         3)*256-1
IN 32070 REM CHECK FOR INVER
         SE CHAR.
PB 32071 REM IF NOT,ADD TO N
         AME STRING
CN 32072 REM AND GET NEXT LO
         CATION
KM 32080 IF PEEK(LOCATION)<1
         28 THEN ZNAME$(LEN(
         ZNAME$)+1)=CHR$(PEE
         K(LOCATION)):NEXT L
         OCATION
PL 32090 REM IF LOCATION IS
         NOT {RVS}${OFF}
KB 32091 REM THEN JUMP AHEAD
MN 32100 IF PEEK(LOCATION)<>
         164 THEN GOTO 32160
LB 32110 REM IF VARIABLE IS
         "NAME"
AE 32111 REM VARIABLES IN CH
         ANGER
FH 32112 REM HAVE BEEN ENCOU
         NTERED
BK 32113 REM SO WE ARE DONE
IG 32120 IF ZNAME$="ZNAME" T
         HEN GOTO 32340
BA 32130 REM SINCE LAST CHAR
         ACTER
NE 32131 REM OF NAME IS {RVS}${OFF}
KJ 32132 REM PRINT "STRING"
         AND NAME.
FI 32133 REM GET NEXT LOCATI
         ON
NC 32140 ? "STRING: ";ZNAME$
         :GOTO 32200
BC 32150 REM SINCE LAST CHAR
         ACTER
NK 32151 REM OF NAME IS {RVS}<{OFF}
FD 32152 REM PRINT "ARRAY" A
         ND NAME.
FK 32153 REM GET NEXT LOCATI
         ON
OF 32160 IF PEEK(LOCATION)=1
         68 THEN ? "ARRAY
         ";ZNAME$:GOTO 32200
BE 32170 REM SINCE LAST CHAR
         ACTER
NG 32171 REM OF ZNAME IS INV
         ERSE,
CD 32172 REM CHANGE TO NORMA
         L.
IN 32173 REM PRINT "SCALAR"
         AND NAME.
FN 32174 REM GET NEXT LOCATI
         ON
MG 32180 ZNAME$(LEN(ZNAME$)+
         1)=CHR$(PEEK(LOCATI
         ON)-128):? "SCALAR:
          ";ZNAME$
PP 32190 REM INPUT NEW NAME
         OR {RVS}RETURN{OFF}
LG 32191 REM IF NO CHANGE
CF 32200 ? :? "NEW NAME OR {RVS}R
         ETURN{OFF}":INPUT RENAME
         $
MN 32210 REM USE DOWN-ARROW
         TO SLIDE
JJ 32211 REM NAME OFF SCREEN
AO 32220 POSITION 0,7:FOR LI
         NE=1 TO 15:? CHR$(1
         57):NEXT LINE:POSIT
         ION 2,7
IP 32230 REM IF {RVS}RETURN{OFF} PRESS
         ED,
JN 32231 REM ADD OLD NAME TO
          NEW TABLE
OK 32240 IF LEN(RENAME$)=0 T
         HEN RENAME$=ZNAME$
NA 32250 REM IF VARIABLE IS
         ARRAY
AP 32251 REM OR STRING ADD {RVS}<{OFF}
         OR {RVS}${OFF}
AM 32260 IF PEEK(LOCATION)=1
         64 OR PEEK(LOCATION
         )=168 THEN RENAME$(
         LEN(RENAME$)+1)=CHR
         $(PEEK(LOCATION)):G
         OTO 32300
AJ 32270 REM IF VARIABLE IS
         SCALAR
NL 32271 REM CHANGE LAST CHA
         R
KD 32272 REM TO INVERSE
FJ 32280 RENAMES(LEN(RENAME$
         ))=CHRS(ASC(RENAME$
         (LEN(RENAME$)))+128
         ):GOTO 32300
FL 32290 REM ADD NAME TO NEW
LE 32291 REM VARIABLE NAME TI
         ABLE
EK 32300 VARNAME$(LEN(VARNAM
         E$)+1)=RENAMES
PP 32310 REM RESET ZNAMES
HI 32311 REM FOR NEXT VARIAB
         LE.
HC 32312 REM GET NEXT VARIAB
         LE.
ID 32320 ZNAME$="":RENAME$="
         ":NEXT LOCATION
NC 32330 REM ALL VARIABLE NA
         MES
AO 32331 REM REVISED. ADD CH
         R$(0) TO
AE 32332 REM TABLE TO-INDICA
         TE END
PE 32340 VARNAME$(LEN(VARNAM
         E$)+1)=CHR$(0)
EE 32350 REM CHANGE ORIGINAL
          TABLE
NN 32351 REM ADDRESS TO NEW
         TABLE
MK 32360 POKE 131,INT(ADR(VA
         RNAME$)/256):POKE 1
         30,ADR(VARNAME$)-PE
         EK(131)*256
AC 32370 ? CHR$(125):? "NOW
         LISTING TO TAPE OR
         DISK.":? "CHANGE LI
         NE 32380 IF DESIRED
         ."
IL 32380 LIST "D:XXXXXXXX.XX
         X",0,31999
NI 32390 END


Program 3: Squeezer

BA 32000 ? CHR$(125):? :?
GK 32011 REM COUNT(0)= NUM.
         STRINGS
BE 32012 REM COUNT(1)= NUM.
         ARRAYS
EN 32013 REM COUNT(2)= NUM.
         SCALARS
EI 32014 REM COUNT(3)= COUNT
         ER
AO 32015 REM COUNT(4)= ARGUM
         ENT IN SUB
L0 32016 REM VARNAMES= NEW N
         AME TABLE
EN 32019 CLR :DIM VARNAME$(3
         84),COUNT(4)
KO 32020 ? "VALUE AT LOCATIO
         N 130: ";PEEK(130):
         ? "VALUE AT LOCATIO
         N 131: ";PEEK(131):
         ?
FN 32022 COUNT(0)=0:COUNT(l)
         =0:COUNT(2)=0:COUNT
         (3)=0:COUNT(4)=0:GO
         SUB 3204O:GOTO 3212
         0
GP 32030 REM SUBROUTINES TO
         PRINT
JH 32031 REM VARIABLE NAMES
HA 32040 IF PEEK(84)<22 THEN
          GOTO 32045
PC 32041 ? "PRESS {RVS}RETURN{OFF} TO
         CONTINUE"
OB 32042 IF PEEK(764)<>12 TH
         EN GOTO 32042
PG 32043 POKE 764,155:? CHR$
         (125)
DB 32045 ? " NAME: ";:RETUR
         N
ME 32050 ? "RENAME: ";:RETUR
         N
GM 32060 ? VARNAMES(LEN(VARN
         AMES));:RETURN
DA 32070 REM SUBROUTINE TO D
         ETERMINE
0P 32071 REM NEW VARIABLE NA
         ME. IF
MB 32072 REM ALL SINGLE LETT
         ER NAMES
H0 32073 REM HAVE BEEN USED,
HK 32074 REM ADD A SECOND LE
         TTER
FH 32080 GOSUB 32050:IF COUN
         T(4)<25 THEN GOTO 3
         2090
0B 32085 COUNT(3)=1+INT(COUN
         T(4)/25):VARNAMES(L
         EN(VARNAMES)+1)=CHR
         $(64+COUNT(3)):GOSU
         B 32060
PE 32090 COUNT(3)=1+COUNT(4)
         -INT(COUNT(4)/25)*2
         5:VARNAME$(LEN(VARN
         AME$)+l)=CHR$(64+CD
         UNT(3))
IL 32100 GOSUB 32060:RETURN
LO 32110 REM CHECK ALL LOCAT
         IONS
BI 32111 REM FROM START TO E
         ND
PL 32112 REM OF NAME TABLE
CK 32120 FOR LOCATION=PEEK(1
         30)+PEEK(131)*256 T
         O PEEK(132)+PEEK(13
         3)*256
EG 32130 REM IF CHARACTER IS
         CHR$(0) THEN
DK 32131 REM END OF TABLE IS
         REACHED
CF 32140 IF PEEK(LOCATION)=0
          THEN GOTO 32300
II 32150 REM IF CHARACTER IS
          NOT
CM 32151 REM INVERSE THEN GE
         T NEXT ONE
JC 32152 REM IF INVERSE THEN
          END
MC 32153 REM OF NAME IS REAC
         HED SO
AI 32154 REM DETERMINE VARIA
         BLE TYPE
GM 32160 IF PEEK(LOCATION)<l
         27 THEN ? CHR$(PEEK
         (LOCATION));:GOTO 3
         2280
NA 32170 REM IF CHARACTER IS
         {RVS}<{OFF} THEN
FJ 32171 REM TYPE IS ARRAY.
         SET
LK 32172 REM ARGUMENT TO COU
         NT, CALL
DE 32173 REM SUBROUTINE TO D
         ETERMINE
IO 32174 REM VARIABLE NAME.
         ADD {RVS}<{OFF} TO
IF 32175 REM NAME, ADD 1 TO
         COUNT,
CH 32176 REM GET NEXT NAME
JL 32180 IF PEEK(LOCATION)<>
         168 THEN 32200
KK 32181 ? "("
DH 32182 COUNT(4)=COUNT(1):G
         OSUB 32080:VARNAME$
         (LEN(VARNAME$)+1)="
         (":GOSUB 32060:COUN
         T(1)=COUNT(1)+1:GOT
         O 32260
PP 32190 REM IF CHAR IS {RVS}${OFF} TH
         EN
LD 32191 REM TYPE IS STRING.
          SET
LM 32192 REM ARGUMENT TO COU
         NT, CALL
DI 32195 REM SUBROUTINE TO D
         ETERMINE
IO 32196 REM VARIABLE NAME.
         ADD {RVS}${OFF} TO
IJ 32197 REM NAME, ADD 1 TO
         COUNT,
CL 32198 REM GET NEXT NAME
ML 32200 IF PEEK(LOCATION)<>
         164 THEN GOTO 32220
JP 32201 ? "$"
CJ 32202 COUNT(4)=COUNT(0):G
         OSUB 32080:VARNAME$
         (LEN(VARNAME$)+1)="
         $":GOSUB 32060:COUN
         T(0)=COUNT(0)+1:GOT
         O 32260
DE 32210 REM VARIABLE TYPE I
         S SCALAR.
FB 32211 REM PRINT NORMAL CH
         AR
CB 32220 ? CHR$(PEEK(LOCATIO
         N)-128)
DI 32230 REM SET ARGUMENT EQ
         UAL TO NUM
DP 32231 REM OF SCALAR VARIA
         BLES FOUND
LF 32232 REM SO FAR. CALL SU
         BROUTINE
FK 32233 REM TO DETERMINE NE
         W NAME.
FB 32234 REM ADD 1 TO NUMBER
          SCALARS
PE 32240 COUNT(4)=COUNT(2):G
         OSUB 32080:COUNT(2)
         =COUNT(2)+1
CC 32250 REM SET LAST CHARAC
         TER OF
MB 32251 REM NAME TO INVERSE
NJ 32260 VARNAME$(LEN(VARNAM
         E$))=CHR$(ASC(VARNA
         ME$(LEN(VARNAME$)))
         +128):? :? :GOSUB 3
         2040
NL 32270 REM END OF FOR-NEXT
          LOOP
FF 32271 REM FOR NEXT CHAR.
JH 32280 NEXT LOCATION
AN 32290 REM HOLD LAST PARTI
         AL SCREEN
BA 32291 REM FOR DISPLAY.
EL 32292 REM ADD CHRS(0) TO
         END OF NEW NAME
CB 32293 REM NAME TABLE INDI
         CATING END
KO 32300 ? "END OF TABLE":?
         :GOSUB 32041:VARNAM
         E$(LEN(VARNAMES)+1)
         =CHR$ (0)
PD 32330 REM CHANGE TABLE AD
         DRESS
BL 32340 POKE 131,INT(ADR(VA
         RNAME$)/256):POKE 1
         30,ADR(VARNAME$)-IN
         T(ADR(VARNAME$)/256
         )*256
BC 32350 REM DISPLAY WARNING
          MESSAGE
ND 32360 ? CHRS(125):? "NOW
         LISTING TO TAPE OR
         DISK":? "CHANGE LIN
         E 32380 IF DESIRED.
         "
IL 32380 LIST "D:XXXXXXXX.XX
         X",0,31999
NI 32390 END