In the previous five articles, we dealt with the major topics faced during
podule design.  Now we turn to other features of the podule system.  This
month we will deal with interrupts.

What are interrupts?

All the methods we've looked at so far to get data in and out of the computer
have been processor-initiated.  This means that the programmer decides when
they should happen.  This is all very well, but what about events that happen
rarely?  Consider the use of the telephone.  If we had to pick the phone up
to find out if anyone was ringing us, we'd spend all day checking the phone
and never get anything done.  Alternatively the phone can ring, so we can get
on with other things and answer it only if there is an incoming call.  Once
we've finished the call, we can put it down and carry on with what we were
doing beforehand.  This is much more efficient.

This means of the outside world alterting the processor to an event is the
mechanism of interrupts - it works as follows.  The processor is busy with
another task when an interrupt pin on the processor is asserted.  The
processor suspends what it is doing, and branches to an interrupt service
routine (ISR).  The ISR decides which interrupt is being triggered and where
it comes from, then goes off to deal with the hardware device that requested
the interrupt.  Once this is dealt with the device deasserts the interrupt
pin, and the processor leaves the ISR and returns to what it was doing
before. 

Interrupts happen all the time.  Every time you press a key the keyboard
controller generates an interrupt.  The programmer doesn't usually notice
this because, apart from a short delay, the processor jumps out of and
back into the programmer's code as if nothing had happened, and the program
continues normally.

The ARM provides two levels of interrupts, Interrupt ReQuests (IRQs) and Fast
Interrupt reQuests (FIQs).  IRQs are used for most of the interrupts used on
RISC OS machines, whilst Fast Interrupts are reserved for devices that need
an even quicker response than provided by IRQs.  Interrupts are level
sensitive, which means that the processor will assume another interrupt is
active if the interrupt pin is still asserted once the processor has finished
dealing with a first interrupt.  I shall refer only to IRQs when I talk about
interrupts, unless I refer to FIQs specifically.

The ARM chip itself only provides an IRQ pin and a FIQ pin.  This isn't very
helpful when we want to have lots of devices that generates interrupts, since
we might wish to enable some and disable others.  Also we need some means of
determining which device is the one that requires attention.  To do this the
I/O controller (be it IOC, IOMD or other device) expands the
interrupt capability by providing 16 or more IRQ lines, each of which can be
individually enabled or disabled.  It also provides a means for the processor
to find out which devices are asserting an interrupt condition at the present
time.  FIQs aren't handled by this mechanism, and are dealt with separately. 
A list of the uses for the bottom 16 IRQ lines on A5000-class machines is
shown in table 1 - the function of many of these is common across most Acorn
hardware.

Interrupt hardware

The podule bus provides access to both interrupt levels via the pins /PIRQ
and /PFIQ.  These are open collector or wire-OR signals.  This means that the
lines are pulled high by a resistor somewhere in the machine.  If you wish to
pull the line low you can do so by driving it low through a transistor as in
figure 1a.  If there is more than one device that might trigger an interrupt,
just wire together multiple transistors as shown in figure 1b.

Many chips designed for interfacing to a microprocessor bus already have an
open collector interrupt output, meaning the transistor described here is
already included on the chip.  The 6522 shown last month is one example, and
so merely requires connecting its interrupt pin directly to /PIRQ.  To
illustrate the principles however, we'll develop an example from scratch
using simple logic to demonstrate how it is done.

Figure 2 shows a simple circuit to make a push button cause an interrupt. 
Pressing the button sets the RS flip-flop, causing the output to go high and
so drive /PIRQ low, signalling an interrupt.

There are a number of major problems with this circuit however.  The first is
that there is no means of the processor identifying which device is causing
the interrupt.  Whilst some interrupts are dedicated to their task (such as
the floppy disc interrupt), others are shared between many devices; in
particular the podule bus only has only one interrupt shared between all
podules in the machine.  The other problem is that once the processor has
dealt with our button press, it can't make the interrupt go away since the
flip-flop is still asserting the interrupt pin.

