ZalaXa 3 — A Simple Start Menu

I bet everyone is eager to dive right in and start with some multicolour goodness. I definitely am. We’re not quite there yet, though. Lets’s set up a simple start menu first. There’s a good reason for this, which will be apparent later 🙂

Let’s have a look at the code for part 3 in GitHub. The first section, which deals with configuring Zeus and setting org, is the same as part 2.

The next section expands on the starting code that gets run as soon as we jump into our program:

12
13
14
15
16
Start                   proc                            ; A named PROCedure (also our start point)
                        ld sp, Start                    ; Put our stack right below the program
                        Border(Black)                   ; Set the border to black using a helper macro
                        call ClsAttr                    ; Call another named procedure to do a fast CLS (like GOSUB)
                        Print(MenuText, MenuText.Length); Print text on the screen using ROM routines

Immediately, on line 13 we set our stack to just below the program. This is somewhat of a personal preference, but stacks are generally either kept above or below our programs, and there’s a possibility this might end up being a 128K-only game. If that’s the case, the top 16K of RAM becomes quite valuable as it can be switched in and out with other 16K banks. Having the stack in this area tends to put a crimp on this.

Line 14 calls a macro to set the border to black. Hopefully this is self-explanatory. I like to treat macros as opportunities to improve code readability—although the opposite can be true too! The macro, further down at line 45, is the standard way of doing this in Z80.

45
46
47
48
Border                  macro(Colour)                   ; Macro (makes the code more readable) to set border
                        ld a, Colour                    ; Set a to the colour desired
                        out (ULAPort), a                ;   and output it to the ULA Port (defined in constants)
mend                                                    ; No RET is needed - this code is inserted inline

ULAPort is a constant I use instead of $FE (decimal 254), purely to make the code self-descriptive. The other readability win here is that the macro is parameterized—whatever you pass in as the value of Colour gets substituted. As I noted in the comments, there is no difference between writing Border(0), and writing:

                        ld a, 0                         ; Set a to black
                        out ($FE), a                    ;   and output it to the ULA Port

Incidentally, Zeus’s macro expansion is pretty clever. I could use my macro, unchanged, with Border(b) (without any quotes around the b). As b is a valid Z80 register, and ld a, b is a valid opcode, Zeus will assemble exactly that! It’s even smart enough to know that Border(hl) would result in ld a, hl—and grumble mightily that this is an invalid opcode.

But where were we? Oh yes. call ClsAttr on line 15 is a function. The code in this function is big enough that we don’t want to repeat it unnecessarily by inlining it every time we clear the screen. Nor does it take any parameters, so let’s assemble it once, invoke it with call, and let it return to the next line with ret, exactly like GOSUB/RETURN in BASIC. The function looks like this:

33
34
35
36
37
38
39
40
41
ClsAttr                 proc                            ; Do an attribute CLS using LDIR block copy
                        xor a                           ; Set a to 0 (blank ink, black paper)
                        ld hl, AttributeAddress         ; Address to start copying from (start of attributes)
                        ld de, AttributeAddress+1       ; Address to start copying to (next byte)
                        ld bc, AttributeLength-1        ; Number of bytes to copy (767, all the attirbutes)
                        ld (hl), a                      ; Set first byte to attribute value
                        ldir                            ; Block copy bytes
                        ret                             ; Return from the procedure (like RETURN)
pend

This is a pretty easy method to copy the same value into a range of bytes. Again, AttributeAddress and AttributeLength are defined as constants for readability.

On line 16, Print(MenuText, MenuText.Length) is another macro. This one is highly parameterized, and as such really improves readability.

52
53
54
55
56
57
58
59
Print                   macro(TextAddress, TextLength)  ; Macro to print text on the screen using ROM routines
                        ld a, ChannelUpper              ; Channel 2 (defined in constants) is the upper screen
                        call CHAN_OPEN                  ; Open this channel (ROM routine)
PrintLoop:              ld de, TextAddress              ; Address of string to print
                        ld bc, TextLength               ; Length of string to print
                        call PR_STRING                  ; Print string (ROM routine)
 
mend

This makes use of two ZX Spectrum ROM routines, to print a string of text on the upper screen. I won’t say too much about them, as they’re often used in other programs.

