With working graphics, sound, and a fast Basic interpreter, I needed a real demonstration program to show what the system could do. I decided to re-create a version of Pac-Man, similar to the original arcade game but with a few tweaks and modifications.
First, I set about re-creating the game map, using one of the tile engines. The original playfield graphics were easy to find online, already in the form of 8x8 tiles which my tile engine code could easily work with. My tile engine only needed a few tiles to build up the entire maze, as the per-tile flip and rotate feature greatly reduced the amount of raw graphics needed.
The dozen or so map tiles needed, in a simple tile editor program I created for this project.
The shape of the map would not be the same, as the original Pac-Man game had a vertically oriented screen, while I was working with a conventional horizontally oriented screen. Rather than try to use the original playfield map, I decided to just create a new horizontally oriented map for my game. And to be extra fancy, I made the map slightly different every level with a bit of procedural generation. Most of the playfield map would stay the same, but I added a few dozen spots which change with each level. In all there would be several thousand slightly different playfields which the player might encounter while playing the game. The base maze outline was actually saved in a binary file on disk and loaded into memory as needed, as was the bitmap graphics for the tiles. These could have been stored in the Basic program itself in data statements, but that would have made the Basic program listing much longer.
After drawing in the walls of the playfield maze and filling in the level-specific custom details, the code places the four power pellets and then floods the remaining corridor areas with the regular pellets. This does mean that the number of pellets that need to be eaten varies slightly from level to level. It also avoids the level 256 kill screen present in the original arcade game.
All of the sprite graphics needed for the game
I did take advantage of the flip and rotate capabilities in my sprite engine to avoid needing graphics for each different direction of Pac-Man and the ghosts. I also used the sprite animation to cycle between sprite images to animate the ghosts. Later I added an optional 16 color sprite mode with per-sprite palettes, which could have been used to save memory by using the same basic sprite graphics for all 4 ghosts, and also increased the available sprite graphics space to 64K, but I didn't end up needing that for this project.
Movement of Pac-Man unfortunately had to be done with the keyboard, since I have yet to add a joystick port to this machine. It’s good enough for demonstration purposes. I ended up coding a general movement routine that applies to all movable objects in the game which locks them to moving only along lines between the centers of tile spots and through open spaces in the maze, with some special logic for using the tunnels off the sides of the map. Pac-Man moves according to these limits, with a target direction value determined by the keyboard inputs.
Moving around the maze and eating pellets.
The ghosts are also locked to moving along open spaces in the maze, with some special case logic for entering and leaving the ghost house in the center. They all have a target location which they try to move towards. Initially, after leaving the ghost house, they are all in “scatter mode”, where each one moves towards a different corner of the maze. After some time in scatter mode, they switch to chase mode. In this mode each ghost has a slightly different behavior, having a target point which will be the player, a point in front of the player, a point behind them, or alternating between the player’s location and the far corner of the map. This is the same chase AI as in the original arcade game. After the player eats a power pellet, the ghosts change appearance and then attempt to move directly away from the player if possible. Finally, after a ghost has been “eaten”, its target location will be set to the center of the maze to return it to the ghost house.
The game gets much harder once the ghosts have been added.
I also added a moving bonus item that bounces as it travels from one side of the arena to another, as was seen in Ms. Pac-Man. It just seemed more fun than just having it appear in the middle of the arena.
To recreate the sound of the original game, I found a You Tube video of a long gameplay with good audio, fed the audio into spectrum analyzer software, and then spent a while working out the exact frequency and timing of the various sounds. The cross-channel modulation features of my audio generation code were very useful here, allowing me to recreate the background siren sound, the rising whoops when the player eats a ghost or bonus item, and other complex sound effects with just a few lines of Basic. Some of the sound effects don’t exactly match the arcade game original, as the original arcade game used a custom sound generator chip with complex wavetables that I couldn’t easily reproduce. The original “waka” sound from eating a pellet is especially difficult to reproduce, and my code doesn’t get it exactly, but it works well enough for demonstration purposes.
Apologies for the handheld camera work. I haven't managed to get the video capture device to work with this setup yet.
This game is missing several features of the original arcade game. There are no cutscenes. There’s no attract mode, running the program starts a new game instantly. And getting a game over simply locks the game up entirely, forcing the user to exit and then run the program again.
It’s a pretty fun game. I find it actually fairly difficult, but I’m not sure if that’s due to my reflexes being poor, or if I’ve set the speed on the ghosts to be too high. The main thing I’m proud of is that the somewhat complex game logic runs smoothly at 60hz, despite still being essentially interpreted Basic code.
The Basic source code is available here, but obviously this is only going to run on my own custom hardware, so it’s provided purely for reference purposes.
While writing this code the main problem I ran into was the limited memory available for sprites. 16K is really not much space for bitmaps, especially when they’re being stored in full 256 color mode. I did later implement an option for 16 color mode to effectively double the space available for sprites, and also later added some options to potentially increase the available sprite bitmap space to as much as 64K by disabling the other graphics engines and using their memory for sprite bitmaps instead. Ultimately I’m limited by the small amount of memory available on the FPGA, and my choice to try and implement this graphics engine with a single chip solution. Adding an external memory chip would fix the limited memory availability, of course, but that would be an entirely different design than what I’m going for.





