Sega Master System / Mark III / Game Gear
Sega8bit & SMS Power! 2013 Event - 10th August 11 weeks and 3 days from now
Interrupts are a mechanism to make the CPU stop processing one task and temporarily switch to another. They are typically used for time-critical applications where immediate response to a changing condition is required, or to prevent the CPU from polling some type of status input which wastes valuable processing time.
The Z80 supports two types of interrupts, non-maskable and maskable. They are triggered by two pins: /NMI and /INT.
/NMI is an negative edge triggered input. When it goes from the high to low level only, the Z80 will start non-maskable interrupt processing. You will not get continuous interrupts by holding /NMI low. The Z80 has no way to disable this interrupt, hence it is non-maskable.
When an NMI occurs, the Z80 starts executing code at address $0066. This makes it mandatory to place an interrupt handler at that address, even if it's a jump instruction to a larger routine elsewhere in memory.
/INT is an active-low level sensitive input. When pulled low, the Z80 wil start interrupt processing, and repeatedly do this after each instruction executed until /INT goes high again. This type of interrupt can be controlled by the Z80, using the DI (disable interrupt) and EI (enable interrupt) instructions.
When interrupts are disabled via DI, the Z80 will respond to /INT as soon as interrupts are enabled again. If /INT goes high before they are enabled, then the interrupt is 'lost' and the Z80 never responds to it.
Typically the external hardware which triggered the interrupt will have some facility to pull /INT high again, either after a set period of time (auto-acknowledge) or through a dedicated memory address or control register that can be accessed by the interrupt handler, to indicate that the interrupt is being serviced.
The Z80 has three processing modes for maskable interrupts. The mode resets to 0 by default, and can be changed using the IM 0, IM 1, or IM 2 instructions.
When an interrupt is requested, the Z80 reads a byte from the data bus and executes it. This can be a single-byte instruction or the start of a multi-byte instruction; for speed reasons commonly RST $nn is used to start processing an interrupt handler due to it's compact encoding.
If no external device is driving the bus, the Z80 will read a garbage value. In systems with pull-up or pull-down resistors on the bus, the value read will be $FF (RST $38) or $00 (NOP) respectively.
When an interrupt is requested, the Z80 starts executing code at address $0038.
When an interrupt is requested, the Z80 reads the address of the interrupt handler from a vector table that is located at the following address in memory:
(I register * 256) + Data bus value
.. and then jumps to that address. E.g. if register I is $50 and the data bus value is $2A, the two-byte address of the interrupt handler is read from offset $502A onwards.
Despite the official documentation, bit 0 of the low byte ($2A in the previous example) does not have to be zero.
As with interrupt mode 0, if no external device provides the low-byte then a garbage value is used. However this mode can still be used by populating the entire vector table with identical values; regardless of which low-byte is selected, the same address (such as $1111 or $AAAA) is selected. The I register only has to point to the start of the 257-byte vector table.
An interrupt handler has to perform several tasks:
A typical interrupt routine that follows the above steps might look like this:
There are other ways to write an interrupt handler, depending on what functions it needs to perform.