Z88 Developers' Notes
Previous Contents Next

33. The Zilog Z80 Undocumented instructions

The undocumented Z80 instructions are corresponding to 'holes' in the opcodes.

1 - 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.

For example,

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).
 

2 - 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.

3 - 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

4 - 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.
 

5 - 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.

6 - 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.

7 - 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).
 

8 - 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 document is based on combined information from articles written by :

Sean Young, [email protected]
Mark Rison, [email protected]
Marat Fayzullin, [email protected]


Previous Contents Next
The Z80 instruction set reference The Z80 undocumented instructions The Screen Files