Beginning Z80 Machine Code, Lesson 5

Author(s): Syd Wyncoop
Pub Details: Time Designs Magazine v3 n1 (Volume 3, Issue 1)
Date: November/December 1986
Pages: 19-20

I left the last lesson with a challenge to you to rewrite the sample disassembly from Lesson 2 to eliminate the overflow error it contained. If you had difficulty, refer to Lesson 4. The answer was given in the comparison which explained the ADC instruction. How many of you thought of rewriting the routine using the sixteen bit intructions? Did you use LD HL,(pq) and LD BC,(pq)? Can you see how a short Basic interface (program) could collect the values and call the MC routine to perform the addition? I trust some of you are beginning to have some ideas.

We know how to load a register (pair) of memory location and perform arithmetic with the values loaded. We would, however, find MC of very limited value if these were all it could do. Most of you are familiar with the Basic commands GOTO and GOSUB. In truth, it is these instructions that give a program the power to do some real work for us.

In MC, the equivalent instructions are referred to as Jumps and Calls. The syntax for these instructions are given in Chart 5. You will note a new abbreviation, c, which is a test for the condition (or status) of a flag.

We briefly discussed the Carry flag last lesson. Here is how the F (flag) register is arranged:

Bit#   7   6   5   4   3   2   1   0
Flag   S   Z   .   H   .  P/V  N   C

Where:
   S = Sign
   Z = Zero
   H = Half-Carry
 P/V = Parity/Overflow
   N = Subtract
   C = Carry
   . = Not used
Sign Flag -      Stores the sign of the last result. Flag will
                 be set for a negative result and reset for a
                 positive result (always reflects the most
                 signifigant bit of the result).
Zero Flag -      Checks whether last result was zero. Flag will
                 be set if result is zero, else reset.
                 Note: flag = 1 if result = 0. Watch it!
Half-Carry-      Used internally by CPU to record carry from
                 bit 3 to bit 4 in registers or bit 11 to bit
                 12 in register pairs. We will ignore it.
Parity/Overflow- Has two jobs depending on the instruction
                 last executed.
                 Parity is the number of set bits in the result
                 and is referred to as odd or even. Flag will
                 be set if parity is even and reset if odd.
                 Note: even parity generates an odd flag. Watch
                 this one, also!
                 Overflow records a carry from bit 6 into bit 7
                 which effectively changes the sign of result
                 in signed arithmetic operations. Flag will be
                 set for overflow, else reset.
Subtract Flag-   Used internally by CPU to record whether
                 last instruction was addition or subtraction.
                 Flag will be set if was subtraction operation.
                 We will ignore this one, also.
Carry Flag-      Our old friend records a carry from bit 7 to
                 bit 8 in registers or bit 15 to bit 16 in
                 register pairs. Is also used to save the lost
                 bit in the shift and rotate instructions.

You will note that two bits of the flag register are unused. The status of these bits are important and there are no instructions that affect them.

Each flag can be in one of two states…set or reset (on or off). A set bit = 1 (on) and a reset bit = 0 (off). This can become very confusing when using the Zero or Parity/Overflow flags, as the flag will not be as we expect it. For instance, the Zero flag = 0 if the result was not zero. Most of the time, however, you can use the flags without knowing whether they are set or not. You need only test their status and jump accordingly.

Each flag indicates a specific condition based on the result of the last instruction executed. Chart 6 indicates how the flags are affected by the various instructions. It is important to know how the flags are affected as every instruction does not affect them and many instructions do not affect them as you might expect.

Enough of that, back to the Jump instructions. This instruction has two versions, Jump and Jump Relative. The mnemonics are JP and JR, respectively.

JP is equivalent to Basic’s GOTO. JP begins executing the next instruction at the absolute address you specify as its argument. A JP 4000h instruction will send the CPU off to address 4000h to find the next instruction to execute. Your umps can be conditional…that is, they can test one of the flags and jump only if the condition is met.

