No posts yet. Check back soon!
ZX Spectrum: Comparing Notes With an Old Hand
07:30 PM · Aug 08, 2026
I didn’t have a great deal of experience with the ZX Spectrum when I first created my Platform Guide for it. After this little tour, I’ve gotten my hands much dirtier, seen more capabilities, and worked around more limitations. I have, at this point, earned the right to a few opinions of my own about the system. That, in turn, gives me a reason to re-read Jonathan Cauldwell’s book How to Write Spectrum Games with a more informed eye. Cauldwell has significantly more experience with the platform than I do, with multiple releases even during the heyday of the system, but he is also targeting an audience that has just learned Z80 assembly language and is otherwise unfamiliar with both the Spectrum as a platform and game design as a discipline. I’m a little off to the side of the core target audience here, I think, but not so far off that it’s irrelevant to my experience.
I’m basing this article on the 1.1 release of the book on his itch.io page. If you are interested in a guide that gets you, personally, up to runnable game code as quickly as possible, I heartily recommend it. I’m saying that up front because most of the text of this article is about the parts where I either disagree with its choices or feel that I can bring in useful supplements to its techniques and explanations. So, here above the fold: I like this book and I think it is both good and useful.
General Comments
The book is well-divided into chapters by topic while also pushing as rapidly as possible towards a playable game with full source. I will be working through my reactions a chapter at a time here, but before I begin I have some observations that apply to the whole book.
- The code here prefers to use decimal everywhere instead of hex. There’s nothing wrong with this, and for some reason I feel like Z80 code invites mixing hex and decimal more than 6502 code does, but it is a very marked deviation from my own style. I suppose it ends up being more natural for someone who started in BASIC.
- The sample code and explanations in this book lean on the system ROM as hard as they can get away with. I often prefer to peel a layer or two down to get closer to the metal, but that’s a consequence of the slightly different nature of my focus—this book’s willingness to defer to code that doesn’t need to be loaded from a tape is one of its greatest virtues.
- If you are following along with the book and also have Skoolkid’s ROM disassembly handy, be aware that it includes a switch to put everything in decimal for you and it will probably be easier to follow along with the book that way.
- As we might now expect after our own adventures, much of the book’s content is about wrangling the bitmap display. Cauldwell prefers (row, column) coordinates over (X, Y) ones, which is fine (it’s what PRINT AT uses, after all, even when we’re invoking it through console control codes), but he also consistently refers to rows as “X” and columns as “Y”, which is not OK at all. He actually feels the need to explicitly apologize for this in the text—I suppose the habits of 30 years are hard to break.
The Chapters
The book as a whole has 19 chapters, not all of which are about the hardware.
- Chapter 1 introduces the
RST $10instruction and covers most of what I did in the text mode tour. It also includes ROM routines for printing out numbers automatically and for setting the border color through the ROM instead of via direct system control. - Chapter 2 covers keyboard and joystick I/O. There’s nothing here not also in the text mode tour, and overall I’d probably stick with it or the NoCash docs.
- Chapter 3 covers the basics of commanding the 1-bit speaker built into every Spectrum system, both directly and through the system ROM. This mostly tracks my sound article‘s ROM-based beeper approach, though it does also suggest blitting the system ROM through the audio port for a white noise or crash sound effect.
- Chapter 4 covers random number generation, which it glosses by just stepping through the system ROM and using what it finds there. While this is, more or less, the same system that Pac-Man used, I dislike it for the reasons described in this video. It is fast, at least, but I’ve been using Xorshift for a decade and I’ll still stand by that for when you need randomness that matters.
- Chapter 5 describes techniques for exploiting the color attribute table for high-speed graphics updates and collision detection. There are two core techniques here: an object disappears if you make its color cell have the same foreground and background color, and you can do very rapid graphics tests if you encode game-relevant information in the color. I haven’t really leaned on this technique myself, but it’s a good one and it’s not restricted to the Spectrum.
- Chapter 6 is all about index registers! I covered that in great detail and this mostly talks about the case of iterating through tables.
- Chapter 7 covers collision detection more carefully. There are some fun techniques here I’ll break out into their own section.
- Chapter 8 covers sprite rendering and screen address computation. The code is not identical but it covers basically the same ground as my own sprite renderer. One clear difference between my code and his was that I relied on full-copy modes to simultaneously erase and redraw my sprites in one go, while his more general design used XOR as a compositing operator. Some of the discussion here also suggests that the shadow registers are available for our use here on the Spectrum, which would dramatically enhance a lot of code. He also suggests creating a lookup table for Y coordinate addresses on the screen, a technique I used to great effect on the Commodore 64.
- Chapter 9 covers the technique I used in my sprite preprocessing article to rapidly copy graphics into a cell-aligned block. The context here is background graphics, but it’s pretty generic, as we saw ourselves.
- Chapter 10 covers using strings as multiprecision integers, which is unexciting enough that I’ve done it in passing in multiple projects, including in the ZX Spectrum shooting gallery itself. It’s important to have that in a primer like this, though!
- Chapter 11 is entirely about game logic, and in particular making enemies patrol sensibly off of path-defining data tables. It’s a bit out of my scope here.
- Chapter 12 covers basic timing: the
HALTinstruction and theFRAMESsystem variable. I’ve been leaning on this one since I first wrote Lights-Out for the ZX81. - Chapter 13 makes a significantly more thorough attack on the problem of sprite flicker, which I will give its own section below. In this list, though, I will note that we do see the
EXXinstruction in use here, but only with interrupts locked out. That’s enough to convince me that the interrupt handler in the system ROM does not persist values in the shadow registers. - Chapter 14 is more game mechanics, discussing systems like jumping in a platformer or inertial movement. This is again beyond my scope here, though I will note that the gravity-table approach he uses here was the star of the show in this video about the extremely hastily-put-together nature of the NES game Strider. It also offers a brief aside on “fractional coordinates,” which I wrote an entire article on since fixed-point math turns out to be an extremely general and underappreciated technique.
- Chapter 15 is routines for multiplication, division, and square roots, with some discussion of using trigonometry to aim turrets. I generally defer to other people’s routines for this stuff and this is as good a place as any to defer to.
- Chapter 16 discusses the programming of the AY-3-8912 sound chip that ships standard with the Spectrum 128, and introduces the concept of a music driver. The shooting gallery didn’t have sound but we’ve written a few music drivers in our day, including one for the AY-3 itself.
- Chapter 17 talks about how to write custom interrupt handlers, and, at least to my eyes, basically is an argument for not using them. I don’t see much here that you can’t do nearly as well by simply
HALTing once and making sure thatFRAMESchanged appropriately. - Chapter 18 is about making games automatically run when loaded. My SpectraLink utility is a more clever version of this (it allows a clean exit to BASIC and lets you re-run the program as many times as you want), but this chapter does introduce a fun BASIC feature where you can load a file as
SCREEN$and it treats it like a screenshot. - Chapter 19 ends the book by stepping back from the technical stuff and meditating a bit on game design itself.
Clever Hitboxes
Chapter 7 was all about collision detection, at various scales. The core technique offered is very like the SkipDraw-like technique that I used to handle partially offscreen sprites in the sprite renderer. It did, however, add one fun new technique.
Generally speaking, when I’ve had to make my own hitboxes in a 2D space, I’ve used either axis-aligned bounding boxes (the basic rectangle intersection stuff) or circular ones (compare the square of the distance between their centers to the square of the radius of the hitbox). Circular hitboxes aren’t really feasible in Z80 assembly language since multiplications are really expensive. But we can get closer than a full axis-aligned box.
The key insight here is that if you compare the sum of the unsquared deltas on the two objects’ X and Y coordinates, the resulting hitbox is shaped like a diamond. We can grow or shrink the diamond by altering our threshold. By picking a threshold slightly larger than half the dimension of our object, we will get a diamond that extends beyond the edges of the axis-aligned hitbox but does not completely consume it. Testing against both of these shapes at once gives us an octagonal hitbox which will be very close in practice to a circular one if our sprites are small—and 16×16 is certainly not large.
I like it. I think it might even be reasonable to use in modern restricted environments like PICO-8.
Double Buffering
While most of the topics in the book are pretty closely related by topic or complexity, one theme that keeps appearing over and over is dealing with the fact that the display you want isn’t going to be drawable in the time you have each frame. The earlier chapters offer some simple techniques that will still be effective if you can get away with them:
- Do everything with cell-aligned graphics so the graphics copies are faster.
- Animate via color cells instead of the bitmap itself, so you don’t have to do graphics copies at all.
- Erase the previous frame’s version of a sprite while you are drawing the current one. This minimizes the visible glitching. (This is, effectively, what we did in the shooting gallery.)
Chapter 13 starts pushing into more aggressive approaches. The general theme overall is that actual graphic work should be done offscreen, and only brought into the visible graphic space once it is done. It mentions a few approaches in passing, saying one only works on the 128K Spectrum and dismissing techniques based on LDIR as too slow. I actually got away with LDIR myself, but I was only erasing 64 bytes when I used it, as part of the incremental-update strategy.
The capstone project provided here involves using chains of PUSH and POP instructions with a very heavily misplaced stack pointer to achieve the highest possible memory copy throughput on the Z80. I have every reason to believe that this gets excellent results—it closely matches the technique used on the Apple IIgs by Brutal Deluxe’s MrSprite system. The main thing that the Apple’s 65816 offers here that the Z80 does not is the ability to directly push 16-bit constants, which let MrSprite generate specific code sequences to draw a sprite at a particular location as rapidly as possible. The Z80 must resort to using it as an extremely fast 28-byte copy.
Cauldwell does not discuss it, but I do wonder if the “dedicated display list” idea I outlined and discarded when designing my own sprite engine would benefit from an offline screen like this; instead of trying to build up a series of edits to run during the frame, instead do the work offscreen and then queue up a list of character cells that must be updated this frame and work through those with character-cell block copies. It’s such a different approach to the other systems he discusses that it’s not easy to see an immediate comparison point.
He does, however, briefly discuss a technique adjacent to an entirely different kind of display list; he mentions in passing that one of his games that did smooth vertical scrolling did this by starting the screen copy from a slightly different location in his offscreen buffer each frame. This is how I accomplished the partial screen scrolling in the Atari 800 port of Simulated Evolution—the ANTIC’s display list takes arbitrary pointers for screen memory and so the technique is basically identical.
Graphical Swap Chains on the 128K Spectrum
Cauldwell dedicates maybe one sentence to this technique in the book, noting that it exists and lets you effectively have two independent graphics screens, but that it only works on the 128K and thus would get no further explanation. I went to Korth’s documentation to see if I could deduce the technique from that. I’m pretty sure I succeeded.
That said, one other thing that came up from this is that apparently the incompatible Timex-branded foreign-market quasi-clones just had two graphics screens in the first place, Apple II style, and let you select which one was visible. That’s clearly easy enough to make work, but it does not help us when dealing with the 128K or any of the later Amstrad editions.
The story, I think, is told by the memory map section of the documents. These later Spectrums had 128KB of RAM divided into 8 16KB pages, as well as multiple ROMs that could be swapped in and out of the bottom region. I imagine that the ROM-swapping is what allows the system to transform itself into a 48KB Spectrum when requested, and the rest of the RAM can swap in and out to serve as the RAM drive or to store the variables needed to run the BASIC editor or main menu without interfering with the BASIC program’s own space. We do not have a completely free hand in laying out the ROM, but we do see that pages 5 and 7 are labeled as including the “first VRAM block” and the “second VRAM block”. The section on the ULA ports describes using port $7FFD to map either to the $4000–$7FFF range. This gives us our two graphical screens, though with the caveat that we will need to be maintaining two copies of the system variables alongside them. This capability does seem like it would be enough to do everything we wanted, though; we just need to swap between the two pages every frame and potentially copy over the system variables as well. Our actual rendering code will need to remain fixed in the $8000–$BFFF range.
The 128K Spectrum system code itself seems to stick to page 5 at all times for video, but it swaps in page 7 when the full-screen BASIC editor is running, and it’s remapping memory pretty aggressively. That’s the last piece of the initial tour we’ve left unvisited, so that’s the next place we’ll go.