|
The AVR instruction set doesn't seem to have an
indirect call option, ie. I've got an address pointer in memory
(or in a register) and I want to call that address. A common thing
to do if for example you have a lookup table.
How to get around this problem? It does have an
indirect jump instruction, so I came up with this.
callback_addresses:
.dw tmr_0_callback
.dw tmr_1_callback
.dw tmr_2_callback
.dw tmr_3_callback
.dw tmr_4_callback
;
;
; make Y point to array of function addresses
;
ldi YH,HIGH(callback_addresses<<1)
ldi YL,LOW(callback_addresses<<1)
; now get our function's address from memory
table
; let's get func #3 which will be offset 4
ldd ZL,y+4
ldd ZH,y+5
; put the return address on the stack
ldi r24,LOW(callback_returns_here)
push r24
ldi r24,HIGH(callback_returns_here)
push r24
; and jump to function,
; function must "ret" at the end of course
ijmp
some other code:
;
;
callback_returns_here:
; called function returns here but could be anywhere
There may be a better way, after all I've only been programming
with AVR assembly language for about a day at this point, but this
works a treat. Naturally you would have defined some .EQUs instead
of hard coded numbers for offsets etc. and you wouldn't do all this
for a single call, you do this sort of thing as part of a larger
routine, say a software timer function that wants to call a timer's
callback function when the timer expires.
Some time later. Guess who just found the ICALL instruction? We
can still use the same example as a method of using a jump table,
there's just less faffing around.
callback_addresses:
.dw tmr_0_callback
.dw tmr_1_callback
.dw tmr_2_callback
.dw tmr_3_callback
.dw tmr_4_callback
;
;
; make Y point to array of function addresses
;
ldi YH,HIGH(callback_addresses<<1)
ldi YL,LOW(callback_addresses<<1)
; now get our function's address from memory
table
; let's get func #3 which will be offset 4
ldd ZL,y+4
ldd ZH,y+5
; and call to function,
; function must "ret" at the end of course
icall
That's less work because we don't have to explicitly push the return
address, but there's probably little difference in time because
the address still gets pushed by the processor when it does the
ICALL (although that will be quicker).
One advantage to using IJMP is that you can return anywhere you
like, not just to to following instruction. You could even not bother
pushing a return address at all so when the IJMPed routine returns
it really does a return from this routine. Dodgy but a time saver
if you need a few extra cycles.
|