TLDR
I dusted off my shelved C++ ZX Spectrum emulator project from two years ago. I had originally quit because implementing the hundreds of Z80 opcodes became a grind. To do this I switched from my usual JetBrains setup to Google’s Antigravity IDE and Gemini 3 Pro to see if AI could finish the job – spoiler yes.

The Successes: What previously took weeks was done in a single evening; the emulator was booting the BASIC ROM quickly. Antigravity’s agentic workflow (creating .md plans before coding) was transparent and collaborative, like coaching a junior engineer.
The Pains : Gemini struggled to refactor the codebase—duplicating code, deleting necessary files, and claiming false successes. Fixing a visual glitch in the game Jetpac required engineer intervention. The AI couldn’t “see” the bug; I had to manually find and feed it the original assembly code to identify an undocumented Z80 behaviour.
The Verdict: AI is a powerful accelerator that handles boilerplate and logic implementation effectively, but the “last mile” of complex debugging and architecture management still requires an experienced human hand. Link to GitHub Repo
The History
A couple of years ago, I fell down a technical rabbit hole. I decided to build a ZX Spectrum emulator in C++.
At the time this seemed like a fun challenge, and a break from my standard web dev home projects. Since I’ve spent plenty of time repairing retro hardware, I already had a good understanding of the internals of the Spectrum and the Z80 processor. I started the project by building Object-Orientated (OO) representations of core components: memory, graphics, and the processor. I even modelled the opcodes with precise t-state timings to match the real hardware.
With the basics in place, I started to implement opcodes in the order they are executed in the standard 48k Basic ROM – getting a little further through the start-up sequence with each fix applied.
It was a good start, then I realised what CISC really meant.
For those who don’t understand processor hardware, it’s a Complex Instruction Set Computer, meaning there are a lots of instructions to implement. In summary, the Z80 CPU in the ZX Spectrum has a large instruction set, with around 158 basic instruction types that expand to over 800 when considering variations, prefixes (like CB, DD, FD), and addressing modes.

Despite success working my way through the instructions my endurance was tested with the grind of implementing individual opcodes, and the project was shelved.
To give an indication of the scale of the challenge….here is Gemini’s assessment of the current project.
I've analyzed the source code to count the implemented opcodes.The emulator has comprehensive coverage of the Z80 instruction set, split across several modules:
Main Opcodes (Processor.cpp): 257 handlers.
129 explicit case handlers.
64 implicit handlers for 8-bit Loads (0x40-0x7F).
64 implicit handlers for ALU operations (0x80-0xBF).
Index Opcodes IX/IY (Processor_Index.cpp): 88 explicit handlers (covering offsets and index arithmetic).
Extended Opcodes (Processor_Extended.cpp): 51 explicit handlers (covering block moves LDIR, CPIR, etc.).
This covers effectively 100% of the documented legal opcodes used by commercial games.
The Revival: Christmas 2025
Jump forward to Christmas 2025. I had been playing with agentic coding agents on a number of home projects over the past few months and in a flash of nostalgia and inspiration, I decided to dust off the partially implemented emulator.
I thought it would be an interesting project to revive, using AI, for a number of reasons;
- C++: A nice change from my normal Typescript.
- Pure Logic: No UI frameworks or CRUD operations – just hardware emulation.
- Strict NFRs: The Spectrum is uses various timing quirks. Emulating the ‘floating bus’ and signal timings requires precision.
- Well documented: The Z80 and Spectrum are well documented in both formal documentation and forums, detailing all of the standard and undocumented ‘features’.
Plus, I’d just started playing with Google’s Antigravity IDE, and the newly released Gemini 3 Pro model, so wanted a project to test its capabilities.
Coding with Antigravity
My normal IDE is Jetbrains, WebStorm (Typescript) and CLion (C++), and I have been using Gemini with their AI assistant and Junie pair programming plug-ins, so I was interested in differences in workflow and the results.
Armed with Antigravity and the Gemini model, I dusted off the 2-year-old CLion project and started prompting.

My first prompt was to ask for a high-level approach to continue the project. I was was immediately impressed with iterative ‘thought process’ and the visibility: the agents showed their working thoughts following by their findings and a resulting solution/recommendation. They generated an implementation plan and a task list as .md files directly in the project folder. This allowed me to review the plan-of-action in detail and add comments directly before giving the green light to proceed.
The recommended way forward continued my approach iteratively:
- Implement a block of instructions.
- Build codebase and run tests
- Attempt to boot the default BASIC ROM.
- Identify missing opcodes.
- Repeat.
It also stuck to my original architecture (clean, OO-heavy, but assumed not performant) and within a single evening, the BASIC ROM was booting. It was slow, but it was running.

