Copyright (c) 2012-2016 Hyperion Entertainment and contributors.

How to install a hardware interrupt

From AmigaOS Documentation Wiki
Jump to: navigation, search

Author

Alfkil Wennermark
Copyright (c) 2010 Alfkil Wennermark
Used by permission.

Tutorial

I was looking for a way to install a genuine hw interrupt, and this code is the proper way to do so. Thanks to Steven Solie, Thomas Frieden, Jörg Strohmayer and Colin Wenzel.

When trying to install an exec interrupt handler, I quickly realised, that the IExec->AddIntServer didn't really do the job. All the trapnumbers defined in exec/interrupts.h will make this function fail, except for TRAPNUM_ILLEGAL_INSTRUCTION, that has no relevance on ppc machines.

After some (a lot of) guidance from the real people, I have come up with this example, that uses SetIntVector to install a global interrupt handler for that particular interrupt. Beware, when you use SetIntVector, you _have to_ manually call the old interrupt handler (returned from SetIntVector) if you don't want your entire system to go boom!

Also, if you are trying to catch exceptions from a specific task (eg. for debugging purposes) you should use the AddDebugHook function from the exec debug interface (more on this in a future article).

Here is the example:

/* realinterrupt.c - HW interrupt example code.
Edited and updated for amigaos 4 by Alfkil T. Wennermark 2010
Edited and bug-tested by Steven Solie.
 
Thanks to: Steven Solie, Jörg Strohmayer, Thomas Frieden and Colin Wenzel :-) */
 
/* REMARK: On a SAM/SAM-flex system, the returned counter will be a very large number.
           We have yet to figure out, why this is. On AmigaOne's it will be 1 as expected. */
 
 
#include <exec/types.h>
#include <proto/exec.h>
#include <proto/dos.h>
 
#include <exec/memory.h>
#include <exec/interrupts.h>
 
#include <dos/dos.h>
 
 
#include <stdio.h>
 
uint32 *beef;
 
 
struct IData {
    uint32 counter;
	struct Interrupt *oldinterrupt;
};
 
struct IData *idata;
 
#if 0		//This will not work, because it doesn't call the old interrupt
asm (
"	.globl icode\n"
"    				\n"
"icode:				\n"
"	lwz %r3, 0(%r5)	\n"
"	addi %r3, %r3, 5\n"
"	stw %r3, 0(%r5)	\n"
"	li %r3, 1		\n"
"	blr				\n"
);
#endif
 
 
ULONG icode( struct ExceptionContext *context, struct ExecBase *sb, APTR trapData)
{
	struct IData *d = (struct IData *)trapData;
	struct Interrupt *oldi = d->oldinterrupt;
 
	d->counter++;
 
	//NB-NB-NB: You _need_ to call the old interrupt, otherwise you will go boom!
	typedef uint32 (*OLDVEC)();
 
	return ((OLDVEC)oldi->is_Code)(context, sb, oldi->is_Data);
}
 
 
int main()
{
	enum enTrapNumbers trapnumber = TRAPNUM_DATA_SEGMENT_VIOLATION;
 
	if (!(idata = IExec->AllocMem(sizeof(struct IData), MEMF_SHARED | MEMF_CLEAR)))
		return RETURN_FAIL;
 
    struct Interrupt *interrupt = IExec->AllocSysObjectTags(ASOT_INTERRUPT,
		ASOINTR_Code, icode,
		ASOINTR_Data, idata,
		TAG_END);
 
	if (interrupt == NULL)
	{
		IExec->FreeMem (idata, sizeof (struct IData));
		return(-20);
	}
 
    interrupt->is_Node.ln_Pri = -1;
	interrupt->is_Node.ln_Name = "my personal interrupt";
 
	//printf("icode = %#08x\n", icode (NULL, NULL, idata));
	//printf("counter = %d\n", idata->counter);
 
	printf("calling AddIntServer...\n");
	fflush(stdout);
 
//	IExec->Forbid();
	idata->oldinterrupt = IExec->SetIntVector(trapnumber, interrupt);
//	IExec->Permit();
 
	if(!idata->oldinterrupt)
	{
		printf("server not installed!\n");
	}
	else
	{
		printf("server installed\n");
		fflush(stdout);
 
		idata->counter = 0;
 
//#if 0
		/* cause a data storage violation: */
		beef = (uint32 *)0;
		*beef = 0L;
//#endif
 
		IDOS->Delay(10);
 
		printf("counter = %d\n", idata->counter);
 
		/* Remember to restore the old interrupt vector, or you will die ;-) */
		IExec->SetIntVector(trapnumber, idata->oldinterrupt);
	}
 
	IExec->FreeSysObject(ASOT_INTERRUPT, interrupt);
	IExec->FreeMem (idata, sizeof (struct IData));
 
	return 0;
}