# How do I call a C function in another module from inline assembler in IAR EWARM?

I don't know if this is recommended or not but I am sure someone will give me a stern warning if not. I have posted a duplicate question in stackoverflow, however this may be just as appropriate here given that it's for an embedded platform and a number of embedded developers frequent this stackexchange. Not getting much traction over there either...

I have a bit of assembly in a hard fault handler. The assembly is basically meant to pass the current stack pointer as a parameter (in R0). It looks like so...

__asm("    mov     r0, sp\n"
"    bl      SavePC\n"
"    bx      lr");


This works fine when SavePC is in the same c file. However, when SavePC is placed in another c file I have no luck. I have tried to IMPORT the function like so...

__asm("IMPORT SavePC\n"
" mov r0, sp\n"
" bl SavePC\n"
" bx lr");


... but I must be doing something incorrect. The compiler reports the following...

Error[Og005]: Unknown symbol in inline assembly: "IMPORT"
Error[Og005]: Unknown symbol in inline assembly: "SavePC"
Error[Og006]: Syntax error in inline assembly: "Error[54]: Expression can not be forward"
Error[Og005]: Unknown symbol in inline assembly: "SavePC"
Error while running C/C++ Compiler


The c file with the assembly includes the header file with the SavePC prototype...

extern void SavePC(unsigned long);


Suggestions?

-
check this first Error[Og006]: Syntax error in inline assembly: "Error[54]: Expression can not be forward"... –  perilbrain Oct 5 '12 at 22:02
Something that can be really helpful in figuring out the details is to write a C file that does nothing but invoke the function, and compile it to assembly to examine. –  Chris Stratton Oct 25 '12 at 18:49

Using extern is a bad habit since it is prone to errors. C-99 standard provides an safe alternative for extern. You should write the function prototype in the header file without extern keyword. Then include the header file in both C files. The linker is then responsible for linking the function in different files.

Example:

void SavePC(unsigned long);


# File : source_c_file.c

#include "custom_header.h"

void SavePC(unsigned long)
{
....
....

....

}


# File : user_c_file.c

#include "custom_header.h"

void someFunction(void)
{
.
.
.

__asm("    mov     r0, sp\n"
"    bl      SavePC\n"
"    bx      lr");

.
.
.
}

-

## Some things you might look at

Often assembly language must use a name decorated with an underscore ("_SavePC") to refer to a C function that is defined in C without an underscore ("SavePC"). (The assembly-language name of C++ functions has much more decoration).

I only use the "extern" keyword on global variables, not on global functions, exactly as Kanwar Saad describes.

Have you considered maybe writing that bit of code in C, rather than assembly language, and letting the compiler handle the calling convention details? Today's C compilers often support special syntax that lets you write such handlers in more-or-less pure C -- often with something like a "__interrupt" specifier, but alas the exact details vary from one compiler to another. (It's possible that, like many older C compilers, the compiler you're using now absolutely requires writing things like hard fault handlers and interrupt vector tables in assembly language -- have you considered maybe upgrading to a more modern C toolchain such as GNUARM or YAGARTO that uses gcc ?)

The sequence

bl      SavePC
bx      lr


probably creates an infinite loop. Have you considered possibly using something more like the hard fault handlers already written and tested by other people? Perhaps trying a b c d or e ? if you are lucky, at least one will work for you.

If none of those little details helps, you might consider using the following general process:

## General process

When I want to call a C function from some assembly language, I

• write a "similar" piece of temporary test code in C -- a stub that sends the same types of parameters to the function and getting the same types of return values, but perhaps missing some critical functionality.
• compile it with the special option that produces a text file full of assembly-language mnemonics
• copy-and-paste that assembly-language skeleton into my assembly language
• Test the system -- assemble, compile, link, etc. the unmodified test code to make sure it actually compiles and links and runs the way I expect.
• Commit the properly-linking assembly language file to the version control system (even though some critical functionality is still missing)
• Fill in the remaining missing critical functionality.
• Test the system. Invariably, I discover something has broken so badly it doesn't even finish linking.
• Revert to the properly-linking version of the assembly code
• Try to fill in the remaining missing critical functionality.
• Repeat until it's good enough to ship.

tips for mixed C and assembly programming

rough draft of a book on ARM assembly-language programming.

-