Some features of the Z80
Contents
Flags/Status Register (F and F')
This register contains the condition flags. When a command is executed the flags are used to show information about the result. (e.g. the flags may be used to check if the result was positive, negative or zero).
The flags register has this form:
Bit 7 | Sign flag |
Bit 6 | Zero flag |
Bit 5 | (note 1) |
Bit 4 | Half-carry flag |
Bit 3 | (note 1) |
Bit 2 | Parity/Overflow flag |
Bit 1 | Subtract flag |
Bit 0 | Carry flag |
Note:
There are two bits in the flags register which are documented by Zilog as being unused. But in fact these bits do change: these bits are updated according to an undocumented register of the Z80 named either WZ or MEMPTR. This undocumented register is used to store temporary results such as 16-bits arithmetic results, and address (for example, loading I register and the vector byte for interruption mode 2 to compute the value to load in PC register).
Sign flag
This flag indicates if the result is positive or negative. If a result is negative, this flag is set to 1, if the result is positive, this flag is set to 0.
Zero flag
This flag indicates if the result is zero or non-zero. If the result was zero, this flag would be set to 1, if the result was non-zero (i.e. a number other than zero), this flag would be set to 0.
Parity/Overflow
This bit has two functions. In some commands it is used to show parity and other commands it is used to show overflow.
Parity:
If a byte has an even number of 1 digits then it has "even parity", and if the byte has an odd number of 1 digits then it has "odd parity".
The Parity Overflow is set to 1 when the byte has even parity, and set to 0 if the byte has odd parity.
Overflow:
This bit will hold the overflow after a arithmetic command.
Subtract
This flag is set to 1 if the command used subtraction, it is set to 0 if the command used addition. This flag is used in the execution of the DAA instruction to calculate the correct BCD representation of the contents of A.
Half-Carry flag
This flag holds the carry from bit 3 to bit 4. It is used by the DAA instruction to convert the contents of the A register to BCD form.
Carry flag
This flag holds the carry.
Refresh Register (R)
This register is special and it cannot be used to store data. Some people consider it to be random, but it is not, and you can easily predict it's value. However, by the way it works, it could be used as a simple but crude random number generator.
This register is used mainly in protections, because when an instruction is added or removed the value of R will be different to the intended value and the data will not be deprotected correctly.
Bit 7 remains unchanged, and it's value is only defined when a LD R,A instruction is executed. The lower 7 bits hold a count which is incremented after each z80 instruction executed.
Generally, all commands which have a &CB, &ED, &DD or &FD prefix, increment R by 2, and all other commands increment R by 1. The execeptions to this rule are commands which repeat.
Interrupt Modes
The interrupt mode can be defined using the "IM" instruction. There are 3 interrupt modes: mode 0, mode 1 and mode 2.
The descriptions below assume:
- Interrupt requests are accepted by Z80,
- An interrupt request has been issued
Mode 0
In this mode, the interrupting device will generate an interrupt and put a single-byte instruction onto the CPU bus. The Z80 will execute this instruction, and then continue with program execution. Since we can only put a single byte onto the bus, the RST instructions provide an excellent way of handling different interrupts.
On a standard CPC the byte on the cpu data bus is normally &FF (but this is not guaranteed). &FF corresponds to the "RST 38H" instruction, and therefore on the CPC interrupt mode 0 is the same as interrupt mode 1.
On the Plus, the ASIC will generate a byte to put onto the processor data bus, see the documentation on the ASIC for a description of this byte.
Mode 1
In this mode the following will be done when a interrupt is "executed":
- The current program counter (PC) is pushed onto the stack. (SP)
- The program counter address is set to &0038.
Mode 2
In this mode the following will be done when a interrupt is "executed":
- The current program counter (PC) is pushed onto the stack (SP),
- A address is formed using the I register and the data on the bus.
Bit 15..8 | from I register |
Bit 7..0 | Byte put onto processor bus by interrupting device |
- The Z80 will read the interrupt handler address from the location pointed to by the interrupt handler pointer, and then jump to that address. interrupt handler pointer:
On a standard CPC, the byte put onto the processor bus is always &FF. So, on a standard CPC the following will be done:
- The current Program Counter is pushed onto the stack
- The z80 fetches an address from &xxFF and jumps to that address. (where xx is the value from the I register)
On a Plus, the ASIC can generate a different byte to put on the processor bus. So the lower 8 bits may be different. This is part of the extended interrupt handling features to cope with DMA Sound interrupts, scan line interrupts etc..
The Interrupt Vector Register (I)
The I register is used in Z80 Interrupt Mode 2 to form When it is used in this way, it forms the upper 8-bits of an address which points to the interrupt handler. The lower 8-bits are put onto the CPU bus by an interrupting device. If you program uses Mode 0 or Mode 1 interrupts, then this register can be used to store data.
DAA (Decimal Adjust accumulator)
This Z80 command will convert the contents of the A register to BCD (Binary-Coded-Decimal) representation. (e.g. the BCD representation of 15 is &15). DAA uses the carry flag, subtract flag, half-carry flag and the A register to calculate a value to add to the A register to change it into a BCD number. I believe that the original value in the A register must be in BCD for this conversion to work. It is provided so that any non-BCD value can be added to a BCD value and then the result can be adjusted to BCD again. I tried to convert from a non-BCD number to BCD using DAA and it didnt work. In the following tables, x corresponds to the upper nibble of register A (before the command has executed), and y corresponds to the lower nibble. These tables show the value that is added to the A register to convert it to BCD representation depending on the flags. These values are the ones used by my emulator (A-CPC). I have found information about other tables, but these did not produce the correct values.
Flags set Byte (0..9)(0..9) --------------------------------------------
(None) + &00 Carry: + &60 Subtract: + &00 Subtract+Carry: + &A0 Half-carry: + &06 Half-carry+Carry: + &66 Half-carry+Subtract: + &FA Half-carry+Subtract+Carry: + &9A
Flags set Byte (0..9)(A..F) --------------------------------------------
(None) + &06 Carry: + &66 Subtract: + &00 Subtract+Carry: + &a0 Half-carry: + &06 Half-carry+Carry: + &66 Half-carry+Subtract: + &fa Half-carry+Subtract+Carry: + &9A
Flags set Byte (A..F)(0..9) --------------------------------------------
(None) + &60 Carry: + &60 Subtract: + &00 Subtract+Carry: + &A0 Half-carry: + &66 Half-carry+Carry: + &66 Half-carry+Subtract: + &fa Half-carry+Subtract+Carry: + &9A
Flags set Byte (A..F)(A..F) --------------------------------------------
(None) + &66 Carry: + &66 Subtract: + &00 Subtract+Carry: + &a0 Half-carry: + &66 Half-carry+Carry: + &66 Half-carry+Subtract: + &fa Half-carry+Subtract+Carry: + &9A
LDIR and LDDR
This is a block copying function, it is used to transfer data from one part of memory to another fast.
- HL must be pre-loaded with the start address
- DE must be pre-loaded with the destination address
- BC must be pre-loaded with the length of data
LDIR
LDIR operation:
- read byte from memory address pointed to by HL and write it to the memory address pointed to by DE
- Increment HL by 1
- Increment DE by 1
- If BC=0 then increment PC by 2 (otherwise dont increment it)
LDDR
LDDR operation:
- read byte from memory address pointed to by HL and write it to the memory address pointed to by DE
- Decrement HL by 1
- Decrement DE by 1
- Increment Program counter by 2
- If BC=0 then increment PC by 2 (otherwise dont increment it)
NOTES
- an interrupt can occur while a LDIR/LDDR is being executed.
- If the LDIR/LDDR opcode is overwritten by itself then command execution will stop. Effectively, the LDIR opcode is repeatidly fetched and decoded for each byte fetched from memory.
IFF1 and IFF2
IFF1 reflects the state of the maskable interrupt. The maskable interrupt can be enabled using the Z80 instruction "EI" and disabled using the Z80 instruction "DI". When IFF1 is "1" masked interrupts are enabled, when a interrupt request is received by the Z80 it will be acknowledged and the interrupt handler executed. When IFF1 is "0" masked interrupts are disabled, any interrupt requests are ignored.
When a non-maskable interrupt (NMI) is executed by the Z80, IFF2 is set to the value of IFF1. Then IFF1 is set to "0" to disable maskable interrupts. The NMI handler will be executed. When a "RETN" instruction is executed by the Z80, IFF1 is set to the value in IFF2, therefore the maskable interrupt state is restored. The state of IFF1 can be set at any time using the "DI" and "EI" instructions.
IM 0, IM 1, IM 2
- The instructions "IM 0 ","IM 1" and "IM 2" set the Z80 interrupt mode which defines the actions taken to execute the maskable interrupt handler.