Getting started with ZX Spectrum Development

Excited by meeting Henrique Olifiers and Jim Bagley at last year's Revival 2016,
I was all set to start on developing a game for the upcoming Spectrum Next.

It's taken them slightly longer than expected to launch the Kickstarter for this
absolutely stunning looking machine
; tweaks to the spec being the main reason
for the delay. The team are super passionate about delivering something that
really captures the essence of the Spectrum, while bringing it up-to-date with
new abilities that it could never have had back in the day.

That Kickstarter is already well over its initial goal; as I write this, it's
sitting at £431,013 - the target amount was £250,000. It's looking likely that
it'll meet at least the next stretch goal at £450,000 by the end of this week,
and hopefully then blast on to meet the others.

All this has gotten me excited about Spectrum development again, and so I'm
looking at setting up a development environment to make it easier for me to do
so. Bear in mind that I was a kid when I had my Spectrum back in the 80s and
early 90s, so any programming I did was fairly limited. I barely touched machine
code, although I did experiment a little with a cool package called White
Lightning
(not the cider - it was a version of FORTH intended specifically for
games programming).

I'm working on a Mac, so not all of the information I share here will necessarily
be relevant to other environments. That said, I expect to mostly be working with
Z80 Assembly language which will be relevant.

Since I don't yet have my Next, I'll be working in an emulator. I'm told that
ZEsarUX is the best emulator currently available, and it's supposed to be able to
emulate the TBBlue, the forerunner to the Spectrum Next. I'll also need to be
able to compile my programs, and Z88DK seems
to be the best solution for that. It's going to take me a while to cobble these
together, but I'm sure I'll get there in the end :)

My first challenge to myself: be able to get a machine code program running that
will clear the screen. Sounds simple, right? Well, Paul Land posted a very
useful Spectrum memory map
on his website. As I understand it, I should be
able to clear the screen by blanking out all the bytes from $4000 (16384) to
$5AFF. I'll give that a go, and update this post with my progress. And an video
of it, if I get that far...

I've also found these very good videos by Michael Daley, who recommends a slightly
different toolset for building Spectrum software... part 1 is here,
and part 2 is here. Michael recommends
using ZXSP, and the Pasmo assembler. He's also taken the time to explain how to
set up building your programs from within Sublime Text 3, which I found extremely
useful. Michael is the author of SpectREM;
I'd assume that means he's moved away from ZXSP since his earlier videos ;)

Progress

After watching the Michael Daley videos I mentioned above, I got a (slightly
less elegant) development environment configured. I'm currently using Atom,
Pasmo and ZXSP.

Atom

Well, it's just a text editor, which is all that we really need. I've installed
the language-z80asm package to get some minimal syntax highlighting. It's not
brilliant, but it does the job for now.

You can get Atom from the official website.

Pasmo

Pasmo seems to be pretty highly recommended. It can even compile to .TAP files,
which is rather handy. There wasn't a precompiled version for MacOS, so I had to
do that part.

Download Pasmo from the website here. You can try
the preview release of 0.6.0 if you like, but I'm using 0.5.4.beta2. For MacOS,
you'll want the Gzipped tar file. The Mac can unpack that format natively.

To compile it, you'll likely need Xcode installed, or at least the command-line
tools. Getting those setup is beyond the scope of this post, and is well
documented elsewhere.

Once you've downloaded and unpacked the .tgz file, you'll want to switch into
the folder you unpacked it into, and run the following commands:

./configure
make

You could also run make install; I didn't. If you're like me, you'll want to
be able to run Pasmo from anywhere, so copy it to /usr/local/bin with the
following command:

cp pasmo /usr/local/bin

If you haven't got access to that folder (I'm not sure if you can write to it
by default), then just copy Pasmo to your home folder for now.

ZXSP

ZXSP hasn't been updated in a while, but it'll do for the moment. SpectREM looks
nice, but I couldn't find a precompiled version - I built my copy in Xcode.

Download ZXSP from the creator's website;
I chose zxsp-0.8.0.pre27.zip, but that might have changed by the time you read
this.

Unpacking that will give you a Mac app, which you should drag to your Applications
folder as you would any other app. That's all you need to do for now.

Your first code

Ok. This is the (not very) exciting bit. We're going to start by clearing the
screen. I know, I know.

Open Atom (or your preferred text editor), and create a new file. I usually save
my new files immediately, because I like to save early and often. I've lost too
many hours of work by not doing so. Make sure you save the file with an .asm
extension, to make your life easier later on.

You'll want to add the following code:

; This is where your program starts in RAM.
org 40000

