Learn Z80 assembly · Lesson 4 of 7
Text from a table, a random number from R, CP, and the flags behind the jumps
A whole game this time, if a small one. Press R, P or S on the TRS-80's screen; the program picks its own move, prints both, and says who won. Press again for another round.
Carried over: the screen addresses, 64 cells a row (lesson 1); ADD A and the pairs (lesson 2); the keyboard read with its AND mask and JR Z wait, BIT to tell the keys apart, and a 16-bit add into HL, ADD HL,DE there and ADD HL,BC here (lesson 3). New: copying text with LDIR, a table of words with DB, ADD HL,HL to double, LD A,R for a random number, CP and SUB, the carry flag with JR C and JR NC, and two bytes of memory as variables.
Text is bytes in a table
There is no print. Text on the screen is bytes in video memory, so printing is copying bytes from the program to the screen. DB puts bytes in the program, and a quoted string is its characters' codes:
WORDS: DB 'ROCK ' ; 0: each word is padded to 8 bytes,
DB 'PAPER ' ; 1: so word n is at WORDS + 8n
DB 'SCISSORS' ; 2
DB 'A DRAW. ' ; 3
DB 'YOU WIN!' ; 4
DB 'I WIN. ' ; 5LDIR is the copy loop as one instruction: it copies a byte from where HL points to where DE points, moves both on, counts BC down, and repeats until BC is zero. It is lesson 1's loop, built in. To print word n, HL is WORDS + 8*n, and three ADD HL,HL make the times eight, each one doubling HL:
LD L,A ; HL = A times 8: each word is 8 bytes
LD H,0
ADD HL,HL
ADD HL,HL
ADD HL,HL
LD BC,WORDS ; plus the table's address
ADD HL,BC
LD BC,8
LDIRIn JavaScript the table and the copy are:
const words = ['ROCK ', 'PAPER ', 'SCISSORS', 'A DRAW. ', 'YOU WIN!', 'I WIN. '];
for (let i = 0; i < 8; i++) screen[de + i] = words[n].charCodeAt(i);LDIR uses BC, DE and HL, all three pairs, so anything the program still needs has to be somewhere else while it prints. Here the program's move and the verdict go to two bytes of memory, MINE and VERDICT, with LD (MINE),A. Of the 8-bit registers, only A can be stored at an address named like that, so the move goes through A. The program prints the same way three times, which lesson 5 turns into one routine with CALL.
Waiting for a key
P, R and S are in the row read at 3804H, one of the rows at 3801H to 3840H, as bits 0, 2 and 3: as in lesson 3, that order within the row is the one Fugløy's emulator uses, not a page of the manuals. The wait is lesson 3's, with a different mask:
KEY: LD A,(3804H) ; the keyboard row with P (bit 0), R (bit 2) and S (bit 3)
AND 0DH ; keep those three bits (0000 1101)
JR Z,KEY ; none is down: read againAfter a round the program waits at LETGO for the key to come up again, the same loop with JR NZ, so one press is one round.
A random move from R
R is the Z80's refresh register. It counts up as instructions are fetched, by itself, and its low seven bits run from 0 to 127 and round again. The Level II ROM's own RANDOM takes its seed from it. Nothing about it is random, but the program reads it the moment your key is seen, and while you decide, the KEY loop goes round tens of thousands of times a second, so the value you get depends on when, to the microsecond, you pressed.
That gives a number from 0 to 127, and a move is 0 to 2. The remainder by 3 is a loop:
MOD3: CP 3 ; compare with 3: carry when A is less than 3
JR C,GOT ; A is 0, 1 or 2: the program's move
SUB 3 ; otherwise take 3 away and compare again
JR MOD3CP and the flags
CP 3 is SUB 3 that throws the result away: A stays as it was and only the flags change. Z is set when A was 3, and C, the carry, when the subtraction had to borrow, which means A was less than 3. So after CP n:
JR Zjumps when A equals n, andJR NZwhen it does not.JR Cjumps when A is less than n, andJR NCwhen it is n or more.
That is the whole of if (a < 3) on a Z80: a compare that sets flags, then a jump that reads one. The same carry decides who won. With rock 0, paper 1 and scissors 2, each move beats the one before it, round in a circle, so your move minus the program's, wrapped into 0 to 2, is 0 for a draw, 1 when you win and 2 when it does:
LD A,C ; who won: your move minus the program's
SUB B ; carry when it went below zero ...
JR NC,KEEP
ADD A,3 ; ... so add 3 back: A is now 0, 1 or 2
KEEP: ADD A,3 ; 3 a draw, 4 you win, 5 I win: the verdict's wordIn JavaScript that is (you - me + 3) % 3, and the last ADD A,3 turns it into the verdict's place in the table.
On the Model I
The Model I has the same keyboard rows and the same screen, and every letter here is a capital, so the program runs unchanged.
Loading the program…
Loading the TRS-80…