ZX Spectrum Programming

It’s quite the rabbit hole. There are all sorts of exciting projects to distract me from investigating programming tools for the ZX Spectrum. I’m interested in tools I can use from the command-line in Linux primarily. Tools that can be Dockerised.

Tape file target

There was a question to the JSSpeccy3 repository issues board asking about bas2tap like functionality to run basic programs. The response from the JSSpeccy3 author was that external tools should be used to create a tape file and provided to the emulator that way. After some further consideration, it seems that tape files are a good target for any Spectrum programs to be run, either in an emulator or on the old ZX Spectrum 48K machine itself.

Loading tape files to the old machine

The old machine audio inputs require the volume to be higher than my iPhone could emit. However, I have an Android phone for testing, and I recommend ZX Tape Player. Yet, I’m using a stereo cable, and I haven’t tried this tip on changing the tape audio signal from mono to stereo, but using the Android app is easy and convenient.

ZX Spectrum emulators

I’m currently using Windows 10 as my main OS, with WSL2 running Ubuntu 20.04 LTS also capable of running an X Server using X410. So this selection of emulators is for Windows, though elsewhere on this page, I’m using Ubuntu on the command line.

ZX Spectrum emulators for Windows:

Fuse is the fastest emulator to start up and is the one I have set up for file associations. In addition, Fuse is cross-platform and is available for a wide variety of operating systems.

Programming options

Sinclair BASIC

Sinclair BASIC is the most straightforward programming language on the ZX Spectrum. It’s not what I’m most interested in, but I’ll start with this as it is the easiest to get started with.

Compile the bas2tap command:

wget https://raw.githubusercontent.com/speccyorg/bas2tap/master/bas2tap.c
gcc -Wall -O2 bas2tap.c -o bas2tap -lm ; strip bas2tap

Create a simple basic script to convert to a tape file:

10 PRINT "Hello, world!"
20 GO TO 10

Convert the basic script to a tape file:

bas2tap hello.bas -a -shello

The additional command options are to start the program automatically (otherwise, you have to enter RUN to start the program once loaded) and add “hello” as the program name.

Output is:


BAS2TAP v2.6 by Martijn van der Heide of ThunderWare Research Center

Creating output file hello.tap
Done! Listing contains 2 lines.

Launching the hello.tap file with Fuse produces the following:

Fuse showing hello.tap

C and assembly with Z88DK

I’ve been really interested in Z88DK as it combines C and assembly language programming for the ZX Spectrum, among other targets. Moreover, the source of some excellent example games is available, such as zx-spectrum-snake and z88dk_breakout.

Build Z88DK

I found it a little challenging to get set up with Z88DK initially, so here are the steps that I have followed and then a Dockerfile that can be used.

sudo apt update
sudo apt install -y \
        build-essential \
        git \
        subversion \
        curl \
        libxml2-dev \
        bison \
        flex \
        libboost-all-dev \
        texinfo \
        zlib1g-dev
cd # wherever you want to clone the z88dk repository
git clone --depth 1 --branch v2.1 --recursive https://github.com/z88dk/z88dk.git
cd z88dk/
export Z88DK_PATH="$(pwd)"
export SDCC_PATH="/tmp/sdcc"
export BUILD_SDCC=1
export BUILD_SDCC_HTTP=1
./build.sh
rm -fR ${SDCC_PATH}

If the build does fail, you may need to clear the temporary files using the command rm -r -f /tmp/sdcc before trying again.

Test the Z88DK commands built above

Here are some working examples to test the Z88DK commands we built above.

First, we need to set the following environment variables for the tools on the shell path:

export PATH="${Z88DK_PATH}/bin:${PATH}"
export ZCCCFG="${Z88DK_PATH}/lib/config/"
Build initial Z88DK tutorial examples

Extracts based on The ZX Spectrum Programmer’s Z88DK Getting Started Guide, which is an excellent set of tutorials to get started with Z88DK!

Hello World

The simplest example to get started:

cd # wherever you like
vi hello.c # or use another editor of choice
#include <arch/zx.h>
#include <stdio.h>
 
int main()
{
  zx_cls(PAPER_WHITE);
  puts("Hello, world!");
  return 0;
}

A single command to build the tape file:

zcc +zx -vn -create-app -clib=sdcc_iy -startup=0 hello.c -o hello

Launching the hello.tap file with Fuse produces the following:

Fuse showing hello.tap
Drawing Lines on Screen
cd # wherever you like
vi line.c # or use another editor of choice
#include <arch/zx.h>
#include <input.h>
#include <stdlib.h>

void plot(unsigned char x, unsigned char y)
{
	*zx_pxy2saddr(x,y) |= zx_px2bitmask(x);
}

void line(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1)
{
	unsigned char dx  = abs(x1-x0);
	unsigned char dy  = abs(y1-y0);
	signed   char sx  = x0<x1 ? 1 : -1;
	signed   char sy  = y0<y1 ? 1 : -1;
	int           err = (dx>dy ? dx : -dy)/2;
	int           e2;

	while (1)
	{
		plot(x0,y0);
		if (x0==x1 && y0==y1) break;

		e2 = err;
		if (e2 >-dx) { err -= dy; x0 += sx; }
		if (e2 < dy) { err += dx; y0 += sy; }
	}
}

