LD instructions

Let’s examine how the load instructions are structured in the Z80.

It is important to say upfront that most of Z80 instruction’s design was taken from the Intel’s 8080 CPU. The Zilog engineers kept binary code compatibility with the 8080, while expanding the design with new and extended instructions.

A generic load instruction takes the following form:

LD operand1, operand2

which means operand1 receives the value indicated by operand2, i.e., operand1 ← operand2.

Register addressing load instructions

The generic form for instructions that move 8-bit data between registers is:

LD r, r'

where r and r’ are 8-bit registers within the currently active register set. Their binary code format is:

76 543 210
01  r   r'

Notice that this instruction type has two register fields (register addressing mode), and any one can have one of the following values:

Bits Register
000
B
001
C
010
D
011
E
100
H
101
L
111
A

Thus, these are valid op-codes:

01 010 111 -> LD D, A
01 111 010 -> LD A, D
01 001 001 -> LD C, C1 

1Same effect as a NOP instruction.

So, we have 49 valid 8-bit register-only load instructions. But, what about the 15 binary combinations left? (did you noticed the missing value 0b110 for the register field?).

Well, it happens that 0b110 is a valid value indeed, but it doesn’t correspond to a CPU internal register. Rather, it stands for the indirect addressing expressed by (HL). You could think of (HL) as a special type of register anyway. Remember this scheme was borrowed from the 8080 CPU.

Then we have 7 more load instructions of the type:

76 543 210
01 r 110 -> LD r, (HL)

For instance:

01 010 110 -> LD D, (HL)

Likewise, we also have seven store counterparts:

76 543 210
01 110  r  -> LD (HL), r

01 110 001 -> LD (HL), C

But, what about the binary code 0b01110110? Is it a valid load instruction? As it wouldn’t make much sense to waste machine cycles only to load a memory location with its own content, this binary code was set to correspond to the HALT instruction.

In summary, for the 64 possible binary codes for the template 0b01 r r’, we have 63 8-bit register move instructions and one outlander HALT instruction.

Immediate addressing load instructions

Now, let’s delve into the LD instructions that use immediate addressing. They are like this:

LD r, n

where r is a 8-bit general purpose register and n is a one-byte operand. This instruction’s two-byte op-code is as follow:

76 543 210
00  r  110
<-  n   ->

00 100 110 -> LD H, n
<-  n   ->

And the special case for the value 0b110 still applies:

00 110 110 -> LD (HL), n

In fact, every time you see a register field, remember it can have a value of 0b110 and indirectly access a memory location indicated by the HL register pair (there’s only exception we’ll see later among the undocumented indexed addressing instructions).

Direct and Indirect addressing load instructions

Both types of instructions are structured in a similar way, so let’s talk about them in one topic.

Direct load instructions use HL and A registers for holding data since memory addresses take part of the instruction themselves, whereas indirect ones use BC and DE pairs to hold memory address, loading or storing the accumulator.

The general binary code for those instructions is:

76 5 4 3 210
00 m r t 010

where

Bit Used for Mode 0 1 Register contains
5 addressing mode indirect direct
4 register(s) used indirect BC DE memory address
direct HL A data
3 type of operation store in memory load from memory

Thus, these are indirect load instructions:

76 5 4 3 210
00 0 0 0 010 -> LD (BC), A
00 0 0 1 010 -> LD A, (BC) 
00 0 1 0 010 -> LD (DE), A
00 0 1 1 010 -> LD A, (DE)

And these are the direct ones:

76 5 4 3 210
00 1 0 0 010 -> LD (nn), HL
00 1 0 1 010 -> LD HL,(nn)
00 1 1 0 010 -> LD (nn), A
00 1 1 1 010 -> LD A, (nn)

Remember all direct instructions have two additional bytes for the memory address.

Indexed load instructions

