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 :

the color ram ($D800-$DBFF) that stores the foreground color of each character

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.

PRINT PEEK(1150)

1

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 :

Values:
%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:

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.

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 :

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

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 believethat 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 :

turn bitmap mode ON

for each 8×8 pixel block draw on matrix with a sub-4×4 checker pattern

for each block location in screen RAM, set nibbles according to colors (15 & 11)

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

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 !

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…

Visuals

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.

Characters

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 :

Sprites

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.

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