Pobtastic / Skoolkit / Script Everything

Created Wed, 12 Feb 2025 23:34:50 +0000 Modified Wed, 20 Aug 2025 22:05:19 +0000
1430 Words 7 min

Introduction

Being a programmer I try and automate everything … and my use of Skoolkit is no exception.

Whenever I stumble across a game I used to own, I’ll look up a few details and run something along the lines of:

$ ./generate.py 
usage: generate.py [options] GAME

Creates a sample Skoolkit folder for a given game.

Options:
  -f FILE_DOWNLOAD      Set the file download link for the game.
  -m MANUFACTURER       Set the name of the company who manufactured the game.
  -mw MANUFACTURER_WIKI
                        Set the wiki page of the company who manufactured the game.
  -p POKE_DOWNLOAD      Set a poke file to be parsed.
  -w WIKI               Set the wiki page for the game.
  -y YEAR               Set the release year for the game (default: 1982).

$ ./generate.py \
  -f https://worldofspectrum.net/pub/sinclair/games/s/SpikyHarold.tzx.zip \
  -m "Firebird Software Ltd" \
  -mw "https://en.wikipedia.org/wiki/Telecomsoft#Firebird" \
  -p "https://spectrumcomputing.co.uk/zxdb/sinclair/pokes/s/Spiky%20Harold%20(1986)(Firebird%20Software).pok" \
  -w "https://en.wikipedia.org/wiki/Spiky_Harold" \
  -y 1986 \
  "Spiky Harold"
  ____                     _    _
 / ___| _ __   ___   __ _ | |_ (_) _ __    __ _
