Not so long ago we got a new cool gadget at the office, ZX Spectrum +2 computer released by Amstrad around 1986 including a keyboard and a cassette deck for loading programs. It's a very nice system with a 3.5 MHz Zilog Z80 CPU, 32 KB ROM, 128 KB RAM, three channel AY-3-8912 sound chip, MIDI compatibility, RS-232 serial port and a multipurpose extension port. But it was the high resolution 256x192 video output that differentiated it (and its predecessor ZX Spectrum 48K) from its arch enemy Commodore 64. Just look at this beauty.
The first problem we face is of course: How do I setup a programming environment for this thing?
NOTICE: To follow these instructions you need to have XCode installed from the App Store in order to be able to compile the source code.
I'm running the latest Mac OS X, so I decided to setup a simple build environment for the system. Since I do not want to mess up the local environment, I first decided to create a new directory called speccy under my home directory:
cd $HOME
mkdir speccy
cd speccy
Next thing I need to do is to find a compiler. Back in the day these computers were all programmed with assembly language and the best compilers like Telemark Cross Assembler come with a price. Personally I bought my copy over 10 years ago, but luckily it is now 2014 and there are several assemblers available for free. Probably the most suitable for our case is Pasmo, which is not only able to generate machine code, but it is also able to create a full cassette image complete with BASIC loader stubs. This means you can load the resulting binary in an emulator and it just works.
The steps to install Pasmo to our speccy directory are:
# Download the latest Pasmo source code and unarchive it
curl http://pasmo.speccy.org/bin/pasmo-0.5.3.tgz > pasmo-0.5.3.tgz
tar xzvf pasmo-0.5.3.tgz
cd pasmo-0.5.3
# Compile and install Pasmo to $HOME/speccy/pasmo directory
./configure --prefix=$HOME/speccy/pasmo
make
make install
cd ..
# Set up environment variables, you need to do this for every
# shell session you start or include it in your configuration.
export PATH=$HOME/speccy/pasmo/bin:$PATH
However, Pasmo is not the only tool suitable for our needs. For those who are a bit afraid to dive into the wonderful world of assembly language, there is also a C compiler called z88dk. It was originally developed for the Cambridge Z88 portable computer but is now ported to lots of different Z80 powered platforms, including many TI-8x calculators and of course our ZX Spectrum.
The source code of z88dk is not quite up to date with the latest compilers, but we can compile it to Mac OS X as follows:
# Download the latest z88dk source code and unarchive it
curl http://optimate.dl.sourceforge.net/project/z88dk/z88dk/1.10/z88dk-1.10.1.tgz > z88dk-1.10.1.tgz
tar xzvf z88dk-1.10.1.tgz
cd z88dk
# z88dk assumes Mac OS X is big-endian, but this is not true any
# more with Intel hardware, so we need to patch it accordingly.
# Also it assumes that bool is not defined, but on latest Apple
# clang stdbool is included by some of the other headers so we need
# to patch that as well. Finally we need some compile flags to get past
# some of the errors and warnings given by clang. On some systems it
# might work to simply replace these four lines with "make".
sed -i "" 's/__BIG_ENDIAN__/__APPLE__/g' src/z80asm/config.h
sed -i "" 's/#define ENDIAN$//g' src/z80asm/config.h
sed -i "" '/^typedef int bool/d' src/z80asm/types.h
CFLAGS="-Wno-return-type -Wno-comment" make
# Set up environment variables, you need to do this for every
# shell session you start or include it in your configuration.
export Z80_OZFILES=$HOME/speccy/z88dk/lib/
export ZCCCFG=$HOME/speccy/z88dk/lib/config/
export PATH=$HOME/speccy/z88dk/bin:$PATH
# Finally compile all libraries, this takes some time
make libs
cd ..
Our build environment should now be fully set up!
Ok, how on earth do I test if this whole thing is working?
No worries, I have a sample hello world example available as assembly listing and as C source code:
hello.asm:
org $8000
; Define some ROM routines
cls EQU $0D6B
opench EQU $1601
print EQU $203C
; Define our string to print
string:
db 'Hello world!',13
start:
; Clear screen
call cls
; Open upper screen channel
ld a,2
call opench
; Print string
ld de,string
ld bc,13
call print
; Return to the operating system
ret
end start
hello.c:
#include <conio.h>
main() {
/* Clear screen */
clrscr();
/* Print string */
cprintf("Hello world!\n");
/* Return to the OS */
}
You can compile these examples with:
pasmo --tapbas hello.asm hello_asm.tap
zcc +zx -lndos -create-app -o hello_c hello.c
The result should be two files: hello_asm.tap and hello_c.tap. I didn't have time to mess with the real hardware yet, so I simply downloaded the FUSE emulator on my Mac and loaded both files there respectively. The results can be seen below:
hello_asm.tap:
hello_c.tap:
Notice that the font is very different on the second one, since z88dk includes its own smaller font that is different from the one included in ZX Spectrum ROM. It is also much slower to paint on screen, but that is another topic. It is also worth to note that the hello_asm.tap image is 152 bytes, but the hello_c.tap is 2556 bytes, quite a big difference.
These examples are very simple, but it is easy to do much more complicated stuff once the build environment is there. I will maybe get back to that in another blog post.
- Juho Vähä-HerttuaSenior Software Engineer