gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

ASM > call a C function from asm

#68720 - ProblemBaby - Thu Jan 26, 2006 3:06 pm

Hi

Just a small question I want to call a c-function from my asm routine. Since ive to push some regs to the stack i get problems.

Code:

.thumb_func
.global myFunc
myFunc:
PUSH { r4 }
LDR r4, [r0, #0]

MOV r0, #1
MOV r1, #2
MOV r2, #3
MOV r3, #4
BX r4
   
      
POP { r4 }
BX lr


r4 is correct, the problem is that it doesnt get to the code after BX r4 and I dont know how to solve this in thumb.

thanks

#68727 - FluBBa - Thu Jan 26, 2006 3:48 pm

First you have to push lr to stack together with r4, then you have to set lr to your return address somehow (don't know thumb good enough).
Are you calling a thumb routine or ARM routine? that might as well be of concern.
_________________
I probably suck, my not is a programmer.

#68730 - ProblemBaby - Thu Jan 26, 2006 3:58 pm

Quote:

First you have to push lr to stack together with r4, then you have to set lr to your return address somehow

pop lr isnt supported. The thing is that I aint sure how a standard C function returns.


Quote:

Are you calling a thumb routine or ARM routine? that might as well be of concern.

Thumb

#68791 - Cearn - Thu Jan 26, 2006 8:10 pm

What exactly are you trying to do here? What it looks like is that you have a function pointer in r0, load that in r4 and then jump to there. Have I got that right?

If so, what happens is that when the called function ends it'll probably jump back to lr, which is still the old lr -- from the function that calls myFunc. So last piece of code is skipped.

bx is used for returning from a function. If you want to call functions, use bl. That sets lr to after the call so that the called function returns there. Of course, you'll have to push/pop the old lr to get out of myFunc again. Or something like that, because you can't pop lr directly. However, you don't have to use that particular register for returning, just use something else like r3:

Code:
    .thumb_func
    .align 2
    .global myFunc
myFunc:
    push    {r4, lr}
    ldr     r4, [r0, #0]

    mov     r0, #1
    mov     r1, #2
    mov     r2, #3
    mov     r3, #4
    bl      r4      @ jump to function loaded from r0
    pop     {r4}    @ pop r4
    pop     {r3}    @ pop 'lr'. You _can't_ combine these pops because
                    @ that'd reverse the order!
    bx      r3      @ jump back to 'lr'

Untested for little bugs, but this is more like how it should be. Also compile with -S to see how GCC manages it.

#68800 - ProblemBaby - Thu Jan 26, 2006 8:50 pm

Ive tried bl and it seems it cant call from a register.
But ive fixed it, I changed it to three parameters so I dont have to push anything

Thanks

#68801 - Cearn - Thu Jan 26, 2006 9:00 pm

Quote:
Ive tried bl and it seems it cant call from a register.

Right, I forgot there was something strange there.

Well, here's one thing that might work if you need it. There seem to be functions called '_call_via_r0', '_call_via_r1', etc. These are what GCC uses when you do long calls. You can use bl with those, and they'll take care of the rest. For a sample use, see devkitARM's arm-elf/lib/gba_ctr0.s

#68803 - ProblemBaby - Thu Jan 26, 2006 9:25 pm

thanks I'll check it out!

#68820 - wintermute - Thu Jan 26, 2006 11:12 pm

If code is compiled for interworking then the function returns with bx lr, for non interworking code it's mov pc,lr

There are two ways you can call C functions from asm, the first is to use the functions provided in libgcc as the standard devkitARM crt0s do

Code:

   mov   r0, #0         @ int argc
   mov   r1, #0         @ char *argv[]
   ldr   r3, =main
   bl   _call_via_r3      @ jump to user code


there are _call_via_rX functions for all registers, for your example _call_via_r4 would work fine. You can provide your own code for this function if you don't want to link libgcc, it's simply

Code:

_call_via_r4:
   bx   r4


The bl to this function sets lr.

The simplest way to write the code you've shown here is this

Code:

   .thumb

_call_via_r4:
   bx   r4

   .thumb_func
   .global myFunc
myFunc:
   push   { r4, lr }
   ldr   r4, [r0, #0]

   mov   r0, #1
   mov   r1, #2
   mov   r2, #3
   mov   r3, #4
   bl   _call_via_r4
   pop   { r4, pc }


the pop {r4, pc} returns to the caller and changes thumb/arm state as needed.

The other way is to set the return address directly as the libgba & libnds interrupt dispactchers do but I'm afraid I don't know how to do this efficiently from thumb atm.