Although the Commodore 64 comes with 64K RAM of memory, not all of this amount of memory is available right after the machine has been turned on. 38911 bytes are “only” available to BASIC – that’s just what the start up message is telling you.
This was indeed a big amount of memory for the time, as earlier Commodore PET/CBM machines usually offered roughly 32K RAM memory for BASIC. CBM 2 machines offered much more memory available to BASIC instead, but those computers enjoyed a quite limited success.
Still, you may wonder why the Commodore 64 out of 64K of RAM memory only offers 38K RAM for BASIC programming. But, you may also wonder whether or not it is possible to go past this limit with BASIC.
The Commodore 64 CPU is only capable of addressing 65536 bytes (locations from 0 to 65535 decimal, $0000 to $FFFF hex). That’s because the address bus is only 16 bit wide. But, the Commodore 64 has 64K RAM memory plus 20K ROM. It also has a special 4K I/O area where Interface Adapters (VIC-II, SID, CIAs) are mapped. That sums up to 88K. How is it possible for the Commodore 64 to address all of that memory? As we all know, the 6510 CPU has a special I/O port that allows to select the memory configuration of the Commodore 64. This way, BASIC and/or KERNAL ROMs can be swapped out and replaced with RAM for instance, freeing up 16K of RAM.
The point is, while using BASIC, you are usually told that no more than 38911 bytes are available. Still, there is an additional 4K RAM area starting from $C000 (49152 decimal), but this is commonly thought as an off limits area for BASIC. This is mostly used to add utilities and/or to poke data.
Anyway, if you want to use this 4K RAM area for BASIC, it is possible. You just have to set BASIC pointers properly. This area can be used to store either variables or a short program.
Let’s have a quick review of Commodore 64 BASIC pointers:
- Start of BASIC (locations 43 – 44 decimal): this is the pointer to the location where BASIC programs start from.
- Start of variables (locations 45 – 46 decimal): this is where variables start from.
- Start of arrays (locations 47 – 48 decimal): this is where numeric arrays and string arrays descriptors start from.
- End of arrays (location 49 – 50 decimal).
- String pointer (locations 51 – 52 decimal): that’s the location strings will be stored from – it grows backwards as soon as strings are being created.
- End of BASIC (locations 55-56): that’s the last location used by BASIC, defaults to $A000 or 40960 decimal.
These pointers can be dramatically useful. For instance, we can set up things so that variables can be either stored in the standard BASIC memory area or in the 4K RAM area starting from 49152 decimal. This approach gives us different advantages. First of all, we can have an additional 4K memory space for BASIC variables. Furthermore, we can take advantage of two different sets of variables. In a way, we can have “local” variables. And, those local variables can have the same name as the “global” variables if we want to.
The following program shows off this simple technique.
The program creates two different arrays having the same name – A$. One array is on the conventional variable storage area, the other one is on the 4K area starting from $C000.
Subroutine from line 130 sets BASIC pointers so that BASIC will look for variables on the $C000 area. Subroutine from line 60 sets BASIC pointers so that BASIC will look for variables in the conventional variable storage area. So, we can do things like in the following screenshot:
As a further example, we can create some variables in both areas and print them, from direct mode.
As you can see, a different set of variables has been created on each variable storage area. As a proof, L$ holds no string if you try to print it when the variables area on which it was created is not active. After the command gosub 60 is issued instead, if you try to print L$ the word “C64” will be shown again.
The code is quite simple and subroutines do not use variables. This way, you are free to use any variable you wish. That’s why routines are unrolled. Of course, these subroutines can be rewritten in assembly language. That way, they’ll be fast.
Now, what about the RAM under the ROM? There are 16K RAM potentially available (the remaining 4K are used for mapping the custom chips). This is just the RAM under the BASIC ROM and the KERNAL ROM. This memory cannot be read by BASIC – that would require swapping out the BASIC ROM while using the BASIC interpreter – clearly impossible. But, BASIC can write to this memory. And despite the fact that the 6510 CPU cannot access that memory for reading with the BASIC interpreter active, the VIC-II chip can read it anyway. That means, this amount of memory can be used by BASIC for graphics. The RAM under the KERNAL is all available for the VIC-II to show pictures. The RAM under the BASIC ROM is not all available – 4K are used as the shadow of the characters ROM – but, the other 4K can be used for graphics as well. So, we have a total of 12K that BASIC can easily use to show graphics, although it cannot read it. Possible uses are, for example, loading a picture from disk and showing it, or loading one or more charsets and use them (but without the ability of modifying them with BASIC).
So, adding 4K for BASIC variables like explained earlier and using the RAM under the ROM to show graphics loaded from media storage adds to BASIC another custom use 16K RAM to the 38911 BASIC bytes free.