How do I perform a long jump to a numerical address from my C code? I'm pretty sure I have to do this with inline assembly - however that seems complicated for C51. I figured in C I could do this:
typedef void (*jmpPtr)(void); jmpPtr jmp; ... unsigned int address = 0x4000U; jmp = (jmpPtr) address; (*jmp)();
Your needing an ISR-to-ISR jump complicates things a bit. Conceptually, the "jump using RET" is still do-able, but a C interrupt function returns using RETI instruction to restore the interrupt priority logic and that's (probably) not desireable at that point in servicing the interrupt. There's also the issue of differences in saved processor context needs between ISR functions. Maybe I've fallen victim to some form of limited thinking, but to reliably do what you want requires a bit of assembler, I think. Keil doesn't support inline assembly in the way we both probably understand the term, so I'm providing a short assembly language excerpt to show how I'd dynamically re-vector an interrupt. The idea is to provide a fixed ISR front-end that will "jump" through an address you provide in a global. That address is the dynamic address of your C interrupt function, which has presumably been relocated in flash. Since you want some ISR front-end code, you have to disable the compiler's automatic vector generation by using the NOINTVECTOR compiler option and write your own interrupt vectoring code. The following is a snippet for that assembler file (say, intvects.a51). I have tested this code and know it to work.
EXTRN DATA (Ext0_Addr) EXTRN DATA (Tmr0_Addr) CSEG AT 00003H Ext0_IntVector: ;** Calling the revector function pushes a ;* return address that will be overwritten ;* by the target ISR's address. ;** LCALL Ext0_Revector ; Never returning to here! CSEG AT 0000BH Timer0_IntVector: LCALL Timer0_Revector ; Never returning to here! ;** ==MORE VECTOR CODE SNIPPED== Ext0_Revector: USING 0 PUSH AR0 ; Save MOV R0,SP ; &Saved_R0 DEC R0 ; &RET_ADDR_MSB MOV @R0,Ext0_Addr ; Target ISR's addr MSB DEC R0 ; &RET_ADDR_LSB MOV @R0,Ext0_Addr+1 ; Target ISR's addr LSB POP AR0 ; Restore RET ; "Jump" to target ISR ; ..and RETI from there. Timer0_Revector: USING 0 PUSH AR0 ; Save MOV R0,SP ; &Saved_R0 DEC R0 ; &RET_ADDR_MSB MOV @R0,Tmr0_Addr ; Target ISR's addr MSB DEC R0 ; &RET_ADDR_LSB MOV @R0,Tmr0_Addr+1 ; Target ISR's addr LSB POP AR0 ; Restore RET ; "Jump" to target ISR ; ..and RETI from there. ;** ==MORE RE-VECTOR CODE SNIPPED== END