A tool for creating Spectrum +3 game disks

This article describes DSKTOOL, a tool for creating ZX Spectrum +3 game disks in VTAPE format.

The motivation behind DSKTOOL is to allow creating disk versions of ZX games for the Spectrum +3 as easily as TAP versions, and reusing the same game binaries from the TAP version. The easies way to achieve this is to use the disk as a sort of “virtual tape”, or VTAPE, in which the game data is stored and read sequentially, the same way as a real tape would be used.

VTAPE is just a way to format the disk and data inside it, and a convention for accessing that data in an easy way from the game loader.

This tool generates the VTAPE disk image file (in standard DSK format) that can be later used to boot the game in +3 emulators, or used as a master for physical media generation.

A loader API is also provided to access the VTAPE disk and load the game data.

Requirements

The tool itself is written in Perl with no dependencies, and the ASM files and Makefiles for the needed support code is intended to be compiled with Z88DK.

Design

The main design choice is to not use regular +3DOS formatted disks for storage, but to use a custom disk format and layout that makes it trivial to write a loader for the disk version (this is the only thing that needs to change with respect to the TAP version).

This means that the disks generated with this tool will not be readable by +3 BASIC nor any other software, but they will just boot the game when you select the “LOADER” option in the +3 start menu.

The generated disk is of the +3 bootable type, and so it has a tiny generic bootloader with a special signature that the +3 ROM expects, at track 0 sector 0 (512 bytes). This generic bootloader is provided with DSKTOOL and you don’t need to create it.

The disk geometry is the standard +3/Amstrad format: single sided, 40 tracks, 9 sectors per track, 512-byte sectors. In this document, tracks are numbered 0-39, and sectors 1-9.

The bootloader is detected and invoked by the ROM when the LOADER option is selected, and the bootloader in turn loads the real game specific loader at a given position in RAM (0x8000). The game loader is a binary (i.e. machine code) program, and NOT a BASIC program. No BASIC is involved when using the VTAPE schema.

The game loader must use a special provided disk API for loading the game binaries from the disk, with a function that is fully compatible with the LD-BYTES ROM routine for loading from tape, i.e. it receives the same parameters and loads the bytes from media (just from disk instead of tape). Each successful call to the API function leaves the VTAPE pointer ready for loading the next data block, exactly the same as a regular tape would. This makes it trivial to adapt an existing TAP loader for the disk version.

The API to load the data blocks from disk is provided as an ASM file (diskapi.asm) that must be linked together with the specific loader code. This API talks directly to the FDC (Floppy Drive Controller) and makes no use of ROM calls or interrupts. It runs with interrupts disabled.

The game loader is called in USR0 mode (i.e. ROM48-5-2-0 bank configuration and paging enabled), with SP at 0x8000 and interrupts disabled

The maximum loader size is 4K.

Disc Layout

The layout of the disk is as follows:

The data blocks are laid out in the following way:

Tool Usage

Example usage:

dsktool.pl -b bootloader.bin -o image.dsk -l loader.bin screen.scr bank1.bin bank3.bin main.bin

All the binaries will be laid out on the disk according to the rules indicated in the previous section.

In the previous example, the loader.asm file which is compiled to loader.bin must include the proper instructions to load the SCREEN$ to the display file, switch banks and load their data to 0xC000 (bankX.bin files), and finally set the final memory configuration, load the main code (main.bin) and jump to the entry point for game execution. That is, everything a regular loader would do.

Source Code

Source code for this tool is available in my ZXTOOLS github repository:

Special acknowledgements

This work could no thave been possible without the help of the following people from the spanish “Ensamblador Z80” Telegram channel:

It would have taken me ages to do this without your hints and help:-) Thanks so much to you, guys.

References