The undocumented Z80 instructions are corresponding to 'holes' in the opcodes.
Undocumented index operations
In this section, IX operations will be described.
IY operations, obtained by replacing DDs with FDs, behave identically.
A DD preceding an instruction causes, in general, the following ('main') instruction to be processed as normal, except that:
- any access to (HL) gets treated as an access to (IX+d)
- any access to HL gets treated as an access to IX
- any access to H gets treated as an access to IXh
- any access to L gets treated as an access to IXl
If the main instruction does not access any of (HL), HL, H and L, then the DD effectively acts as a NOP. (In addition, a series of DDs and FDs acts as a series of NOPs with the DD/FD actually obeyed being the
last one in the series.)
There are exceptions to the general rule, however. These are:
Main instruction Effect of preceding DD
LD H,(HL) Causes LD H,(IX+d)
LD (HL),H Causes LD (IX+d),H
LD L,(HL) Causes LD L,(IX+d)
LD (HL),L Causes LD (IX+d),L
EX DE,HL None (left as EX DE,HL)
EXX None (left as EXX)
EDxx None (left as EDxx)
CBxx See below
DDCB sequences always cause the byte following the CB to be taken as a displacement, and always cause an access to (IX+d). If the sequence produces output other than in the flags (i.e. all except BIT), then the result gets placed both into (IX+d) and the register one would normally expect to be altered.
DDCB0100 causes RLC (IX+1) and copies the result into B.
DDCB02FF causes SET 7,(IX+2) and copies the result into A.
DDCB0373 causes BIT 6,(IX+3).
Undocumented shift operations
There are 248 different CB opcodes. The block CB 30 to CB 37 is missing from the official list. These instructions, usually denoted by the mnemonic SLL, Shift Left Logical, shift left the operand and make bit 0 always one. The effect is therefore like SLA except b0 is set instead of being reset. These instructions are quite commonly used.
Undocumented EDxx operations
Instructions in the range ED00 to ED3F have no effect.
Instructions in the range ED80 to EDBF, except those documented as block loads, compares, ins or outs, have no effect.
Instructions in the range EDC0 to EDFF have no effect.
The holes in the range ED40 to ED7F typically duplicate documented instructions:
NEG at ED4C, ED54, ED5C, ED64, ED6C, ED74, ED7C
NOP at ED77, ED7F
RETN at ED55, ED65, ED75
RETI at ED5D, ED6D, ED7D
IM ? at ED4E, ED6E
IM 0 at ED66
IM 1 at ED76
IM 2 at ED7E
IN F,(C) at ED70
OUT (C),0 at ED71
IM ? sets the interrupt mode flip-flops to an undefined state, which seems to act like IM 0 or IM 1.
IN F,(C) performs the input operation, setting the flags as normal, but throws the input value away.
OUT (C),0 outputs $FF to the port.
Note : it would output zero if the Z80 used were the NMOS variant rather than the CMOS variant used in the Z88.
The complete list: (* = not official)
ED40 IN B,(C) ED60 IN H,(C)
ED41 OUT (C),B ED61 OUT (C),H
ED42 SBC HL,BC ED62 SBC HL,HL
ED43 LD (nn),BC ED63 LD (nn),HL
ED44 NEG ED64 * NEG
ED45 RETN ED65 * RET
ED46 IM 0 ED66 * IM 0
ED47 LD I,A ED67 RRD
ED48 IN C,(C) ED68 IN L,(C)
ED49 OUT (C),C ED69 OUT (C),L
ED4A ADC HL,BC ED6A ADC HL,HL
ED4B LD BC,(nn) ED6B LD HL,(nn)
ED4C * NEG ED6C * NEG
ED4D RETI ED6D * RET
ED4E * IM 0/1 ED6E * IM 0/1
ED4F LD R,A ED6F RLD
ED50 IN D,(C) ED70 * IN (C)
ED51 OUT (C),D ED71 * OUT (C),0
ED52 SBC HL,DE ED72 SBC HL,SP
ED53 LD (nn),DE ED73 LD (nn),SP
ED54 * NEG ED74 * NEG
ED55 * RET ED75 * RET
ED56 IM 1 ED76 * IM
ED57 LD A,I ED77 * NOP
ED58 IN E,(C) ED78 IN A,(C)
ED59 OUT (C),E ED79 OUT (C),A
ED5A ADC HL,DE ED7A ADC HL,SP
ED5B LD DE,(nn) ED7B LD SP,(nn)
ED5C * NEG ED7C * NEG
ED5D * RET ED7D * RET
ED5E IM 2 ED7E * IM 2
ED5F LD A,R ED7F * NOP
About the R register
This is not really an undocumented feature, although I have never seen any thorough description of it anywhere. The R register is a counter that is updated every instruction, where DD, FD, ED and CB are to be regarded as separate instructions. So shifted instruction will increase R by two. There's an interesting exception: doubly-shifted opcodes, the DDCB and FDCB ones, increase R by two too. LDI increases R by two, LDIR increases it by 2 times BC, as does LDDR etcetera. The sequence LD R,A/LD A,R increases A by two, except for the highest bit: this bit of the R register is never changed. This is because in the old days everyone used 16 Kbit chips. Inside the chip the bits where grouped in a 128x128 matrix, needing a 7 bit refresh cycle. Therefore ZiLOG decided to count only the lowest 7 bits.
Undocumented flags F3 and F5
Bits 3 and 5 of the F register are not used. They can contain information, as you can readily figure out by PUSHing AF onto the stack and then POPping some it into another pair of registers. Furthermore, sometimes their values change. The values of bits 7, 5 and 3 follow the values of the corresponding bits of the last 8 bit result of an instruction that changed the usual flags.
For instance, after an ADD A,B those bits will be identical to the bits of the A register (Bit 7 of F is the sign flag, and fits the rule exactly). An exception is the CP x instruction (x=register, (HL) or direct argument). In this case the bits are copied from the argument. If the instruction is one that operates on a 16 bit word, the 8 bits of the rule are the highest 8 bits of the 16 bit result - that was to be expected since the S flag is extracted from bit 15.
Interrupt flip-flops IFF1 and IFF2
There seems to be a little confusion about these. These flip flops are simultaneously set or reset by the EI and DI instructions. IFF1 determines whether interrupts are allowed, but its value cannot be read. The value of IFF2 is copied to the P/V flag by LD A,I and LD A,R. When an NMI occurs, IFF1 is reset, thereby disallowing further [maskable] interrupts, but IFF2 is left unchanged. This enables the NMI service routine to check whether the interrupted program had enabled or disabled maskable interrupts.
Interrupt Modes (IM x)
Interrupt modes can be set with the IM x instructions. They only affect mask-able interrupts. When a mask-able interrupt occurs, the interrupting device must supply a value. The Z88 only uses the IM1. All maskable interrupts are managed by the RST $38. The Non-Maskable Interrupt causes a jump to $0066.
IM 0: The value the interrupting device supplies is interpreted as an 8 bit opcode which is executed (usually RST p)
IM 1: A call is made to address 38h. The value the interrupting device supplies is ignored.
IM 2: A call is made to an address read from address (register I × 256 + value from interrupting device).
Z80 hardware bugs
There are several bugs in the NMOS version of the CPU. One of them is with regard to the buggy IFF2->parity flag performance in the LD A,I and LD A,R instructions. An error can occur when an INTACK runs near these instructions, the parity flag will be showing 0 when it should have been 1. We are not sure but this bug concerns the CMOS Z80 too. A Zilog app. note is around somewhere talking about this.
This page is based on combined information from articles written by
Sean Young, firstname.lastname@example.org
Mark Rison, email@example.com
Marat Fayzullin, firstname.lastname@example.org
The original PDF contains more details, click on link to read / download, z80-documented-v0.91.pdf.