CE-2810 Dr. Mark L. Hornick 1 Mixing C and assembly Safety goggles on!
Jan 19, 2016
CE-2810Dr. Mark L. Hornick
1
Mixing C and assembly
Safety goggles on!
CE-2810Dr. Mark L. Hornick
2
Prelude: How do arguments get passed into a function when that function is called from C ?
And how do return values get passed back to the calling function from the called function?
CS-280Dr. Mark L. Hornick
3
What do compilers do?
C/C++ compilers generally Pass parameters using a register/stack
combination Return values via registers Save registers at the beginning of a function Restore registers before the function returns
CE-2810
Dr. Mark L. Hornick
4
Passing arguments among C functions
Every C compiler has it’s own rules and conventions for passing arguments to functions Arguments can be passed
1. via Registers
2. via the Stack
3. Both of the above
CE-2810Dr. Mark L. Hornick
5
GCC for Atmega32 uses Registers as much as possible to pass arguments
Consider a function: int16_t add( uint8_t x, uint16_t y);
uint8_t means “unsigned 8-bit int” uint16_t means “unsigned 16-bit int” int16_t means “signed 16-bit int” (same as int on Atmega32)
GNU GCC passes arguments left to right using registers r25 to r8 All arguments always take an even number of registersAll arguments always take an even number of registers
Above, value of x (single byte) is placed in r24:r25 (low, high bytes) The value of x is placed in r24 The value of 0 is placed in r25
Value of y (2 bytes) is placed in r22:r23 The low byte value of y is placed in r22 The high byte value of y is placed in r23
Note that r26:r31 are not used for passing arguments or return values since these registers are also the X, Y, and Z registers which the compiler uses for other purposes
What about the return value??
CE-2810Dr. Mark L. Hornick
6
GCC convention for returning values from functions
Return values always take an even number of registersReturn values always take an even number of registers 8-bit values are returned in r24:r25
Value in r24 0 in r25
16-bit values returned in r24:r25 Low byte in r24 High byte in r25
32-bit values returned in r22:r25 Lowest byte in r22 Highest byte in r25
64-bit values returned in r18:r25 lowest byte in lowest register
CE-2810Dr. Mark L. Hornick
7
Example of 2-byte argument passing
Consider the function: int add( int x, int y);
Size of int in C is dependent on processor On Atmega32, int is 2 bytes
Within MSVS for your PC, int means 4 bytes Note: Java int is always 4 bytes
GNU GCC passes arguments left to right using registers r25 to r8 All arguments always take an even number of registers
Above, value of x (two bytes) is placed in r24:r25 (low, high bytes) Value of y is placed in r23-r22
The return value is passed back in r24:r25 (low, high)
CE-2810Dr. Mark L. Hornick
8
Example of 4-byte argument passing
Consider the function: long add(int x, long y);
Size of long is 4 bytes
GNU GCC passes arguments left to right using registers r25 to r8
Above, value of x (2 bytes) is placed in r24:r25 r24 is low byte; r25 is high byte
Value of y is placed in r20:r23 r20 is lowest byte; r23 is highest byte
The return value is passed back in r22:r25 r22 is lowest byte
CE-2810Dr. Mark L. Hornick
9
What happens when we run out of registers to pass arguments?
Registers r25 to r8 can accommodate only 9 two-byte arguments 18 bytes total via registers
Consider the function: long add( long a, long b, long c, long d, long e);
20 bytes worth of arguments in this case
CE-2810Dr. Mark L. Hornick
10
When it runs out of Registers, the compiler uses the Stack to pass additional arguments
long add( long a, long b, long c, long d, long e);
20 bytes worth of arguments in this case, but only 18 1-byte registers are available
Approach: Arguments a, b, c, d (16 bytes total) are passed using registers r25 to r10 r22:r25 for a, r18:r21 for b The remaining argument e (4 bytes) is passed on
the Stack Leaving registers r8, r9 unused – the compiler
does not split an argument between Stack and Registers
Return addr (high)
Return addr (low)
e1 (low)
e2
e3
e4 (high)
???SP
CE-2810Dr. Mark L. Hornick
11
What do you think would happen here?
Consider the function: long add( long a, long b, long c, long d, int e, int f);
Also 20 bytes worth of arguments in this case
CE-2810Dr. Mark L. Hornick
12
The called subroutine must find the value of e on the Stack behind the return address
Example:long add( long a, long b,
long c, long d, long e);
Parameters a, b, c, d (16 bytes) are passed using registers r25 to r10 Remaining parameter e is passed as 4 bytes
on the Stack in the calling code: in ZH, SPH ; load SP to Zin ZL, SPL; Z points to cell containing ??? now.ldd temp, Z+3 ; load e1 to templdd temp, Z+4 ; load e2 to temp...
Return addr (high)
Return addr (low)
e1 (low)
e2
e3
e4 (high)
???SP
CE-2810Dr. Mark L. Hornick
13
General rules of register usage in mixed C/ASM
These rules apply when you write assembly subroutines that you’re mixing with compiler-generated code
R0 – used by compiler as a temporary register If you use R0 and then call code generated by the compiler, that
code may overwrite R0 R0 is not saved or restored by compiler-generated C code So save it before calling compiler code, and restore it afterwards
R1 – assumed to always be 0 by the compiler Save and clear it before calling compiler code, restore it after the compiler code
returns R2-R31 – used by compiler for various purposes
You must follow the compiler’s rules for register usage If you are calling compiler code:
Save these before calling; restore after the call returns If you are being called by compiler code:
Save first thing in your assembly subroutine; restore right before returning
CS-280Dr. Mark L. Hornick
14
C and Assembly inEmbedded Systems software
Entire program in C… …or mix C with assembly. Assembly is
generally used for… Critical code (size and efficiency) Accessing certain hardware Existing, proven code already written in assembly
CS-280Dr. Mark L. Hornick
15
Inline Assembly
Useful when writing mostly in C but a few assembly instructions are needed for… Calling assembly subroutines Accessing assembly global variables
(Not covered in detail) Maximum efficiency for a small but often used
piece of code
CS-280Dr. Mark L. Hornick
16
Inline assembly – calling a function
void main()
{
...
__asm(“rcall sub1");
// Call routine without
// a C interface
...
}
CS-280Dr. Mark L. Hornick
17
extern variables
extern – The definition and label for something… are external (present in another module) will be resolved by the linker
extern modifies a declaration Examples
Variables – stored in another module Functions – implemented in another module
CS-280Dr. Mark L. Hornick
18
Accessing an assembly variable from C
Assembly .section .data
.global counter
counter: .word 0x1234 C++
extern int counter;
int main()
{
counter++;
}
The volatile keyword
When a variable is declared volatile, the compiler will always ensure that an up-to-date value of that variable is manipulated whenever that variable is accesses.
CS-280Dr. Mark L. Hornick
19
Code generated without global variable y declared volatile
int fun1() {
y=y+3;
DDRB = 0xFF;
PORTB = 0x00;
return y;
}
CS-280Dr. Mark L. Hornick
20
000000be <fun1>:
be: 80 91 60 00 lds r24, 0x0060
c2: 90 91 61 00 lds r25, 0x0061
c6: 03 96 adiw r24, 0x03 ; 3
c8: 90 93 61 00 sts 0x0061, r25
cc: 80 93 60 00 sts 0x0060, r24
d0: 2f ef ldi r18, 0xFF ; 255
d2: 27 bb out 0x17, r18 ; 23
d4: 18 ba out 0x18, r1 ; 24
d6: 08 95 ret
Code generated with global variable y declared volatile
int fun1() {
y=y+3;
DDRB = 0xFF;
PORTB = 0x00;
return y;
}
CS-280Dr. Mark L. Hornick
21
000000be <fun1>:
be: 80 91 60 00 lds r24, 0x0060
c2: 90 91 61 00 lds r25, 0x0061
c6: 03 96 adiw r24, 0x03 ; 3
c8: 90 93 61 00 sts 0x0061, r25
cc: 80 93 60 00 sts 0x0060, r24
d0: 2f ef ldi r18, 0xFF ; 255
d2: 27 bb out 0x17, r18 ; 23
d4: 18 ba out 0x18, r1 ; 24
d6: 80 91 60 00 lds r24, 0x0060
da: 90 91 61 00 lds r25, 0x0061
de: 08 95 ret