Copyright (c) Hyperion Entertainment and contributors.
Difference between revisions of "How to install a hardware interrupt"
Steven Solie (talk | contribs) (→Author) |
m (Categorized into Debug) |
||
(4 intermediate revisions by one other user not shown) | |||
Line 1: | Line 1: | ||
+ | [[Category:Debug]] |
||
== Author == |
== Author == |
||
Alfkil Wennermark<br/> |
Alfkil Wennermark<br/> |
||
Line 4: | Line 5: | ||
Used by permission. |
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. |
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. |
||
Line 15: | Line 16: | ||
Here is the example: |
Here is the example: |
||
+ | |||
+ | <syntaxhighlight> |
||
+ | /* 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; |
||
+ | } |
||
+ | </syntaxhighlight> |
Latest revision as of 16:20, 18 February 2013
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; }