Commodore Banner Exchange

VIC-II graphics, accessing ROM font images from different banks (C64)

The Commodore 64 has 64K of RAM memory and the VIC-II graphic chip can access all of it, but in blocks of 16K called banks. CIA #2 chip is responsible of selecting a VIC-II 16K bank. But, as we will see in a minute, we also have ROM font images to take into account.

At Commodore 64 start up, bank 0 is used. This is the default condition. Bank 0 is the first 16K RAM bank in memory, and ranges from 0 to 16383 decimal (0 to $3fff hex).

Let us pay attention to the text mode display, and let’s think about what things are required to show and print characters on screen.

  • 16K bank: first of all, we have to tell the VIC-II chip what 16K bank it must use. This just requires writing to a CIA #2 register. Another CIA register may be written to just to make sure that the bank switch will take place. Since we have a total of 64K RAM, four 16K banks are available.
  • Screen memory (video matrix): this is a 1000 bytes memory segment where screen codes of the characters on screen are stored. This is required so that the VIC-II knows which character to put on a given character cell on the screen. Default screen memory is from 1024 to 2023 decimal ($0400 – $7E7 hex).
  • Characters definitions (“shapes”): we just need to tell the VIC-II chip where the character font to be used is placed in memory. The default font with slightly modified PET characters is available at start-up. Physically, it is placed in ROM memory, from location 53248 to location 57343 ($D000 to $DFFF hex). But, since the VIC-II can only access 16K RAM banks, it cannot directly see characters on ROM. So, ROM characters are mirrored in a couple of 16K RAM banks on hardware. This way, the VIC-II will see this 4K ROM image instead of RAM on some locations, thus being able to see ROM characters shapes. Of course, the default bank (bank 0) has a ROM image of Commodore characters.
    So, we just have to tell the VIC-II chip where character shapes are starting in RAM. If there is no ROM characters image, RAM data will be used for the character font. Instead, if there is a ROM characters image, this will be used. Please note that on RAM areas where the ROM font is mirrored, RAM data cannot be accessed by the VIC-II. But, of course, this data can still be accessed by the 6510 CPU.
    To sum up, to gain access to character shapes on memory, we must tell the VIC-II which 16K bank to use, and where the 2K segment characters shapes will start from, within the selected 16K bank. Why 2K? As a maximum of 256 different characters can be shown at the same time on screen, that makes 256*8 = 2048 bytes (each character shape needs 8 bytes).

Let’s now try to access the ROM characters images available.

We have two characters set on a Commodore 64: upper case/graphics (set 1) and upper/lower case (set 2). Each set is made up of 2048 bytes, or 2K (again, this is matching the 256 different characters limit).

Set 1 can be found on the third 2K block within a given 16K bank. Set 2 can be found on the fourth block.

But remember, only two 16K banks support ROM characters (that is, only two VIC-II banks have a ROM characters image available). Those are bank 0 (the default bank, from 0 to 16383 decimal, or from 0 to $3FFF hex), and bank 2 (from 32768 to 49151 decimal, $8000 to $BFFF hex).

Starting from default conditions (C64 just turned on), if we want an upper case/lower case characters set, we may code:

poke 53272,(peek(53272)and240)or 6

This is just the technical counterpart of the known SHIFT + C= key combination.

To switch back to the upper case /graphics font:

poke 53272,(peek(53272)and240)or 4

Again, those POKEs are just selecting the desired 2K segment for character shapes within the current 16K bank.

Now, let’s go hunting for those character sets in bank 2.

We need the following things to do:

  • telling the VIC-II that it must access bank 2;
  • telling the operating system that it must PRINT characters using a new video matrix RAM area (that is, a new 1000 bytes RAM segment for screen codes). In facts, the standard video matrix ranging from 1024 to 2023 cannot be used any longer, since it is in bank 0;
  • content of register 53272 does not need to be changed for now.

So, to show ROM characters from bank 2, we may use this little subroutine:

1100 poke56578,peek(56578)or3
1102 poke56576,(peek(56576)and252)or1
1110 poke 648,(32768+1024)/256:return

And to go back to bank 0:

1200 poke56578,peek(56578)or3
1202 poke56576,(peek(56576)and252)or3
1210 poke 648,4:return

You can just paste these subroutines in VICE. No need to type-in them. Don’t type RUN, just use GOSUBs from direct mode.

Code is not optimal, but it follows the approach of the Commodore 64 Programmer’s Reference Guide for the sake of clearness.

So, gosub 1100 in direct mode will bring you to bank 2, gosub 1200 will bring you back to bank 0.

The first time, you may use:

gosub 1100:print chr$(147)

Otherwise you will see garbage (this is the content on RAM for screen codes when you first switch to bank 2). Then, just use GOSUB 1100 and GOSUB 1200 with no screen delete. Write some things and keep switching between the two banks. You will see that you are just using two alternate screens.

The statements in lines 1110 and 1210 are telling the operating system the new starting location for the video matrix. Since we are not changing register 53272 decimal, the video matrix is the second 1K block on both banks (it’s a 1000 bytes block to be exact, though the VIC-II selects it in chunks of 1024 bytes – some bytes are just unused).

On bank 0, the video matrix starting location is just 1024 decimal (bank 0 starting location + starting location of the video matrix segment within the 16K bank, that is 0 + 1024 decimal).

On bank 2, the video matrix starting location is 33792 (bank 0 starting location + starting location of the video matrix within the 16K bank, that is 32768 +1024 = 33792).

To prove that, the following statement will put an A on the first character cell on screen:

poke 33792,1 : poke 55296,14

A simple way of telling on which bank you are while entering the above GOSUBs in direct mode is just PEEKing location 648. It tells the starting memory page for the video matrix that will be used by the OS to print characters.

print peek(648)

A value of 4 tells that you are in the default bank (bank 0). In fact, screen memory is starting from:

4 * 256 = 1024

A value of 132 is telling that you are in bank 2. In fact, screen memory is now starting from:

132 * 256 = 33792

This is just the location we previously used to POKE an A on screen.

Now, if you want to switch from upper case/graphics to upper case/lower  case in bank 2, you can just type the very same statement that we have used previously:

poke 53272,(peek(53272)and240)or 6

Of course, this is the same statement that can be used for bank 0. As usual, replacing “or 6” with “or 4” will bring the upper case/graphics character set back.

If we now fiddle around with register 53272 decimal, we can also change the video matrix starting location within a single bank, or both banks. For instance, we can modify the above subroutines as follows:

1100 poke56578,peek(56578)or3
1102 poke56576,(peek(56576)and252)or1
1105 poke 53272,(peek(53272)and15)or0
1110 poke 648,32768/256:return
1200 poke56578,peek(56578)or3
1202 poke56576,(peek(56576)and252)or3
1205 poke 53272,(peek(53272)and15)or16
1210 poke 648,4:return

On bank 2, the video matrix now starts from 32768 decimal. On bank 0, it still starts from the default location. So, things may be set up according to your needs.


  • Commodore 64 Programmer’s Reference Guide;
  • Mapping the Commodore 64.


Leave a Reply

Your email address will not be published.

Commodore Banner Exchange
Insert math as
Additional settings
Formula color
Text color
Type math using LaTeX
Nothing to preview