[comp.arch] Interrupt vectors.

jack@mcvax.UUCP (06/04/87)

I was just wondering wether any of the fancy new machines (MIPS,
AMD) did anything about the stupid way interrupts are done in
most modern machines.

Almost all machines I know of provide a list of interrupt vectors
which consist of a program counter, and (sometimes) a module base
address or a PSR.

Now, given most modern operating systems, this is exactly what you
*don't* want. If you want to handle interrupts in a high-level
language, you want all interrupts to jump to the same routine, and
have the interrupt number handy somewhere. Operating Systems I'm
familiar with all simulate this by either stealing a couple of bits
in the PSR and go through pains to extract them again in the common
interrupt routine, or, even worse, provide lots and lots of twisty little
routines, all slightly different (like
intvec128:	push	#128.
		jmp	int_common	).

Now, the sensible thing for the hardware to do seems to be to
provide one vector, and push the interrupt number along with the
PC, PSW and what have you.

Is this scheme implemented on any machine?
-- 
	Jack Jansen, jack@cwi.nl (or jack@mcvax.uucp)
	The shell is my oyster.

csg@pyramid.UUCP (Carl S. Gutekunst) (06/05/87)

In article <7408@boring.cwi.nl> jack@cwi.nl (Jack Jansen) writes:
> If you want to handle interrupts in a high-level
>language, you want all interrupts to jump to the same routine, and
>have the interrupt number handy somewhere.

I can't for the life of me see why you'd want this. On the Pyramid, I hand the
address of my interrupt handler to the device. When it needs service, it puts
an interrupt request on the bus; the first available CPU responds by asking
for an interrupt vector; the device then supplies the address of my interrupt
handler. The CPU calls the handler, which then services whatever it was that
the device was interrupting for. The handler is, of course, written in C,
except for four asm() statements that set up the return-from-interrupt. 

What could easier than that?

<csg>

mash@mips.UUCP (06/05/87)

In article <7408@boring.cwi.nl> jack@cwi.nl (Jack Jansen) writes:

>I was just wondering wether any of the fancy new machines (MIPS,
>AMD) did anything about the stupid way interrupts are done in
>most modern machines....<discussion of evils thereof>...

>Now, the sensible thing for the hardware to do seems to be to
>provide one vector, and push the interrupt number along with the
>PC, PSW and what have you.

>Is this scheme implemented on any machine?

Sure. The MIPS R2000 has 3 vectors: RESET, User TLBMISS (software handler
for TLBmisses for the most frequent case, i.e., frequency makes it
the only one worth a special case), and everything else.  It does
what you say.  See IEEE Compcon, March 1986, SanFrancisco, 138-143.
Doing an interrupt is basically (about 2 cycles):
	1) Shutdown the pipeline, canceling any write-back necessary.
	2) SAve previous kernel/user bit and interrupt-inhibit bits,
	   set current ones to kernel and inhibited.
	3) Save exception Pgm Cntr into on-chip register.
	4) Set a Cause field in another register.
	5) (In some cases) set useful values into some other special registers
	6) Transfer control to the kernel interrupt vector.

The general scheme is not uncommon in RISC designs.
-- 
-john mashey	DISCLAIMER: <generic disclaimer, I speak for me only, etc>
UUCP: 	{decvax,ucbvax,ihnp4}!decwrl!mips!mash, DDD:  	408-720-1700, x253
USPS: 	MIPS Computer Systems, 930 E. Arques, Sunnyvale, CA 94086

mac@uvacs.CS.VIRGINIA.EDU (Alex Colvin) (06/05/87)

In article <7408@boring.cwi.nl>, jack@cwi.nl (Jack Jansen) writes:
> you want all interrupts to jump to the same routine, and
> have the interrupt number handy somewhere. Operating Systems I'm
> familiar with all simulate this ...

> Now, the sensible thing for the hardware to do seems to be to
> provide one vector, and push the interrupt number along with the
> PC, PSW and what have you.
> 
> Is this scheme implemented on any machine?

Almost.  The system you complain about tends to prevail on
microcomputers more than mainframes.  Those (Honeywell) mainframes with
which I was familiar have a single interrupt entry for I/O interrupts.
You are then provided a bit mask of channels attempting an interrupt.
Handles the case of simultaneous interrupts nicely.

Instruction faults are handled differently, each with its own entry,
which makes some sense.  You probably don't want to schedule these.

guy@gorodish.UUCP (06/05/87)