| |    | '__| / _ \ / _` || __|| || '_ \  / _` |
| |___ | |   |  __/| (_| || |_ | || | | || (_| | _  _  _
 \____||_|    \___| \__,_| \__||_||_| |_| \__, |(_)(_)(_)
                                          |___/

 ____          _  _             _   _                      _      _
/ ___|  _ __  (_)| | __ _   _  | | | |  __ _  _ __   ___  | |  __| |
\___ \ | '_ \ | || |/ /| | | | | |_| | / _` || '__| / _ \ | | / _` |
 ___) || |_) || ||   < | |_| | |  _  || (_| || |   | (_) || || (_| |
|____/ | .__/ |_||_|\_\ \__, | |_| |_| \__,_||_|    \___/ |_| \__,_|
       |_|              |___/

... wrote /Workspace/skoolkit/disassemblies/spikyharold/Makefile
... wrote /Workspace/skoolkit/disassemblies/spikyharold/utils/mkasm.py
... wrote /Workspace/skoolkit/disassemblies/spikyharold/utils/mkhtml.py
... wrote /Workspace/skoolkit/disassemblies/spikyharold/utils/skrunner.py
... wrote /Workspace/skoolkit/disassemblies/spikyharold/sources/analytics.ref
... wrote /Workspace/skoolkit/disassemblies/spikyharold/sources/bases.ref
... wrote /Workspace/skoolkit/disassemblies/spikyharold/sources/pokes.ref

Poke file exists!
Parsing...
... appended POKEs to /Workspace/skoolkit/disassemblies/spikyharold/sources/pokes.ref

... wrote /Workspace/skoolkit/disassemblies/spikyharold.pipelines.gocd.yaml

Trying: https://github.com/skoolkid/t2sfiles/blob/master/t2s/s/spiky-harold.t2s
Download exists!
... wrote /Workspace/skoolkit/disassemblies/spikyharold/spikyharold.t2s

... wrote /Workspace/skoolkit/disassemblies/spikyharold/.dreleaserc
... wrote /Workspace/skoolkit/disassemblies/spikyharold/README.md
... wrote /Workspace/skoolkit/disassemblies/spikyharold/sources/changelog.ref
... wrote /Workspace/skoolkit/disassemblies/spikyharold/sources/spikyharold.ref
... wrote /Workspace/skoolkit/disassemblies/spikyharold/sources/spikyharold.ctl

Downloading https://worldofspectrum.net/pub/sinclair/games/s/SpikyHarold.tzx.zip
Extracting Spiky Harold.tzx
Program: S P I K Y
Fast loading data block: 23755,96
Bytes: S P I K Y
Fast loading data block: 23296,48
Fast loading data block: 32000,6912
Fast loading data block: 26540,38990
Tape finished
Simulation stopped (PC at start address): PC=23341
Writing SpikyHarold.z80

To build use:
sna2skool.py -H -c sources/spikyharold.ctl SpikyHarold.z80 > sources/spikyharold.skool && HTML_OPTS="-T dark -T wide -toOa" make html

And once this is done, just change into the new directory and copy/ paste the instruction:

$ cd spikyharold
$ sna2skool.py -H -c sources/spikyharold.ctl SpikyHarold.z80 > sources/spikyharold.skool && HTML_OPTS="-T dark -T wide -toOa" make html
Using control file: sources/spikyharold.ctl
utils/mkhtml.py -T dark -T wide -toOa -d build/html -t 
Found SkoolKit in /Workspace/skoolkit/skoolkit/skoolkit
 ____          _  _             _   _                      _      _
/ ___|  _ __  (_)| | __ _   _  | | | |  __ _  _ __   ___  | |  __| |
\___ \ | '_ \ | || |/ /| | | | | |_| | / _` || '__| / _ \ | | / _` |
 ___) || |_) || ||   < | |_| | |  _  || (_| || |   | (_) || || (_| |
|____/ | .__/ |_||_|\_\ \__, | |_| |_| \__,_||_|    \___/ |_| \__,_|
       |_|              |___/

Using ref files: /Workspace/skoolkit/disassemblies/spikyharold/sources/spikyharold.ref, /Workspace/skoolkit/disassemblies/spikyharold/sources/bases.ref, /Workspace/skoolkit/disassemblies/spikyharold/sources/analytics.ref, /Workspace/skoolkit/disassemblies/spikyharold/sources/changelog.ref, /Workspace/skoolkit/disassemblies/spikyharold/sources/pokes.ref
Parsing /Workspace/skoolkit/disassemblies/spikyharold/sources/spikyharold.skool (1.44s)
Output directory: build/html/spikyharold
Copying /Workspace/skoolkit/skoolkit/skoolkit/resources/skoolkit.css to skoolkit.css
Copying /Workspace/skoolkit/skoolkit/skoolkit/resources/skoolkit-dark.css to skoolkit-dark.css
Copying /Workspace/skoolkit/skoolkit/skoolkit/resources/skoolkit-wide.css to skoolkit-wide.css
Writing disassembly files in asm (8.01s)
Writing maps/all.html (0.00s)
Writing maps/routines.html (0.00s)
Writing maps/data.html (0.00s)
Writing reference/changelog.html (0.00s)
Writing reference/pokes.html (0.00s)
Writing index.html (0.00s)
Done (9.49s)
Using ref files: /Workspace/skoolkit/disassemblies/spikyharold/sources/spikyharold.ref, /Workspace/skoolkit/disassemblies/spikyharold/sources/bases.ref, /Workspace/skoolkit/disassemblies/spikyharold/sources/analytics.ref, /Workspace/skoolkit/disassemblies/spikyharold/sources/changelog.ref, /Workspace/skoolkit/disassemblies/spikyharold/sources/pokes.ref
Parsing /Workspace/skoolkit/disassemblies/spikyharold/sources/spikyharold.skool (2.66s)
Output directory: build/html/spikyharold/dec
Copying /Workspace/skoolkit/skoolkit/skoolkit/resources/skoolkit.css to skoolkit.css
Copying /Workspace/skoolkit/skoolkit/skoolkit/resources/skoolkit-dark.css to skoolkit-dark.css
Copying /Workspace/skoolkit/skoolkit/skoolkit/resources/skoolkit-wide.css to skoolkit-wide.css
Writing disassembly files in asm (8.21s)
Writing maps/all.html (0.00s)
Writing maps/routines.html (0.00s)
Writing maps/data.html (0.00s)
Writing reference/changelog.html (0.00s)
Writing reference/pokes.html (0.00s)
Writing index.html (0.00s)
Done (10.89s)

This creates pretty generic output which looks like this:

The Skoolkit game introduction page
The initial output.

As long as a .t2s file was found in the SkoolKit t2sfiles repo, you should now have a GameEntryPoint and the “Switch to hex/ decimal” links should function correctly:

Noting the addresses are decimals
The "Switch to hex/ decimal" links should work already. The GameEntryPoint will (hopefully) now be present.

It’s not the end of the World if the .t2s file wasn’t found, it’s just then a bit of a manual process to fill in the blanks. What to do may differ between games, but regardless we need to grab the tape image using something like:

$ curl -sO https://worldofspectrum.net/pub/sinclair/games/s/SpikyHarold.tzx.zip
$ unzip SpikyHarold.tzx.zip
Archive:  SpikyHarold.tzx.zip
  inflating: Spiky Harold.tzx

Now we can use the SkoolKit command tapinfo.py to examine what’s on the tape:

$ tapinfo.py Spiky\ Harold.tzx 
Version: 1.10
1: Text description (0x30)
  Text: Created with Ramsoft MakeTZX
2: Standard speed data (0x10)
  Pause: 792ms
  Type: Header block
  Program: S P I K Y 
  LINE: 1
  Length: 19
  Data: 0, 0, 83, 32, 80, 32, 73 ... 96, 0, 1, 0, 96, 0, 121
3: Standard speed data (0x10)
  Pause: 1ms
  Type: Data block
  Length: 98
  Data: 255, 0, 1, 40, 0, 231, 48 ... 0, 0, 0, 91, 0, 13, 244
4: Standard speed data (0x10)
  Pause: 602ms
  Type: Header block
  Bytes: S P I K Y 
  CODE: 23296,48
  Length: 19
  Data: 0, 3, 83, 32, 80, 32, 73 ... 48, 0, 0, 91, 0, 128, 144
5: Standard speed data (0x10)
  Pause: 1ms
  Type: Data block
  Length: 50
  Data: 255, 221, 33, 0, 125, 17, 0 ... 205, 86, 5, 195, 208, 132, 154
6: Standard speed data (0x10)
  Pause: 1ms
  Type: Data block
  Length: 6914
  Data: 255, 255, 255, 255, 255, 255, 255 ... 2, 2, 2, 2, 2, 2, 195
7: Standard speed data (0x10)
  Pause: 17483ms
  Type: Data block
  Length: 38992
  Data: 255, 52, 51, 50, 49, 50, 49 ... 0, 0, 0, 0, 0, 0, 17

From this output, we can clearly see that block 2 here is a header and block 3 contains data. Given these are the first things to load, this is going to be a “loader” which sets up loading the rest of the game.

$ tapinfo.py -d -b 3 Spiky\ Harold.tzx 
   1 BORDER 0: INK 0: PAPER 0: CLEAR 26539
   2 POKE 23624,0
   3 LOAD ""CODE 
   4 RANDOMIZE USR 23296

Nice and simple! We can see that the BASIC loader just loads the game and then boots into it at 23296.

Now … we could just create a .t2s file which stops the execution at 23296 but sometimes games have loaders, or mild “decryption” or set up routines - often it’s nice to let the game run a little in order to set up defaults etc.

So, taking a look at the code at 23296:

; Loader
;
; Set up loading the loading screen data.
@label=Loader
c$5B00 LD IX,$7D00   ; Set the start address in #REGix to #N$7D00.
 $5B04 LD DE,$1B00   ; Set the block length in #REGde to #N$1B00 bytes.
 $5B07 LD A,$FF      ; Set #REGa to #N$FF to indicate this is a data block.
 $5B09 SCF           ; Set the carry flag.
 $5B0A CALL $0556    ; #HTML(Call <a
                     ; href="https://skoolkit.ca/disassemblies/rom/hex/asm/0556.html">LD_BYTES</a>.)
 $5B0D LD B,$0C      ; Set a counter in #REGb of #N$0C loops.
; Flash the border.
@label=LoaderBorderFlash_Loop
*$5B0F HALT          ; Halt operation (suspend CPU until the next interrupt).
 $5B10 LD A,B        ; {Set the border colour to the value held in the counter.
 $5B11 OUT ($FE),A   ; }
 $5B13 DJNZ $5B0F    ; Decrease the counter by one and loop back to #R$5B0F until the
                     ; counter is zero.
; Display the loading screen.
 $5B15 LD HL,$7D00   ; {Copy #N$1B00 bytes of data from #N$7D00 to the screen
 $5B18 LD BC,$1B00   ; buffer.
 $5B1B LD DE,$4000   ;
 $5B1E LDIR          ; }
; Load the game data.
 $5B20 LD IX,$67AC   ; Set the start address in #REGix to #R$67AC.
 $5B24 LD DE,$984E   ; Set the block length in #REGde to #N$984E bytes.
 $5B27 LD A,$FF      ; Set #REGa to #N$FF to indicate this is a data block.
 $5B29 SCF           ; Set the carry flag.
 $5B2A CALL $0556    ; #HTML(Call <a
                     ; href="https://skoolkit.ca/disassemblies/rom/hex/asm/0556.html">LD_BYTES</a>.)
@label=GameEntryPointAlias
 $5B2D JP $84D0      ; Jump to #R$84D0.

It is a loader as well! So let’s just say that the game “starts” at location $5B2D (23341). Which is the first point where it JUMPs into the actual code.

There actually is a .t2s for Spiky Harold, and we can see that’s exactly what this does as well.

https://worldofspectrum.net/pub/sinclair/games/s/SpikyHarold.tzx.zip
--tape-name "Spiky Harold.tzx"
--tape-sum 71e48610f31ec554592f602e1f530953
--start 23341