5.4 The Z80 microprocessor The Z80 processor is quite straightforward, and contains to my knowledge no interesting bugs or quirks. However, it has some undocumented features. Some of these are quite useful, and some are not, but since many programs use the useful ones, and a few programs use the weird ones, I tried to figure them out and emulate them as best as I could. There is a Z80 emulator around, intended as a CP/M emulator, which halts the program if an undocumented opcode is encountered. I don't think this makes sense. ZiLOG doesn't dictate the law, the programs which use the processor's features do! 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. 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. Bounder and Enduro Racer use them, to name just two. The SamRam monitor can disassemble these and uses the mnemonic SLL. These instructions are quite commonly used. 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 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 instead of HL. (A notational awkwardness, that will only bother assembler and disassembler writers: JP (HL) is not indirect; it should have been denoted by JP HL) If a DD 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 register! Example: 44 LD B,H FD 44 LD B,IYh These types of inofficial 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. (But try to let MONS disassemble such a block.) The doubly-shifted opcodes that start with DD CB and DD ED behave differently. If a DD or FD precedes an ED instruction, the DD or FD is ignored. ED instructions never operate on the IX or IY register, and are executed normally instead. With CB instructions, the situation is more interesting. Within an 8-instruction block, every DD CB instruction works as the official one, but also copies the result to the specified register (except when it is (HL)). For example, CB CE SET 0,(HL) CB C0 SET 0,B DD CB nn CE SET 0,(IX+nn) DD CB nn C0 SET 0,(IX+nn) ; copy result to B (The information about the inofficial CB instructions was given to me by Arnt Gulbrandsen, and originated from David Librik.) There are a number of inofficial 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. 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 ED6E * IM 0 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 1 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 The ED70 instruction reads from port (C), just like the other instructions, but throws away the result. It does change the flags in the same way as the other IN instructions, however. The ED71 instruction OUTs a byte zero to port (C), interestingly. 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 almost the same thing, and since the Z80 cannot access memory twice in one instruction (disregarding instruction fetch of course) it can't fetch or store the data byte. (A hint in this direction is that, even though the NOP-synonyms LD B,B, LD C,C etcetera do exist, LD (HL),(HL) is absent and replaced by the HALT instruction.) The instructions ED 4E and ED 6E are IM 0 equivalents: when FF was put on the bus at interrupt time, the Spectrum continued to execute normally, whereas when an EF (RST #28) was put on the bus it crashed, just as it does in that case when the Z80 is in the official interrupt mode 0. In IM 1 the Z80 just executes a RST #38 (opcode FF) no matter what is on the bus. 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 (read: the Z80 PIO). The RETN however is different from RET in that it resets IFF1 to the current value of IFF2. Now 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. Since the state of IFF2 can be read by using LD A,R and LD A,I, the RETN instruction is not used much in Spectrum ROM software, and it is utterly useless in normal software. In other words, I have not tried to figure out whether the unofficial RET's are RETI's or RETN's. 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 instructions 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. The highest bit of the R register is never changed (except possibly by LD R,A of course). 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. If the R register emulation is switched on the R register will behave as is does on a real Spectrum; if it is off it will (except for the upper bit) act as a random generator. You can easily check that the R register is really crucial to memory refresh. Assemble this program: ORG 32768 DI LD B,0 L1 XOR A LD R,A DEC HL LD A,H OR L JR NZ,L1 DJNZ L1 EI RET It will take about three minutes to run. Look at the upper 32K of memory, for instance the UDG graphics. It will have faded. Only the first few bytes of each 256 byte block will still contain zeros, because they were refreshed during the execution of the loop. The ULA took care of the refreshing of the lower 16K. (This example won't work on the emulator of course!) Then there's one other dark corner of the Z80 which has its effect on programs like Sabre Wulf, Ghosts'n Goblins and Speedlock. The Mystery of the 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. I found the following empirical rule: The values of bit 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 that 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. Ghosts'n Goblins use the undocumented flag due to a programming error. The rhino in Sabre Wulf walks backward or keeps running in little circles in a corner, if the (in this case undocumented) behaviour of the sign flag in the BIT instruction isn't right. I quote: AD86 DD CB 06 7E BIT 7,(IX+6) AD8A F2 8F AD JP P,#AD8F An amazing piece of code! Speedlock does so many weird things that everything must be exactly right for it to run. Finally, the '128 rom uses the AF register to hold the return address of a subroutine for a while. To keep all programs happy, and still have a fast emulator, I had to make a compromise. The undocumented flags are not always emulated right, but they are most of the time. Not telling you when not. Now for the emulated Z80. I have added eight instructions, to speed up the RS232 input and output of the Interface I and several things of the SamRam. These opcodes, ED F8 to ED FE are of little use to any other program. ED FF is a nice one: it returns you to DOS immediately. I used it for debugging purposes, and it is also used in TAP2TAPE and SAMLIST. The opcode ED F6, which is used by the SamRam, is now also used to use multi-load games on the emulator. If the emulator encounters the opcode ED F6 in RAM (above 16384), it loads a block of code into memory at address HL. The name of the file to be loaded is the name of the snapshot file currently run, with the decimal value of the A register attached to it. The extension is .DAT. If such a file is not found, the user is informed of the value of the A register and allowed to supply a file name himself (no sexual prejudice implied).