Color BASIC info memory locations.

From the “wish I knew then what I (almost) know now” department..

On the Color Computer, the first 1K (1024 bytes) of RAM is reserved for the BASIC ROM code to use. In this article, we will look at some memory locations that pertain to our BASIC code, variables, arrays and strings.

Here are the locations as described in the Color BASIC Unraveled book:

Color BASIC Unraveled, page A1.

Each location contains a 16-bit number which is the address in memory of that information. Here are the values we are interested in, translated to decimal:

  • TXTTAB – 25 and 26
  • VARTAB – 27 and 28
  • ARYTAB – 29 and 30
  • ARYEND – 31 and 32
  • FRETOP – 33 and 34
  • STRTAB – 35 and 36
  • MEMSIZ – 39 and 40

To turn two bytes in to the 16-bit address, we take the PEEK of the first byte, multiply it by 256, then add the PEEK of the second byte. For TXTTAB (Beginning of BASIC program) we would do:

PRINT PEEK(25)*256+PEEK(26)

That value returned will be different depending on the configuration of the Color Computer.

Let’s dig in to what each of these locations means.

TXTTAB: Beginning of BASIC program – 25/26 (&H19/&H1A)

This location represents where in RAM the BASIC program starts.

PRINT PEEK(25)*256+PEEK(26)

On a CoCo with only Color BASIC (no Extended or Disk BASIC), storage for a BASIC program will be right after the 512 bytes of screen memory. The CoCo’s screen is stored at 1024 to 1535, so BASIC programs load at 1536.

START   END   SIZE   DESC
-----  -----  -----  --------------
    0   1023   1024  ROM USE
 1024   1535    512  TEXT SCREEN
 1536  32767  31232  BASIC PROGRAM

With Extended BASIC added, some RAM after the text screen is used for high resolution graphics screens. By default, 6K is reserved on startup, but more or less can be specified using the PCEAR command. Since the smallest graphics screen using 1.5K (1536 bytes), allocation is done in multiples of that size. Thus, on startup, Extended BASIC has four 1.5K pages reserved for graphics. This means BASIC would start at 7668 on a CoCo with Extended BASIC.

START   END   SIZE   DESC
-----  -----  -----  --------------
    0   1023   1024  ROM USE
 1024   1535    512  TEXT SCREEN
 1536   7679   6144  HI-RES GFX
 7680  32767  25088  BASIC PROGRAM

With Disk BASIC, there is an additional 2K of RAM used just after screen memory at 1536, with the high resolution graphics screens after that.

START   END   SIZE   DESC
-----  -----  -----  --------------
    0   1023   1024  ROM USE
 1024   1535    512  TEXT SCREEN
 1536   3583   2048  DISK ROM USE
 3584   9727   6144  HI-RES GFX
 9728  32767  23040  BASIC PROGRAM

NOTE about “START”: As I calculated this, I see that the starting location for BASIC is actually one byte higher than I expected. On a 4K CoCo, I expected BASIC to start at 1536, but PEEK(25)*256+PEEK(27) shows 1537. On an Extended BASIC CoCo, those peeks show 7681 instead of 7680. And on a Disk BASIC system, they show 9729 instead of 9728. I am not sure why it is off by one, but I’m sure someone smarter than I will have the answer in a comment.

NOTE about “END”: You will notice in my table the end location for BASIC is listed as 32766. That would only be true if the machine has 32K or 64K. If the machine had only 4K or 16K, it would be less. (Basically, MEMSIZE – START OF BASIC PROGRAM). And then it’s also off by by one byte for some reason (4094 on a 4K machine, 16382 on a 16K machine, and 32766 on a 32K machine). I’ll have to look in to this. Maybe I’m the one who is off…

NOTE about BASIC Memory: You may note I just said a 32K and 64K CoCo will show the same location for the end of BASIC memory. This is because reasons. See this article for a discussion about getting the most memory for BASIC, or just read this excerpt:

64K NOTE: The reason BASIC memory is the same for 32K and 64K is due to legacy designs. The 6809 processor can only address 16-bits of memory space (64K). The BASIC ROMs started in memory at $8000 (32768, the 32K halfway mark). This allowed the first 32K to be RAM for programs, and the upper 32K was for BASIC ROM, Extended BASIC ROM, Disk BASIC ROM and Program Pak ROMs. Early CoCo hackers figured out how to piggy-pack 32K RAM chips to get 64K RAM in a CoCo, but by default that RAM was “hidden” under the ROM address space. In assembly language, you could map out the ROMs and access the full 64K of RAM. But, since a BASIC program needed the BASIC ROMs, only the first 32K was available.

