From character mode to Bitmap

Original picture by Uka

Original picture by Uka


The graphic chip (VIC II) on the Commodore 64 can work in two modes, either ‘characters’ or ‘bitmap’.

Most of games and a vast majority of basic programs run in the first mode. It is preferred when performance is required with a lot of graphisms that must be updated on each frame refresh. Unfortunately, this mode is based on 8×8 tiles and not adapted to complex pictures (e.g cover pictures, photos, …). Moreover, it becomes tricky to draw a single pixel at a given location or simply draw a line…

When booting the C64, the basic editor is displayed in this mode. You can read and type characters in a 40×25 matrix where each character is subdivided in a 8×8 pixels grid. The VIC II need 3 memory areas to draw a screen in this mode :

  1. the screen ram (default $0400-$07FF) that stores the characters (or ’tiles’) codes
  2. the color ram ($D800-$DBFF) that stores the foreground color of each character
  3. the character rom ($D000-$DFFF) that defines characters pixels (kind of Font)

All we need to know for Maze Master is how the character data are stored in ROM.

Figure 1. Character ‘A’

Screen RAM

The ‘A’ character above is located at column 6 (starting from 0) and row 3.

We know that the screen ram is used by VIC II to know which character to draw at each 40×25 location. For instance, memory address $0400 stores the char code of character at column 0, row 0. Second char at column 1, row 0 is located at address $0401 (1025 in decimal). Then, the ‘A’ character above is referenced in screen ram at address $0400+6+3*40=$047E.


If you try to modify theses addresses in basic (in VICE emulator or real C64), you can display new characters at any 40×25 location :


Figure 2. Poking in Screen RAM

You notice that this short program display characters with codes 0..39 in the first row of display. So char ‘A’ is the character with identifier 1 (starting from 0) but also the second character defined in C64 ROM ‘FONT’.

Character ROM

This ‘FONT’ or ‘character generator’ is an area of the C64 memory used by VIC II to draw pixels of an individual character. It is stored in a chip on the motherboard :


Mos 901225-01 Character ROM Chip

If you dear old C64 is starting to burn out, this chip can return bad values to VIC II and mess up your display…


But let’s get back on track…

All we need to know for our Maze Master case study is how characters are defined in this ROM.

If you look back at figure 1, you can see that a character is defined in an 8×8 dots matrix. Each row of this matrix is encoded in 1 byte. Thus one character takes up 8 bytes and an entire charset requires 2048 bytes (256 chars * 8 bytes).

C64 Character rom is located at addresses $D000-$DFFF, thus defining two charsets of 256 chars each :

  • $D000-$D7FF stores uppercase & symbols charset


  • $D800-$DFFF stores uppercase & lowercase charset


So, if you’d just like reading pixels values for character ‘A’, you would read memory locations $D008-$D00F. But it is not as easy as it looks ! Let’s try out…


Figure 3. Reading character ROM

Ouch ! We expected to get $18, $3c, … and got null values…

Actually, C64 is using what is called a memory overlay :

What is read at address $D000 by the CPU (I/O area) is not necessarily what is read by VIC II (char ROM). Moreover, $D000-$DFFF is also mapped to RAM !! It’s getting a bit confusing but this complexity is also mandatory to make C64 so powerfull with only 64Kbytes of addressable memory…

The block of RAM or ROM visible at a given address is controlled by the register mapped to memory address $1.

If you look at this address content in basic, you get the default memory overlay :

print peek(1)


Here is the meaning of the first 3 bits of this register :

Bits #0-#2: Configuration for memory areas $A000-$BFFF, $D000-$DFFF and $E000-$FFFF.
%x00: RAM visible in all three areas.
%x01: RAM visible at $A000-$BFFF and $E000-$FFFF.
%x10: RAM visible at $A000-$BFFF; KERNAL ROM visible at $E000-$FFFF.
%x11: BASIC ROM visible at $A000-$BFFF; KERNAL ROM visible at $E000-$FFFF.
%0xx: Character ROM visible at $D000-$DFFF. (Except for the value %000, see above.)
%1xx: I/O area visible at $D000-$DFFF. (Except for the value %100, see above.)