JR requires the introduction of another Hex to Decimal conversion chart, Chart 7. You will note that the first half of this chart is the same as our previous Hex to Dec chart (Lesson 1). The last half, however, indicates negative numbers. When numbers are used in this fashion, they are referred to “signed numbers”. Signed numbers merely means that the most significant bit (bit 7) is used to represent the sign of the number. A set bit (1) is a negative number and a reset bit (0) is positive.

JR also requires a brief discussion of the register pair PC. PC is a special register pair not normally accessible to us. It is called the Program Counter and its job is to keep track of where the next instruction to execute is located. All Z80 instructions are 1,2,3 or 4 bytes in length. The CPU will always advance PC by the correct number of bytes for the instruction it is about to execute. The effect of this is to skip any arguments belonging to the current instruction so as to be in position to fetch the next instruction.

Any jump instruction causes PC to discard the address it contains and replace it with the new address, as specified in the jump instruction. Note, PC will always contain the address of the next instruction to execute, not the current one.

The JR instruction adjusts the PC by adding the value specified to the current value of PC. In other words, JR tells the CPU to Jump to address X, which is Y bytes from where PC is. Y can only be in the range of -128 to 127 and X is the calculated new address. In the case of negative values, the program would jump back to a previous instruction (loops) while positive numbers would cause the skipping over of the next Y bytes.

JR can also be conditional as indicated in Chart 5 and discussed above for JP.

When programming in Basic, it is quite common to have a line such as:

100 GOTO 10*VAL A$+1000

There is a MC instruction, JP (HL), which emulates this type of operation. This instruction will jump to the address held in the HL register pair. This allows a routine to build up an address from tables or inputs and transfer program control to that address. We will not discuss this much further now as it represents some pretty advanced programming.

CALL is our GOSUB equivalent. It acts exactly like BASIC’s GOSUB. A jump is made to the specified address and a return is made to the instruction that would have been executed next had the CALL not been encountered. This is accomplished by saving the address in PC on the stack (we will explain the stack later) before making the jump.

There is a special case of CALL, that does not require an address to be specified, which is know as RST. RST is read restart , and is unique because it is the only instruction that uses an eight bit address. RST calls a subroutine with a one byte instruction.

Some important points about RST are that it is unconditional and usually computer specific (can not run on another Z80 based computer). Being computer specific is due, unfortunately, to there already being instructions at all the RST addressed, which cannot be changed. This is due to our operating system being in a ROM type memory. All is not lost though. Since these are very handy instructions, Sinclair put some of the most accessed routines there. We will find that we can use some of the RST instructions, after all.

As with any GOSUB instruction, Calls and RSTs require a return instruction to let the CPU know the routine has finished its task. The mnemonic for return is amazingly enough RET. RET will perform exactly the operation you would expect it to, and your returns can be conditional. Conditional returns allow for many exit points based on completing certain tasks. There are two special RETs which we will discuss later because they are used to return from the interrupts.

We have learned about the flags and how to make jumps and calls based on their status. We now need to explore some of the ways to set these flags in order for our tests to be meaningful. One of the ways to do this is directly with the CCF and SCF instructions.

CCF means Complement the Carry Flag. If Carry was set, it will be reset and vice versa. SCF means Set the Carry Flag. The Carry flag will set by this instruction.

Another way to affect the flags is with the remainder of the arithmetic instructions (I’ve been holding out on you again). These are also listed on Chart 5, and can not truely be refferred to as arithmetic instructions, except for CP.

CP, which means Compare, is a neat and often used instruction. CP sets all the flags as if a value were subtracted from the Accumulator, but without changing the value of the Accumulator! It is important to realize the result of the Compare is not stored anywhere, only the flags are affected. CP has two special forms, CPI and CPD, which are read Compare with Increment, and Compare with Decrement. CPI performs the same as a CP (HL) instruction would, except that HL is incremented and BC is decremented. The only flag affected is the P/V flag which is set according to the value of BV. If BC = 0, then P/V = 0.

CPD is the same as CPI except that HL is decremented. The effect on the flags is the same.