You’ll notice I’m using dot notation to refer to MenuText.Length in the macro invocation. MenuText is another procedure which I’ve used to encapsulate some data that will be assembled into bytes in RAM, and also some constants. All the constant definitions inside a proc are local to that proc, so there’s an opportunity to namespace your labels for greater semantic clarity. I’m also making use of colour and attribute constants, so that the code resembles a BASIC PRINT statement as much as possible.

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
MenuText                proc                            ; Named procedure to keep our print data tidy
                        db At, 7, 13                    ; These codes are the same as you would use
                        db Paper, Black, Bright, 1      ;   with Sinclair BASIC's PRINT command
                        db Ink, Red, "Z"
                        db Ink, Yellow, "A"
                        db Ink, Cyan, "L"
                        db Ink, Magenta, "A"
                        db Ink, White, "X"
                        db Ink, Green, "A"
                        db At, 21, 6
                        db Ink, Yellow, "PRESS "
                        db Ink, White, "SPACE"
                        db Ink, Yellow, " TO START"
Length                  equ $-MenuText                 ; Let Zeus do the work of calculating the length
pend                                                   ; ($ means the current address Zeus is assembling to)

After printing the menu, the next section goes into an endless loop until the space key is pressed.

17
18
19
20
21
22
23
WaitForSpace:                                           ; All labels inside procedures are local to that procedure
                        halt                            ; Wait for the next 1/50th second interrupt (like PAUSE 1)
                        ld bc, zeuskeyaddr(" ")         ; Get the IO address to input
                        in a, (c)                       ; Read those 5 keys
                        and zeuskeymask(" ")            ; AND with the bit for SPACE
                        jr z SetupGame                  ; If it's zero the key is pressed
                        jp WaitForSpace                 ; Otherwise check keys again

Again, this is standard Spectrum Z80 stuff for reading keys. Zeus has nice zeuskeyaddr and zeuskeymask functions to make it slightly easier to code and read.

The final section does a clear screen and goes into another endless loop after the space key is pressed. This will probably get replaced in the next tutorial, but I wanted it to be clear that pressing space actually does something.

24
25
26
27
28
29
SetupGame:
                        call ClsAttr                    ; Clear the screen to prove we pressed space
EndlessLoop:
                        halt
                        jp EndlessLoop                  ; Go into an endless loop (for now...)
pend

The code at the end is worth mentioning briefly—Zeus can create TAP, TZX, SNA and Z80 files directly from code. What I’m doing here is similar to what Pasmo does with it’s --tapbas mode. Where Zeus comes into its own, though, is generating tape files for 128K Spectrums. We will see later, perhaps 🙂

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
BinPath                 equ "..\bin"                    ; Relative to main.asm
TapFile                 equ BinPath+"\ZalaXa.tap"       ; Filename of tap file
 
 
 
; Make tape file
End                     equ $                           ; Calculate the last byte of our program
Size                    equ End-Start                   ; Count the bytes to save to tape
output_tap              TapFile, "ZalaXa", "seven-fff.com/zalaxa", Start, Size, 2, Start
                                                        ; Make a .TAP file. Parameters:
                                                        ;   1) the file name
                                                        ;   2) the name of the BASIC loader program
                                                        ;   3) a comment that goes in the TAP header
                                                        ;   4) Start of machine code program
                                                        ;   5) Length of machine code program
                                                        ;   6) Zeus mode 2 files use the standard ROM loader
                                                        ;   7) Tell the BASIC loader what to run with RANDOMIZE
                                                        ;       USR (like Zeus_PC tells the Zeus emulator)

Well, that was a very long, very explain-y post. Next time I will talk a little more about NIRVANA+ and introduce some multicolour code!

ZalaXa 2 — Setting up the dev environment

I’m going to dive right in and talk about setting up my dev environment. All the code and techniques we use with Z80 and ZX Spectrum development can be made to work with any toolchains, and every established developer has their favourites.

I’m no exception, and I’m an enthustiastic user of Simon Brattel‘s Zeus. Zeus has a lineage going back to 1977 on Simon’s homebrew computers, and on the Spectrum from the beginning. Many now-legendary games developers used Zeus back in the day.

Simon was also an early pioneer of cross-development. Many of his classic games, like Halls of the Things and Dark Star, were written on his Z80 homebrew computer, Basil, with a Parasys debugger link between Basil and the Spectrum.

The Windows cross-development IDE has had a continuous pedigree since, as the main IDE for Simon’s electronics and processor design business. And, for the last decade, as a continuously-developed Spectrum-oriented assembler, IDE and emulator with, incidentally, inbuilt support for NIRVANA+.

My personal affiliation to Zeus is based on how easy it makes my development process. I suspect most of this is an affinity between the way Simon and I think—some of this being that our thinking is similar, and some being the way I’m challenged to think differently.

