LD instructions
May 31, 2017
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:
- There are 161 LD instructions overall in the Z80 instruction set.
- Instructions like LD r,r work as a NOP instruction.
- 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.
- Because the compatibility with the 8080, the extended load instructions brought up redundancy for the LD HL, (nn) and LD (nn), HL instructions.
- The only LD instructions that affect the flag register are those that move data between the accumulator to/from I or R registers.