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 afoo.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!