Space Invaders emulator
I thought it was about time I added the Space Invaders emulator (port?) to my website, as I’d not touched it in over 3 years. Most of the work to get it running was done, with just sound and display rotation left to add. While mulling over the tricky display code I moved on to other projects and it was pretty much forgotten about.
It’s still unfinished but I’ve cleaned up the code, prepared a bootable disk, and refreshed myself on the technical details. It was an interesting contrast to the Pac-Man project I’d worked on previously. As before, the challenge was to modify as little of the original ROM as possible, with a virgin copy of the ROM patched at runtime.
CPU
The Space Invaders arcade machine uses an Intel 8080 CPU running at just under 2MHz. The Z80 was released 2 years after the 8080 and was designed to be object-code compatible, so the Invaders code runs on SAM (almost) unmodified. The Z80 also added many new features, including: IX/IY index registers, alternate registers sets, multiple interrupt modes, CB/ED extended instruction sets, and the relative jump instructions JR [cc]
and DJNZ
.
The 8080 has a single interrupt mode equivalent to the Z80’s IM0, where an instruction is supplied on the bus at interrupt time. The Invaders hardware supplies both RST 08
and RST 10
instructions at a frequency of 60Hz, which drive the overall game logic, including the attract screen. SAM lacks the extra hardware, but they can both be simulated using IM2 and a line interrupt, without modifying the ROM.
I/O ports 1 to 6 are used for coin and button inputs, as well a hardware bit-shifter circuit. The shifter takes a 16-bit value (written to port 4 in low/high order), and a left-shift count (written to port 2). Reading from port 3 returns just the high byte of the result — more on this later.
As we’re running the ROM code natively, trapping the I/O requires patching the instructions that make the requests. The only I/O instructions supported by the 8080 are IN A,(n)
and OUT (n),A
, which include the port number as an immediate operand. This allows us to use a simple loop to find and patch instructions that access ports 1 to 6 (later checked manually to ensure no false-positive matches). Each occurrence is replaced by a RST 08
instruction, with the original operand modified to include a flag indicating whether the original instruction was IN or OUT. We could have used separate RST calls for each, but that requires duplicating the RST handler and modifying more of the original ROM.
Since we’re simulating the interrupt calls, we have control over how the original RST 08
and RST 10
handlers are invoked. The ROM code for both start with 4 register push instructions, which can be moved to our own interrupt handler, freeing the space for our I/O hook.
Display
Space Invaders uses a monochrome bitmapped display with a linear layout, similar to SAM’s mode 2. The display resolution is 224x256, but like most portrait arcade games the display hardware works in landscape mode. Fitting the 256x224 (rotated) area on SAM’s 256x192 screen means we lose 4 character columns from the width of the play area.
As with SAM’s mode 2 (and the Spectrum), drawing to a non-character aligned position requires bit shifting of data. Invaders uses this for more control over the vertical position of the invaders, as well as the smooth scrolling of player and invader bullets. The hardware shifting circuit makes easy work of this, which is a good thing considering the slow CPU speed! That said, the invader pack does only move one invader at a time, keeping the per-frame drawing to a minimum.
The Invaders display is stored at &2400-3fff, which isn’t compatible with the 16K boundary requirement for SAM’s mode 2. That means redirecting ALL display writes to a suitable upper memory location; something difficult to do from a centralised point in the code. About the only option is to identify ROM routines accessing the display and provide alternative implementations.
Copying the first 6K of Invaders display to a SAM mode 2 screen in upper memory confirmed the game was running, but revealed another issue — the bit order within display bytes was reversed compared to SAM, requiring each byte be flipped before writing. The byte rotation could be avoided by rotating the display in the opposite direction, but that would leave scanline rows in reverse order, requiring a much larger display mapping table to correct.
To map the display accesses to a SAM-compatible location we offset the high byte of the address. Subtracting an additional 2 from this value also pulls the display up (well, left!) by two columns, centralising the game area on the SAM display. This clips a character from each side of the title area, and half an invader at the left and right edges, but it’s only a small difference. The movement range for the player turret is more limited so it’s unaffected.
The game now looked great, but play-testing revealed some issues. When the invader pack reaches the edge of the display it’s supposed to lower and turn back, but that wasn’t happening. Also, player bullets were passing through the invaders without hitting them. It turned out that collision detection was done by checking the display contents, but it was still reading from the original display location. Hooking an extra couple of routines to look at the new display area soon fixed that.
A final change was to add a splash of colour to match the original machine. As the video hardware didn’t support colour, cellophane strips were added to areas of the monitor: green for lives, bases and player turret, red for the flying saucer at the top. An equivalent effect can be achieved in the SAM version using blocks of mode 2 attributes, which are unaffected by the display data writes.
Rotating the display to the normal SAM orientation remains a challenge. My original approach was to apply rotation and scaling to each display write, preserving the original layout. That meant scaling/masking/combining each byte, so the iconic graphics would suffer some scaling distortion. A better approach might be to relocate some areas of the display, as I did with the score and fruit areas in my Pac-Man emulator. It still requires rotation, but only within simple 8 pixel blocks. Writes from some hook reimplementations could also be optimised for full block writes.
Sound
The sound effects in the original game are generated using analogue circuits rather than a sound chip, which makes them difficult to emulate in a traditional sense. Most Space Invaders emulators use sound samples taken from the original machine instead. I haven’t implemented the sound yet, but will attempt to create approximate effects with the SAM sound chip.
The source code and bootable disk image are now available on my website, but you’ll need to provide your own Space Invaders ROM image.