First, yes, you should use the INTERRUPT macro, not OSIntEnter/OSIntExit; read the following massive explanation to understand why. No, OSIntEnter and OSIntExit should not be marked deprecated. They are still a vital necessity; they simply aren't needed by most users.
Ok, I've been thinking this one over for the last couple days trying to figure out
why your conceptions about OSIntEnter and OSIntExit are misguided. It's one of those things where you know something so deeply, that you can intuit a correct or incorrect understanding, but articulating it is hard. Well, here goes the explanation...
Let's start the discussion by taking a large step back... the operating system you are using is largely based on uC/OS, which stretches back over 20 years (In fact the book on it I have in front of me is copyright 1992...). It was originally written for the Intel 80188. At that time, everything was absolutely written in assembly or, at most, C. Keep in mind that these were 8 or 16 bit micros running in the 8-25MHz. When it comes to ISRs, those were pretty much guaranteed to be written in assembly.
With this in mind, OSIntEnter and OSIntExit were never intended to be called from a C function. Furthermore, a normal C function cannot operate as an ISR on most processors. "Wait, what?!" I'm hearing you say. This is true, a normal C function cannot operate as an ISR, as most processors have a separate 'Return from Subroutine' and 'Return from Exception' (RTS and RTE) opcodes. This is certainly the case with the Coldfire. Since there's different return opcodes and they do different things with the stack, clearly the compiler can't know how you intend to use the function, so it will assume that it's just going to be called normally, and return with an 'rts'.
Since we've established that you can't use a C function directly as an ISR, how the hell do any of our interrupt functions actually work? By lying. That's how. Let's take a look at a representative example from 'Pin_Irq.cpp' for the MOD5441X and look at the trampoline ISR for IRQ1.
Code: Select all
// Pin J2.45
INTERRUPT( firq1_pin_isr, 0x2100 )
{
sim2.eport.epfr = 0x02; // Clear the interrupt flag
if ( TheIrqFuncs[1] ) TheIrqFuncs[1]();
}
The important part here is that declaration 'INTERRUPT( firq1_pin_isr, 0x2100 )'. The important part is that this creates a function called 'firq1_pin_isr' that we can link to in the vector table and execute as an ISR. And in that function, it will execute the code we gave here. But what about that 0x2100? What about that all caps INTERRUPT? where's the return type? For this, let's take a look at the header 'cfinter.h'. In it we will find a macro defined called INTERRUPT.
Code: Select all
#define INTERRUPT(x,y)extern "C" { void real_##x(); void x(); } void fake_##x(){\
__asm__ (".global "#x);\
__asm__ (#x":");\
__asm__ ("move.w #0x2700,%sr ");\
__asm__ ("lea -60(%a7),%a7 ");\
__asm__ ("movem.l %d0-%d7/%a0-%a6,(%a7) ");\
__asm__ ("move.w (OSISRLevel),%d0 ");\
__asm__ ("move.l %d0,-(%sp) ");\
__asm__ ("move.l (OSIntNesting),%d0");\
__asm__ ("addq.l #1,%d0");\
__asm__ ("move.l %d0,(OSIntNesting)");\
__asm__ ("move.w #"#y",%d0 ");\
__asm__ ("move.w %d0,%sr ");\
__asm__ ("move.w %d0,(OSISRLevel)");\
__asm__ ("jsr real_"#x );\
__asm__ ("move.l (%sp)+,%d0 ");\
__asm__ ("move.w %d0,(OSISRLevel)");\
__asm__ (" jsr OSIntExit ");\
__asm__ ("movem.l (%a7),%d0-%d7/%a0-%a6 ");\
__asm__ ("lea 60(%a7),%a7 ");\
__asm__ ("rte");} void real_##x()
A couple quick things to note real fast: this macro ends up creating three functions. In reality, it should only need to create 2, but to make the compiler happy about inline assembly, that needs to go inside a function. The C/C++ function that becomes the ISR, ends up being named 'real_<insertnamehere>', so in the case of the 'firq1_pin_isr', this creates the function 'real_firq1_pin_isr'. What then are you linking to when telling the vector table to use 'firq1_pin_isr'? That would be a wrapper subroutine, written in assembly, which first masks all interrupts (other than level 7), makes space on the stack, and saves off all the registers. It then saves the OSISRLevel as well, and then increments OSIntNesting directly (instead of calling OSIntEnter). If you look at the lock code around the increment in OSIntNesting inside of OSIntEnter, you'll see why the function call is unnecessary here. Finally, we set the status register to the second argument of the INTERRUPT macro (in our case 0x2100), set OSISRLevel equal to it, and then 'jsr' Jump, Set Return to our 'real' C ISR.
The C ISR then executes, and then returns. We restore the old OSISRLevel from the stack, and then call OSIntExit. Finally, the registers are restored from the stack, the stack pointer is restored, and we return from exception.
-Dan