.png)
Every program you have written so far runs in a straight line: first instruction to last, once, and then it stops. That was enough to print a word. It is not enough for anything else.
A game needs to do the same thing repeatedly - draw the screen, read the keys, move things, over and over. It needs to skip work that is not needed right now. Neither is possible while the processor only ever runs the next instruction along.
This section is the first tool for that: JP, which changes what happens
next.
You have already seen the thing JP works on.
At the end of every program, in the line of registers, there is a column
marked PC:
A BC DE HL PC SZ-H-PNC
5D 3D41 1EFF FB3E 0769 10010010
PC is the program counter - the address of the instruction the processor
is about to carry out. It is how the machine keeps its place. After each
instruction, the processor adds that instruction's length to PC, which is
what makes a program run forwards through memory.
JP puts a different address into PC. That is the whole of it. The
processor does not know it has jumped; it simply carries on from wherever PC
now points.
Here is a program that needs one. Type it in as arrow.asm:
ORG 256
JP start ; step over the data
arrow: DEFB 93 ; right arrow - see Appendix I
start: LD A,(arrow) ; fetch that byte
RST 8
DEFB 158 ; ZOUTC
RET
Run it and you get a right arrow on screen.
Two new things are happening.
arrow: and start: are labels. A label is a name for a place in your
program. You put one in front of a line, ending in a colon, and afterwards you
can refer to that place by name. The assembler works out what address it
turned out to be.
LD A,(arrow) reads memory. The brackets mean "the contents of", so this
loads the byte stored at arrow - the 93 - rather than the address itself.
This is the distinction S3 mentioned and here it is doing real work.
And the jump is not decoration. Without it, the processor would reach
DEFB 93 and try to run it. It has no way of knowing you meant that byte as
data. Anything in the path of execution gets executed, so data that sits
before the code that uses it has to be jumped over.
Ask the assembler to show its working. From a terminal, in the folder with your file:
z80asm -l -o ARROW.COM arrow.asm
The -l prints a listing:
0100 c3 00 00 JP start
0103 5d arrow: DEFB 93
0104 3a 03 01 start: LD A,(arrow)
0107 cf RST 8
0108 9e DEFB 158 ; ZOUTC
0109 c9 RET
010a
04 01
The left column is the address of each line. The next few bytes are what that line assembled to. Read it and several things fall out at once.
Instructions are different lengths. JP start is three bytes, RST 8 is
one, LD A,(arrow) is three. This is why you cannot work out an address by
counting lines - you have to count bytes, and the assembler is better at it
than you are.
arrow: is at 0103, and start: is at 0104. Those are the addresses your
labels turned into. Nothing about the names survives into the program; they
exist only while assembling.
JP start shows c3 00 00 in the listing, but the finished program holds
c3 04 01. start is defined further down than the jump that names it, so
the address was not yet known when that line of the listing was printed. The
04 01 printed after the end of the file is the correction: the real target
address, filled in once the assembler has seen the whole source. C3 is the
instruction for jump; the two bytes after it are the address 0104 - and they
are the wrong way round. The low byte comes first.
That last point is not a mistake in the listing. The Z80 stores every 16-bit
value with its low byte first, an arrangement called little-endian. You
will see it everywhere: addresses, counters, anything that does not fit in a
single byte. Get used to reading 04 01 as 0104 now and it will stop
surprising you later.
LD A,(arrow) shows the same thing: 3a 03 01 is the instruction 3A
followed by the address 0103, low byte first, which is where arrow: is.
There are only three, and the assembler enforces all of them.
The colon is required, and nothing may come between it and the name.
loop: is a label. loop is not - the assembler will read it as an
instruction it does not recognise. loop :, with a space before the colon,
fails the same way.
No spaces inside the name. next item: is two words as far as the
assembler is concerned, and it will reject the line.
Capitals matter. loop: and LOOP: are two different labels. Jumping to
LOOP when you defined loop gives you error: unable to resolve reference,
and the two can even coexist in one program as separate names - which is a
good way to confuse yourself. Pick one style and keep to it.
That is the lot. Everything else is up to you:
counter: DEC C ; label and instruction on one line
JR NZ,counter
verylongname: ; length is not limited
NOP
spaced: NOP ; spacing after the colon is free
Names can contain digits and underscores, and may even start with a digit. A label on its own line, with the instruction underneath, is the usual way to write a loop target - it makes the destination easy to spot when you are scanning for it.
The one thing worth doing that the assembler will not enforce: make the name
say what the place is for. loop: and done: cost nothing to read. l1: and
x: will mean nothing to you in a fortnight.
If you read Einstein code from elsewhere you may find much stricter label rules described - names of six characters or fewer, and exact spacing. Those belong to other assemblers, not this one. Appendix II covers the differences.
Nothing says a jump has to go forwards. Point one at a label above it and the program repeats:
ORG 256
loop: LD A,'*'
RST 8
DEFB 158 ; ZOUTC
JP loop
Run it. Asterisks fill the screen and keep coming.
They keep coming because nothing stops them. JP loop is unconditional: it
jumps every single time, so the program has no way out and never returns to
the prompt. You will need to press Restart to get your machine back.
That is worth doing once deliberately, because it is going to happen to you by accident, and it is much less alarming when you recognise it.
This is also the limitation that shapes the next section. You can now repeat work, but only forever. To repeat something ten times, or to keep going until a key is pressed, the program has to be able to ask a question and jump only if the answer comes out a particular way.
JP start line from the arrow program and run it. Does the arrow
still appear? Before you decide what that means, look at DE in the
register line and compare it with what it read before. Something has
changed. Working out what, and why, is the most useful thing in this
section.DEFB 93 after the RET instead, and delete the jump. Does it
work? Why does it not need a jump now?LD A,(arrow) to LD A,arrow - no brackets.
What gets printed, and what does that tell you about the difference between
a value and an address?loop: down one line so it sits on RST 8. What should
happen now, and what does the screen fill with?4.1 - Three arrows. Codes 91, 93 and 94 are left, right and up arrows.
Write a program that stores all three with DEFB, jumps over them, and prints
them in order on one line.
4.2 - A pattern that never stops. Make a loop that prints two different characters each time round, so the screen fills with a repeating pattern rather than one character. Add a line break to the loop and see what changes.
4.3 - Read the map. Assemble one of your own programs with -l and find
the address of every label in it. Then work out, by adding up the byte lengths
in the listing, why each one landed where it did.
4.4 - Predict before you look. Write a short program with a label, and
before assembling it, write down the address you think the label will get.
Then check with -l. Repeat until you are right first time - it is a better
test of whether you have understood this section than anything you can read.
| What you see | What it means |
|---|---|
error: command or comment expected (was start LD A,1 ) |
A label without its colon. start: works, start does not. |
error: unable to resolve reference: strat |
The label in your JP does not match the label you defined. Usually a spelling difference - or a difference in capitals, which counts. |
| The program never comes back and the screen fills | A loop with no way out. Press Restart. |
| The program prints nothing and returns immediately | Something is being executed that you meant as data, or a jump is landing past the part that prints. Check the listing for where your labels actually are. |
| The right output, but a register you never used has changed | A data byte in the execution path was run as an instruction. It did no visible harm this time. It is still a bug. |
PC, the program counter, holds the address of the next instruction. JP
writes to it.JP <label> jumps forwards or backwards. A backward jump repeats, and an
unconditional one repeats forever.z80asm -l shows the address and bytes of every line. Instructions are
different lengths, so count bytes and not lines.c3 04 01 is a jump to 0104.S5. Jumping without saying where. There is a second jump instruction that
is smaller than JP and does not name an address at all - which turns out to
matter more than it sounds.