this article

VARTAB: Start of Variables – 27/28 (&H1B/&H1C)

This location represents where the variable table begins.

PRINT PEEK(27)*256+PEEK(28)

Variables are stored directly after the BASIC program, so subtracting the value here from the value at 25/26 will give you the size of your program:

PRINT (PEEK(27)*256+PEEK(28))-(PEEK(25)*256+PEEK(26))

Note the use of parenthesis around each calculation. I was originally not using them, and was getting bad results because I made, as William Astle noted, “an elementary arithmetic error.”

Math is hard. Just ask Barbie.

Variable Table

The variable table is a series of 7 byte entries. The first two bytes are the variable name.

  • Variable Name (bytes 1 and 2) – If a variable is “LN”, the first two bytes would be “LN”. But, if the variable is a string such as LN$, the first byte would be “L” and the second byte would be the value of “L” + 128 (high bit set).
  • Variable Value (bytes 3-7) or String Descriptor (bytes 3, and 5-6)
    • If the variable is a number, the next five bytes are a floating point representation of the value.
    • If the variable is a string (high bit of the second name byte is set), the five bytes will be a string descriptor entry that contains the size and location of the string data bytes. A string descriptor only uses three of those five bytes. The first byte will be the length of the string (0-255), the next byte is unused, then the third and fourth bytes are the address of the string data in memory (located in reserved string space, to be discussed later in this article).
VARIABLE TABLE ENTRY
----------------------
[L][N][x][x][x][x][x] - numeric variable "LN"

Here is a short program that will print out all the variables. The variables MUST be declared before this code runs. If any new variables are created inside the FOR/NEXT loop, it will not work as expected since the table will have been changed.

0 ' SHOWVARS.BAS
5 DIM VS,VE,L,VN$
10 VS=PEEK(27)*256+PEEK(28)
20 VE=PEEK(29)*256+PEEK(30)
30 FOR L=VS TO VE-5 STEP 7
40 VN$=CHR$(PEEK(L))+CHR$(PEEK(L+1) AND 127)
50 IF PEEK(L+1) AND 128 THEN VN$=VN$+"$"
60 PRINT VN$;
70 FOR I=0 TO 4
80 PRINT TAB(5*I+6);PEEK(L+2+I);
90 NEXT:PRINT
100 NEXT

Running this will show a list of all the variables in use, and the five bytes after the name.

Above, the VN$ entry shows that it is a 3 byte string located in memory at location 127*256+236 (32748). More on this later in the article.

You can test this code further by declaring more variables before line 10, such as with the DIM statement, or just adding declarations like A=42 or ZZ$=”TOP”.

6 DIM A,B,C,D,A$,B$,AB$,ZZ$
7 AB$="ABBA":ZZ$="TOP"

ARYTAB: Start of Arrays – 29/30 (&H1D/&H1E)

This location represents where arrays will be stored.

PRINT PEEK(29)*256+PEEK(30)

And if we know the start…

ARYEND: End of Arrays (+1) – 31/32 (&H&1F/&H20)

And this location represents where arrays end (though it returns one byte past the last entry of the arrays).

PRINT PEEK(31)*256+PEEK(32)

Arrays

This part gets a bit complicated and messy, so unless you plan on doing stuff with arrays, feel free to skip this section completely… :)

Arrays get stored in their own block of memory. Each array entry starts with a 5-byte header that contains the name of the array (2-bytes), the length of the array (2-bytes) and how many dimensions the array is (1-byte). It looks like this:

ARRAY TABLE ENTRY HEADER (5 bytes)
--------------------
[  ][  ][  ][  ][  ] - numeric array "NM"
  N   M  AB  CD  #D  - ABCD = memory used (header to end)
                       #D = number of dimension
                            DIM(X)=1 DIM(X,Y)=2

This is followed by one or more 2-byte entries that contain the dimension size for each dimension. A one dimensional array such as DIM X(10) would have two bytes representing 11. Remember that in BASIC, DIMensions are base 0. DIM X(10) gives you X(0) through X(10) for a total of 11 entries.

Since an array has to have at least one entry, the header will really always have at least 7 bytes. Here is an example for a two dimension array like DIM X(10,20)

