SkoolKit

Spectrum game disassembly toolkit

Offering a few MEMPTRs

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

Before we get into the meat of what’s new in 10.0, a word of warning. As is often the case with an N.0 release, there are some compatibility-breaking changes, which you can read about in the migration guide. In particular, the #UDGARRAY* macro is no more, the syntax of the #AUDIO macro has had a complete overhaul, and the #CALL, #FONT, #LINK and #UDGARRAY macros no longer support the syntax that was deprecated in 9.x. But never fear - in keeping with tradition, there is a migration script (skoolkit9to10.py) to help you get your skool files, control files and ref files ready for 10.0.

When support for simulating code execution on a 128K Spectrum was added back in SkoolKit 9.1, you might have been forgiven for thinking that it would also gain the ability to convert AY audio into a WAV file. However, you would have thought wrong. A couple of years later, though - which, as the saying goes, is better than never - SkoolKit 10.0 steps in to provide that missing functionality. So, for example, you could capture the AY speech at the beginning of Robocop with:

$ tap2sna.py -c machine=128 "Robocop - Side A.tzx" robocop.z80
$ trace.py --ay --stop 39978 robocop.z80 robocop.wav

Of course, the #AUDIO macro can now capture AY audio as well by setting its brand new ay parameter to 1. And for those cases where you want to capture both AY and beeper audio at the same time, you can use the --ay and --beeper options of trace.py, or set the ay and bpr parameters of the #AUDIO macro to 1.

In other simulator-related news, fans of MEMPTR (or WZ, as the case may be) can rejoice in the fact that 10.0 brings full support for this often overlooked internal register pair. It is simulated whenever memory and I/O contention are enabled, i.e. when the --cmio option of trace.py is specified, and when the cmio parameter of the #AUDIO, #SIM or #TSTATES macro is set to 1. In addition, snapinfo.py now shows the value of MEMPTR in SZX snapshots, and bin2sna.py, snapmod.py, tap2sna.py and trace.py can all set the value of MEMPTR. tap2sna.py and trace.py can also log the value of MEMPTR via the r[memptr] replacement field. So what does all of this mean in practice? Well, you could, for example, now use trace.py to play MEMPTR Snake properly. The possibilities are truly endless.

Speaking of using trace.py to play games, that experience - still not recommended, by the way - should be slightly less awful in 10.0 thanks to the border area now being included in the display. It won’t show multi-colour border effects (at least not yet), but the effect of entering ‘BORDER 0’, for example, being distinguishable from nothing counts as progress in my book. Obviously rzxplay.py benefits from this new feature as well.

Remember the comment generator introduced in SkoolKit 9.5? No need to apologise if you don’t. Anyway, it has been updated to handle some keyboard-reading instructions a little more intelligently. Specifically, the comment for ‘IN r,(C)’ immediately after ‘LD BC,$XXFE’, along with the comment for ‘IN A,($FE)’ immediately after ‘LD A,$XX’, now indicates the half-row of the keyboard being read when eaxctly one bit of XX is reset. Which should make it easier to spot that all-important keyboard-reading code in a freshly made skool file.

And that’s about it for the main new features in this release. For details of the non-main new features, visit the changelog. After that, get a copy of 10.0 and test your mental MEMPTR-tracing skills against trace.py running on your favourite game.

Two sides of the same tape

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

Although tap2sna.py has recently transformed into something that’s actually easy to use (thanks to the introduction of a Z80 instruction set simulator in SkoolKit 8.7), it still lacks a couple of abilities that would make it even more useful: loading from two tape files (as when side 1 and side 2 are in separate files), and simulating keypresses while a tape is loading. Until now, that is.

If you do happen to be trying to load one of those annoying games that require both sides of a tape that are in two different files, you can now do the natural thing and supply each filename as an argument:

$ tap2sna.py side1.pzx side2.pzx

And if you happen to be trying to load one of those even more annoying games that require you to stop the tape for no good reason at some point in the middle of a load, and then press a key and start the tape again to continue loading, the new --press option is there to help. For example:

$ tap2sna.py --press 5:ENTER more-annoying-game.tap

This will stop the tape when block 5 is reached, simulate pressing the ENTER key until the row containing that key has been read, and then resume playing the tape. If more than one keypress is required before the tape can resume playing, don’t worry: --press can simulate as many as are needed.

And as a bonus, if you happen to be trying to load one of those annoyingest games of all that require you to stop the tape at some point and do some fast-forwarding before continuing the load - yes, such games, though rare, do exist - then the new --tape-skip option comes to the rescue. It can fast forward over one or more blocks like this:

$ tap2sna.py --tape-skip 4-5 annoyingest-game.tzx

In other tape-related news, 12 new tape-sampling loop accelerators have been added, and tap2sna.py can now accelerate both types of ‘DEC A’ delay loop (the more common one that ends with ‘JR NZ’, and the less common one that ends with ‘JP NZ’) at the same time, instead of just one or the other exclusively.

