You are on page 1of 12

C & ASM

C & ASM

Using the languages together
C and Assembler Together
C and Assembler Together
• C
C & ASM need not be used separately.
& ASM need not be used separately
• Within “C” you can insert assembly language 
using “asm(“ nop”);
using  nop”) ”
• ASM is generally used to speed up C code.
– Typically a function is written in assembler, and 
called from C.
– How is information passed between the two?
Let’s See how C does it
// Add two numbers.
int add2_c(int a1, int a2) { Stack (Initial) Stack (w/ locals)
int a3;
a3 = a1 + a2; Mem Data Mem Data
return(a3); SP=200 200
}
1FE 1FE k
main() { 1FC 1FC j
volatile int i, j, k;
1FA SP=1FA i
i = 3;
3 j = 5
5; 1F8 1F8
1F4 1F4
// Call "C" to add two numbers
k = add2_c(i,j); 1F2 1F2
} 1F0 1F0
1EF 1EF
10 main() {
main:
0x03122: 8031 0006 SUB.W #0x0006,SP
13 i=3; j=5; Performing the function call
Performing the function call
0x03126: 40B1 0003 0000 MOV.W #0x0003,0x0000(SP) • move 1st argument, i, into R12
0x0312C: 40B1 0005 0002 MOV.W #0x0005,0x0002(SP) • move 2nd arg, j, into R13
// Call "C" to add two numbers
15 k = add2_c(i
add2 c(i ,j); • call function
0x03132: 412C MOV.W @SP,R12 • move result from R12 to k
0x03134: 411D 0002 MOV.W 0x0002(SP),R13
0x03138: 13B0 3158 CALLA #add2_c
0x0313C: 4C81 0004 MOV.W R12,0x0004(SP) Remember, 1st arg in R12, 2nd in R13
int add2_c(int a1, int a2) {
int a3;
a3 = a1 + a2;
What happens in function?
return(a3);
} Stack (after call)
Stack (after call) Stack (in func)
Stack (in func) Stack (clean)
Stack (clean)
Mem Data Mem Data Mem Data
Remember,  Stack (w/ locals)
1st arg in R12,  200 200 200
Mem Data
2nd in R13 1FE k 1FE k 1FE k
200
1FC j 1FC j 1FC j
1FE k
1FA i 1FA i 1FA i
1FC j
SP=1F8 old PC 1F8 old PC SP=1F8 old PC
SP 1FA
SP=1FA i
1F4 1F4 a3 1F4 a3
1F8
1F2 1F2 a2 1F2 a2
1F4
1F0 SP=1F0 a1 1F0 a1
1F2
1EF 1EF 1EF
1F0
1EF In function
• Set up stack
0x03148: 8031 0006 SUB.W #0x0006,SP • Move args into locals
0x0314C: 4D81 0002 MOV.W R13,0x0002(SP)
0x03150: 4C81 0000 MOV.W R12,0x0000(SP)
• Move a2 to R15
0x03154: 411F 0002 MOV.W 0x0002(SP),R15 • Add a1 to R15 (i.e., a2)
0x03158: 512F ADD.W @SP,R15 • Move R15 to a3
Move R15 to a3
0x0315A: 4F81 0004 MOV.W R15,0x0004(SP) • Move a3 (the result) into R12
0x0315E: 411C 0004 MOV.W 0x0004(SP),R12
0x03162: 5031 0006 ADD.W #0x0006,SP • Clean up stack
0x03166: 0110 RETA • Return
Can we do better with ASM?
main()
i () {
volatile int i, j, k;

i = 3; j = 5;

// Call “ASM" to add two numbers


k = add2_asm(i,j);
}

.cdecls C,LIST, "msp430fg4618.h"


.global add2_asm ASM function
;--------------.text segment is
i for code--- • Share header with “C”
Sh h d ith “C”
.text • Declare function “global” so linker finds it.
;------------------------------------------
add2_asm add.w R13, R12 • Put code in “text” section
reta • Label “add2_asm”
_
.end • Add 2nd arg to 1st and return this value
• Return
• End of ASM

To be fair, the compiler generates the same 
code from the function add2_c if  
optimizations are turned on.
Rules for passing arguments 
and using registers
and using registers
In function 
• Registers R11 through R15 can be used without saving values  registers are:
(except when using ISR) • R12 = i1
• Caller places first arguments in registers R12‐R15, in that order.    • R13 = i2
• R14 = i3
(If more arguments, put on stack ‐ more about this below)
• R15 = i4
– ints require  one register (16 bits)
– chars (8 bits) use one register Stack (in function)
– long ints and floats use two registers (32 bits) Mem Data
– a pointer to an array passes its address (16 bits) – (example later) 1F0
??
(arbitrary)
• Return values are placed in R12 (or R12, R13 if 32 bits).
( )
1EE i6
1EC i5
1EA old PC

int manyVars(int a1, int a2, int a3, int a4, int a5, int a6) { 1E8 a6
return (a1 + a2 + a3 + a4 + a5 + a6); 1E6 a5
}
1E4 a4
main() {
volatile int i1, i2, i3, i4, i5, i6, k; 1F2 a3
1E0 a2
k = manyVars(i1, i2, i3, i4, i5, i6);
} SP=1DE a1
Global variables
Global variables
int n_g=4; // Global variable .cdecls C,LIST, "msp430fg4618.h"
.global add2_asm
// Add three numbers
numbers, one global .global
global n_g
n g
int add2_c(int a1, int a2) { ;-------------------.text----------
volatile int a3; .text
a3 = a1 + a2 + ng; ;----------------------------------
return(a3); add2_asm add.w R13, R12
} add.w &n_g, R12
Main () { reta
volatile int i, j, k; .end
i=2; j=3;

k = add2_c(i, j);

k = add2_asm(i, j);
}
Example
• Weighted average (i.e., FIR filter) N −1

• yi is output, xi is input: y = ∑ b x i
k =0
k i −k

• Example weighted sum with middle point 
g
weighted twice as much as others:
b = {0.25, 0.50, 0.25}
2
yi = ∑b x
k =0
k i −k =0
0.25x
25xi−2 + 0.50x
0 50xi−1 + 0.25x
0 25xi

• Very powerful technique at heart of digital 
signal processing
l
• Without loss of generality we can restate as:
N −1
y = ∑ b x where the xk = most recent inputs
i k k
k =0
Implement FIR in C
main() {
const int n=3;
const int b[]={0x2aaa, 0x2aaa, 0x2aaa}; // 1/3, 1/3, 1/3 (weights - Q15)
int x[]={0x4000, 0x4000, 0x4000}; // last values of input
int y; // O
Output
tp t

y = fir_c (b, x, n);

int fir_c(const int *b, int *x, int n) {


long sum=0;
int i;
for (i=0; i<n; i++)
sum += (long) b[n] * x[n]; // Q15*Q15 is Q30
return((int) (sum>>15)); // Q30 to Q15
}

For 3 weights:  353 cycles   or   118 cycles/weight   
For 100 weights:  8792 cycles   or   88 cycles/weight   
16 bit multiplier/accumulator (MAC)

If OP1 and OP2 are Q15, RESHI:RESLO is Q30.
Implement FIR in ASM
main() {
constt int
i t n=3;
3
const int b[]={0x2aaa, 0x2aaa, 0x2aaa};
int x[]={0x4000, 0x4000, 0x4000};
int y;

y = fir_asm
fir asm (b, x, n);
}

.global fir_asm
;----------------------------
.text
;----------------------------
fir_asm mov #0, &RESHI
FIR in ASM
mov #0, &RESLO
fir loop
fir_loop mov @r12+, &MACS • Clear result register
g
mov @r13+, &OP2 • Move b[0] into MACS, increment pointer
dec r14 • Move x[0] into OP2, increment pointer
jnz fir_loop
rlc &RESLO (this performs mult/accum)
rlc
l &RESHI • Decrement n, check for zero, loop
Decrement n check for zero loop
mov &RESHI, r12 • Left shift RESLO, MSB into Carry
reta
• Left shift RESHI, Carry into LSB
.end
• Mover RESHI into result and return

For 3 weights:  74 cycles   or   25 cycles/weight  (4.8 times faster than C)   
For 100 weights:  1141 cycles   or   11 cycles/weight  (7.7 times faster than C)   
Can we make C faster with pointers?
int fir_c(const int *b, int *x, int n) {
long sum=0;
while (--n>=0) Yes – but only about 5%
sum += (long) (*b++) * (*x++);
return((int) (sum>>15));
}

U i ASM
Using ASM routines in practice
ti i ti
In practice: write and debug routine in C, then convert to assembler (perhaps using 
compiled C code as template)
compiled C code as template)

References
• MSP430x4xx Family User’s Guide http://focus.ti.com/lit/ug/slau056j/slau056j.pdf
• MSP430 Optimizing C/C++ Compiler v 3.3 User's Guide http://focus.ti.com/lit/ug/slau132e/slau132e.pdf
• MSP430 Assembly Language Tools v 3.3 User's Guide http://focus.ti.com/lit/ug/slau131e/slau131e.pdf

You might also like