The next instruction is DJNZ…which is not Greek! DJNZ is read “decrement the B register and jump relative if B is not zero”. This is an extremely useful instruction which leads to the B register being used as a counter. DJNZ can be compared to the Basic loop control variable. The equivalent Basic statement would be as follows:

10 For X = 10 to 0 Step -1
20 (do job here)
30 Next X

In order to perform the same operation as DJNZ using any other register, you would need two instructions:

DEC L
JR NZ, Loop

To use DJNZ, you must properly load the B register. You can then construct a loop to do whatever task you wish. You can even reuse the B register in the loop, if you properly preserve its value first. More on this preservation of values later.

CPL stands for Complement. Each bit of the Accumulator is altered (complemented). For example: if the Accumulator contains 11011101b, its complemented form would be 00100010b.

NEG is the last unexplained instruction on chart 5. NEG will negate the Accumulator, which means to place the two’s complement of the A register in the Accumulator. if the Accumulator contains 5, it will be negated to -5.

You now have about one third of the Z80 instruction set, and with the stack instructions next issue (they are certainly the most used of the instructions). You are now armed with the tools to write a MC program of your own design. I encourage you to experiment and see if you get the desired results. I will reply personally to all enquiries that contain a S.A.S.E., if you have difficulty (send to- [address omitted]).

With the next lesson, we will explore printing to the screen as that will give us some immediate feedback as to how we are doing and whether our routine is working. If you have any information on the display file and/or ROM routines, you should review it, in anxious anticipation.

CHART 5

Jumps                     Flag setting

JP nn                     CCF
JP c,nn                   SCF
JP (HL)
                          CP n
JR e                      CP r
JR c,e                    CP (HL)
DJNZ e                    CPI
                          CPD
CALL nn
CALL c,nn                 CPL
RST xx
                          NEG
RET
RET c

Where: n  = any numeric constant 0 to 255
       nn = any numeric constant 0 to 65535
       r  = any single register
       e  = any numeric constant -128 to 127
       c  = flag status
       xx = 00h, 08h, 10h, 18h, 20h, 28h, 30h, or 38h

CHART 6

Instruction(s)! C ! Z !P/V! S ! N ! H ! Comments
ADD, ADC      ! * ! * ! V ! * ! 0 ! * ! 8 bit add or add
              !   !   !   !   !   !   ! w/carry
ADD           ! * ! - ! - ! - ! 0 ! - ! 16 bit add
ADC           ! * ! * ! V ! * ! 0 ! - ! 16 bit add w/carry
AND           ! 0 ! * ! P ! * ! 0 ! 1 ! Logical operations
BIT           ! - ! * ! - ! - ! 0 ! 1 ! Specified bit copied
              !   !   !   !   !   !   ! into zero the flag
RES, & SET    ! - ! - ! - ! - ! - ! - ! Bit instructions
CCF           ! * ! - ! - ! - ! 0 ! - ! Complement carry flag
SCF           ! * ! - ! - ! - ! 0 ! 0 ! Set carry flag
CP, NEG, SUB, ! * ! * ! V ! * ! 1 ! * ! 8 bit subtract or sub-
SBC, DEC, &   !   !   !   !   !   !   ! tract w/carry, compare
INC           !   !   !   !   !   !   ! or negate accumulator
              !   !   !   !   !   !   ! & 8 bit decrement
DEC, & INC    ! - ! - ! - ! - ! - ! - ! 16 bit decrement and
              !   !   !   !   !   !   ! increment
SBC           ! * ! * ! V ! * ! 1 ! - ! 16 bit subtract w/carry
CPI, CPIR,    ! - ! * ! P ! - ! 1 ! - ! Block searches; Z=1 if
CPD, & CPDR   !   !   !   !   !   !   ! A=(HL), else Z=0;P/V=1
              !   !   !   !   !   !   ! if BC not equal to 0,
              !   !   !   !   !   !   ! else P/V=0