; Clear the screen to black.
    ld a,71         ; White ink (7), black paper (0), bright (64).
    ld (23693),a    ; Set our screen colours.
    xor a           ; Load accumulator with zero.
    call 8859       ; Set permanent border colours.
    call 3503       ; Clear the screen, open channel 2.

; Pasmo needs this to know where to start running your program from.
; It should be the same as the address you specified at the top!
end 40000

Save that as something like clear-screen.asm, and open a Terminal window in
the folder where you saved it.

Now, use the following magical invocation to compile that code into a Spectrum
.TAP file:

pasmo -1 --tapbas clear-screen.asm clear-screen.tap >> debug.txt

Let's break that down a bit, so that you know what we're doing.

The -1 means that Pasmo will output a log of the compiled output. You can open
this in Atom alongside your code. I don't really understand this yet, but I'm
told it's useful :)

The --tapbas means that Pasmo will create a .TAP file with a BASIC loader
that will automatically execute your code. This is great. Saves messing around,
you can load the .TAP file straight into your emulator.

The third parameter is the name of the file you're compiling.

The fourth is the name of the output file. In this case, you'll see that I've
given it the extension of .tap, which is what we're producing.

The final part of the command redirects the output from Pasmo into a text file
called debug.txt; this is our debug log.

All being well, you should now have a clear-screen.tap file in the same folder
with your source code. If not, you may have made a typo. Check the listing above
and make sure you haven't!

Now, load up ZXSP or your emulator of choice (if you've got real hardware handy,
you should also be able to load the TAP file there, but I don't have that at the
moment). Loading the TAP file should automatically execute your program, and
give you a black screen. Amazing, isn't it?

black screen

.... applause ....

Ok, so it's not that exciting. But it is machine code. Let's modify it slightly,
so that you can understand what's going on. We're going to change the colour of
the screen again.

The Speccy, in its original form at least, isn't terribly colourful. It has 8
colours; we set ink and paper colours at the same time, and also control whether
it should be bright or not. That's all held in a single byte, at position 23693,
which corresponds to the permanent colours - that is, those colours will stay
set until you override them with another value (look at Paul Land's page) to
confirm that).

The colour we chose earlier was black, with white ink and bright mode set,
adding up to a total of 71. This is made up as follows:

INK (0-7) + PAPER (0-7 * 8) + BRIGHT (0/64)

The colours are:

  • 0 = Black
  • 1 = Blue
  • 2 = Red
  • 3 = Magenta
  • 4 = Green
  • 5 = Cyan
  • 6 = Yellow
  • 7 = White

So, if we wanted to make the screen cyan with bright yellow ink, we'd go for:

INK (6) + PAPER (5 * 8 = 40) + BRIGHT (64) = 110

Let's try that.

; This is where your program starts in RAM.
org 40000

; Clear the screen to black.
    ld a,110         ; Yellow ink (6), cyan paper (5 * 8), bright (64).
    ld (23693),a    ; Set our screen colours.
    xor a           ; Load accumulator with zero.
    call 8859       ; Set permanent border colours.
    call 3503       ; Clear the screen, open channel 2.

; Pasmo needs this to know where to start running your program from.
; It should be the same as the address you specified at the top!
end 40000

Compile that, and load it back into the emulator. You'll end up with the screen
being cyan in the middle, and with a black border.

cyan screen

That's not quite right. I wanted the entire screen to be cyan, not just the
middle bit. So what did we do wrong? The secret is in the third line:

xor a

This sets the accumulator back to zero. Zero means black. That'll be why. Let's
try it, and see if we're right. Change the xor 0 line to the following, and
recompile/reload:

ld a,5           ; Set the colour to cyan (5).

That's better. Now you should have this:

cyan all over

That's still not quite what we want though. Unfortunately, unless I'm missing
something obvious, you can't set the border colour to be a bright shade. Only
the three least significant bits of the byte are used, which if you know any
binary means you can only represent 0-7 - just the colours we know, then!

So, I'm compromising. Let's make the centre of the screen NOT bright. Here's
the modified code:

; This is where your program starts in RAM.
org 40000

; Clear the screen to black.
    ld a,46         ; Yellow ink (6), cyan paper (5 * 8), not bright (0).
    ld (23693),a    ; Set our screen colours.
    ld a,5           ; Set the colour to cyan (5).
    call 8859       ; Set permanent border colours.
    call 3503       ; Clear the screen, open channel 2.

; Pasmo needs this to know where to start running your program from.
; It should be the same as the address you specified at the top!
end 40000

And the result:

perfect cyan

Yes, that's what we wanted.

More next time. Maybe we'll even print our names... or something equally as
exciting.