Now go outside and look at the sky.
The Best Kind Of Side Project
About a year ago I decided that I really needed to get serious about Swift - I had written a few mobile apps for iOS in Swift, but these were mostly gluing together different UI elements with very lightweight code snippets. It's hard to learn a language if it's all API calls and three lines of simple logic in between.
I've always been interested in writing emulators and had given it several attempts that all fizzled... so how about another emulator. And this time, keep the target small, like an 8-bit machine... Well, there was really only one choice.
So all through 2022 and into this year I spent a few hours every week writing a ZX Spectrum emulator in Swift for Mac OS. It was a from-the-ground-up attempt, so I wrote the Z80 emulation mostly from scratch (I did peek at a few other emulators for inspiration whenever I ran into things where multiple approaches looked promising).
But I wasn't really interested in a code-complete timing-perfect emulator, those already exist. I didn't want to spend weeks and months optimizing timings to match a real ZX Spectrum. I did want to learn how to write a Metal renderer... so my emulator uses a Metal renderer as it's screen output.
And that's when another idea popped up: What if the ZX Basic could be used as a simple scripting language to play with the renderer?
The result - and at this point it is a very open-ended result - is BitPrism.
It's a ZX Spectrum emulator that runs the original ROM and can load and run many games in SNA and TZX format. It runs too fast, approximately like a ZX Spectrum at 5Mhz instead of the original 3.5Mhz, which actually makes many games more pleasant to play.
Another big limitation is that there is no sound, since I couldn't be bothered to make that work with the wonky timing on my Z80 emulation.
And speaking of the Z80, I did not implement some of the undocumented opcodes, so some games will crash. But the majority of the games work and the Spectrum ROM is happy.
What makes this emulator different is that the ZX Basic has access to the Apple Metal renderer that runs the screen. Through several IO ports, the emulator can move the camera, change the shader settings, insert simple 3D objects, move them or set vectors on them to have the renderer animate them. Essentially, ZX Basic learned a bunch of 21st-century tricks!
What does this look like?
These examples are all still very preliminary, and there are some hard limits to what interpreted ZX Basic at 5MHz can do. But this turned out to be a great way to learn how to write a Metal renderer, since BitPrism allows me to simply test any changes to the renderer and shader in Basic. Can't beat the turnaround time between tests.