SkoolKit

Spectrum game disassembly toolkit

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.

Zone of contention

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

Continuing the theme of “more 128K support” that started in 9.0, this release does indeed bring more 128K support. Specifically, bin2sna.py can now create 128K snapshots, bin2tap.py can create 128K TAP files, skool2bin.py can create 128K binary files, snapmod.py can modify 128K snapshots, and skool files can create internal 128K memory snapshots.

I think most of that was self-explanatory, but perhaps the internal 128K memory snapshots in skool files need a little more explanation. The new @bank directive either specifies the RAM bank mapped to 49152 (0xC000), or loads the contents of another skool file into a 128K RAM bank - and converts the snapshot to 128K at the same time. When that happens, the 128K ROM is loaded as well. After that, how do you actually access all the extra memory? Enter the new #BANK macro, which switches the RAM bank that is mapped to 49152. Got a sprite in RAM bank 4 you’d like to create an image of? Do a quick #BANK4 and you’re ready to go.

One other thing about snapmod.py: not only can it now modify 128K snapshots, but it can also modify SZX snapshots. That brings it up to par with its friends bin2sna.py, tap2sna.py and trace.py, which gained the ability to write SZX snapshots in the previous release.

In other news, SkoolKit’s Z80 instruction set simulator has gained the ability to simulate memory and I/O contention. It’s much slower when this feature is enabled, but it can be useful for various reasons. For example, there are a few game tapes out there that require it in order to LOAD correctly (the original release of Gold Mine being the quintessential example), so tap2sna.py can certainly benefit from it. For another example, the sound created by routines running in contended memory can now be captured precisely by the #AUDIO macro (instead of being approximated in post-processing). And finally, the #TSTATES macro can now do its job accurately when presented with code that’s subject to contention.

On the subject of #AUDIO and #TSTATES, those two macros can also now simulate the execution of interrupt routines, for extra faithfulness to the behaviour of the original hardware. In the case of #AUDIO, this means its two post-processing options (approximating contention and interrupt delays) are now redundant when simulating code execution.

In case you were wondering, yes, of course the #SIM macro has not been left out when it comes to memory and I/O contention and interrupts. It also now has parameters for specifying the value of the simulator’s clock, the interrupt mode, and whether interrupts are enabled. In addition, if the stop parameter is omitted, all other parameters are evaluated, but no code is executed. This technique can be used to prepare the simulator with the required initial register values and hardware state for later use by #AUDIO or #TSTATES.

And that’s about it. Anyone wanting more information on the the new features and bug fixes in SkoolKit 9.1 should pop over to the changelog. After that, go and get your copy of 9.1 and enjoy contending memory and switching RAM banks until the cows come home.

128K ought to be enough for anyone

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

Yes, the 8.x series has come to an end. It had a good four-year run, but now’s the time for it to step aside and let 9.0 in. As you might expect from an N.0 release, there are some compatibility-breaking changes, which you can read about in the migration guide. But unless you were particularly fond of the #DEFINE macro (deprecated since 8.5), there’s not much to worry about. Most of the other breaking changes are in tap2sna.py and trace.py, but I hope you’ll agree they’re all changes for the better.

The main new feature in this release is support for the (original) 128K Spectrum in tap2sna.py and trace.py. That is, tap2sna.py can load 128K games from tape, and save 128K snapshots. And trace.py can then execute code in those 128K snapshots (or any other 128K snapshots you care to throw at it). The t2sfiles repository (a collection of ready-made tap2sna.py argument files) has already celebrated this new capability by including over 1400 recipes for 128K games, which now accompany the more than 11000 recipes for 48K games.

The next most important feature in this release is support for a ‘phantom typist’ in tap2sna.py. Now, the right-minded among you will surely agree that there are few things more annoying in this world than a tape that requires something other than LOAD "" (or LOAD ""CODE) in order to LOAD and RUN correctly. Unfortunately for us, quite a few such tapes exist out there, but the phantom typist is here to help: it can enter a custom command line before starting the tape. For example:

$ tap2sna.py -c 'load=CLEAR 35000: LOAD ""' Tridex.tzx

And perhaps the third most important feature in this release is support for writing SZX snapshots, which has been added to bin2sna.py, tap2sna.py and trace.py. Until this release, SkoolKit’s one and only output snapshot format of choice was Z80, because it’s well supported by other software and adequate for most purposes. But tap2sna.py occasionally runs into a deficiency in the Z80 format: it has no slot for the last OUT to port 0xFE. This value actually matters for some games that have poorly written keyboard-reading routines.

And there I shall stop, and advise any readers who want more information on the the new stuff in SkoolKit 9.0 to consult the changelog. After that, go and grab a copy of 9.0 and enjoy the 128K’s worth of goodness within.