Up to now, we have been talking about instructions that came from the 8080 CPU. Z80 has introduced some extensions to the instruction set. Indexed addressing load instructions are among them, which use two new registers for indexing memory, IX and IY.

Indexed load instructions general form is:

LD r, (ir + d)  
LD (ir + d), r

where r is an 8-bit register, ir is a index register (IX or IY) and d is a signed byte displacement (-128 to 127).

All indexed addressing instructions have at least two op-codes. When dealing with the IX register the first op-code is always 0xDD and 0xFD when using the IY register.

The binary format is the following

76 543 210
11 011 101 -> IX op-code
01  r  110 -> LD r, (IX + d)
<-  d   ->

76 543 210
11 111 101 -> IY op-code
01  r  110 -> LD r, (IY + d)
<-  d   ->

where r is a 3-bit register field.

Notice that the second binary code has the same pattern of the instruction LD r, (HL). Which makes perfect sense for the instruction decoding circuitry in the CPU. The first op-code sets the index register to be used while the second one indicates the real operation to be performed, which is a indirect load in this case.

Let’s check the store counterparts LD (ir + d), r

76 543 210
11 011 101 -> IX op-code
01 110  r  -> LD (IX + d), r
<-  d   ->

76 543 210
11 111 101 -> IY op-code
01 110  r  -> LD (IY + d), r
<-  d   ->

The same pattern applies to the second op-code, i.e., it is the same binary code format used by store instructions LD (HL), r.

Notice that 0xDD76 and 0xFD76 are not valid instructions. You should know why by now…

Indexed load instructions with immediate addressing

In this case, we have instructions that load an indexed memory location with an immediate 8-bit value.

LD (ir + d), n

which binaries code are:

76 543 210
11 011 101 -> IX op-code
00 110 110 -> LD (IX + d), n
<-  d   ->
<-  n   ->

76 543 210
11 111 101 -> IY op-code
00 110 110 -> LD (IY + d), r
<-  d   ->
<-  n   ->

As expected the same pattern for the indexed instruction second op-code still applies.

(Undocumented) Indexed load instructions with register addressing

Here things get a little bit twisted. First, all instructions in this section were not documented by Zilog (I’m still not sure why).

This type of instruction allows to load/save the most/least significant byte of an index register from/to some general purpose 8-bit registers (B, C, D, E, A). The mnemonics for the index register parts are IXH, IXL, IYH and IYL.

Thus, we have generic forms as follow:

LD r, irh
LD r, irl
LD irh, r
LD irl, r

where irh is the most significant byte of an index register and irl is the least significant one. Examples of these instructions are:

LD B, IXL
LD IYH, A

Now, let’s see the binaries codes. Remember that all indexed instructions have a two-byte op-code, starting with 0xDD or 0xFD for IX or IY registers, respectively.

76 543 210
11 011 101 -> IX op-code
01  r  100 -> LD r, IXH

76 543 210
11 111 101 -> IY op-code
01  r  101 -> LD r, IYL

Well, r can assume the values we already know of, except that it can’t be 0b110 as a standard register field would. Moreover, the values 0b100 and 0b101 correspond to registers IXH/IYH and IXL/IYL respectively.

Bits Register
000
B
001
C
010
D
011
E
100
IXH/IYH
101
IXL/IYL
111
A

Notice that the three least significant bits of the second op-code follow this convention for specifying the most/least significant parts of IX/IY. That is, they form a second register field which, in this case, can only assume two values (0b100 and 0b101). Which makes perfect sense, since we are talking about an instruction that deals with one of the two available index registers.

Being the HL pair the standard memory pointer (as usual in the 8080 CPU), using an indexed instruction in Z80 means that you are going to use IX or IY as the default pointer to memory for that particular instruction (represented by the second op-code).

Thinking of the internal Z80 implementation, decoding the first op-code of an indexed instruction simply means changing the default HL memory pointer to either IX or IY. It also means that the next op-code should be decoded as one of the possible instructions that make use of indirect addressing.