ARRAY TABLE ENTRY HEADER (5 bytes + at 2 bytes per each dimension)
------------------------------------
[  ][  ][  ][  ][  ][  ][  ][  ][  ]- numeric array "LN"
  N   M  AB  CD  #D  D1  D1  D2  D2 - ABCD = memory used
                                    - #D = number of dimensions
                                    - D1D1 = number of dimension 1 entries
                                    - D2D2 = number of dimension 1 entries

And, dimensions are backwards from how they are entered in BASIC. If you had this:

DIM AB(1,2,3,4)

You would have an array table entry that would be 15 bytes and look like this:

[65][66] [xx][xx] [4] [0][5] [0][4] [0][3] [0][2] [0][1]

[65][66] - name "AB"
        [xx][xx] - total bytes used from header to end
                [4] - four dimensions
                   [0][5] - DIM (x,x,x,4)
                         [0][4] - DIM (x,x,3,x)
                               [0][3] - DIM (x,2,x,x)
                                     [0][2] - DIM (1,x,x,x)

After each header are a series of 5-byte entries (one for each element in the array) in the same format as variables – either 5-bytes representing a floating point number, or 5-bytes that make up the string descriptor (size and location of string data).

The blocks of entries are in the order listed in the array table. i.e, if you have DIM(1,2) you will first have 3 5-byte entries for the second dimension DIM(x,2) followed by 2 5-byte entries for the first dimension DIM(1,x).

I think.

Here is a program that shows the Array Table:

0 ' SHOWARRAYS.BAS
5 DIM SA,EA,L,AN$,I,AL
6 DIM X(1),Y(10),XY(2,3),ZZ(10,15,20),A$(10),UL$(10,3)

10 SA=PEEK(29)*256+PEEK(30)
20 EA=PEEK(31)*256+PEEK(32)
30 IF SA=EA THEN PRINT "NO ARRAYS":END
40 PRINT "ARRAYS FROM";SA;"TO";EA
45 PRINT "NAME TSIZE DIM ..  ..  ..  .."
50 L=SA
60 AN$=CHR$(PEEK(L))+CHR$(PEEK(L+1) AND 127)
70 IF PEEK(L+1) AND 128 THEN AN$=AN$+"$"
80 PRINT AN$;
90 AL=PEEK(L+2)*256+PEEK(L+3)
100 PRINT TAB(4);AL;
110 PRINT TAB(11);PEEK(L+4);
120 FOR I=0 TO PEEK(L+4)-1
130 PRINT TAB(14+I*4);PEEK(L+5+I*2)*256+PEEK(L+5+I*2+1);
140 NEXT:PRINT
150 L=L+AL:IF L<EA THEN 60

And, with a bit of work, a program could be written to dump the 5-byte entrees for each array. I’ll add that to my “TODO” list…

FRETOP: Start of String Storage – 33/34 (&H21/&H22)

This location represents where reserved string memory begins. String space is reserved using the CLEAR command, with 200 bytes allocated by default. Strings are stored at the top of memory. Thus, on startup, this should print 200 bytes less than the end of BASIC memory:

PRINT PEEK(33)*256+PEEK(34)

If you change the amount of reserved string space, this number will change accordingly. Here we start out with 200 bytes reserved for strings, then change that to 0 bytes, then to 500 bytes:

If you wanted to know how much room is available for a BASIC program plus variables, you could take this value and subtract the start of BASIC location:

PRINT (PEEK(33)*256+PEEK(34))-(PEEK(25)*256+PEEK(26))

However, there is still some extra overhead so this value won’t match with what PRINT MEM shows. I guess it’s better to just PRINT MEM:

Maybe we’ll figure out what those “missing” 17 bytes are being used for.

STRTAB: Start of String Variables – 35/36 (&H22/&H24)

This location represents where the next string will be stored. Sorta.

PRINT PEEK(35)*256+PEEK(36)

Didn’t we just cover this? Not exactly. FRETOP shows where the string memory starts, but strings are actually stored from the top of memory, and grow downward. Meaning, if you have 200 bytes reserved, and place a ten byte string there (A$=”1234567890″), it will be located at the END of that 200 bytes, not at the start. This value points to where strings are stored within the reserved area.

If string memory were set to 16 bytes by using CLEAR 16, string memory would look like this:

FRETOP
 |
[.][.][.][.][.][.][.][.][.][.][.][.][.][.][.][.]
                                              |
                                           STRTAB

If we created a 5 byte string like A$=”12345″, it would then look like this:

FRETOP
 |