So, in this default memory configuration, our basic program displayed the 8 first bytes of I/O area. This deals with horizontal and vertical positions of sprites 0..3 !

To display properly the character rom content, we must modify this overlay by clearing the third bit of this register. Let’s try to write value 51 to address $1:

Unhide charset memory

Figure 4. Unhide charset memory

Well done, we finally got the ROM values for character ‘A’ ($18=27, $3C=60, …).

But wait… what are those lines 10 and 50 needed for ?

Mmmm… here we tell C64 to temporary deactivate CIA1 timer to avoid using I/O area while we read from ROM.


The CIA (Complex Interface Adapter)

The CIA (which is an other custom CHIP of C64) can generate interrupts used by BASIC for various tasks (make cursor blink, scan keyboard, …) and need to read some I/O registers.

As the CPU cannot read both I/O and char rom in range $D000-$DFFF at the same time, C64 would freeze when such an interrupt occurs and I/O area is hidden by characters rom.

Line 10 deactivates CIA1 timer and line 50 reactivates this timer. We will write the same code in 6510 assembly when creating our custom charset in Maze Master !

I won’t discuss further more about charset set. There is a lot more to learn about it :

  • color character mode
  • color RAM
  • X/Y scrolling registers
  • VIC bank and custom charset in RAM…

But we have seen more than what is required to code the Maze Master display routines…

Bitmap RAM

When using Bitmap mode, VIC II is getting each pixel state of screen directly from a specific memory area. Thus, in order to display an entire screen, 320*200/8=8000 bytes of memory must be reserved for display.

As only 16Kbytes of memory can be addressed by VIC II at once, this means that only half of ‘graphic’ memory is now available for other graphic ressources (sprites).

This is not a severe issue for Maze Master since only 1280 bytes of sprites data are stored in the 16Kbyte cartridge.

But let’s enter Bitmap mode directly from BASIC :

POKE 53265,PEEK(53265)OR32



Figure 5. Entering BITMAP mode


Display mode is controlled by register at address $D011 :