Other implication that we can take away here is that the two most significant bits 0b01 for a standard op-code always means a register addressing load instruction.

The following are the previous instructions’ counterparts:

76 543 210
11 011 101 -> IX op-code
01 100  r  -> LD IXH, r

76 543 210
11 111 101 -> IY op-code
01 101  r  -> LD IXL, r
16-bit load instructions

Now, let’s see how load instructions that deal with 16-bit values are structured. We have already discussed some instructions of this type on the second part of the section Direct and indirect addressing load instructions (LD HL, (nn) and LD (nn), HL).

The basic form for these instruction is:

LD dd, nn

Where dd is register pair and nn is 16-bit value. Theses instructions load an two-byte value into a register pair.

The binary code format is as follow

76 54 3 210
00 dd 0 001 -> LD dd, nn
<-   n   ->
<-   n   ->

The dd bits make up for a 16-bit register field, as illustrated in the following table:

dd Register Pair
00 BC
01 DE
10 HL
11 SP

These op-codes came from the 8080 as usual. Also, notice that LD dd, nn has no counterpart, as LD nn, dd makes no sense at all.

It is also possible to load and store from/into memory using direct addressing:

LD dd, (nn)
LD (nn), dd

These instructions were added by Zilog and are among those called extended instructions. Extended instructions have a two-byte op-code having the first one the value 0xED.

76 54 3 210
11 10 1 101 -> 0xED
01 dd 1 011 -> LD dd, (nn)

76 54 3 210
11 10 1 101 -> 0xED
01 dd 0 011 -> LD (nn), dd

Notice that, as we have seen earlier, when using direct or indirect addressing modes, bit 3 indicates the operation type (0 – store, 1 – load).

It is also noteworthy to say that we have here an overleap between extended and standard instructions which deal with the HL pair. That is, LD HL, (nn) have both an standard 8080 op-code (0x2A) and an Z80 extended one (0xED6B). Likewise, LD (nn), HL has two valid op-codes (0x22 and 0xED63).

The remaining 16-bit load instruction is one that moves data from HL to the stack pointer:

LD SP, HL

Its binary code is:

76 543 210
11 111 001 -> LD SP, HL

At last, let’s see the 16-bit indexed load instructions

LD ir, nn
LD ir, (nn)
LD (nn), ir
LD SP, ir

where ir can be IX or IY. The rule about using a standard (not extended) instruction op-code as the second op-code for an indexed instruction keeps sounding true as expected.

76 543 210
11 011 101 -> IX
00 100 001 -> LD IX, nn
<-   n   ->
<-   n   ->

76 543 210
11 011 101 -> IX
00 101 010 -> LD IX, (nn)
<-   n   ->
<-   n   ->

76 543 210
11 011 101 -> IX
00 100 010 -> LD (nn), IX
<-   n   ->
<-   n   ->

76 543 210
11 011 101 -> IX
11 111 001 -> LD SP, IX
I and R registers

The special registers I and R have load instructions to move data from/to the accumulator:

765 43 210
010 00 111 -> LD I, A
010 01 111 -> LD R, A
010 10 111 -> LD A, I
010 11 111 -> LD A, R

where bit 4 defines the load/store operation and bit 3 selects between I and R registers.

Bit 0 1
4 load store
3 I R

Of all LD instructions shown here, these are the only ones that affect the Flag register.

Conclusion

Here are some interesting takeaways from this study:

  1. There are 161 LD instructions overall in the Z80 instruction set.
  2. Instructions like LD r,r work as a NOP instruction.
  3. The first op-code of any indexed instruction just changes the standard memory pointer. The second op-code is the same of a standard indirect addressing instruction that uses the HL pair.
  4. Because the compatibility with the 8080, the extended load instructions brought up redundancy for the LD HL, (nn) and LD (nn), HL instructions.
  5. The only LD instructions that affect the flag register are those that move data between the accumulator to/from I or R registers.