Z80 Microprocessor Undocumented Instructions ============================================ mdfs.net/Docs/Comp/Z80/UnDocOps - Update: 0.15 Author: J.G.Harston - Date: 18-12-1997 The Z80 processor is quite straightforward, and contains no major bugs or quirks. However, it has some undocumented features. Some of these are quite useful, and some are not, but many programs use the useful ones, and a few programs use the weird ones. Most Z80 opcodes are one byte long, not counting a possible byte or word operand. The four opcodes CB, DD, ED and FD are 'shift' opcodes: they change the meaning of the opcode following them. CB Block Instructions --------------------- 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 SLS, Shift Left and Set, shift left the operand and make bit 0 always one, bit 7 falling out into the carry. These instructions are quite commonly used. Index Instructions ------------------ The DD and FD opcodes precede instructions using the IX and IY registers. If you look at the instructions carefully, you see how they work: 2A nn LD HL,(nn) DD 2A nn LD IX,(nn) 7E LD A,(HL) DD 7E d LD A,(IX+d) A DD/FD opcode simply changes the meaning of HL in the next instruction. If a memory byte is addressed indirectly via HL, as in the second example, a displacement byte is added. Otherwise the instruction simply acts on IX/IY instead of HL. (A notational awkwardness, that will only bother assembler and disassembler writers: JP (HL) is not indirect; think of it as JP (x) where x is HL rather than JP x where x is (HL).) If a DD/FD opcode precedes an instruction that doesn't use the HL register pair at all, the instruction is executed as usual. However, if the instruction uses the H or L register, it will now use the high or low halves of the IX/IY register! Example: 44 LD B,H FD 44 LD B,IYH These types of unofficial instructions are used in many programs. By the way, many DD or FD opcodes after each other will effectively be NOPs, doing nothing except repeatedly setting the flag 'treat HL as IX' (or IY) and taking up 4 T states. Note that the FD or DD 'opcode' is treated as part of an instruction, so that the Z80 cannot be interrupted during execution of such a block. When (parts of) HL are used both as source and target, the instruction always uses the offset index instead of (HL), ie: 66 LD H,(HL) DD 66 LD H,(IX+nn) Two instructions do not obey the DD/FD rule. They are EX DE,HL and EXX. These instructions operate by toggling a flag that changes the reference to the registers; no data is actually being moved, which explains why they cannot operate on IX or IY. Indexed ED Instructions ----------------------- If a DD or FD precedes an ED instruction, it is ignored. ED instructions never operate on the IX or IY register. Indexed CB Instructions ----------------------- If a DD or FD precedes a CB instructions that uses (HL), it is modified to use (IX+nn) or (IY+nn), as with the following: CB CE SET 0,(HL) DD CB nn CE SET 0,(IX+nn) If the CB instruction does not use (HL) the result is more interesting. The indexed byte is always operated on, but then the result is copied to the unindexed register. For example: CB C0 SET 0,B DD CB nn C0 SET 0,(IX+nn) ; then copy result to B CB 05 RLC L DD CB nn 05 RLC (IX+nn) ; then copy result to L The usual notation for this is: DD CB nn C0 SET 0,(IX+nn)->B ED Block Instructions --------------------- There are a number of unofficial ED instructions, but none of them are very useful. The ED opcodes in the range 00-3F and 80-FF (except for the block instructions of course) do nothing at all but taking up 8 T states and incrementing the R register by 2. Most of the unlisted opcodes in the range 40-7F do have an effect, however, due to the opcode not being fully decoded. These are the extra instructions: ED 4C NEG ED 70 IN F,(C) ED 4E IM 0 ED 71 OUT (C),0 ED 54 NEG ED 74 NEG ED 55 RETN ED 75 RETN ED 5C NEG ED 5D RET ; RETI ED 64 NEG ED 65 RETN ED 66 IM 0 ED 76 IM 1 ED 77 LD I,I ED 6C NEG ED 7C NEG ED 6D RET ; RETI ED 7D RET ; RETI ED 6E IM 0 ED 7E IM 2 ED 7F LD R,R The ED 70 instruction reads from port (C), just like the other instructions, but the data doesn't get stored anywhere. It does change the flags in the same way as the other IN instructions, however. The ED 71 instruction OUTs a zero byte to port (C). These instructions 'should', by regularity of the instruction set, use (HL) as operand, but since from the processor's point of view accessing memory or accessing I/O devices is the same thing except for activation of the ~IORQ line instead of the ~MREQ line, and since the Z80 does not access memory twice in one instruction (disregarding instruction fetch of course) it can't fetch or store the data byte. The ED 4E instruction is a mirror of the ED 46 IM 0 instruction. All the IM instructons in ED 4x are mirrored in ED 6x. The RETI instruction is functionally exactly equivalent to the RET instruction. It is used only to signify the end of an interrupt routine to an external hardware device, the Z80 PIO and SIO recognise the instruction bytes being executed. The reflections of RETI therefore are just RETs as hardware will not recognise them as RETIs. The RETN however is different from RET in that it resets IFF1 to the current value of IFF2. IFF1 and IFF2 are usually equal (and become equal after DI and EI and after a maskable interrupt has been accepted). They're different only if an NMI occurs when interrupts are enabled; then IFF1 is off, and IFF2, holding the previous state of the interrupt flip flop, is on, signifying that interrupts were enabled before the non-maskable interrupt. The state of IFF2 can be read by using LD A,R and LD A,I. ED 77 and ED 7F act as NOPs, but they set the P/V flag according to the state of the interrupts, in the same was ay LD A,I and LD A,R. The mapping of the opcodes suggests that ED 77 should be LD I,I and ED 7F should be LD R,R. R Register ---------- This is not really an undocumented feature, although precise explanations are hard to find. 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 instructions will increase R by two. There's an exception: doubly-shifted opcodes, the DDCB and FDCB ones, increase R by two also. The highest bit of R is never changed other than by LD R,A. This means that R either increments from 0 to 127 and back or 128 to 255 and back. This is because in the old days everyone used 16 Kbit chips. Inside the chip the bits were arranged in a 128x128 matrix, and needed a 7 bit refresh cycle. Probably for this reason Zilog decided to count only the lowest 7 bits. The sequence LD R,A then LD A,R results in A being incremented by 2. I Register ---------- There is a lot of confusion and misinformation floating around about exactly what happens when an INT occurs in IM 2. The I register forms the high byte of an address in memory to find the address of the interupt routine. Documentation states both that the low byte if formed from all 8 bits of the data bus, and from the top 7 bits of the data bus with bit 0 forced to zero. The Gospel According to Zacs states a 7-bit offset. It can't be both. If I=&80 and the data bus is &FF does the interupt vector through &80FE or &80FF? Some emulators implement 7-bit offset and some 8-bit. Testing with real hardware shows that, contrary to stated documentation, bit zero is not forced to zero, an IM2 interupt jumps to I*256+databus, not to I*256+(databus AND 254). If you want to rely on an even address, you must use hardware to supply an even address. Block Instructions ------------------ When repeating block instructions are executed the opcode is executed, BC or B is decremented and if it is not zero, PC is decremented by 2 and the instruction refetched. This means that the repeating instructions increment R by 2 times BC or 2 times B. This also means that if you do a block copy that overwrites the instruction, it will terminate prematurely. Take as an example: ORG 32768 L1 LD HL,L1 LD DE,L1+1 LD BC,65535 LD (HL),0 LDIR This segment of code only executes the LDIR 11 times, at which point the ED part of the LDIR instruction is overwritten. The instructions there are now 00 B0 - NOP and OR B. Undocumented Flags ------------------ Bit 3 and 5 of the F register are not used. They can contain information, as you can readily figure out by using PUSH AF and POP AF. 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 operation that changed the flags. Bit 7 is the sign flag and so fits correctly. If the instruction operates on a 16 bit word, the 8 bits used are the highest 8 bits of the 16 bit result - that is to be expected since the S flag is extracted from bit 15. For instance, after an ADD A,B bits 7, 5 and 3 bits will be identical to those bits of the A register. With the CP x instruction, the bits are taken from the argument. This means that odd things like the following are possible: BIT 6,(IX+nn) JP P,ArgumentLess128 JP Z,ArgumentLess192 JP NZ,Argument192to255 An amazing piece of code! Some programs even hold return addresses in the AF register for a while! Instructions on Emulators ------------------------- There are various Z80 emulators available and many of them add some extra opcodes in order to communicate with the host they are running on. The following is a brief list I have been able to find out about. J.G.Harston's !Z80Tube Z80 Emulator ----------------------------------- Extra opcodes: EDF0 to EDFF ED FF MOS_QUIT - Leaves the emulator ED FE MOS_CLI - Passes string at HL to command line interpreter ED FD MOS_BYTE - Does Osbyte A,L,H ED FC MOS_WORD - Does OSWORD A with parameters as HL ED FB MOS_WRCH - Writes character in A to current output ED FA MOS_RDCH - Wait for a character from current input to A ED F9 MOS_FILE - Do an operation on a whole file ED F8 MOS_ARGS - Read or write info about an open file ED F7 MOS_BGET - Get a byte from an open file ED F6 MOS_BPUT - Put a byte to an open file ED F5 MOS_GBPB - Read or write multiple bytes ED F4 MOS_FIND - Open or close a file ED F3 MOS_SYS - Pass a SWI to the host ED F2 MOS_MISC - Various miscellaneous functions ED F1 MOS_RDINF - Read emulator info ED F0 MOS_WRINF - Write emulator info If Z80Tube is not emulating a Z280 or later, then ED00 to ED0F also call MOS functions, ordered in the opposite way, ED 00 MOS_QUIT and so on. G.A.Gunter's 'Z80' Spectrum Emulator ------------------------------------ Extra opcodes: EDF8 to EDFF ED FF QUIT - Leaves the emulator ED FE ROMPOKE - LD (HL),A writing into ROM ED FD SENDBYTE - Sends A to RS232 ED FC READBYTE - Reads byte from RS232 to A, NZ/CS=ok, Z/CC=no byte ED FB WRITEMEM - If PC<4000, store byte in A in SamRam at (HL) If PC>3FFF, multi-level loader, loads code to HL from last accessed .Z80 or .SLT file, or from a file name xxxxnnn.dat, where xxxx is the last snapshot filename, and nnn is the A register in decimal. If no file found, the user is prompted for a file. ED FA READMEM - Load A with byte in SamRam at (HL) ED F9 BYTECOPY - LD A,(DE), LD (HL),A to SamRam, INC E, INC H ED F8 UNUSED Full Opcode List ---------------- See document mdfs.net/Docs/Comp/Z80/Z80OpList for a full list of all opcodes. References ---------- Harston J.G., Investigative research. Harston J.G., Interupt Mode 2 http://mdfs.net/Docs/Comp/Z80/IntMode2 Harston J.G., Z80Tube documentation. http://mdfs.net/Apps/Emulators/Tube/Z80Tube/ Lunter G.A., Z80 Spectrum emulator documentation. http://mdfs.net/Apps/Emulators/Spectrum/Z80/v304/