Spectrum game disassembly toolkit

LOADs more simulation

SkoolKit 8.9 has been released. To get a copy, please head over to the download page, the Python Package Index, or GitHub.

I am aware that Z80 instruction set simulation has been a recurring theme here lately, so let me apologise up front to readers who have no interest in the topic that this article continues - out of necessity - with the simulation motif. Perhaps it will have faded by the time 8.10 arrives; we shall see.

So with that apology out of the way, let’s get started. First, the good old --sim-load option of Not content with the fourfold increase in its performance from 8.7 to 8.8, I have added support for accelerating the tape-sampling loop of the most common loading routines out there: Speedlock, BleepLoad, Alkatraz, to name but three. To give an idea of the improvement in simulated LOAD times that this feature brings, here are some examples showing the performance of 8.8 and 8.9 on my computer using Python 3.11:

  • 0m25s (8.8) v. 0m16s (8.9) - Fairlight (Alkatraz)
  • 0m49s (8.8) v. 0m12s (8.9) - Black Lamp (BleepLoad)
  • 0m35s (8.8) v. 0m08s (8.9) - Satan (Dinaload)
  • 0m35s (8.8) v. 0m17s (8.9) - Skool Daze (Microsphere loader) in 8.9 also comes with improved support for TZX files, specifically for loops, pauses, and unused bits in data blocks. This along with the new --tape-start and --tape-stop options (for snipping redundant parts off each end of a tape) means that --sim-load now works with more games than ever before. In fact, in a recent test of over 6800 TAP and TZX files that the Fuse emulator is able to LOAD correctly, --sim-load produced a working snapshot from all but three of them:

  • 1999 - the TZX file fails to LOAD, but the TAP file LOADs successfully; interestingly, I have a copy of an older TZX file named ‘1999 (Alternative).tzx’ that has different pulse timings and LOADs perfectly
  • Blood Brothers - the TZX files for the Spanish releases of this game fail to LOAD, but the TZX file for the Gremlin Graphics release LOADs successfully
  • Gold Mine - this one fails because the loader depends on timing-accurate simulation of code running in contended memory, which is not supported at the moment (and may never be)

In other simulation-related news, now sports an --interrupts option, which switches on the execution of interrupt routines. This new feature enables us to answer such important questions as: What does the screen look like after Skool Daze (whose main loop depends on interrupts being enabled) has loaded and 10 million instructions have been executed without any keyboard input?

$ --sim-load --start 24288 skooldaze.tzx sd.z80
$ -i --max-operations 10000000 --dump sd-10m.z80 sd.z80
$ -s 2 sd-10m.z80 sd-10m.png

So now we know. And now you know all the good stuff about SkoolKit 8.9. There were a few other changes and several bug fixes since 8.8 that are not detailed here, but if you wish you can consult the changelog for information on those. Let me keep you no longer from downloading a copy of 8.9 and converting your collection of 48K TAP/TZX files into pristine snapshots.


SkoolKit 8.8 has been released. To get a copy, please head over to the download page, the Python Package Index, the Ubuntu PPA, or the Fedora copr repo.

Arriving hot on the heels of SkoolKit 8.7, 8.8 is a minor update with just a few (but important) changes, chief among them being an improvement in the performance of the Z80 instruction set simulator. (Yes, strap in for some more simulator-related discussion.)

Now, the simulator’s performance (i.e. speed) does depend to some extent on what the code it is simulating actually does, so allow me to give some concrete examples comparing 8.7 and 8.8 to illustrate the improvement. First, everyone’s favourite (OK, perhaps just my favourite) application of the simulator: --sim-load (in this case operating on a TZX file for Skool Daze). Second: running the z80doc tests in Patrik Rak’s z80test suite. And third: expanding an #AUDIO macro to generate a WAV file of the Manic Miner theme tune (which runs to 20.25s). Here are the numbers showing the performance of 8.7 and 8.8 on my PC using Python 3.11:

  • 2m20s (8.7) v. 0m35s (8.8) - --sim-load skooldaze.tzx sd.z80
  • 2m42s (8.7) v. 0m56s (8.8) - z80doc tests
  • 7.56s (8.7) v. 2.98s (8.8) - #AUDIO4(blue-danube.wav)(34351,34358)