Screen control register #1. Bits:
Bits #0-#2: Vertical raster scroll.
Bit #3: Screen height; 0 = 24 rows; 1 = 25 rows.
Bit #4: 0 = Screen off, complete screen is covered by border; 1 = Screen on, normal screen contents are visible.
Bit #5: 0 = Text mode; 1 = Bitmap mode.
Bit #6: 1 = Extended background mode on.
Bit #7: Read: Current raster line (bit #8).
Write: Raster line to generate interrupt at (bit #8).
Default: $1B, %00011011.

Thus, we entered bitmap mode by writing to bit #5 of the control register.

Oups… the screen is completly broken now, and we can see some pixels blinking in the first rows of display.

Actually, what we are watching is the C64 memory content in range $0000-$1FFF.

When using bank 3 for its 16Kbyte ‘graphic’ memory, VIC II can read the character ROM at addresses $1000-$1FFF. As you can see in figure 5, the character ROM is displayed in the lower half part of the screen. This should give you an hint about the way pixels data are stored in a bitmap…

Bitmap data are divided in 8×8 pixels blocks, where data is encoded the same way as in character ROM.

This means that the pixel at location (0,0) is mapped by the most signifiant bit of byte at address $0000. Equally, pixel at location (7,7) is mapped by the least signifiant bit at address $0007 and pixel at (15,7) is mapped by the least signifiant bit at address $000F !

How to clear bitmap

With defaults settings (i.e after a C64 reset), VIC II is using addresses $0000-$1FFF for its bitmap. As a lot of data in the first memory page ($0000,$00FF) are needed by the operating system and basic interpreter, we can’t simply erase bitmap memory to clear the screen.

Moreover, as the VIC II ‘sees’ character rom at $1000-$1FFF, we can’t erase this area of the screen with the CPU.

In sum, we’d better modify the bitmap location if we want to clear screen in our game. And its definitely one role of the VIC Memory Control Register at address $D018 :

Memory setup register. Bits:
Bits #1-#3:
In bitmap mode, pointer to bitmap memory (bit #13), relative to VIC bank, 
memory address $DD00. Values:
%0xx, 0: $0000-$1FFF, 0-8191.
%1xx, 4: $2000-$3FFF, 8192-16383.
Bits #4-#7: Pointer to screen memory (bits #10-#13), relative to VIC bank, memory address $DD00. Values:
%0000, 0: $0000-$03FF, 0-1023.
%0001, 1: $0400-$07FF, 1024-2047.
%0010, 2: $0800-$0BFF, 2048-3071.
%0011, 3: $0C00-$0FFF, 3072-4095.
%0100, 4: $1000-$13FF, 4096-5119.
%0101, 5: $1400-$17FF, 5120-6143.
%0110, 6: $1800-$1BFF, 6144-7167.
%0111, 7: $1C00-$1FFF, 7168-8191.
%1000, 8: $2000-$23FF, 8192-9215.
%1001, 9: $2400-$27FF, 9216-10239.
%1010, 10: $2800-$2BFF, 10240-11263.
%1011, 11: $2C00-$2FFF, 11264-12287.
%1100, 12: $3000-$33FF, 12288-13311.
%1101, 13: $3400-$37FF, 13312-14335.
%1110, 14: $3800-$3BFF, 14336-15359.
%1111, 15: $3C00-$3FFF, 15360-16383.


If you read the default control register value after a reset, you will get value ’21’ = %10101. According to register description, this means :

  • bitmap memory at $0000-$1FFF
  • screen RAM at $0400-$07FF (1024-2047)

Let’s modify this value to display bitmap at $2000-$3FFF :

POKE 53272,PEEK(53272)OR8 : POKE 53265,PEEK(53265)OR32
Figure 6. Bitmap at $2000-$3FFFF

Figure 6. Bitmap at $2000-$3FFFF

VIC II now displays an area of RAM reserved for BASIC programms. But as nothing has been written yet in these locations, default values of $00 or $FF are displayed on screen. It results in horizontal alterning bands of empty or full 8×8 blocks.

But wait, I didn’t tell you anything about this display mode colors…

At first sight, one could believe that HiRes bitmap display is limited to 2 colors (one for background, one for foreground) and only the bitmap memory determines the pixel state on screen. Actually, the background color (unlit pixels) and foreground color (lit pixels) are defined by the lower nibble (4 least signifiant bits) and upper nibble (4 most signifiant bits) of … screen RAM !

Indeed, on one hand the memory area at $0400-$07FF (default) is used to store characters code in character display mode and, on the other hand, the same locations are used in bitmap mode to define colors of each 8×8 pixels blocks.

This explains why, when turning on bitmap mode from BASIC, some blocks have distinctive colors.

In figure 6, if we consider line 9, we should read the basic prompt ‘READY.’ in character mode. This means that screen ram is filled with values $12,$05,$01,$04,$19,$2E.

If we keep only the upper nibble of each values, we get : $1,$0,$0,$0,$1,$2.

And if we get the corresponding colors from the table below, we obtain WHITE, BLACK, BLACK, BLACK, WHITE, RED

Black 0
White 1
Red 2
Cyan 3
Violet 4
Green 5
Blue 6
Yellow 7
Orange 8
Brown 9
Lightred 10
Grey 1 11
Grey 2 12
Lightgreen 13
Lightblue 14
Grey 3 15

Knowing that bitmap values for pixels located in the 8×8 blocks of the ‘READY.’ characters are set to $FF, VIC II displays foreground color in each of theses 6 blocks.

If you are still a bit confused on how VIC II display colors pixels in bitmap mode, let’s try to fill a bitmap with a 4×4 checker pattern with light grey foreground on dark grey background :

  1. turn bitmap mode ON
  2. for each 8×8 pixel block draw on matrix with a sub-4×4 checker pattern
  3. for each block location in screen RAM, set nibbles according to colors (15 & 11)
10 POKE 53272,PEEK(53272)OR8:POKE 53265,PEEK(53265)OR32
20 FORI=8192TO16191 STEP 8
30 POKEI,240:POKEI+1,240:POKEI+2,240:POKEI+3,240
40 POKEI+4,15:POKEI+5,15:POKEI+6,15:POKEI+7,15
60 FORI=1024TO2034:POKEI,251:NEXT
70 GOTO 70

Then you shoud get the following screen :


Figure 7. HiRes checkers

We can observe the obvious slowness of BASIC when dealing with bitmap graphics ! There are too many memory addresses to change if you have the modify the entire screen on each frame refresh…

This will end our session on C64 display modes.

Next time, we will rewrite the last program in 6510 assembly to compare execution speed !


Main menu workflow

In the first serie of articles on Maze Master reengineering, we will rewrite the main menu management code in 6510 assembly language.

The following tools will be used :

Relauch64 IDE supports color highlightment and direct execution of output code in Vice emulator.

Once all code is implemented, we should have a menu screen similar to this one


Each step in implementation should match an action of this activities diagram

Main menu activity Diagram

This seems rather easy but there is an enormous amount of work still to be done.

We will learn how to clear bitmap, draw a char at a given location on screen, read keyboard input, generate random numbers and deal with data structure in memory to store characteristics of our heroes !


Maze Master general design

Game media

This game was released in 1983. As far as I remember, it was available as a cartridge and tape.

Thus, the binary code and data are limited to 16KB of ROM, overlaping basic ROM and basic upper RAM at addresses $8000-$BFFF.


Maze master uses ALL these 16KB. Michael did not use data cruncher and it must have been tedious to fit all data in so few memory. However, we can store today much more complexe games in a 16KB cartridge. Have a look at the C64 16KB Cartridge Game Development Competition.

In next articles, we will rewrite parts of the game, ignoring the cartridge limitations…

In spite of it all, I will avoid self-modifying code. A cartridge is generally a Read Only memory, and this impact the way we implement routines…


The game is divided into three different views :

  • The menu screen where player manages his party and buy items.


  • The maze 3D view where player moves from square to square in a 20×20 area.


  • The encounter view where player select his characters actions during fight.




Even if these screen are using standard characters to display text, all is done in hi-res bitmap mode ! Actually, Michael created his own minimal charset with custom characters for the ‘maze master’ logo in header.

All characters are 8×8 blocks drawn in hi-res bitmap mode by CPU. A subroutine copies 8 bytes from custom charset at $4000-$431F to default bitmap memory at $2000.

Here is the charset used in all screens :

maze master charset


Remaining graphics are monsters pictures, displayed only during encounters. There is a limited set of pictures, matching a total of 40 different monsters. if you play the game, you can notice that a lot of monsters share the same graphics, using a modified color palette.

Humanoids always have the same legs and head/torse choosen among 7 variants.

There are specific graphics for dragons.

Michael used multicolor sprites to draw your ennemies. Each creature is composed of 4 sprites, drawn in double size to fill a 96×84 pixels area on top of the 3D view.

maze master sprites


In next article, I will write an article about the flow of activities in the game.

Disassembling Maze Master

I’ve spent a lot of time playing this game in the mid-80’s.

Maze Master is rather simple RPG, kind of 3D rogue game, where player must create a party of 3 characters and defeat monsters through a 5 levels dungeon.

A fine review has been posted on


This game has been coded by Michael Cranford, famous creator of “The Bard’s tale” serie of games.

I’ve always wondered how this ‘high resolution’ 3D view of maze was done on the commodore 64. Thus, I’ve enterily disassembled and commented the binary code to figure out…

In a following serie of articles, we will see how this game has been designed and implemented !

But for now, you can try out this game using the great commodore 64 emulator Jac64.

Welcome to Coding64

After many years leaving my old Commodore 64 computer taking dust in the family house, I decided to resurrect it and start coding 6510 assembly.

This site will details some software and hardware technics used in various games that have left their mark on my childhood.