
INTERRUPTS IN Z88DK


The z80 supports three interrupt modes: IM0, IM1 and IM2.


1. IM 0 - The Forgotten Mode

IM0 is the least used mode and is mainly there for 8080
compatibility.  In this mode an interrupting peripheral
supplies an instruction to execute when the interrupt
is acknowledged.  That instruction is typically a
single-byte "RST n" which effectively causes a subroutine
call to one of eight fixed entry points in the first
256 bytes of memory.  A pro for this mode of operation
is the quick interrupt response since the interrupt
service routine for up to eight different peripherals
is directly jumped to right away.  There is no direct
support for this mode under z88dk but it is a simple
matter to assemble a 3-byte JP instruction at these
entry points to cause a jump to an ISR of your creation.
One or more copies of the generic ISR described below can
be used to handle the interrupts.


2. IM 1 - Keep It Simple

IM1 is more commonly used.  In this mode, an interrupt
causes a jump to address 0x38.  Typically the interrupt
service routine there might poll attached peripherals
to determine which need servicing.  If only a single
device or two can cause interrupts this might be
acceptable.  Again, There is no direct support for this
mode under z88dk but it is a simple matter to assemble
a 3-byte JP instruction at 0x38 to cause a jump to an ISR
of your creation.  The generic ISR described below can be
used to handle the interrupt.


3. IM 2 - The Sexy Mode

IM2 is the most powerful interrupt mode.  In this mode
an interrupting peripheral supplies a single-byte vector
when the interrupt is acknowledged.  The contents of the
I register is concatenated with this vector byte (with
I most significant) forming a 16-bit address where the
address of the interrupt service routine to jump to
is stored.  Effectively register I*256 points at a
256 byte table of interrupt service routine addresses.

By convention (and convention only since this seems to
be frequently violated) vectors supplied by interrupting
peripherals are even.  This ensures that interrupt
service routine addresses do not overlap in the table
pointed at by register I.  In particular, it is worthwhile
noting that Zilog's peripheral chips can only store
even vectors and therefore reset bit 0 of vectors written
to them automatically.

This means that normally only even vectors 0-254 can
be generated by peripherals making the table pointed
to by register I exactly 256 bytes long and fitting
nicely into a single 256-byte page.  However in the
real world, some peripherals can generate odd vectors,
including 255 meaning the vector table could be 257
bytes long and may (without care) have overlapping
entries due to the mix of odd and even vectors
generated by attached peripherals.

z88dk supplies an im2 library; consult the header
file im2.h for function prototypes in the library.

To use the library you must first declare the location
of the 256(257) byte interrupt vector table as described
in the header file.

im2_Init() is called to initialize im2 mode.  This call
will create the im2 vector table, set im2 mode, and allow
you to choose between a conventional 256 byte table or
the less conventional 257 byte variant.  Each vector entry
will point at a default interrupt service routine that you
pass in as parameter; this can be a C function or an
absolute memory address.

Typical interrupt service routines can be installed
on any vector with im2_InstallISR().  This can be
the address of a C function, assembly function or
absolute memory address.  Any such interrupt routine
must save and restore registers as well as reenable
interrupts prior to a "reti" to return from interrupt.
If the function registered is a C function this must
be done with embedded assembler.

The library also supplies a Generic Interrupt Service
Routine.  This is an ISR that can be installed on any
vector with im2_InstallISR() as any typical ISR is.
The difference is that this ISR can have any number of
hooks registered with it.  Each hook will be executed
one after the other on interrupt.  Should any hook return
with the carry flag set, succeeding hooks will not be
run.  The ISR itself will take care of saving and
restoring registers as well as reenabling interrupts
so any hooks registered with it do not need to worry
about those details.  This model is the usual model
used when several peripherals could supply the same
vector, but can also be used to register plain C
ISRs without their having to worry about the low-level
housekeeping associated with ISRs.

To create a generic ISR, it must be copied into RAM.
This is done with im2_CreateGenericISR().  Any number
of independent generic ISRs can be created in this
manner and can be registered on one or more interrupt
vectors with im2_InstallISR().

When making changes to the z80's interrupt state
whether using im2.lib or not, make sure to have
interrupts disabled using some inline assembler.
Im2.lib will not enable or reenable maskable interrupts
so as not to interfere with the program.

Further questions can be answered on the z88dk users
mailing list.


- aralbrec 04.2006