Figure 3 shows a means of doing this.  A write to the upper half of our
podule space, with any data value, will reset the flip-flop and so clear the
interrupt.  A read from the same addresses will return the status of the
interrupt in bit 0 (being 1 for interrupt being requested, 0 for no
interrupt).  The interrupt will also be cleared when the podule bus is reset
- otherwise on boot the machine will hang waiting for it to be serviced.

This circuit is all very well, but it doesn't help if the interrupt source
comes from another chip which doesn't have such a convenient means of
clearing the interrupt.  Figure 4 shows a more conventional interrupt status
and control circuit, as used on many podules.  This assumes the IRQ from the
other chip is active high, common on many chips intended for PCs.  Status is
as before, but the interrupt can be enabled by writing 1 in bit 0 to an
address in the upper half of our podule space.  It can be disabled by writing
a 0 in bit 0.

Of course, some peripheral chips provide means internally to clear interrupts
and check their status.  If this is the case, and they are compatible with
the use of status pointers as described below, then no extra hardware is
needed.


Interrupt status pointers

Now we have a means of generating an interrupt, and a method of finding out
its source and controlling it.  But RISC OS doesn't know anything about it
yet, so we need to tell it where it can look to find out if our podule (as
opposed to all the other podules) is asserting an interrupt.  This is done
using the expansion card ID (EcID).

If you are using a simple one-byte EcID, you must provide interrupt status in
this byte.  Bit 0 must be a one if the podule is requesting an IRQ, and bit 2
must be a one if it requests a FIQ, or zero otherwise.  The circuit in figure
4 will do this for IRQs if LA13 is inverted before it is passed to this
circuit, so it is active in the lower half of our podule space.  Addition of
more tristates for bits 1 and 3-7 with their inputs forced high or low allows
the other bits of the EcID to be specified without affecting the IRQ/FIQ
bits.

If you are using a ROM it is rather inconvenient to factor in these status
bits to the output of the ROM, so RISC OS provides a means of placing the
status bits elsewhere and providing pointers to them.  In the extended EcID,
bit 1 of word 1 is set if 'interrupt status has been relocated', or clear if
it is in the low byte as detailed above.  Relocated interrupt status means
after the 8 words of extended EcID are found interrupt status pointers, as
shown in figure 5.  The status address pointers contain offsets from the base
of slow space (&03240000 on pre-Iyonix hardware) to the status registers and
are calculated as if the podule is in slot zero.  For example the status
register shown in figure 4 can be accessed via any address in the top half of
podule space.  We have 4Kwords of podule space, so this is 16Kbytes of
address space.  Therefore the register in figure 4 can be found 16Kbytes/2 =
8Kbytes above the start of podule space.  So if we wish our status register
to be accessed in slow mode, we set the pointer to this status register to be
8Kbytes or &2000, being the difference between the absolute address of the
register (&03242000) and the base of the podule (&03240000).  If we wish to
access our status register in fast mode, we must add the offset from slow to
fast mode, in other words set our pointer to be (&03340000-&03240000)+&2000 =
&00102000, where &03340000 is the address of slot 0 in fast mode on
pre-Iyonix hardware.  This offset is still valid on Iyonix, despite the base
addresses of podule space changing.

The bitmasks are used to determine whether an interrupt is pending.  They are
ANDed with the contents of the status register, where a non-zero result is
determined to be an interrupt that needs to be dealt with.  So for figure 4
we might use a bitmask of &01, as we only care about the least significant
bit.  Thus the least significant bit being set in the status register will
indicate an interrupt that needs attention.  If the bitmask was &C8, any of
bits 3, 6 or 7 set in the status register would flag an interrupt.  Since the
bitmasks are only 8 bits wide as only 8 bits are reserved for them in the Interrupt Status Pointers, this means you must have the status in the
lowest 8 bits of the status register.

The cliffhanger...

I'm afraid I'm going to have to leave the software for dealing with
interrupts until next time, as I need to describe the RISC OS 5 Hardware
Abstraction Layer which is an article in itself.  Never fear, users of
earlier machines will be also catered for.

In the meantime I'd welcome any comments on this series, particularly if it
doesn't make sense or you've tried any of the things described.  Until next
time, happy interrupting!
