From WikiTI
Revision as of 18:01, 22 March 2013 by Dr. D'nar (Talk | contribs)

Jump to: navigation, search

The Z80 CPU supports three different modes of handling interrupts. They are called IM 0, IM 1, and IM 2. In all modes, after accepting an interrupt, the Z80 disables interrupts and you must manually enable them again at the end of your interrupt service routine. The IM 0 mode is identical to the Intel 8080's interrupt handling and is provided for compatibility. In IM 0, the interrupting hardware takes over the bus and sends an instruction to the CPU to execute. This instruction would usually be an RST instruction, which is what the RST instructions were designed for. In IM 1, the Z80 always executes RST 38h.

In IM 2, the Z80 uses an interrupt vector table. The interrupting hardware gives the Z80 an index into the table, such that up to 128 devices can automatically specify which interrupt service routine (ISR) needs to be run. The I register specifies where the IVT starts. Since I is 8 bits, it only specifies the high byte; the table always starts at an address in which the low byte is zero. The interrupting hardware is supposed to send an 8-bit number that's an index into the table. So the Z80 combines the index and the value of the I register to form a 16-bit address. The Z80 uses the 16-bit number at that address as the location of the ISR.

The TI-83+ family operating system always operates in IM 1, in which any interrupt signal causes the CPU to execute the RST 38h instruction. The OS provides no hooks for allowing software to catch and process interrupts. Fortunately, the Z80 also has the IM 2 mode, which allows us to move the interrupt vector table (IVT) to any of 256 different locations. Unfortunately, in the TI-83+ IM 2 is semibroken because it would actually be useful to us if it functioned properly. The ASIC doesn't bother to specify an index into the table, so we have to poll different ports to figure out what generated the interrupt. Furthermore, you will note that the number the Z80 expects to be passed is an 8-bit number. Because the addresses in the table are 16-bits, each address takes 2 bytes. But the number the Z80 receives from the ASIC is effectively random. And the Z80 doesn't mask out the bottom bit. So you have to fill the table with 257 repeating bytes, and your ISR can only be at an address in which the high and lows bytes are the same.

If you want to know whether interrupts are currently enabled or not (so you can disable them, and then enable interrupts again if and only if they were enabled before), the ld a, i and ld a, r instructions will copy the value of IFF2 into the P/V flag. In other words, if interrupts are enabled, the P/V flag will be set. The original NMOS-based Z80 had a bug that would cause ld a, i and ld a, r to reset the P/V flag if an interrupt occurred near the instruction. This bug was corrected in the CMOS version of the Z80, which is the version that is in the ASICs; therefore, you do not need to worry about the bug if you don't need your software to run on the older TI-83+s that were manufactured with a discrete Z80. The workaround for the TI-83+ is either to prohibit using older units, or to perform the ld a, i/r operation twice, and compare the results.