SkoolKit

Spectrum game disassembly toolkit

Tracing with a screen near you

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

With the 9.x series well under way, and full 128K support (9.0 and 9.1), RZX support (9.2) and PZX support (9.3) now firmly entrenched, 9.4 quite reasonably steps down a gear and modestly introduces some minor enhancements to the Skoolkit commands and skool macros that we have come to know and love.

First, the --screen option of trace.py. As its name suggests, it displays the contents of the Spectrum’s screen while code is executing (so long as you have pygame installed). This can help if you want to produce a snapshot at a particular point that’s easier to judge by eye than by, say, the number of instructions executed (--max-operations) or the number of T-states elapsed (--max-tstates).

By default, the screen is refreshed at a rate of 50 frames per second (i.e. normal Spectrum speed), but that can be changed by setting the ScreenFps configuration parameter. A value of 0 sets trace.py (and screen updates) running at maximum speed. Also by default, the screen is rendered with a scale factor of 2 (i.e. 512x384 pixels), but that can also be changed by setting the ScreenScale configuration parameter.

In addition to writing a snapshot file or WAV file after code execution has completed, trace.py can now write a PNG image file of the screen contents. Or, indeed, all three at the same time, now that multiple output file arguments are allowed. By default, the image scale factor is 2 (i.e. 512x384 pixels again), but that can be changed by setting the PNGScale configuration parameter.

In other command news, skool2bin.py can now read configuration from skoolkit.ini, and can also pad its output with zeroes on the left by using the PadLeft configuration parameter, or (perhaps more usefully) on the right by using the PadRight configuration parameter. So if PadRight is set to 65536, you can (for example) use bin2sna.py on the output of skool2bin.py without ever having to specify the origin address.

Turning now to skool macros, this release bestows upon #FOREACH support for a new special variable: POKEname. This expands to a list of the POKEs made by the #POKES macro on the named snapshot created by the #PUSHS macro. For example, if the udgfix snapshot has three POKEs:

#PUSHSudgfix #POKES30000,1;30001,2;30002,3 #UDG30000 #POPS

then:

#FOREACH(POKEudgfix)(p,p,: )

would expand to:

POKE 30000,1: POKE 30001,2: POKE 30002,3

Individual POKEs and subsequences of POKEs on a snapshot can be specified by using Python’s square brackets notation for indexing ([i]) and slicing ([start:stop]) a list. For example, POKEudgfix[0] yields the first POKE, and POKEudgfix[1:] yields every POKE but the first.

There are more new features to discover, but for details on those and the bug fixes in SkoolKit 9.4, head over to the changelog. When you’re finished there, get a copy of 9.4 and start rendering POKE lists with absolute precision.

Let's talk about PZX

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

As if introducing support for RZX files in SkoolKit 9.2 wasn’t enough, 9.3 continues on the new file format bandwagon by introducing support for PZX files. In case you’re not aware, PZX is a tape file format that’s much simpler than TZX, and leaves no room for ambiguity about where one pulse ends and the next begins. Which is quite important for a computer whose tape-loading routines are all about those ‘edges’ between pulses (and in some cases about whether those pulses are high or low, another topic on which PZX is unambiguous).

So now that you are aware, you will no doubt want to convert your entire collection of TAP and TZX files to PZX format. And you can do that safe in the knowledge that SkoolKit 9.3 has you covered: tapinfo.py can show information on the blocks in a PZX file, and tap2sna.py can LOAD that same PZX file. On top of that, bin2tap.py can now write PZX files too.

In other news, sna2skool.py has finally gained the ability to disassemble some of the more obscure ‘undocumented’ instructions in the Z80 assembly language, such as ‘IN F,(C)’, ‘OUT (C),0’ and ‘RLC (IX+d),B’. It can also now disassemble ED6B0000 to ‘LD HL,(0)’ if you want it to, along with a batch of other variant opcode sequences, such as ED4E to ‘IM 0’, or ED4C to ‘NEG’. This new feature is disabled by default, though, for good reasons. One is that your assembler might not recognise some of these instructions, which will be a problem if you want to run it on the output of skool2asm.py. Another is that your assembler probably won’t assemble instructions with variant opcode sequences back to the original byte values.

But if you want to enable this feature despite these dangers, you can set the Opcodes configuration parameter for sna2skool.py to one or more of the following values in a comma-separated list:

  • ED63 - LD (nn),HL
  • ED6B - LD HL,(nn)
  • ED70 - IN F,(C)
  • ED71 - OUT (C),0
  • IM - IM 0/1/2 variants
  • NEG - NEG variants
  • RETN - RETN variants
  • XYCB - RLC (IX+d),B etc.
  • ALL - all of the above