CPL           ! - ! - ! - ! - ! 1 ! 1 ! Complement accumulator
DAA           ! * ! * ! P ! * ! - ! * ! Decimal adjust accum.
IN            ! - ! - ! - ! - ! - ! - ! Input register direct
IN            ! - ! * ! P ! * ! 0 ! 0 ! Input register indirect
INI, IND,     ! - ! * ! - ! - ! 1 ! - ! Block in & out instruc-
OUTI, OUTD,   !   !   !   !   !   !   ! tions; Z=0 if B is not
INIR, INDR    !   !   !   !   !   !   ! equal to 0, else Z=1
OTIR, & OTDR  !   !   !   !   !   !   !
LD            ! - ! - ! - ! - ! - ! - ! Assignment instructions
LDI, LDD,     ! - ! - ! P ! - ! 0 ! 0 ! Block transfers; P/V=1
LDIR, & LDDR  !   !   !   !   !   !   ! if BC is not equal to 0
              !   !   !   !   !   !   ! else P/V=0
OR, & XOR     ! 0 ! * ! P ! * ! 0 ! 0 ! Logical OR accumulator
RLA, RLCA,    ! * ! - ! - ! - ! 0 ! 0 ! Rotate accumulator
RRA, & RRCA   !   !   !   !   !   !   !
RL, RLC, RR,  ! * ! * ! P ! * ! 0 ! 0 ! Rotate and shift left
RRC, SLA, SRA,!   !   !   !   !   !   ! or right
SRL           !   !   !   !   !   !   !

Where: * = Flag changed according to result
       - = Flag either unchanged or undeterminable
       0 = Flag reset
       1 = Flag set
       P = Parity changed according to result
       V = Overflow changed according to result

CHART 7

Signed Numbers–Hex/Dec Conversions

   !   0!   1!   2!   3!   4!   5!   6!   7!   8!   9!   A!   B!   C!   D!   E!   F!
 0 !   0!   1!   2!   3!   4!   5!   6!   7!   8!   9!  10!  11!  12!  13!  14!  15!
 1 !  16!  17!  18!  19!  20!  21!  22!  23!  24!  25!  26!  27!  28!  29!  30!  31!
 2 !  32!  33!  34!  35!  36!  37!  38!  39!  40!  41!  42!  43!  44!  45!  46!  47!
 3 !  48!  49!  50!  51!  52!  53!  54!  55!  56!  57!  58!  59!  60!  61!  62!  63!
 4 !  64!  65!  66!  67!  68!  69!  70!  71!  72!  73!  74!  75!  76!  77!  78!  79!
 5 !  80!  81!  82!  83!  84!  85!  86!  87!  88!  89!  90!  91!  92!  93!  94!  95!
 6 !  96!  97!  98!  99! 100! 101! 102! 103! 104! 105! 106! 107! 108! 109! 110! 111!
 7 ! 112! 113! 114! 115! 116! 117! 118! 119! 120! 121! 122! 123! 124! 125! 126! 127!
 8 !-128!-127!-126!-125!-124!-123!-122!-121!-120!-119!-118!-117!-116!-115!-114!-113!
 9 !-112!-111!-110!-109!-108!-107!-106!-105!-104!-103!-102!-101!-100! -99! -98! -97!
 A ! -96! -95! -94! -93! -92! -91! -90! -89! -88! -87! -86! -85! -84! -83! -82! -81!
 B ! -80! -79! -78! -77! -76! -75! -74! -73! -72! -71! -70! -69! -68! -67! -66! -65!
 C ! -64! -63! -62! -61! -60! -59! -58! -57! -56! -55! -54! -53! -52! -51! -50! -49!
 D ! -48! -47! -46! -45! -44! -43! -42! -41! -40! -39! -38! -37! -36! -35! -34! -33!
 E ! -32! -31! -30! -29! -28! -27! -26! -25! -24! -23! -22! -21! -20! -19! -18! -17!
 F ! -16! -15! -14! -13! -12! -11! -10!  -9!  -8!  -7!  -6!  -5!  -4!  -3!  -2!  -1!