In article <7408@boring.cwi.nl> jack@cwi.nl (Jack Jansen) writes:
> Now, given most modern operating systems, this is exactly what you
> *don't* want. If you want to handle interrupts in a high-level
> language, you want all interrupts to jump to the same routine, and
> have the interrupt number handy somewhere. Operating Systems I'm
> familiar with all simulate this by either stealing a couple of bits
> in the PSR and go through pains to extract them again in the common
> interrupt routine, or, even worse, provide lots and lots of twisty little
> routines, all slightly different

In the OSes I'm familiar with, all interrupts *for a given type of
device* ultimately end up calling the same routine, providing a unit
number as an argument, but interrupts for two different devices end
up calling two different routines.  I presume you're talking about
the initial trap, though; the code invoked by that trap could figure
out both which interrupt routine to call (i.e., which type of device
is interrupting) and what unit number to pass as an argument (i.e.,
which of those devices is interrupting).

In article <438@winchester.UUCP>, mash@mips.UUCP (John Mashey) writes:
> Sure. The MIPS R2000 has 3 vectors: RESET, User TLBMISS (software handler
> for TLBmisses for the most frequent case, i.e., frequency makes it
> the only one worth a special case), and everything else.  It does
> what you say.

The diagram in "Operating System Support on a RISC" shows both the
Status and Cause registers.  The Cause register includes a 6-bit
ExtInt field, which I presume is a bitset indicating which of the 6
external interrupts are pending.  To what do these 6 interrupts
correspond?  Do they correspond to classes of devices?  I presume
you are not limited to 6 different devices attached to a system
containing an R2000, so you would "double up" on external interrupts
as necessary.

If so, how are the devices distinguished?  Is this done with polled
interrupts, or is some code provided in one of the other special
registers?
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com

shebanow@ji.Berkeley.EDU (Mike Shebanow) (06/05/87)

In article <2878@pyramid.UUCP> csg@pyramid.UUCP (Carl S. Gutekunst) writes:
>In article <7408@boring.cwi.nl> jack@cwi.nl (Jack Jansen) writes:
>> If you want to handle interrupts in a high-level
>>language, you want all interrupts to jump to the same routine, and
>>have the interrupt number handy somewhere.
>
>I can't for the life of me see why you'd want this.

Here's why:

Assume a machine (when it interrupts) (like a 68010, 68020) pushes
the PSW, PC, and the interrupt vector number:

	+---------------+
SP->	|     PSW	|
	+---------------+
	|     PC	|
	+---------------+
	|     IVECT	|
	+---------------+

An interrupt handler could then be written once in assembly:

	.globl	i_entry
	.extern	i_table
i_entry:
	movem.l	???,-(sp)	* save all registers which C doesn't save
	move.?	ivect(sp),r0	* get the interrupt vector number
	move.l	i_table(r0),r1	* get a handler routine address
	push	r0		* pass vector number as argument
	jsr	(r1)		* call handler
	add	#4,sp		* pop ivect number argument
	movem.l	(sp)+,???	* unsave registers
	rte			* return from interrupt

Now all one needs to do to set up an interrupt handler (in C) is
define the "i_table" contents:

extern void handler_1(), handler_2()....;

void (*i_table[])() = {
	handler_1,		/* called for interrupt vector #1 */
	handler_2,		/* called for interrupt vector #2 */
	etc...,
};

The handler routines are now all pure C -- no assembly at all. Having
written many many drivers, this would be a blessing. If you
really want to know where this is nice, try writing a driver for a
multiple UART board (one which has for example, 8 UARTs all of the same
type with three interrupt service routines each -- receive, transmit,
and control). I know -- I had to write one once. With standard machine
architectures (68000), one has to have this asm glue code in each routine,
one for each uart/interrupt (24 in all). This makes for a large asm file.
If the ivect number is passed, a three common routines can be used -- the
ivect number is used to tell which uart interrupted (a unique ivect is
assigned to each uart). This makes life a lot easier.

Mike Shebanow

rpw3@amdcad.UUCP (06/06/87)

In article <7408@boring.cwi.nl> jack@cwi.nl (Jack Jansen) writes:
>Now, the sensible thing for the hardware to do seems to be to
>provide one vector, and push the interrupt number along with the
>PC, PSW and what have you.
>Is this scheme implemented on any machine?

Yup! (Almost.) The IBM/370 architecture (in "EC mode") provides one
vector for each major class of event, where all I/O is one class, and
a nice "reason" code which then tells you the specific channel/device
(or interrupt cause for non-I/O), etc. (No flames about all the *other*
uglinesses of 370 interrupts, please! It does what he asked...)


Rob Warnock
Systems Architecture Consultant

