Search code examples
cassemblyarmmbed

What is proper syntax to pass variables from C code to Assembly and back?


Struggling electrical engineering student trying to link C and Assembly (ARM32 Cortex-M) for an Embedded Systems final project. I don't fully understand the proper syntax for this project.

I was instructed to combine 2 previous labs - along with additional code - to build a simple calculator (+,-,*,/) with C and Assembly language in the MBED environment. I've set the C file to scan a keypad, take 3 user inputs to 3 strings, then pass these strings to an Assembly file. The Assembly file is to perform the arithmetic function and save the result in an EXPORT PROC. My C file then takes the result and printf to the user (which we read with PuTTY).

Here is my assembly header and import links:

    AREA calculator, CODE, READONLY ; assembly header

compute_asm
          IMPORT OPERAND_1 ; imports from C file
          IMPORT OPERAND_2 ; imports from C file
          IMPORT USER_OPERATION ; imports from C file
          ALIGN ; aligns memory

initial_values PROC 
          LDR R1, =OPERAND_1; loads R1 with OPERAND_1
          LDR R2, =OPERAND_2; loads R2 with OPERAND_2 

Here are a few lines from my C file linking to Assembly:

int OPERAND_1; //declares OPERAND_1 for Assembly use
int OPERAND_2; //declares OPERAND_2 for Assembly use
int USER_OPERATION; //declares USER_OPERATION for Assembly use

extern int add_number(); //links add_number function in Assembly
extern int subtract_number(); //links subtract_number function in Assembly

I expected to be able to compile and use this code (the previous labs went much smoother than this project). But after working through some other syntax issues, I'm getting "Error: "/tmp/fOofpw", line 39: Warning: #47-D: incompatible redefinition of macro "MBED_RAM_SIZE" when I compile.

Coding is my weak spot. Any help or pointers would be appreciated!


Solution

  • In general the calling convention used by a specific version of a compiler for a specific target is specific to that compiler and version. And technically is subject to change at any time (even with gnu and arm we have seen that) and no reason to expect any other compiler conforms to the same convention. Despite that compilers like gcc and clang conform to some version of the arm recommended abi, which that abi has changed over time and gcc has changed along with it.

    As Peter pointed out:

    LDR R1, =OPERAND_1; loads R1 with OPERAND_1
    

    (you are clearly not using gnu assembler, so not the gnu toolchain correct? probably Kiel or ARM?)

    puts the address of that label into r1 to get the contents you need another load

    ldr r1,[r1]
    

    and now the contents are there.

    Using global variables gets you around the calling convention problem.

    Using a simple example and disassembling you can discover the calling convention for your compiler:

    extern unsigned int add ( unsigned int, unsigned int);
    unsigned int fun ( void )
    {
        return(add(3,4)+2);
    }
    00000000 <fun>:
       0:   b510        push    {r4, lr}
       2:   2104        movs    r1, #4
       4:   2003        movs    r0, #3
       6:   f7ff fffe   bl  0 <add>
       a:   3002        adds    r0, #2
       c:   bd10        pop {r4, pc}
       e:   46c0        nop         ; (mov r8, r8)
    

    first parameter in r0, second in r1, return in r0. which could technically change on any version of gnu going forward but can tell you from gcc 2.x.x to the present 9.1.0 this is how it has been for arm. gcc 3.x.x to the present for thumb which is what you are using.

    How you have done it is fine, you just need to recognize what the =LABEL shortcut thing really does.