Our project was basically inspired by our interest in gaming and various past 476 projects. We started looking at the Snake project's color-video display and wanted to improve upon it. We also noticed the various projects that used different game controllers and thought it would be fun to make a multiplayer game.
gray-scale output, start screen
First, we tried using Microsoft SideWinder gamepads, but we quickly found the communications protocol was too complicated. After evaluating several different game controllers, we settled upon the NES controller because it was simple, robust, and we could fit all the necessary communication lines for 4 simultaneous controllers on to one 8-pin port connected to the microcontroller.
We wanted to do color, but we wanted a decent pixel resolution. Stepping up to the ATmega32 from the ATmega163 used in the Snake project would help, but we figured we would not be able to have a straight, uncompressed memory representation of the display. Some kind of compression algorithm would be needed.
Since the microcontroller needs to generate the NTSC video signal with the color portion coming from the RGB converter, we would need a computationally simple algorithm. The system we came up with is basically a memory map, and ends up resembling a texture scheme. We divide the 128-by-96-pixel screen into 8-by-8-pixel sections, and each section points to a bin. There are 44 bins to choose from, and this is where actual image data is stored.
The screen buffer is really a 16-by-12 array of pointers to image bins. The bins themselves hold an 8-by-8-pixel image, with 4 bits per pixel. To display the image, we have to read from the screen buffer, then read the appropriate line from the corresponding bins. Because we have 128 pixels and 4 bits per pixel, we cannot just blast a line from registers because we would need 64 of them. Instead, we optimized the code to be able to read straight from a line buffer.
In order to fill this line buffer and then draw from it, we need more processing time. We decided to not line-double, and use half the line-time to decompress the image into the line buffer for the succeeding line. This creates an interlace effect, but we find it has a nice "old-school" aesthetic quality. (The idea for the blank-line interleave was inspired from older computer games that first used full-motion video, such as Command & Conquer and Wing Commander III.)
We therefore successfully boiled a 6-KB image into data that takes up under 1.5-KB of memory. The limitation though, is that you can only have 44 unique 8-by-8 textures displayed at any given time. However, because the screen buffer is just an array of pointers, one can fill the entire screen with pixel data. This suits an application that has a sparse display, perhaps a multiplayer video game involving spaceships.
To make the graphics that are displayed by the system, we wrote a utility program on a PC to convert image files into an array that could be stored in flash memory. The program reads a black-and-white image file and breaks it apart into 8-by-8 sections, and then encodes each pixel as one hexadecimal character (4-bit resolution), outputting the resulting C-code array as text we pasted into our project code.
The images displayed were made with a paint program (Paint Shop Pro), and one can basically display any black-and-white image with our video system within the limitations described above.
A simple example of a vector-based game like Asteroids or Subspace, the game involves shooting other people's ships and not getting shot. The spaceships move with basic vector physics, meaning you control the ship's rotation and acceleration, indirectly controlling velocity. Each ship can fire a projectile at the other ships, and the projectile carries an initial velocity based on the ship's velocity and facing.
The ships and projectiles wrap across the screen boundaries, making for chaotic but fun gameplay. Up to 3 players can play, but we could in theory support 4 players if we did more code optimization.