Back to 1 MHz
What rebuilding Space Invaders on a Commodore 64 taught me about burnout, ambition, and going back to basics (and programming).
A wise person once said that if you want to start making games, you should start simple. For example, with something like Space Invaders.
Even though I’ve made games before, I recently decided to take that advice seriously. Because the truth is, I haven’t really programmed in quite a few years now. Which is ironic, considering it’s technically been the foundation of my career ever since I started getting semi-professionally paid for it while still at university in the 90s. Also: I am, objectively, a huge nerd.
But after my heart incident and surgery in 2014, something shifted. The motivation slowly faded. Over time, I started questioning whether it was sustainable to pour both work and free time into chasing a paycheck that - while not terrible - isn’t remotely proportional to the physical, mental, and temporal cost of staying current in the field. Especially in tech - a field which increasingly moves in directions that sometimes feel borderline irresponsible to keep up with.
At some point, I also realized I’d become everything I once promised my younger self I wouldn’t. I originally learned to program so I could make cool games. Instead, I found myself building “management systems for operational leasing”.
That wasn’t exactly the dream.
But around Christmas last year, a quiet but persistent thought emerged: What if I went back to what made me want to do this in the first place?
Maybe doing what I really wanted to do all along could be… a good idea?
So now I’m building Space Invaders from scratch.
For the Commodore 64.
Yes. A 1 MHz computer from 1982.
With roughly 0.0014% of the processing power of the machine I use today.
Will it make me any money? Probably not.
But I’m having an absolute blast 😊
For the technically curious (feel free to skip this if you are not):
Space Invaders on the original arcade hardware moved objects by directly manipulating pixels. That works, but it’s computationally expensive — and the arcade CPU was twice as fast as the one in a C64. Not exactly a good approach for a port.
So I went with one of the C64’s superpowers: hardware sprites.
These are small graphical objects the system can move around cheaply and efficiently. The C64 only gives us 8 hardware sprites, but through the dark and slightly glitchy arts of sprite multiplexing - exploiting the way the raster beam draws the screen - we can trick it into displaying all 61 moving objects using just those 8 sprites.
Here’s how I’m setting it up:
Sprite 0: Player ship + player bullet (they never overlap, so they can share).
Sprites 1–4: Enemy clusters, dynamically multiplexed into position with dark raster interrupt magic.
Sprite 1 also moonlights as the UFO, sharing time with an enemy cluster that doesn’t need it on that particular raster pass.
Sprites 5–7: Enemy bullets - dedicated sprites so they can spawn and fly wherever the action requires.
The video above is an early alpha/demo. To the sharp-eyed among you: yes, it actually runs smoother than the original arcade version.
Right now I’m stress-testing the engine - pushing sprites, timing, and every moving part to make sure it holds up under load. Once the performance foundation is solid, I’ll start building and fine-tuning the core game logic.
First make it run. Then make it fun. 🚀
Tools: TRSE (“Turbo Rascal Syntax error, “;” expected but “BEGIN”)
Music: Courier by Uctumi
Source code: https://github.com/sveinjo/Space-Invaders-64
Update on How Things Are Going - 08.03.2026
After implementing most of the game functionality (except player death, since invincibility makes testing a lot easier), a few things have become apparent.
Multiplexing via interrupts is not the solution to every problem.
Initially, I treated it like it was and tried cramming as much code as possible into the raster interrupt chain. But the reality is that not everything fits there. A modest processor like the C64’s MOS 6510 simply takes longer to do some things than can realistically be squeezed into the tiny 1/60th-of-a-second frame available during a screen refresh.
Things like setting up the game board or generating temporary graphics data for the breakable shields take too long to run inside the raster chain.
Some tasks just belong in the main loop.
The raster interrupts should only handle the time-critical parts - things like synchronizing sound and making sure the moving pieces are where they need to be.
This becomes especially obvious once you start adding features like title screens, scoring, and multiple levels.
The brutal scarcity of pointers
Another thing I’ve learned is that pointers - one of the most important concepts in programming - are extremely limited on 8-bit machines.
This isn’t immediately obvious if you come from a modern programming environment, where pointer usage is rarely something you must contend with.
The keyword here for a C64 developer is “zero-page”, which refers to the first 256 bytes of system memory. It’s a high-speed RAM location, essentially functioning as an extended register for the CPU, and only parts of it can realistically be used for pointers.
It gets even trickier when the compiler assigns this limited pool of pointers dynamically. Since raster interrupts allow code to run out of order, you can suddenly have two pieces of code trying to use the same pointer registers at the same time. When that happens, things break in spectacular and unpredictable ways.
I only discovered this because the music player - originally added as a placeholder until I could implement the classic Space Invaders marching sound - occasionally clashed with pointer usage generated by the compiler.
For context, my programming environment: TRSE, uses Pascal as its programming language. The Pascal code is then translated into assembly before being passed to a cross-compiler that produces the final C64 machine code.
The result of these pointer collisions was a series of wonderfully unpredictable bugs that required stepping through the generated assembly code to diagnose.
And I am glad I had to do it, because it was very enlightening. And somewhat ironically, it left me feeling quite fond of the music player that caused the problem in the first place, so I guess it stays in now 😊
Two ways to avoid pointer collisions
To prevent these conflicts, you essentially have two options:
Disable interrupts while running pointer-heavy code
Ensure interrupt routines do not rely on contested pointers
Which solution to choose depends on how long the code block takes to execute.
If the code is short, temporarily disabling interrupts works fine because there is often some timing slack during a screen refresh. But if the code runs too long, interrupts can miss their window - which results in dropped frames, visual stutter, or choppy audio.
None of those are things you want in a game.
In situations like this, it’s often easier to write the critical sections directly in assembly, where you have complete control over pointer usage - or can avoid them entirely - rather than relying on the compiler.
Pascal for logic, Assembly for speed and control
Fortunately, TRSE makes this possible by allowing assembly code to be seamlessly inlined inside Pascal.
So now I do high-level game logic in Pascal, and low-level data shuffling and manipulation in assembly. Which works out swimmingly, because this is just the kind of thing that is easy to implement in assembly, but kind of a hassle in Pascal.
