7

(avery) #1

Getting retro with the Z80: software


TUTORIAL


The first thing the code does is load the address of
the string (which we labelled with hello) into the BC
register pair using the ld instruction. We’ll be using
BC as a 16-bit register, rather than two 8-bit ones.
This is simply a matter of using instructions that use
it that way, and that is just a matter of referring to bc
in the assembly code rather than b and c separately.
Keep in mind that you can mix and match how you
use the registers; the assembler doesn’t care, and the
CPU (or the simulator) doesn’t either.
Next, we have the start of the loop that outputs
the string, a character at a time. We label this point
in the code so that we can refer to its address later;
loop seems like a reasonable name. If you look at
line 10, you can see the label being used in a jp (aka
jump) instruction.
The loop starts by loading A from where BC
specifies. Next, we want to check if what was loaded
into A was a zero. Every CPU has a flag register that
includes bits indicating if the last operation resulted
in a zero value (very commonly used for decision
making), or overflowed, or generated a carry or
borrow, etc. Here we’re looking for the 0 byte at
the end of the string, but loading a value in the Z80
doesn’t change those flag bits, so we have to force
the zero bit to be updated. A simple way to do that
is to OR the A register with 0. That will put the result
(which will be the same as the value previously in A)
back into A and, more importantly, update the zero
flag. Then we use a conditional jump instruction that
will jump to the specified address when the zero flag
is set. Here, the zero flag is set if the number in A is
zero. In that case, we jump past the end of the loop
and stop.
If A didn’t contain a zero, we output its value
to port 1. After that, BC has 1 added to it, i.e. it is
incremented, so that it will contain the address of the
next character (or the zero terminator). Adding and
subtracting one are such common operations that
they have special instructions: inc and dec.
Now we’ve reached the end of the loop and simply
jump back to the start.

IN A ROUTINE
One important programming concept is the
subroutine, aka functions. Assembly language
provides two instructions that implement this
concept: call jumps to a subroutine, saving the
program counter (PC) on the stack; ret pulls the
return address off the stack and puts it into the PC.
Both call and ret have conditional versions.
We can rewrite our code using a function to output
a string, and use relative jumps as a demonstration:

THE CODE
Now we’re ready to walk through the Hello
World code.
The first line simply gives the program a name.
This will appear at the top of each page of the listing
file along with the file name. This is optional, but a
good idea.
As we saw earlier, the string we want to output
is stored in memory starting at the location labelled
hello. The defm on line 15 sets aside and initialises
memory for the string we want to display, one byte
per character. The defb on the next line does the
same for a series of 8-bit numbers. Here it’s used to
add the carriage return and line feed (characters 13
and 10, respectively) as well as a 0 to mark the end
of the string.

The first line simply gives the program a
name. This will appear at the top of each page
of the listing file, along with the file name



Above
The Z80 board we
built last issue. Head
to hsmag.cc/issue7
for details
Free download pdf