One other potential danger with instructions that have variant opcode sequences is that skool2asm.py and skool2html.py won’t assemble them back to their original byte values, thus creating a discrepancy between the internal memory snapshot derived from the skool file and the snapshot that was used to create the disassembly. To work around this, the new @bytes directive can be used to remove any ambiguity. Whenever sna2skool.py encounters an instruction with a variant opcode sequence, it will automatically insert a @bytes directive. For example:

@bytes=$ED,$4E
 $8000 NEG     ; This assembles to ED4E (not ED44)

On a completely different note (pun intended), trace.py can now write a WAV file after code execution has completed. This means, for example, that you could create a WAV file of the Fairlight theme tune like this:

$ tap2sna.py https://worldofspectrum.net/pub/sinclair/games/f/Fairlight48V1.tzx.zip fairlight.z80
$ trace.py --stop 49217 fairlight.z80 fairlight.wav

And that’s just some of the news. Information on the the other new features and bug fixes in SkoolKit 9.3 can be found in the changelog. After you’re done reading that, grab a copy of 9.3 and unleash it on your new collection of PZX files.

Let's talk about RZX

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

Back in SkoolKit 3.3, I added the -M option to sna2skool.py, which could be used to read a code execution map produced by an emulator, and thus generate a more accurate control file than is possible by default. That -M option on sna2skool.py later became the -m option on sna2ctl.py, which is still there to this day. That’s all well and good, but one thing that has bugged me all this time is the need to use an external tool (external to SkoolKit, that is) to produce the code execution map. Which is why I’m pleased to announce, 11 years later, that SkoolKit 9.2 includes its very own map-producing tool: rzxplay.py.

As the name suggests, rzxplay.py plays RZX files. It can draw a screen if you want to watch the playback, but this is not its intended purpose. Instead, I would expect the dedicated reverse engineer to use the --map option (in conjunction with the --no-screen option, to maximise playback speed), and feed the resultant code execution map file to sna2ctl.py. Naturally, in this release sna2ctl.py has gained the ability to read map files produced by rzxplay.py.

In the initial stages of development, rzxplay.py could only play RZX files that were recorded in plain 48K or 128K mode. However, there are many RZX files out there that, for reasons unknown, were recorded in +2 mode or above (even for 16K Spectrum games). So, in order to increase compatibility with existing RZX files, I added support for +2 mode to rzxplay.py. As a consequence, trace.py gains the ability to execute code in +2 snapshots for free, as it were. You are welcome.

Barging in alongside rzxplay.py in this release is its sibling, rzxinfo.py. This tool can be used to show the contents of an RZX file (down to individual frames), and extract the snapshots from an RZX file. In the past I’ve made the mistake of adding support to SkoolKit for a particular file format without providing a tool to dump the contents of such files. (For example, tap2sna.py arrived in SkoolKit 3.5, but tapinfo.py had to wait until 5.0.) Not this time, though, thank goodness.

One thing that became clear during the development of rzxplay.py is that, for RZX files more than a few minutes long, producing a map file is a rather time-consuming process. In other (less kind) words, SkoolKit’s pure Python Z80 simulator is, frankly, way too slow. To address this, SkoolKit 9.2 includes a new Z80 simulator implemented in C, which is quite a bit faster than its predecessor. But rzxplay.py is not the only beneficiary of this speed boost: tap2sna.py, trace.py, #AUDIO, #SIM and #TSTATES will also use the new simulator if it’s available.

So for old times’ sake, let’s look at the improvement in speed the new Z80 simulator brings. Tradition requires that I include loading the Skool Daze TZX in such comparisons, so I will do that. I’ll also look at running Patrik Rak’s z80doc test suite, and using #AUDIO to generate a WAV file for the Fairlight theme tune. These are the relevant timings on my PC:

  • 19.6s (Python) v. 1.3s (C) - tap2sna.py skooldaze.tzx
  • 54.4s (Python) v. 1.2s (C) - z80doc tests
  • 12.0s (Python) v. 1.9s (C) - #AUDIO12(fairlight.wav)(49164,49217)

And that’s all the news. More information on the the new features and bug fixes in SkoolKit 9.2 can be found in the changelog. After you’re finished there, grab a copy of 9.2 and a few RZX files, and start cranking out some code execution maps.