Disassembly DIY

The following sections describe how to use SkoolKit to get started on your own Spectrum game disassembly.

Getting started

The first thing to do is select a Spectrum game to disassemble. For the purpose of this discussion, we’ll use Hungry Horace. To build a pristine snapshot of the game, run the following command in the directory where SkoolKit was unpacked:

$ tap2sna.py @examples/hungry_horace.t2s

(If that doesn’t work, or you prefer to make your own snapshot, just grab a copy of the game, load it in an emulator, and save a Z80 snapshot named hungry_horace.z80.)

The next thing to do is create a skool file from this snapshot. Run the following command from the SkoolKit directory:

$ sna2skool.py hungry_horace.z80 > hungry_horace.skool

Note that the ‘.skool’ file name suffix is merely a convention, not a requirement. In general, any suffix besides ‘.ref’ (which is used by skool2html.py to identify ref files) will do. If you are fond of the traditional three-letter suffix, then perhaps ‘.sks’ (for ‘SkoolKit source’) or ‘.kit’ would be more to your liking. However, for the purpose of this particular tutorial, it would be best to stick with ‘.skool’.

Now take a look at hungry_horace.skool. As you can see, by default, sna2skool.py disassembles everything from 16384 to 65535, treating it all as code. Needless to say, this is not particularly useful - unless you have no idea where the code and data blocks are yet, and want to use this disassembly to find out.

Once you have figured out where the code and data blocks are, it would be handy if you could supply sna2skool.py with this information, so that it can disassemble the blocks accordingly. That is where the control file comes in.

The control file

In its most basic form, a control file contains a list of start addresses of code and data blocks. Each address is marked with a ‘control directive’, which is a single letter that indicates what the block contains: c for a code block, or b for a data block (for example). A control file may contain annotations too, which will be interpreted as routine titles, descriptions, instruction-level comments or whatever else depending on the control directive they accompany.

A control file for Hungry Horace might start like this:

b 16384 Loading screen
i 23296
c 24576 The game has just loaded
c 25167
...

This control file declares that there is:

  • a data block at 16384 titled ‘Loading screen’

  • a block at 23296 that should be ignored

  • a code block (routine) at 24576 titled ‘The game has just loaded’

  • another code block at 25167

For more information on control file directives and their syntax, see Control files.

A skeleton disassembly

So if we had a control file for Hungry Horace, we could produce a much more useful skool file. As it happens, SkoolKit includes one: hungry_horace.ctl. You can use it with sna2skool.py thus:

$ sna2skool.py -c examples/hungry_horace.ctl hungry_horace.z80 > hungry_horace.skool

This time, hungry_horace.skool is split up into meaningful blocks, with code as code, data as data (DEFBs), and text as text (DEFMs). Much nicer.

By default, sna2skool.py produces a disassembly with addresses and instruction operands in decimal notation. If you prefer to work in hexadecimal, use the --hex option:

$ sna2skool.py --hex -c examples/hungry_horace.ctl hungry_horace.z80 > hungry_horace.skool

The next step is to create an HTML disassembly from this skool file:

$ skool2html.py hungry_horace.skool

Now open hungry_horace/index.html in a web browser. There’s not much there, but it’s a base from which you can start adding explanatory comments.

In order to replace ‘hungry_horace’ in the page titles and headers with something more appropriate, or add a game logo image, or otherwise customise the disassembly, we need to create a ref file. Again, as it happens, SkoolKit includes an example ref file for Hungry Horace: hungry_horace.ref. To use it with the skool file we’ve just created:

$ skool2html.py hungry_horace.skool examples/hungry_horace.ref

Now the disassembly will sport a game logo image.

See Ref files for more information on how to use a ref file to configure and customise a disassembly.

Generating a control file

If you are planning to create a disassembly of some game other than Hungry Horace, you will need to create your own control file. To get started, you can use sna2ctl.py to perform a rudimentary static code analysis of the snapshot file and generate a corresponding control file:

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

This will do a reasonable job of splitting the snapshot into blocks, but won’t be 100% accurate (except by accident). You will need to examine the resultant skool file (game.skool) to see which blocks have been incorrectly marked as text, data or code, and then edit the control file (game.ctl) accordingly.

To generate a better control file, you could use a code execution map to tell sna2ctl.py where at least some of the code is in the snapshot. sna2ctl.py will read a map (otherwise known as a profile or trace) produced by rzxplay.py, Fuse, SpecEmu, Spud, Zero or Z80 when specified by the -m option:

$ sna2ctl.py -m game.map game.z80 > game.ctl

Needless to say, in general, the better the map, the more accurate the resulting control file will be. To create a good map file, you should ideally play the game from start to finish in the emulator, in an attempt to exercise as much code as possible. If that sounds like too much work, you could grab a recording of your chosen game from the RZX Archive, and either run the RZX file through rzxplay.py with the --map option, or set an emulator’s profiler or tracer going while it plays the recording back.

By default, sna2ctl.py and sna2skool.py generate control files and skool files with addresses and instruction operands in decimal notation. If you prefer to work in hexadecimal, use the --hex option of each command to produce a hexadecimal control file and a hexadecimal skool file:

$ sna2ctl.py --hex game.z80 > game.ctl
$ sna2skool.py --hex -c game.ctl game.z80 > game.skool

Developing the disassembly

When you’re happy that your control file does a decent job of distinguishing the code blocks from the data blocks in your memory snapshot, it’s time to start work on adding annotations that describe what the code does and what the data is for.

