How to emulate arcade game graphics

I have wanted to write about this topic for while and since several people who took my survey have asked about it, I figured now would be a good time.  There are basically two systems of graphics used in arcade games: Vector and Raster.

Vector
An example of a vector game is Asteroids.  The game code directly controls the movement and intensity of a beam of light on the monitor.  The picture tube of the monitor has a horizontal and vertical set of magnets which direct the beam to a specific coordinate.  By specifying two pairs of coordinates with the proper timing, a line is drawn.  In the majority of vector games, a special circuit takes care of the exact timing of the beam movement to prevent flicker and take a load off of the CPU.   Specifically, Asteroids has a vector list processor which continuously draws a list of vectors stored in RAM.  The CPU simply updates the list of vectors to make objects move on the screen.  That's why it is possible for a 1Mhz 6502 to handle the graphics, sound and gameplay of Asteroids.  Vector graphics can be either monochrome and color (e.g. Tempest).  Vector games tend to display stick figure objects since it would require too much time to move the beam back and forth to fill in solid objects.   You can notice some flicker on games which try to display too many objects simultaneously; the display phosphors do not retain the light long enough before the beam retraces the images.  The main advantages of vector graphics are that they require very little RAM and can display razor sharp lines and arcs.

To emulate this system of graphics, you will work from the vector table that the game generates and draw the lines to a video buffer for display to the user.  For Asteroids, you must emulate the behavior of the vector list processor and execute the vector instructions to draw the objects.  There are many ways to accomplish this task and many efficient means of only re-drawing the lines which change.  Some of the newer emulators also have added anti-aliased line drawing which uses different shades of color to hide the stair-step effect of lines drawn on a limited resolution raster display.   I use a brute force method in HiVE and redraw the entire scene each frame.  I am more interested in the raster video games and have not devoted any time to optimizing my vector graphics routines.  For a look at how Asteroids graphics can be emulated, download source.zip and look in asteroid.c

Raster
Raster graphics come in several flavors, but for all of them the display is refreshed in basically the same manner.  The video monitor traces a beam from left to right and top to bottom at a typical rate of 60 frames per second.  As this beam is traced across the display, the color and intensity of any spot is determined by the contents of RAM.  Since RAM retains its contents from frame to frame, once an image is loaded, it will remain displayed until changed.

Raster - Pixel Based
Some games use a scheme where each pixel is individually addressable and generated by 1 or more bits of RAM.  An example of games which do this are the Williams games (Robotron, Joust, Splat, ...).  Since a display of reasonable resolution requires a relatively large amount of memory (304 x 256 x 4 bits per pixel = 38912 bytes), the 8-bit CPU's of the past would have a hard time manipulating that much memory at high speed.  To speed up modifying the display memory, the Williams games use a 'blitter' chip.  What this chip does is a fast block copy of memory from a source to a destination and can perform various logical operations on the bits during the process.  The game code will typically manipulate the graphics memory directly as well as make use of the blitter chip for drawing/erasing the game's moving objects.   Some older / more primitive games such as those that run on the Space Invaders platform did not have a blitter chip and performed all graphics by having the CPU change the contents of memory.   In HiVE, I emulate the Williams blitter chip and translate all writes to video memory.  Shown below is a snippet of code taken from the Williams video write routine.  In the following code, usAddr is the address being written to, ucByte is the byte to write, pBitmap points to the DIB used to hold the current video frame, and iYOffset is used to speed up calculating the destination address in the DIB.  HiVE emulates all games into a 256 color bitmap since it is easiest to work with one byte per pixel.  Since a byte of video RAM holds two pixels, each byte written to video memory must modify two bytes in my destination bitmap.  The offset of 16 added to each pixel is to match the colors in my identity palette.

x = 2 * (usAddr >> 8); /* Calculate pixel coordinates from mem address */
y = usAddr & 0xff;
cDirtyRect |= 1<< (y >> 5);
/* Mark this strip as needing painting */
bm1 = &pBitmap[iYOffset[y] + x]; 
/* Destination DIB address */
*bm1++ = 16 + (ucByte >> 4); 
/* Upper nibble pixel */
*bm1 = 16 + (ucByte & 0xf);  
/* Lower nibble pixel */

Some raster games have the pixels oriented 'sideways' compared to a PC's video display.

Raster - Character / Sprite Based
The majority of video games that I have seen use a combination of characters and sprites.  Character graphics is where one or two bytes of video memory specify a character pattern retrieved from ROM by the video controller.  This way, a screen full of graphics may require only 1 or 2K of RAM memory.  Each character will typically map a 8x8 pixel character cell where one byte of the character memory specifies the pattern and another may specify the color.  Phoenix is an example of a character based video game.  Since the entire screen is mapped to only 1 or 2K of RAM, a slow 8-bit CPU is able to handle screen updates without having to modify very much memory.

Since character graphics tend to be rather chunky and are not addressable on a pixel by pixel basis, one solution is to add what are generally called sprites.  Sprites are graphics blocks (typically 16x16 pixels) which can be placed at any position on the display (in pixel coordinates) and are drawn by additional video hardware.  The game code merely has to update 3 pieces of information to draw a sprite: pattern (bitmap), color, position.  Sprites can number from a few to a hundred per game and are usually used for the moving objects.  Super PacMan is a game which uses sprites for both moving objects and static objects such as keys and fruits/pellets.  Sprites usually have priority over characters (the character will be hidden by the sprite drawn on top of it).  Sprites also have transparent parts so that their edges don't look like square black blocks and objects 'behind' them can be seen through holes in the object such as eyes.  Mappy and Jr. PacMan are examples of games where characters can have priority over sprites.

The sprites have priority over each other as well.  The order in which the sprites are drawn determines their priorities over one another; the last one drawn has the highest priority.  The PacMan games are examples of character/sprite graphics.   PacMan has a memory map of 2K for characters/colors and 6 sprites used for the moving objects and bonus points display.

One way to handle refreshing the display of a character/sprite video game each frame is to draw all of the characters and sprites at the end of each frame.  A more efficient way to update the display which I use is a set of flags to tell me when a character/color/sprite has changed and only draw the changes from one frame to the next.  A write to video memory causes the flag for that character to be set.  At the end of each frame, only the changed characters/sprites are drawn and the flags are all reset.  This is one of the reasons my PacMan emulation is so fast.  Shown below is the handler routine for video writes used in my PacMan driver:

if (mem_map[MEM_RAM + usAddr] != ucByte) /* Only update flags if changed */
   {
   mem_map[MEM_RAM + usAddr] = ucByte;
/* Store the new character/color */
   cDirtyChar[usAddr & 0x3ff] = 1;    
/* Mark this char as changed */
   }


For code samples of how to draw sprites and characters, download source.zip and look in public.c

Back