UUCP:	  {amdcad,fortune,sun,attmail}!redwood!rpw3
ATTmail:  !rpw3
DDD:	  (415)572-2607
USPS:	  627 26th Ave, San Mateo, CA  94403

mash@mips.UUCP (06/06/87)

In article <20461@sun.uucp> guy%gorodish@Sun.COM (Guy Harris) writes:
 (regarding MIPS R2000)
>The diagram in "Operating System Support on a RISC" shows both the
>Status and Cause registers.  The Cause register includes a 6-bit
>ExtInt field, which I presume is a bitset indicating which of the 6
>external interrupts are pending.  To what do these 6 interrupts
>correspond?  Do they correspond to classes of devices?  I presume
>you are not limited to 6 different devices attached to a system
>containing an R2000, so you would "double up" on external interrupts
>as necessary.
>
>If so, how are the devices distinguished?  Is this done with polled
>interrupts, or is some code provided in one of the other special
>registers?

1) Thank goodness for people who actually read the literature!

2) The 6 bits correspond to whatever they're hooked to on the board.

3) On MIPS cpu boards, the 6 bits happen to be:
	Bus Error timeout
	profiling clock
	coprocessor interrupt
	scheduling clock
	onboard uarts
	vectored interrupts from VME

4) Some additional registers on the board record the VME interrupt info,
so you can just go there and look, then take the appropriate vector.

Nones of this is particularly different from anything else, i.e.,
the distinguishing info to identify an interrupt has to come from somewhere,
and you can either do it in hardware, microcode, or software, or some
combination, with RISCs confusing the issue a little, since the software
is more like microcode.
-- 
-john mashey	DISCLAIMER: <generic disclaimer, I speak for me only, etc>
UUCP: 	{decvax,ucbvax,ihnp4}!decwrl!mips!mash, DDD:  	408-720-1700, x253
USPS: 	MIPS Computer Systems, 930 E. Arques, Sunnyvale, CA 94086

csg@pyramid.UUCP (06/06/87)

<<<If you want to handle interrupts in a high-level language, you want all
<<<interrupts to jump to the same routine, and have the interrupt number handy
<<<somewhere.
<<
<<I can't for the life of me see why you'd want this.
<
<Assume a machine (when it interrupts) (like a 68010, 68020) pushes
<the PSW, PC, and the interrupt vector number....

