si 78 cis a memory accurate reimplementation of the 1978 arcade game Space Invaders in C.
It requires the original arcade ROM to function to load various sprites and other data, but does not use the original game code.
It is not an emulation, but rather a restoration.
It is however, accurate enough that it can be used to understand the inner workings of the original system, in a more accessible manner.
Many thanks to Christopher Cantrell atcomputerarchaeology.com. Without his painstaking work and excellent notes, this project would have taken a lot longer.
This was a reasonably large undertaking, requiring many iterations over several months, and I would conservatively estimate that around 200 hours of work have been put into the project so far.
The original ROM is around 2000 lines of 8085 assembler, all of it game code. The final published version of
si 78 cis around 1500 lines of game code, 500 lines of support code, and around 800 lines of comments.
There are about twenty thousand more lines of unpublished code in the background consisting of the previous iterations and other support scripts and tools that needed to be written to get the job done.
si 78 cproduces identical memory states (apart from the stack) to the original version. As a natural side effect, it produces pixel accurate frames compared to the original.
Cycle timing is not particularly accurate, but the game code is not particularly sensitive to this, as it uses interrupts for timing most things, instead of clock cycles.
The intended audience is hackers, enthusiasts, scholars, students, historians, and anyone else engaged in digital archeology.
The code is intended to be used as a more accessible guide to studying the original game, and learning about its inner workings.
Where practical, I have used the same or similar function and variable names as Cantrell, to more easily aid people studying both versions. The code is also laid out in a similar order to the original.
Every function with a matching analog in the original system is signposted with a comment like
e, which gives the address of the matching routine in the original ROM.
A few other places in the code like loops and branches are annotated similarly.
To find more detail on code near an xref, you can use Cantrell’s excellent noteshere.
The original code is interrupt driven, and partially co-operatively multitasked. The game spends about a third of the time running the main thread, which gets pre-empted by the midscreen and vblank interrupts. The other two thirds of the time is split between those interrupt contexts, which are not pre-empted, but decide when to return to main.
It also contains some interesting parts like this:
02 D0: 31 00 24 LD SP, $ 2400 // wipe the stack 02 D3: FB EI // drop isr context (D4: CD D7) CALL DsableGameTasks // keep going from this point
That code (running in an interrupt context) after detecting the player’s death, essentially wipes out all thread contexts (including itself), and then becomes the new main context.
To handle things like this in the this in
si 78 c, I decided to use
ucontext(user level threading) to give me more fine grained control over thread switching, creation and destruction.
The equivalent piece of code in C to the above is a bit messier, and involves using
swapcontextto swap to a scheduler context, which then resets all the contexts, and then swaps back and re-enters at the desired point.
There is no sound, as the sound hardware is not emulated.
Cycle timing is not particularly accurate, as mentioned, but its not very important in this case.
The code will currently only work on little endian systems, as the original system (8085) was little endian, and we use the ROM data as is.
The game is known to build and run on Ubuntu 18. 04 and MacOSX El Capitan, and will likely run on other x 86 Unix systems, as long as they support
ucontextis deprecated on MacOSX, so it may not run on later versions.
The game is written in the subset of C 99 that is compatible with C , and uses no compiler extensions apart from attribute packed. It will build fine on GCC 3 and above, and most likely any Unix C or C compiler newer than that.
Porting to a non unix system like Windows would require changing out
ucontextto use threads or fibers. Porting to a simpler system like DOS, or something bare metal will require writing some code to blit to the framebuffer, and adapting to whatever native interrupt facilities are available.
In terms of CPU grunt required, the code isn’t particularly optimized, and currently requires a 32 – bit processor capable of at least 10 Mips. It could be made to fit on a smaller system with a bit of work.
The code assumes little endian. To port to a big endian system would require further dissection of the ROM, to identify and swizzle any little endian data when it is loaded.
Porting the code to another language would not be a small task, and would most likely require switching out the threading system, and converting the code to not use pointers.
As mentioned, the original arcade ROM is required,
MAME_0 78set is known to work.
The constituent parts must be placed in a folder called
inv1, and have these checksums:
$ md5sum inv1 / * 7d3b 201 f3e 84 af3b4fcb8ce 8619 ec9c6 inv1 / invaders.e 7709 a 2576 adb6fedcdfe 175759 e5c 17 a inv1 / invaders.f 9ec2dc 89315 A0D 50 C5E 166 f (f) A 48 inv1 / invaders.g e 87815985 f 5208 BFA (d) C3FB 52418 inv1 / invaders .h
SDL2is required as a dependency, to install on Ubuntu, do this:
$ sudo apt-get install libsdl2
To grab the code and build the binary, do this:
$ git clone https://github.com/loadzero/si78 c.git && cd si 78 c $ make
To run it:
$ ./bin/si C) ***************
The keyboard controls are:a LEFT d RIGHT 1 1P 2 2P j FIRE 5 COIN t TILT
GIPHY App Key not set. Please check settings