Still on the subject of --sim-load, that option now also performs any call, move, poke and sysvars operations specified by the --ram option, in case you want to modify the pristine memory contents - no judgement! - before writing the Z80 snapshot.

Naturally, this improvement in simulator performance also means that the #SIM macro is now faster, and so too is the #TSTATES macro (when it uses the simulator to execute instructions, that is).

The final bit of simulator-related news is that 8.8 has a new command: I had originally planned to squeeze this into 8.7, but it didn’t quite make it into that already jam-packed release. Anyway, uses the simulator to run machine code in a 48K memory snapshot, optionally showing the instructions executed and register values along the way. It can also compute the delays between speaker flips, in case you ever wanted to manually supply the delays parameter to an #AUDIO macro (which might be desirable if the code in question takes a long time to simulate).

Speaking of the #AUDIO macro (again), or more specifically the [AudioWriter] section that controls WAV file creation: the MaxAmplitude parameter has been removed. Its default value was 65536, and roughly speaking it determined the ‘rectangularity’ of the waveform produced when converting delays into samples. But some experiments with low-frequency sounds made it clear that a better default value is ∞ (infinity), for maximum rectangularity and therefore similarity to the sound of a real Spectrum. Leaving MaxAmplitude around just so WAV files can be made to sound less Spectrum-like seems pointless, so out it goes.

And that’s all the news. At this point I would usually direct readers to the changelog for details on any changes not described here, but this time there are no changes not described here, so, well, don’t bother. Just fire up a copy of 8.8 and get simulating.

Simulation stimulation

SkoolKit 8.7 has been released. To get a copy, please head over to the download page, the Python Package Index, the Ubuntu PPA, or the Fedora copr repo.

The tradition in these release announcements is to start by listing the main new macros, commands, or parameters thereof that you can get your teeth sunk into. In this case, however, you may consider that tradition (slightly) bucked. The main bit of newness in version 8.7 is not a macro, or a command - or, indeed, a parameter thereof - but a Z80 instruction set simulator.

The question, then, is how this simulator has been applied to provide the new and exciting features you’ve come to expect from a SkoolKit release. Well, taking centre stage is the --sim-load option of It simulates a freshly booted 48K ZX Spectrum running LOAD "" (or LOAD ""CODE, if the first block on the tape is a ‘Bytes’ header). Which means it’s now much easier to convert a TAP/TZX file into a pristine snapshot. No more messing around with --ram load, --ram poke or any of the other --ram commands, and no more worrying about headerless blocks and turbo loaders. Give --sim-load a try and see how much cruft you can remove from your t2s file.

Next, taking stage right, is the #AUDIO macro. It converts a sequence of delays between changes in the Spectrum’s speaker state into a WAV file, and then presents it for playing via an HTML5 <audio> element. But how does the simulator fit into this picture? Good question. While you can provide the sequence of delays manually, a much easier option is to use the simulator to calculate them for you by running the code that produces the sound effect. For example, here’s how you might create an <audio> element that plays the theme tune for Manic Miner:


Now, taking stage left, is the #TSTATES macro. As its name suggests, it computes the number of T-states it takes to execute a single instruction or a sequence of instructions. Where the simulator really helps here is if the code you want to time is a loop, or contains conditional instructions with variable timing. For example:

; This code creates a delay of #TSTATES(32768,32776,4) T-states.
 32768 LD DE,0
*32771 DEC DE
 32772 LD A,D
 32773 OR E
 32774 JR NZ,32771

And finally in the simulator department, what if none of the applications described so far scratches your itch? In that case, perhaps the #SIM macro will help. Instead of executing Z80 machine code for the specific purpose of loading a tape, calculating speaker flip delays, or timing instructions, it just executes Z80 machine code and lets you do whatever you like with the result - that result being changes to register values (available as replacement fields in the sim dictionary) or changes to the contents of the internal memory snapshot constructed from the contents of the skool file. For example, you could create an image of the Manic Miner title screen like this:


As for non-simulator-related new features, well, there are some, but they’re just not as interesting. If you really want to know what they are, you can consult the changelog. After that, why not take 8.7 for a spin and see what the simulator can do for you?