Whatever the philosophical underpinnings, the end result is that I’ll be doing this tutorial series on Zeus. Feel free to follow along with Zeus if you’re a relative beginner, or adapt my examples for your own favourite IDE, assembler and emulator.

Installing Zeus is easy. Download the latest version of zeus.exe here, stick it in a directory and make a shortcut to it. If you’re on Windows 7/8/10, you’ll have to do the usual unblocking the first time you run the program. Zeus works well on Linux and MacOS under Wine, although I only use Windows myself.

I’ve made a GitHub repository for this series of tutorials. Familiarize yourself with the git version control system, and the process of cloning a repository, if you don’t already know this. I use TortoiseGit on Windows, and find it very simple and intuitive.

I will establish a convention that the source for the latest version of ZalaXa will be inside the /src directory, and the source for a particular tutorial post will be inside the /tutorials directory—this posts code is found at /tutorials/part2, for example.

Once you have cloned the repository and updated it to the latest version, choose File >> Open in the Zeus menu, and open \tutorials\part2\main.asm. You will see this code—the bare minimum template to assemble a Z80 program and run it in Zeus’s emulator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
; main.asm
 
zeusemulate             "48K"                           ; Tell the Zeus emulator to be a 48K Spectrum
zoLogicOperatorsHighPri = false                         ; Zeus assembler options
zoSupportStringEscapes  = false                         ;   (see Config tab
zoAllowFloatingLabels   = false                         ;   for details)
Zeus_PC                 = Start                         ; Tell the Zeus emulator where to start running code at
org                     $8000                           ; Tell the Zeus assembler where to place the code
 
 
 
Start                   proc                            ; A named PROCedure (also our start point)
                        jp Start                        ; Go into an endless loop!
pend

Click the Assemble Then Emulate button:

You should be rewarded with an assembler status message saying this:

nErrors = 0 nRedef = 0 nUndef = 0

and a completely blank white spectrum screen in the Zeus emulator:

And on that bombshell, we will continue in the next post!

ZalaXa 1 — The Idea

Talking to my Spec-Chum Andy Dansby the other day, I came up with the idea of doing a blog tutorial series on writing a multicolour ZX Spectrum game.

Maybe not for the complete beginner, as there are any number of excellent tutorials for n00bs out there already. But something where you follow along and let it carry you through into a specialised subject area.

I will talk about the history of multicolour on the Spectrum in more detail later, but for now, let’s say that one of the strengths, and quirks, of the Spectrum is its display format. Unlike other home micros of the era, the Spectrum restricted you to one foreground and one background for every block of 8 by 8 pixels—the primary motivation of the designers being economy: a relatively small amount of precious RAM was taken up by graphics data, leaving more for other program features. Being as popular as it became, this was taken as a creative challenge by 1980s programmers, and many excellent games and other programs were written, making maximum use of both the available memory and these particular visual constraints.

It’s not the only way of doing things, though. Instead of the dedicated Spectrum ULA handling the graphics output, earlier homebrew computers often used the microprocessor to draw the display. This took up much of the available processor time, and involved precise timing—the program would “chase” the TV’s raster beam as it scanned across and down the screen, outputting pixels at the precisely-synchronised time.

At some point during the Spectrum’s heyday, some talented people figured out raster chasing could be used on the Spectrum too—you could let the ULA chip draw the pixels, but you could change the colours on the fly, as you chased the raster beam. In this way you could have one foreground colour in every 8×4, 8×2 or even 8×1 pixel block. Fast forward to 2015, and the smart folks had systemised this to the extent of enabling Einar Saukas to publish the NIRVANA bicolor graphics engine, using 8×2 attributes.

It’s this engine, or rather its fullscreen variant NIRVANA+, that I’d like to base this tutorial series around.

As a taster, here’s Einar’s NIRVANA+ demo program. Download the .TAP here.

ZalaXa — Table of Contents

  1. The Idea
  2. Setting Up the Dev Environment (code)
  3. A Simple Start Menu (code)
  4. First Glimpse of Multicolour (code)
  5. Wide Tiles (code)
  6. Preshifted Sprites (code)
  7. Moving the Ship (code)
  8. Clearing the NIRVANA+ Screen (code)
  9. Proportional Fonts With FZX (code)
  10. A Naïve Scoring Routine (code)
  11. Flashy 1UPmanship (code)
  12. Towards a Scrolling Starfield (code)
  13. You Gotta Roll With It (code)