[.][.][.][.][.][.][.][.][.][.][.][1][2][3][4][5]
                               |
                            STRTAB

If we wanted to know how much string space is available, we could subtract FRETOP from STRTAB:

PRINT (PEEK(35)*256+PEEK(36))-(PEEK(33)*256+PEEK(34))

If we added another string of ten byte string like B$=”ABCDEFGHIJ”, it would look like:

FRETOP
 |
[.][A][B][C][D][E][F][G][H][I][J][1][2][3][4][5]
 |
STRTAB

At this point, we could add another one byte string, but anything longer would result in an ?OS ERROR (out of string space).

Here is a test program (and notice we are adding +”” to the end of each string. If that isn’t done, the string will be stored inside the BASIC program itself, and not in string memory.)

10 ' STRTAB.BAS
20 CLEAR 16
30 GOSUB 110
40 PRINT "ADDING 5 BYTE STRING"
50 A$="12345"+"":GOSUB 110
60 PRINT "ADDING 10 BYTE STRING"
70 B$="ABCDEFGHIJ"+"":GOSUB 110
80 PRINT "ADDING 2 BYTE STRING"
90 C$="??"+"":GOSUB 110
100 END
110 PRINT PEEK(33)*256+PEEK(34);
120 PRINT TAB(6);PEEK(35)*256+PEEK(36);
130 PRINT TAB(20);"FREE:";(PEEK(35)*256+PEEK(36))-(PEEK(33)*256+PEEK(34))
140 RETURN

Which leaves us with one final location…

MEMSIZ: Top of String Space – 39/40 (&H27/&H28)

This location represents where the end of string storage is, which will also be the end of RAM available to BASIC.

PRINT PEEK(39)*256+PEEK(40)

Using our diagram from STRTAB, let’s add MEMSIZ:

FRETOP                                     MEMSIZ
 |                                            |
[.][.][.][.][.][.][.][.][.][.][.][.][.][.][.][.]
                                              |
                                           STRTAB

MEMSIZE should never move, as it is the highest memory location available to BASIC. FRETOP will move, depending on how many bytes are reserved for strings using the CLEAR command.

After the two strings in the previous example were added, it would look like this:

FRETOP                                     MEMSIZ
 |                                            |
[.][A][B][C][D][E][F][G][H][I][J][1][2][3][4][5]
 |
STRTAB

This means we should be able to subtract STRTAB from MEMSIZ and know how many bytes of string space we are actually using:

PRINT (PEEK(39)*256+PEEK(40))-(PEEK(35)*256+PEEK(36))

Modifying the previous test program, we can print used/total string space at each step:

10 ' MEMSIZ.BAS
20 CLEAR 16
30 GOSUB 110
40 PRINT "ADDING 5 BYTE STRING"
50 A$="12345"+"":GOSUB 110
60 PRINT "ADDING 10 BYTE STRING"
70 B$="ABCDEFGHIJ"+"":GOSUB 110
80 PRINT "ADDING 2 BYTE STRING"
90 C$="??"+"":GOSUB 110
100 END
110 PRINT PEEK(33)*256+PEEK(34);
120 PRINT TAB(6);PEEK(35)*256+PEEK(36);
130 PRINT TAB(14);(PEEK(39)*256+PEEK(40))-(PEEK(35)*256+PEEK(36));
140 PRINT ;"/";
150 PRINT (PEEK(39)*256+PEEK(40))-(PEEK(33)*256+PEEK(34));
160 PRINT "USED"
170 RETURN

Yes … but what’s the point?

With these memory locations, we can determine how large a BASIC program is. We can detect how much string space is reserved, and calculate how much is being used or is free. We can figure out how much variable storage is being used, as well as array storage.

Most of these locations are never meant to be set by a program, but there is one exception. The CLEAR command can also be used to prevent BASIC from growing past a certain memory location. If you use a second parameter such as CLEAR 200,16000, it changes the MEMSIZ to that value.

10 ' CLEARIT.BAS
20 GOSUB 200
30 PRINT "CLEAR TO 16000"
40 CLEAR 200,16000
50 GOSUB 200
60 PRINT "CLEAR TO 10000"
70 CLEAR 200,10000
80 GOSUB 200
90 PRINT "CLEAR TO 32767"
100 CLEAR 200,32767
110 GOSUB 200
120 END
200 PRINT PEEK(39)*256+PEEK(40)
210 RETURN

