Characters, sprites and colours
Screen characters and colours
The Pacman video system consists of a 1024 (#400) byte screen 8x8 character array at CPU location #4000, a 1024 (#400) byte colour array at location #4400 and 8 sprites. Each of the sprites has 2 memory-mapped attribute bytes and 2 memory-mapped position bytes. The attribute bytes are a 16-byte array at #4ff0 and the position bytes are at #5060.
The screen layout is unusual in that the top 2 rows and the bottom 2 rows have a different geometry to the central maze part of the screen. There is a good explanation of the layout on Alessandro Scotti's page here. Each byte in the character array represents a tile.
There is a "flipscreen" register that inverts the whole display for two-player cocktail mode. This register does not invert the orientation or position of the sprites though and these are changed by the code when play changes.
Colour Palette
A byte in the colour array, corresponding to the screen array location, contains the colour table index to be used for each of the character colours. There are 32 tables (5 bits, #00 to #1f). Each table has 4 colours (2 bits, #00 to #03), in which colour #00 is always black. Each colour is also 4-bits (#00 to #0f). The other 3 colours per palette are shown on the left.
The video combines the 5-bit value from the colour table with the 2 bits from the character definition to create a 7-bit address to select a 4-bit entry in the 82s126.4a ROM.
This 4-bit entry is then in turn used to find an 8-bit colour in the palette in the 82s123.7f ROM. This 8-bit colour represents an RGB value with 3 bits for red (mask #07), 3 for green (mask #31) and 2 for blue (mask #c0).
The palette entries have some repetition and several are empty. The entries for the ghosts each have the same pattern, black, grey, blue and then the ghost colour. These are used to create the ghosts' eyes as blue dots on a grey background. Characters and sprites used the same colour scheme. The colour black is treated as transparent when drawing sprites. Some common colours used are:
- #01 - Red - Blinky
- #03 - Pink - Pinky
- #05 - Cyan - Inky
- #07 - Orange - Clyde
- #09 - Yellow - Pacman
- #11 - Blue - edible ghost
- #12 - White - edible ghost (flashing when about to turn back)
- #19 - Black - dead ghost (eyes only)
Other palette colours are used to draw fruit, scores, game over and other messages. Aaron Giles's page here has some more information on colours on palettes.
Character definitions
Character bytes point to an entry in the 4096 (#1000) byte ROM "pacman.5e". The character table is not quite ASCII but some text is recognisable in the ROM. Some characters such as uppercase letters are the same as in ASCII (#41 is 'A', #30 is '0', etc) but many are different. For example, a space is #40 instead of #20. Each character definition is 16 bytes long (256 x 16 = 4096). The reason it is 16 bytes per character instead of 8 is that each pixel in a character can have one of 4 colours so each byte represents 4 pixels of 2 bits each.
Character Definitions |
Some other notable characters numbers are:
- #10,#11 - small pill
- #12,#13 - medium pill (are these used anywhere?)
- #14,#15 - powerup pill
- #28-#2f - Namco logo
- #90-#1f - fruits
- #80-#83 - fruit points
- #b0-#b5 - these are static (non-sprite) definitions of the ghosts used on the intro screen
- #d0-#fb - maze walls
Sprite definitions
Sprite definitions sprites to an entry in 4096 (#1000) byte ROM "pacman.5f" are similar to character definitions but are slightly more complicated as each sprite is 16x16 bits which requires 64 bytes to fully define a sprite character. This means there are only 64 sprite character definitions (64 x 64 = 4096) so only the 6 lower bits of the 1-byte sprite character attribute are used to lookup the sprite definition. The upper two bits are used to "mirror" and "invert" the sprite so any sprite can have one of four orientations. The Pacman sprite for example only has two definitions but by using the mirror and invert bits, four different Pacman orientations can be generated. This doesn't work for the ghosts since their eyes point in the direction they are going, so 4 unique definitions are used for ghosts. Well actually 8 since each ghost direciton has two animations. The same definition is used for all ghosts -only the colours are different. When a two-player game is running in cocktail mode the mirror and invert bits are flipped (using xor #c0) when players change.
For some reason, the mirror and invert bits (#80 and #40 respectively) are the most significant bits when the ROM code is drawing the sprites. But the ISR rotates each of the sprite definitions by 2 bits after copying to the memory-mapped hardware registers so that mirror and invert are the two least significant bits. It's unclear to me why they didn't just store them in the order they are used by the hardware.
Sprite definitions (in Blinky's colour palette) |
Alessandro's page described here describes the strange layout of the character definition bytes but it doesn't talk about the sprite definition bytes. With a bit of trial and error I found this algorithm decodes them correctly:
for (y = 0; y < 16; y++)
{
for (x = 0; x < 16; x++)
{
int z = shape * 64;
z += ((y+4)&0xc) << 1;
z += (7-(x&7));
if ((x&8) == 0)
z += 32;
uint8_t pixelData = charset[z];
uint8_t col = colour << 2;
col |= (pixelData & (0x08 >> (y&3))) ? 0x01 : 0;
col |= (pixelData & (0x80 >> (y&3))) ? 0x02 : 0;
// Plot the colour at x,y if not black
}
}
Maze walls, ghost house and tunnel
The maze data is stored in a data area in CPU ROM at #3435. To save space, it uses a run-length-coding type scheme where a low value indicates a number of characters to skip to the next character. Also, the table only stores the left half of the maze. ROM code mirrors the maze to the right side of the screen.
The screen character array and colour array are used as a reference when allowing characters to navigate the maze. Reading back characters from the screen allows the code to avoid having to maintain the maze in RAM, which was expensive at the time.
Maze wall characters all have the two most significant bits set so detecting a maze wall is done by doing and with #c0 and comparing the result to #c0.
In addition, colours are used to mark particular areas of the maze. The colours used are:
- #0 - black - powerup pills flash between #0 and #10
- #10 - maze walls blue + pill colour
- #18 - entrance to ghost house
- #1a - "no chase" areas over the ghost house and at the Pacman start position
- #1b - tunnel
- #1f - maze walls white
The colour palettes for #10, #1a and #1b are all the same but using a different colour marker allows the code to detect these special areas.
Displaying Messages
The message display function displayMsg_2c5e() is a relatively complicated function that displays messages on the screen. It can display messages in a single colour or multiple colours and can erase messages using a single or multiple colours.
The function takes a parameter (int register b) that is the message number to display. If the message is or'd with #80 then the message is to be erased. A table of 16-bit CPU ROM addresses at #36a5 contains pointers to message contents.
Each message has the following format (where n is the length of the message):
- bytes 0 and 1 are the location on the screen where the messages is to be displayed. If this value is or'd with #8000 then it is a message to be displayed on the top or bottom of the screen where the difference between horizontally adjacent characters is +1. Otherwise, it is displayed in the middle (maze area) where the delta is +32 (+#20).
- Bytes 2 to n+1 are the message text.
- Byte n+2 is the end-of-text marker byte #2f
- byte n+3 is the colour to use when displaying the message. If this byte is or'd with #80 then a single colour is used for all characters. If not, then n+3 is the beginning of a sequence of colour bytes, the same length as the message itself
- byte n+4 is the end of colour string marker #2f.
- byte n+5 is the colour to use when erasing the message. Again, this can be or'd with #80 for a single colour or can be a string of colours.
the "string of colours" method is used when erasing the "PLAYER ONE" and "READY!" messages as the colours underneath these messages contain the "no chase" marker colour byte 0x1a. To illustrate, the ROM data, in C code, for the "PLAYER ONE" message is at #3741:
// 3741
{
0x8c, 0x02
'P','L','A','Y','E','R','@','O','N','E', 0x2f,
0x85, 0x2f,
0x10, 0x10, 0x1a, 0x1a, 0x1a,
0x1a, 0x1a, 0x1a, 0x10, 0x10
}
The first two bytes say this message is to be displayed at screen location #028c (row 14, column 11). The text for the message follows ending in #2f. The colour byte #85 means draw all the characters in colour #5 (Cyan, Inky's palette) followed by #2f again. Then a string of colour bytes to be used when erasing the message. Here we can see the string is #10 for the maze pills area with a band of 6 #1a bytes to replace the "no chase" area that was there before the message was displayed.
Comments
Post a Comment