0

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!

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
BlueSky
  • 3
  • 2
  • Prefer passing function args instead of storing into global variables. And BTW, `LDR R1, =OPERAND_1` puts the *address* of `OPERAND_1` into `R1`. – Peter Cordes Jul 16 '19 at 03:13
  • 'procedure call standard for the arm architecture' is relevant, but it seems you want to use global variables. – Sean Houlihane Jul 16 '19 at 07:44
  • See: [ARM link and frame pointer](https://stackoverflow.com/questions/15752188/arm-link-register-and-frame-pointer) as well as [ARM parameter passing](https://stackoverflow.com/questions/261419/what-registers-to-save-in-the-arm-c-calling-convention). Regarding **MBED_RAM_SIZE**, you have either include some file multiple time or your build process is redefining this value (perhaps from a command line). It look like a linker file may use these values as well. Just googling 'MBED_RAM_SIZE' gives some hints. – artless noise Jul 16 '19 at 14:07
  • Thank you for the helpful comments! I edited my code to reflect the OPERAND_1 address versus content. Also reading the ARM manuals recommend above was very instructional for me. My code is currently working and meets the project requirements. – BlueSky Jul 18 '19 at 01:27

1 Answers1

4

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.

old_timer
  • 69,149
  • 8
  • 89
  • 168
  • Thank you for your thoughtful answer! I am using ARM via the MBED online environment. I've edited my code to use the global variables as suggested. My project is now working and meets the requirement for this project. Grateful for your assistance! – BlueSky Jul 18 '19 at 01:31