Developing for Sinclair Computers

Introduction

Many years ago I spent a lot of time programming for the Sinclair ZX81 and then the Sinclair Spectrum.  In those days you had to do it “the hard way”, by entering code on those tiny keyboards and trusting your code to audio cassettes.  Nowadays we can still have the fun of programming for those platforms but we now can make use of an emulated system and helpful host programs.

The principles here should work on any Unix-based system; you might need to use a different emulator, but the other stuff should pretty much just work.

Scenario 1 – Cross-assembling to an emulated ZX Spectrum

First, we’re going to write a short program in Z80 assembly language on our host computer and then cross-compile it to an emulated 48K Sinclair Spectrum.

Prerequisites

  • An editor. I use Emacs but you can use whatever you’re comfortable with.
  • An assembler. I use z80asm.
  • An emulator. On macOS I use Fuse as well as ZEsarUX.
  • BIN2TAP.
  • Optionally, GNU Make to help automate the development cycle.

Installing the tools

I’m assuming that locally-compiled software is installed into ~/local/bin and this path already exists.

First, install Fuse or ZEsarUX the usual way.

Download z80asm from http://savannah.nongnu.org/projects/z80asm, in my case this is z80asm-1.8.tar.gz. Build and install it like this:

cd
tar -xzf Downloads/z80asm-1.8.tar.gz
cd z80asm-1.8
make
cp z80asm ~/local/bin/
cd
rm -rf z80asm-1.8

Download bin2tap from http://zeroteam.sk/bin2tap.html, this gives us bin2tap13.zip. Build and install it like this:

cd
mkdir build
cd build
unzip ../Downloads/bin2tap13.zip
gcc -o bin2tap bin2tap.c
cp bin2tap ~/local/bin/
cd
rm -rf build

Building and running the binary

We start with an assembler file containing our Z80 code (see below), which we then compile into a binary file and then convert it into a TAP file for the emulator to load.

Assuming we’ve got an assembler source file in ~/test/hw.asm we build and run it like this:

export PATH=~/local/bin:$PATH
cd ~/test
z80asm -o hw.bin hw.asm
bin2tap -b -o hw.tap hw.bin

Run Fuse (or the emulator of your choice), make sure you’re emulating a 48K Spectrum, and then load the TAP file. It should work! Note that we’ve told bin2tap to produce a BASIC loader in the TAP file; consult the ‘help’ output of bin2tap for more information about that.

Ideally you’d use a Makefile for this build process, creating one should be fairly trivial.

The sample code

My simple sample code is this (copied from somewhere on the web and improved a bit, and that original code was in turn based on other code, etc etc!):

        org 32768
start:
        ld a, 2         ; channel 2 = "S" for screen
        call $1601      ; Select print channel using ROM
        ld hl,line      ; Print line
        call printline
        ret

printline:              ; Routine to print out a line
        ld a,(hl)       ; Get character to print
        cp '$'          ; See if it '$' terminator
        jp z,printend   ; We're done if it is
        rst 16          ; Spectrum: Print the character in 'A'
        inc hl          ; Move onto the next character
        jp printline    ; Loop round
printend:
        ret

line:   defb 'Hello, world!',13,'$'

Scenario 2 – Cross Compiling C to either a ZX81 or a ZX Spectrum

Another approach is to develop in a high-level language (compared to Z80 assembly language), and compile it into Z80 machine code.

There is an excellent tool called z88dk which provides a small C compiler, a Z80 assembler,   some impressive runtime libraries and several tools. This very nice piece of software allows you to write fairly normal C code which can then be compiled for a ZX81 or a Spectrum, or many other Z80-based computers.

To use it, download the latest nightly snapshot from http://nightly.z88dk.org and build it using:

tar -xzf Downloads/z88dk.tar.gz
cd z88dk
export Z80_OZFILES=~/z88dk/lib/
export ZCCCFG=~/z88dk/lib/config/
./build.sh

To use the SDK you’ll need to have set some environment variables:

export Z80_OZFILES=~/z88dk/lib/
export ZCCCFG=~/z88dk/lib/config/
export PATH=$PATH:~/z88dk/lib

Then, assuming you’ve got a C program in hw.c in your current directory, you can compile for a ZX81 like this:

zcc +zx81 -lndos -create-app hw.c

This produces hw.P which you can then load into a ZX81 emulator (e.g. ZEsarUX or MESS etc).

Or, you can use the same C program and build it for a Spectrum like this:

zcc +zx -lndos -create-app hw.c

This produces hw.tap which you can then load into a Spectrum emulator such as Fuse or ZEsarUS. This should work correctly.

Scenario 3 – Cross-Assembling to a ZX81

To do this there are two steps required:

  • Assemble your foo.asm file into a foo.obj object file. You could use the z80asm which is part of z88dk but this seems to append some stuff to the front of the file, I can’t work out what and why this is. So it’s better to use the “real” z80asm (from the Savannah site mentioned earlier)
  • Convert the object file to a P file suitable for loading into an emulator (which wraps it in a simple BASIC loader)
  • Optionally, convert the P file to a WAV file for recording onto cassette tape, not covered here.

So if we use this example code (which prints some of the ZX81 character set) as zob.asm:

        ORG 16514  ; Important so that absolute addressing works correctly
        LD A,1
loop:   RST $10
        INC A
        CP 36
        JP NZ,loop
        RET

This can be compiled and converted to a P file like this:

z80asm -o zob.bin zob.asm
./b2p zob.bin zob.p

(b2p is my own version of the old BIN2P program, and is a work in progress)

The P file can be loaded in a ZX81 emulator. If you’re keen, the P file can be converted to a WAV file and then recorded onto a cassette tape for the most realistic approach!