Skip to content

Justin-Credible/space-invaders-emulator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Space Invaders Emulator

This repository contains my implementation of an emulator for the Intel 8080 CPU and the related hardware for the 1978 arcade game: Space Invaders.

It emulates the graphics and sound, supports save states, has an interactive debugger, has rewind functionality, and includes 600+ unit test cases.

showcase

Implementation

I wrote the emulator and disassembler in C# targeting the cross-platform .NET Core runtime.

I used SDL2 and SDL2_mixer for the GUI and audio via the SDL2# wrapper.

The controls are hardcoded as:

  • Insert Coin: 5
  • 1 Player Start: 1
  • 2 Player Start: 2
  • Player 1 - Left/Right: left arrow / right arrow
  • Player 1 - Fire: space
  • Player 2 - Left/Right: a / d
  • Player 2 - Fire: p
  • Tilt: t
  • Break/Debug: BREAK / PAUSE / 9

Compiling / Running

  1. Install .NET Core 3.1
  2. Install SDL2 and SDL2_mixer
  3. Clone this repository
  4. cd emulator
  5. dotnet restore
  6. dotnet run -- followed by the commands to pass to the CLI program

Currently there is only one command, run:

$ dotnet run -- run --help

Usage: siemu run [arguments] [options]

Arguments:
  [ROM path]  The path to a directory containing the Space Invaders ROM set to load.

Options:
  -?|-h|--help          Show help information
  -sfx|--sound-effects  The path to a directory containing the WAV sound effects to be used.
  -ss|--starting-ships  Specify the number of ships the player starts with; 3 (default), 4, 5, or 6.
  -es|--extra-ship      Specify the number points needed to get an extra ship; 1000 (default) or 1500.
  -l|--load-state       Loads an emulator save state from the given path.
  -d|--debug            Run in debug mode; enables internal statistics and logs useful when debugging.
  -b|--break            Used with debug, will break at the given address and allow single stepping opcode execution (e.g. --break 0x0248)
  -r|--rewind           Used with debug, allows for single stepping in reverse to rewind opcode execution.
  -a|--annotations      Used with debug, a path to a text file containing memory address annotations for interactive debugging (line format: 0x1234 .... ; Annotation)

For example: dotnet run -- run ../roms --sfx ../roms --starting-ships 6

Interactive Debugger

If the emulator is launched with the --debug option, the debugger will be enabled. You can press the pause/break or 9 key which will stop execution and print the interactive debugger in the console.

showcase

From there you can use F1 and F2 to save and load the emulator state.

To single step over an opcode use F10, or F5 to continue until the next breakpoint.

Breakpoints can be set via the --break option at startup, or in the debugger by pressing F4.

If the emulator was started with the --annotations option, F11 can be used to toggle between the disassembler's generated psuedocode or the provided annotation file. This is used to show comments for each disassembled opcode inline in the debugger, which makes tracking down issues and/or understanding the game code easier. I collected annotations from the amazing Computer Archeology page on Space Invaders Code, and placed them at roms/annotations.txt.

F12 is used to print the last 30 opcodes, so you can see execution history.

Finally, if --rewind was specified at startup, F9 can be used to single step backwards over opcodes, effectively allowing you to rewind CPU state one instruction at a time. I found this to be very helpful when tracking down bugs in the CPU core.

Unit Tests

While building the emulator I found it essential to write unit tests for each opcode and along the way. This made it much easier to track down bugs late in development.

Each opcode test contains Intel 8080 assembly code which is assembled using zasm. This assembled binary is then executed on the emulated CPU and then has assertions ran against the CPU state to verify opcode behavior.

Additionally, there is an integration test which uses a CPU test program written for the Intel 8080 CPU originally from 1980! The assembled program along with its disassembly can can be found in the intel8080.tests/CPUDiag directory.

Emulator tests (9 test cases):

  1. cd emulator.tests
  2. dotnet restore
  3. dotnet test

Intel 8080 CPU tests (605 test cases):

  1. cd intel8080.tests
  2. dotnet restore
  3. dotnet test

Disassembler

While the disassembler is mainly used by the interactive debugger, it can be run from the command line as well:

  1. cd disassembler
  2. dotnet restore
  3. dotnet run -- followed by the commands to pass to the CLI program

Currently there is only one command, disassemble:

$ dotnet run -- disassemble --help

Usage: i8080disasm disassemble [arguments] [options]

Arguments:
  [ROM path]  The path to the ROM file to disassemble (or directory containing invaders.e through .h).

Options:
  -?|-h|--help     Show help information
  -o|--output      The path to the to output file.
  -a|--address     Include addresses in the disassembly.
  -p|--pseudocode  Include pseudocode in the disassembly (a comment on each line).

For exmaple: dotnet run -- disassemble ../roms -a -p -o ../roms/output.asm

Resources

I found the following resources useful in building this emulator:

Releases

No releases published

Packages

No packages published