Pardon my blinders, I wasn't thinking. The Pyramid 90x, which has a register
architecture, is hardly representive of the entire world. (All I had to do was
go back and look at my own O/S code for PDP-11, 68000, Z80, and Z8. Gee, it
would have been handy it they'd used a single interrupt entry point... *SIGH*) 

<csg>

John_M@spectrix.UUCP (06/07/87)

In article <7408@boring.cwi.nl> jack@cwi.nl (Jack Jansen) writes:
> ...  you want all interrupts to jump to the same routine, and
>have the interrupt number handy somewhere. Operating Systems I'm
>familiar with all simulate this by either stealing a couple of bits
>in the PSR and go through pains to extract them again in the common
>interrupt routine, or, even worse, provide lots and lots of twisty little
>routines, all slightly different (like
>intvec128:	push	#128.
>		jmp	int_common	).
>
>Now, the sensible thing for the hardware to do seems to be to
>provide one vector, and push the interrupt number along with the
>PC, PSW and what have you.
>
>Is this scheme implemented on any machine?
>-- 
>	Jack Jansen, jack@cwi.nl (or jack@mcvax.uucp)
>	The shell is my oyster.

The Motorola 68020 provides almost what you ask for.  The exception
vector pushed onto the stack contains the exception index that caused
the trap.  Thus you can use a common routine that does all of the
shared code and then uses its "argument" in the exception vector to
determine which interrupt (or other trap) actually occurred.

It does not do one thing that you ask for - it does have separate
vectors for each exception.  You have to make them contain the same
address to have a shared handler.  It would have been a mistake for
Motorola to do things exactly as you ask - not every operating system/
hardware design would be set up in such a way that ALL of the interrupts
would be handled by a common routine.  It would be as much a mistake
for the hardware to insist that the software uses a common routine (which
you ask for) as it is to ensure that a common routine couldn't be used
(which you complain about).

This is a case where the hardware should provide mechanisms which are
capable of supporting any policy that the software designer chooses
to use.
-- 
---------  .signature eater food ---------
John Macdonald   UUCP:    {mnetor,utzoo} !spectrix!jmm
                 internet:Sorry. We're in range, but we're in no domain.
Disc-claimer: (noun) any hack's source directory

jfh@killer.UUCP (John Haugh) (06/08/87)

some one stole my uppercase (maybe? do you see any? :-) ...

yes, this is slower, so don't flame me for it, and besides, this code wasn't
my idea anyway ...

start with a vector table for a 68000.

	.long	start		; start of startup routines
	.long	kstack		; top of kernel stack
	.long	buserr		; bus error address
	.long	adredor		; address error address
		.
		.
	.long	interupt_vec	; your interupt vector

now, define a little piece of code with the name you want.

vectable:
	.long	error		; only reset uses this one ..
	.long	error		; ... and no one uses this
	bsr.w	trap		; call trap, push pc ...
	bsr.w	trap		; call trap, push pc (catching on yet?)
		.
		.
	bsr.w	trap		; even your routine goes there

now i write my trap routine.  it figures out the vector number and
does a few other things

trap:
	movem.l	#<d0,d1,a0,a1>,-(sp)	; save those registers
	mov.l	16(sp),d0		; get the stack pc ...
	sub.l	#vectable, d0		; ... and offset from ...
	lsr.l	#2,d0			; ... and make it into an index!
	mov.l	usp,-(sp)		; stack user stack poiner for kicks
	mov.l	d0, -(sp)		; stack interupt number
	jsr	_trap			; call c trap routine
	add.w	#8,sp			; pop args
	movem.l	(sp)+,#<d0,d1,a0,a1>	; those registers again...
	add.w	#4,sp			; waste some cycles ...
	rte				; ... and poof - back you go.

what is so horrible about this?  (no, on second thought, don't answer that.)
for a cisc machine, i can't imagine doing any better.

- john.

.signature:	can't read: forgery

radford@calgary.UUCP (06/10/87)

In article <971@killer.UUCP>, jfh@killer.UUCP (John Haugh) writes:

> yes, this is slower, so don't flame me for it, and besides, this code wasn't
> my idea anyway ...
> ...
> now i write my trap routine.  it figures out the vector number and
> does a few other things
> 
> trap:
> 	movem.l	#<d0,d1,a0,a1>,-(sp)	; save those registers
> 	mov.l	16(sp),d0		; get the stack pc ...
> 	sub.l	#vectable, d0		; ... and offset from ...
> 	lsr.l	#2,d0			; ... and make it into an index!
> 	mov.l	usp,-(sp)		; stack user stack poiner for kicks
> 	mov.l	d0, -(sp)		; stack interupt number
> 	jsr	_trap			; call c trap routine
> 	add.w	#8,sp			; pop args
> 	movem.l	(sp)+,#<d0,d1,a0,a1>	; those registers again...
> 	add.w	#4,sp			; waste some cycles ...
> 	rte				; ... and poof - back you go.
> 
> what is so horrible about this?  (no, on second thought, don't answer that.)
> for a cisc machine, i can't imagine doing any better.

The easiest way is to store the vector number in the unused top eight
bits of the interrupt vector address. Then you can have all interrupts
go directly to the same place, do a jsr to the next instruction, and
fetch the interrupt number from the stack.

Before you flame about using the top eight bits of the PC this way,
remember that this piece of code will be inherently incompatible with
the 68020 whatever you do.

    Radford Neal

wolfgang@haddock.UUCP (06/10/87)

As already noted, a 68000 unlike the 68010/68020 doesn't push the
vector number onto the stack for you.  Here is another way for you to
wrestle this information from the 68000, with NO individual stubs for
each vector.

First notice that the 68000 discards the top 8 address bits a31-a24.
(Oh oh, I can here the groans already.) We can use these 8 bits for 
encoding a separate value for each of the 256 vectors. (And then 
purify the pc of these dirty bits!)

----
| vector table 
.long trap
.long trap + 0x01000000
.logg trap + 0x02000000
.long trap + 0x03000000
....
.long trap + 0xff000000

trap:
	moveml #SAVE_A0A1D0D1, -sp@
	jsr _trap	| this also abandons the dirty bits in the pc
			| check that this assembles to an absulute 
			| value jump (and not a bsr)
	moveml sp@+, #RESTORE_A0A1D0D1
	rte

(Please no flames for typos in the 68000 assembly, I don't have a 
68000 assembler on hand to run this through.)
------

I have actually used this on an old homebrew 68000 computer. The
biggest problem was the so-called intelligent assembler that didn't
understand the difference between jsr and bsr. (I had to hand assemble
the jsr to get the jsr opcode).

-- 
Wolfgang Rupprecht 			haddock.ISC.COM!wolfgang
{decvax!cca|yale|ihnp4|cbosgd|bbncca|harvard}!ima!haddock!wolfgang