Figuring out what the code blocks do and what the data blocks contain can be a time-consuming job. It’s probably not a good idea to go through each block one by one, in order, and move to the next only when it’s fully documented - unless you’re looking for a nervous breakdown. Instead it’s better to approach the job like this:

  1. Skim the code blocks for any code whose purpose is familiar or obvious, such as drawing something on the screen, or producing a sound effect.

  2. Document that code (and any related data) as far as possible.

  3. Find another code block that calls the code block just documented, and figure out when, why and how it uses it.

  4. Document that code (and any related data) as far as possible.

  5. If there’s anything left to document, return to step 3.

  6. Done!

It also goes without saying that figuring out what a piece of code or data might be used for is easier if you’ve played the game to death already.

As for where to write annotations, you now have a choice. You can add them either to the control file or to the skool file. The recommended approach, unless you are already familiar with the syntax of skool files, is to add annotations to the control file. The benefits of continuing to work on the control file are:

  • its syntax is much simpler than that of the skool file

  • you are never in danger of breaking the skool file, and potentially causing skool2asm.py and skool2html.py to fail

  • if you ever need to modify how an address range is disassembled, it is usually as simple as replacing one letter (e.g. c for code) with another (e.g. t for text)

If you would rather edit the skool file, however, then it is highly recommended to do so only for the purpose of adding, removing or updating annotations. Don’t be tempted to manually convert code to data, or vice versa. Unless extreme care is taken, doing so could easily result in a broken skool file that is very difficult to fix.

Annotating the code and data in a skool file is done by adding comments just as you would in a regular assembly language source file. For example, you might add a comment to the instruction at 26429 in hungry_horace.skool thus:

 26429 DEC A         ; Decrement the number of lives

See the skool file format reference for a full description of the kinds of annotations that are supported in skool files. Note also that SkoolKit supports many skool macros that can be used in comments and will be converted into hyperlinks and images (for example) in the HTML version of the disassembly.

As you become more familiar with the layout of the code and data blocks in the disassembly, you may find that some blocks need to be split up, joined, or otherwise reorganised. If you are working on the skool file, the best way to do this is to regenerate the skool file from a new control file. To ensure that you don’t lose all the annotations you’ve already added to the skool file, though, you should use skool2ctl.py to preserve them.

First, create a control file that keeps your annotations intact:

$ skool2ctl.py game.skool > game-2.ctl

Now edit game-2.ctl to fit your better understanding of the layout of the code and data blocks. Then generate a new skool file:

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

This new skool file, game-2.skool, will contain your reorganised code and data blocks, and all the annotations you carefully added to game.skool.

Adding pokes, bugs and trivia

Adding ‘Pokes’, ‘Bugs’, and ‘Trivia’ pages to a disassembly is done by adding [Poke:*], [Bug:*], and [Fact:*] sections to the ref file. For any such sections that are present, skool2html.py will add links to the disassembly index page.

For example, let’s add a poke. Add the following lines to hungry_horace.ref:

[Poke:infiniteLives:Infinite lives]
The following POKE gives Horace infinite lives:

POKE 26429,0

Now run skool2html.py again:

$ skool2html.py hungry_horace.skool examples/hungry_horace.ref

Open hungry_horace/index.html and you will see a link to the ‘Pokes’ page in the ‘Reference’ section.

The format of a Bug or Fact section is the same, except that the section name prefix is Bug: or Fact: (instead of Poke:) as appropriate.

Add one Poke, Bug or Fact section for each poke, bug or trivia entry to be documented. Entries will appear on the ‘Pokes’, ‘Bugs’ or ‘Trivia’ page in the same order as the sections appear in the ref file.

See Ref files for more information on the format of the Poke, Bug, and Fact (and other) sections that may appear in a ref file.

Themes

In addition to the default theme (defined in skoolkit.css), SkoolKit includes some alternative themes:

  • dark (dark colours): skoolkit-dark.css

  • green (mostly green): skoolkit-green.css

  • plum (mostly purple): skoolkit-plum.css

  • wide (wide comment fields on the disassembly pages, and wide boxes on the Changelog, Glossary, Trivia, Bugs and Pokes pages): skoolkit-wide.css

In order to use a theme, run skool2html.py with the -T option; for example, to use the ‘dark’ theme:

$ skool2html.py -T dark game.skool

Themes may be combined; for example, to use both the ‘plum’ and ‘wide’ themes:

$ skool2html.py -T plum -T wide game.skool

Base switching

If you would like to build both decimal and hexadecimal versions of your disassembly in HTML format and have them link to each other, then one possible approach is to define a custom page footer that contains a link to the corresponding page in the alternative disassembly.

An example of such a page footer can be found in examples/bases.ref, and the required Python code that generates the appropriate link for each page can be found in examples/bases.py. To use bases.ref and bases.py with your disassembly, first place copies of them alongside your existing skool and ref files. Then:

$ skool2html.py -D -c Config/GameDir=html/dec -c Config/InitModule=:bases game.skool bases.ref
$ skool2html.py -H -c Config/GameDir=html/hex -c Config/InitModule=:bases game.skool bases.ref

The first command here builds the decimal version of the disassembly in the directory html/dec, and the second command builds the hexadecimal version in the directory html/hex. The footer of each page in the decimal version will contain a link to the corresponding page in the hexadecimal version, and vice versa.