But if all of that fails to capture your interest, perhaps SkoolKit 9.6’s new component - the RST handler - can fail to capture it even more. As its name suggests, it handles RST instructions, which means it can alert its consumer - sna2ctl.py or sna2skool.py - that a byte or word argument follows. To activate the RST handler, use the new --handle-rst option:

$ sna2ctl.py --handle-rst --org 0 some-rom.bin

By default, the stock RST handler recognises the byte argument of ‘RST $08’ instructions, and nothing else - which is useful (if at all) only for the 48K ROM. But it can be configured to recognise the byte or word argument of any RST instruction by setting the RSTHandlerConfig configuration parameter in the [skoolkit] section of skoolkit.ini, in case you’re interested in disassembling some other ROM.

When used with sna2ctl.py, the RST handler inserts B (byte) and W (word) sub-block directives as appropriate immediately after the RST instructions of interest. But if you want to bypass the control file stage and go straight to disassembling a ROM image, you can activate the RST handler thus:

$ sna2skool.py --handle-rst --org 0 some-rom.bin

When used with sna2skool.py, the RST handler inserts DEFB and DEFW statements as appropriate immediately after the RST instructions of interest. Note that the RST handler only needs to be invoked once on any ROM image or snapshot; if you use it with sna2ctl.py to generate a control file, and then again with sna2skool.py on that same control file, confusion will result - both for sna2skool.py and for you. You have been warned.

For details of the other new features that have nothing to do with loading tapes or recognising the arguments of RST instructions, visit the changelog. Once you’re done there, grab a copy of 9.6 and see how much quicker tap2sna.py is now at loading Basil or Lone Wolf.

No instruction left uncommented

On the 15th anniversary of the release of SkoolKit 1.0, SkoolKit 9.5 has been released. To get a copy, please head over to the download page, the Python Package Index, or GitHub.

When it comes to annotations, SkoolKit has always stayed out of the way and allowed the reverse engineer (that’s you) free rein to decide where to place comments in the skool file and how to word them. Which is how it should be, because only the reverse engineer (that’s you again) really understands what the code is doing and how to convey that information to a reader of the disassembly. However, if you’ve ever, when starting a new disassembly project, been filled with dread by the daunting sight of a completely bare skool file, then SkoolKit 9.5 is here to help (a little). New in this release is a comment generator component, which can be enabled by using the --comments (or -C) option of sna2ctl.py or sna2skool.py. For example:

$ sna2ctl.py -C game.z80 > game.ctl

Now take a look at game.ctl and revel in the ready-made comments that describe what each individual instruction is doing. What if you’ve already started on a control file, for which sna2ctl.py did not employ the comment generator? No problem. sna2skool.py can use it to generate comments for the instructions that don’t yet have any:

$ sna2skool.py -C -c game.ctl game.z80 > game.skool

Of course, the comment generator is not in any way smart, doesn’t understand how the game you’re disassembling actually works, and should never be used to produce a ‘complete’ disassembly automatically from scratch. But if you’re a bit hazy on how an arithmetic or logic operation works on the flags, or what one of the rarely used instructions (e.g. RLD or OTIR) actually does, then the comment generator can provide a useful jog to the memory. Which is better than nothing.

If, by chance and over time, you find the comment generator to be more useful than not, you can enable it by default for both sna2ctl.py and sna2skool.py by setting their Comments configuration parameters to 1 in skoolkit.ini.

Anyway, moving on to other new features, trace.py, when running with screen contents displayed, will now respond to keypresses. This means you could use it to enter programs into the simulated ZX Spectrum, or even play games, though I wouldn’t recommend it. With no border, or sound, or menus to control its operation, trace.py is a poor substitute for a proper emulator. But in a pinch it could be useful if the code you’re tracing depends on user input.

Another new feature for trace.py - which makes more sense now that it can respond to keypresses - is the --map option. Just like the --map option of rzxplay.py, it produces a code execution map. And, just like a code execution map produced by rzxplay.py, one produced by trace.py can be used with the --map option of sna2ctl.py when generating a control file.

Finally, with an eye to the looming SkoolKit 10.0 (no release date planned yet, so don’t panic), there are several macro-related deprecations in the works in this release. They all concern syntax oddities that have been around for a long time, and which I’m now determined to eradicate (eventually) by encouraging the use of parentheses where appropriate. Specifically, #CALL, #LINK and #FONT now support alternative syntaxes that do away with the ugly colons, and the new #FRAMES macro is an aptly named drop-in replacement (more or less) for the frame-splicing #UDGARRAY* macro that avoids the weird asterisk. In addition, the plain #UDGARRAY macro now strongly prefers its UDG specifications to be enclosed in parentheses, and its attribute address range specifications to be enclosed in square brackets.

For details of the other new features that may or may not be overshadowed by the comment generator, check out the changelog. After that, why not download a copy of 9.5 and use the comment generator to remind yourself what RRD and INIR do, and how ‘RET C’ works after a CP?