PyGame UI

The pygame user interface was the first piece of software we implemented, as it forms the backbone for the rest of our code. It acts not just as a user interface, but a manager for other classes. A few of its core functions are listed below:

  • Find, select and load games into the CPU.
  • Draw the CPU's output to the screen and play sounds, if any.
  • Provide parameters to scale the size of the CPU screen.
  • Update the CPU's input section of memory if keypresses are detected.
  • Map and multiplex physical keys to CPU keys.
  • Control the speed of execution of the CPU.
  • Allow the user to pause, play, restart, and select new games.

It is implemented in emulator.py, named so because it encapsulates all of our code. The class interfaces with a launch script that can configure emulator parameters as necessary. The class supports parameters for screen size scaling, environment (laptop vs the Raspberry Pi), execution speed, and input source. This allows the same code to be run on a variety of different computers with different performance limitations and screen sizes.

Design Steps

We started out with a minimal working version of the emulator that simply launched a blank window. This version was critical, as it defined the most important functions in the code: init, set_cpu, and run. The next step was to define a simple interface for the user. This proved to be more difficult than expected, and resulted at first in very messy code.

To deal with this increased complexity in an orderly way, we designed the following simple finite state machine for our emulator:

Once this was in place, we designed simple graphical menus for selecting new games and pausing/restarting. These are shown below:

ROM Selection Menu Pause Menu

At this stage, the emulator was essentially complete. However, a number of additions were made to it in the following weeks as problems were found. These are highlighted below.

Challenges Faced

While the emulator was not conceptually difficult to write, there were a number of subtleties that introduced challenges in the process. Some of these are described below:

  • Scaling display sizes. This proved to be difficult because not only did we need to scale the cpu graphics, but the menu size as well. Different resolutions for different CPUs added complexity.

  • Ensuring constant game speed, regardless of available computing power. This was an extremely interesting challenge. We noticed significantly different rates of gameplay on laptops and Raspberry Pi. To solve this, we added a parameter to our class that determined the number of CPU cycles that would be emulated per screen draw.

  • Managing pygame variables. Once we added menus and other UI elements, the code quickly began to become unmanageable. Splitting this up into clean, organized functions was somewhat challenging.

  • Optimizing screen drawing. While screen drawing posed no issues on laptops, it slowed things down tremendously on the Pi. To solve this, we changed our code to only draw pixels that had changed since the last draw.