int main(void)
{
	unsigned char i;

	zx_cls(PAPER_WHITE);

	for( i=0; i<15; i++ )
	{
		line(rand()%256, rand()%192, rand()%256, rand()%192);
	}
	return 0;
}

The compile line is the same as for the last example:

zcc +zx -vn -startup=31 -clib=sdcc_iy line.c -o line -create-app

Launching the line.tap file with Fuse produces the following:

Fuse showing line.tap
Build zx-spectrum-snake

Clone and build the game:

cd # wherever you want to clone the z88dk_sp1_breakout repository
git clone https://github.com/dcrespo3d/zx-spectrum-snake.git
cd zx-spectrum-snake/
mkdir build
zcc +zx -vn -create-app -lndos -lm -I./zxlib snake.c -o build/snake.bin

Launching the build/snake.tap file with Fuse produces the following:

zx-spectrum-snake screen 1
zx-spectrum-snake screen 2

This is a fun game. However, it has no sound.

Build z88dk_breakout

Clone and build the game:

cd # wherever you want to clone the z88dk_sp1_breakout repository
git clone https://github.com/antoniocmateus/z88dk_sp1_breakout.git
cd z88dk_sp1_breakout/
zcc +zx -v -startup=31 -DWFRAMES=3 -clib=sdcc_iy -SO3 --max-allocs-per-node200000 --fsigned-char "@zproject.lst" -pragma-include:zpragma.inc -o build/z88dk-breakout -m
bas2tap -a10 -sz88break src/loader.bas build/loader.tap
mv build/z88dk-breakout.map build/z88dk-breakout_CODE.map
z88dk-appmake +zx -b build/z88dk-breakout_CODE.bin -o build/game.tap --blockname z88dk-breakout --org 25124 --noloader
cat build/loader.tap gfx/loading.tap build/game.tap > build/z88dk-breakout.tap

This example is a bit more complicated, with a loader and multiple commands to make.

Launching the build/z88dk-breakout.tap file with Fuse produces the following:

z88dk_breakout screen 1
z88dk_breakout screen 2
z88dk_breakout screen 3

This is a fun game. I used to play Arkanoid on the ZX Spectrum back in the day. This version of the game has basic sound – but it’s open-source and could be a fun project to work on.

Dockerfile

This is more of a recreation of the environment that I run on my desktop with Ubuntu, except using the Debian base image because it did build quicker at the time of writing.

# To create the image:
#   $ docker build -t z88dk .
# To run the container:
#   $ docker run -v ${PWD}:/src/ -it z88dk <command>

FROM debian:buster

ENV Z88DK_PATH="/opt/z88dk" \
    SDCC_PATH="/tmp/sdcc" \
    PATH="${Z88DK_PATH}/bin:${PATH}" \
    ZCCCFG="${Z88DK_PATH}/lib/config/"

RUN apt update && apt install -y \
        build-essential \
        git \
        subversion \
        curl \
        libxml2-dev \
        bison \
        flex \
        libboost-all-dev \
        texinfo \
        zlib1g-dev

RUN git clone --depth 1 --branch v2.1 --recursive https://github.com/z88dk/z88dk.git ${Z88DK_PATH} \
    && cd ${Z88DK_PATH} \
    && export BUILD_SDCC=1 \
    && export BUILD_SDCC_HTTP=1 \
    && ./build.sh \
    && rm -fR ${SDCC_PATH}

WORKDIR /src/

This container image can compile programs, but note that this version runs as root, which is not ideal. Initial testing of using a non-root user didn’t work.

Z80 Assembler

I suspect that programming the ZX Spectrum in Z80 Assembler will probably be the most fun & engaging. I am interested in learning more about programming a primitive computer like the ZX Spectrum to appreciate the limited options available and the style of programming that this necessitates. I’m curious to know more about this machine in detail while reading the book The ZX Spectrum Ula: How to Design a Microcomputer.

ZXSpin Z80 Assembler

The YouTube playlist ZX Spectrum Z80 programming looks like an excellent dive into ZX Spectrum assembly language programming using the Z80 Assembler embedded in ZXSpin. However, that emulator is no longer maintained, and I read some forum posts noting problems with the assembler. However, it could be fun.

Unfortunately, it’s just for Windows.

Zeus

The Zeus Z80 Assembler looks like another great alternative, at least for Windows. There is a command-line version of the assembler, but it is a Windows executable.

Pasmo

The Pasmo multiplatform Z80 cross-assembler is probably the most interesting one from my point of view due to being able to run it on Linux, and more importantly, Docker.

Clone and build:

cd # wherever you want to clone the pasmo repository
git clone --branch 0.5.3 https://github.com/compilersoftware/pasmo.git
cd pasmo/
./configure
make

Add the pasmo command to the shell path:

export PATH=$(pwd):$PATH

Copy the example scripts out into a new folder:

mkdir ~/zx-asm
cp *.asm ~/zx-asm

Build the hellospec.asm script:

cd ~/zx-asm/
pasmo --tapbas hellospec.asm hellospec.tap

Launching the hellospec.tap file with Fuse produces the following:

Fuse showing hellospec.tap

So, that’s a start!

Share this post