First impressions: This first stage went very well, and I was impressed with the progress in such a short time. I didn’t encounter any real issues while it was adding new code using the existing structure, i.e. lots of new opcodes with distinct functionality. It regularly introduced code that didn’t build, but identified this during the build cycle and fixed it as part of the current task before reporting completion.
My initial assumption was correct, and the performance was poor due to the OO design that I had started with (I knew this would be an issue). I prompted for a code review to find solutions to the performance issue – which it identified correctly, i.e. lots of function calls to find and execute each opcode, and made a number of suggestions from a tweek to complete refactor.
I continued with the recommended ‘tweek’, which improved things to a good level – but a proper fix would require significant refactoring.
This is where the problems started…..
Refactoring: The Cracks Begin
The emulator worked, but it was slower than I wanted, or expected. So, I needed to go ahead with the full refactor to reduce the number of function calls per execution cycle.
Gemini planned to migrate the opcodes in blocks of related instructions (Loads, then Arithmetic etc), which was the way I would have tackled it, so I gave it the go-ahead to start.
This is where the “magic” started to fade.
- Schrodinger’s Code: It often moved code without deleting the original, leaving duplicate, conflicting implementations still in use.
- The Black Hole: Opcodes vanished entirely during the migration.
- False Confidence: The model frequently declared a fix “successful” when the build or functionality was clearly broken.

Some lessons learned the hard way
- Branch, branch, branch: Branch frequently as a safety net before letting the model loose on your code.
- Micro-tasking: Break requests into smaller chunks.
- Trust, but verify: The AI behaves like an inexperienced eager engineer. It doesn’t know that it doesn’t know…. test and review what has been done incrementally.
It took a while, but we got there eventually. The BASIC ROM was booting again and it was fast (about 300x faster than an original Spectrum).
The Boss Level: Jetpac
Booting BASIC is one thing; running a game that relies on undocumented instructions and video refresh hacks is another.
I loaded up Jetpac (1983), which is both a classic and a personal favourite. It’s an early Spectrum game, so I assumed that it would only use ‘standard’ Z80 features – it’s also only a 16K game so less code. To my amazement, the menu loaded. I ambitiously entered the game…it was running, but the platforms were in the wrong positions, and the player sank through the floor.
My assumption was some kind of arithmetic error causing the drawing offset – it must be subtle though as the core of the game was working.
The screenshots below show how it should be on the left in Fuse, and the ‘alternative’ version on the right in my emulator.

A prompting challenge: How do you describe the graphical issue to the model when I didn’t really know specifically what was going wrong?
- I tried describing the visual glitch – platforms offset to the right and up?
- I tried uploading screenshots.
- Gemini guessed it was an arithmetic error.
- We then went in circles, reviewing and changing the same opcodes over and over, breaking the build multiple times.
The Solution: This required me to do some engineering. I found a disassembly of the Jetpac source code on GitHub, isolated the platform drawing routines, and pasted the assembler into the chat. This enabled the model to fix it on its first try.
The finally identified bug was due to an undocumented Z80 behaviour, details below for those that are interested.
The bug was related to Undocumented Z80 Behavior regarding Index Bit Instructions, specifically in src/spectrum/Processor_Index.cpp.The "Jetpac Platform Drawing" Bug
The Issue: Jetpac's code uses DD CB instructions (like RL (IX+d), SET b, (IX+d)) to manipulate data for drawing platforms.Standard Behavior: These instructions modify the value in memory at address IX + d.
Undocumented Behavior: On essentially all Z80 CPUs, the result of this operation is also copied into one of the main registers (B, C, D, E, H, L, or A), depending on the opcode's encoding.
Why it broke Jetpac: Jetpac relies on this "side effect" to update a register while modifying memory in a single instruction. Because our emulator originally only updated memory (the documented behavior), the register contained stale data. This caused the platform rendering calculations to fail, leading to missing or corrupted platforms.The Fix: I implemented the setUndocReg lambda in Processor_Index.cpp (lines 44-68) to verify the opcode bits and copy the result to the corresponding register, matching real hardware behavior.
The Back Catalogue
With Jetpac working, I moved on to the rest of the Spectrum back catalogue of games following the same methodology.
- Load game
- Check functionality, i.e. play the game
- Add missing opcodes encountered (debug output to console)
- Add missing features (joystick support, sound)
It was interesting to see which games introduced ‘undocumented’ features as I tested; and as I moved up through the years the ‘special features’ increased.
Work in Progress
I have implemented a few additional features including;
- Tape loading, including fast loading – still needs work
- .z80 file loading
- Snapshot saving and loading
- Mac file system integration
Along with formal release builds for Mac silicon and Linux on GitHub.
The Verdict
Resurrecting this project with Gemini 3 Pro and Antigravity was a very satisfying project. The velocity in the early stages was incredible—doing in one evening what took me weeks previously.
Antigravity’s approach to agent based development is slick and feels very much like coaching another engineer. Understand how they are going to tackle the problem and provide feedback, let them write the code, then review and test before accepting onto main.
It can still get stuck in a bug fix loop, continually applying and removing a specific ‘fix’ that doesn’t actually solved the identified problem.
I don’t know whether it’s anecdotal, but providing a well structured code skeleton for the agents to continue seemed to help and kept the code clean – but that could have just been the type of problem being addressed.
The “last mile” is still very much human territory. When the architecture gets messy or the bugs get visual, it’s difficult to prompt your way out of it. You need to understand the problem, codebase, find the context, and guide the AI.
I do wonder where junior engineers are going to gain the experience to know good code if agents are churning out the boiler-plate?
It’s not magic, but it’s a very powerful tool.
If you want to try it yourself the repo is here -> https://github.com/gpimblott/zxemulator