The CLEAR command erases all variables, so there would be nothing for BASIC to relocate. Instead, this just changes MEMSIZE and adjusts the string values FRETOP and STRTOP and accordingly.

10 ' CLEARIT2.BAS
15 PRINT "FRETOP";TAB(10);"STRTAB";TAB(20);"MEMSIZ"
20 GOSUB 200
30 PRINT "CLEAR TO 16000"
40 CLEAR 200,16000
50 GOSUB 200
60 PRINT "CLEAR TO 10000"
70 CLEAR 200,10000
80 GOSUB 200
90 PRINT "CLEAR TO 32767"
100 CLEAR 200,32767
110 GOSUB 200
120 END
200 PRINT PEEK(33)*256+PEEK(34);
210 PRINT TAB(10);PEEK(35)*256+PEEK(36);
220 PRINT TAB(20);PEEK(39)*256+PEEK(40)
230 RETURN

While it would be possible to manually POKE those six values and accomplish the same thing, there could always be unintended consequences. (But for fun, a BASIC program could be written that moves the string memory somewhere else, then POKEs the values to update it so BASIC keeps running.)

Conclusion

There might be some interesting things one could do by monitoring string space closely… Perhaps all the research I did for this article may be leading somewhere…

Until then…


BONUS CONTENT

Here is the program I used to calculate the tables in this article.

0 ' MEMINFO.BAS

10 GOSUB 1500
20 ZL=0:ZS=1024:ZD$="ROM USE":GOSUB 1000
30 ZS=512:ZD$="TEXT SCREEN":GOSUB 1000
40 ZS=0:ZD$="BASIC PROGRAM":GOSUB 1000
50 GOSUB 2000

60 GOSUB 1500
70 ZL=0:ZS=1024:ZD$="ROM USE":GOSUB 1000
80 ZS=512:ZD$="TEXT SCREEN":GOSUB 1000
85 ZS=6144:ZD$="HI RES GFX":GOSUB 1000
90 ZS=0:ZD$="BASIC PROGRAM":GOSUB 1000
100 GOSUB 2000

110 GOSUB 1500
120 ZL=0:ZS=1024:ZD$="ROM USE":GOSUB 1000
130 ZS=512:ZD$="TEXT SCREEN":GOSUB 1000
135 ZS=2048:ZD$="DISK ROM USE":GOSUB 1000
140 ZS=6144:ZD$="HI RES GFX":GOSUB 1000
150 ZS=0:ZD$="BASIC PROGRAM":GOSUB 1000
160 GOSUB 2000

999 GOTO 999
1000 ' PRINT MEM INFO
1001 ' ZL=START LOCATION
1002 ' ZS=SIZE
1010 IF ZS=0 THEN ZS=32768-ZL
1020 PRINT USING("##### ##### ##### ");ZL;ZL+ZS-1;ZS;
1030 PRINT ZD$
1040 ZL=ZL+ZS:RETURN

1500 ' PRINT MEM INFO HEADER
1510 PRINT "STRT   END  SIZE  DESC"
1520 PRINT "----- ----- ----- -------------"
1530 RETURN

2000 ' WAIT FOR KEY
2010 IF INKEY$="" THEN 2010
2020 RETURN

2 thoughts on “Color BASIC info memory locations.

  1. William Astle

    In your peeks vs mem example, your screen shot shows the same “elementary arithmetic error” you mentioned earlier in the article.

    The missing byte at the top of memory is so VAL works properly on a string stored at the very too of string space. The CoCo3 actually doesn’t have that missing byte.

    The extra byte before the start of the program is needed due to some confusing details of how the interpreter operates.

    Reply
  2. Lee

    With regards to arrays and the 5-byte entries that follow the header, in the case of “DIM AR(1,2)”, I would assume you would have six 5-byte entries. Think of a 2-dimensional array as a 2-D grid. In our case, it would be 3 cells wide and 2 cells tall. So, there would be three 5-byte entries for the top row of 3 cells, followed by three more 5-byte entries for the bottom row of 3 cells. You’d have groups of however many your 2nd dimension is, repeated however many times your 1st dimension is. And if you had 3 dimensions, you’d have groups of however many your 3rd dimension is, repeated however many times your 2nd dimension is, and all of that repeated however many times your 1st dimension is. I am, of course, assuming that the farthest DIM’d dimension to the right is the one that is grouped closest together in memory, then that repeated for the next dimension to the left, etc. I haven’t tested this myself, but it looks like you did, and I trust your findings. :)

    Reply

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.