Converting a C
program to Assembly Program using ARM DS-5.
The best way to learn ARM assembly is by converting a C
language program to Assembly.
1.First we'll set the Environment Variable. Press Winkey+R
to open Run. Type SystemPropertiesAdvanced
and press Enter
Click Environment
Variables .Meanwhile open DS-5 vx.xx
Command Prompt from Start Menu.
2.Click New... in
User variables . Set variable name as Path
and Variable Name as dispalyed
in DS-5 command prompt.In my case it is C:\Program
Files\DS-5 v5.22.0\bin
Click OK when finished.
3.Now close and reopen DS-5 command prompt.
Use select_default_toolchain
to change the compiler. Here I chose ARM Compiler 5
4.Now open Notepad and type the following source code.
void main()
{
int a[5],i;
for(i=0;i<5;i++)
{
a[i]=0;
}
}
stdio.h is not necessary
as printf,scanf aren't used. Save it as somename.c .In my case I named it as
Ex1.c.The C file is in D:\C2ASM
.Keep the file in a location as you wish.Using the command armcc -S somename.c generates somename.s which contains the assembly
code.
Commands
D:
cd C2ASM
armcc -S Ex1.c
Warnings can be ignored. Now open
Ex1.s file. Now the check the highlighted code and ignore the remaining.
You come across new instructions such as STR(Store)
,CMP(Compare),BX(Branch with exchange),BLT(Branch
if less than) etc.,
AREA ||.text||, CODE, READONLY, ALIGN=2
main PROC
SUB sp,sp,#0x14 ; Allocate 5*4 =
20B = 0x14 for array a
MOV r0,#0 ; i=0
MOV r1,r0 ; Load 0 in r1
MOV r2,sp ; Starting address of array a is stored in
r2
|L0.16|
STR r1,[r2,r0,LSL #2] ; 0-->address[r2 + 4*r0] or *a+4*i or
a[i]=0 in C
ADD r0,r0,#1 ; r0=r0+1 or i=i+1
or i++
CMP r0,#5 ; Compare i and 5
BLT |L0.16| ; if i < 5 jump
to |L0.16|
ADD sp,sp,#0x14 ; De-allocate
memory for array a
BX lr ; Return
ENDP
Note: Size of int is 4 B and hence 4 memory locations are
need.To understand the working of BLT ,knowledge of conditional execution of ARM instructions and CPSR , SPSR registers
is required.
Alternate way to
perform C to assembly conversion:
1.Create a new C
project.Add a new C file to the project and add code as shown below
Scroll through and search for main . The
remaining code is related to C Run time and it is one of the reasons why code written
in C is larger than in assembly.
A few more
programs....
1.Calculating sum of
6 numbers
C code:
void main()
{
int i,a[6];
volatile int sum=0;
for(i=0;i<6;i++)
sum+=a[i];
}
Equivalent
Assembly Code:
AREA ||.text||, CODE, READONLY, ALIGN=2
main PROC
SUB sp,sp,#0x1c ; allocate space
for sum and a[5] ie 24 B = 0x1C
MOV r0,#0 ; i=0
MOV r1,sp ; pointer for a
STR r0,[sp,#0x18] ; 0 --> sum
|L1.16|
LDR r2,[r1,r0,LSL #2] ; r2 <-- a[i]
LDR r3,[sp,#0x18] ; r3 <-- sum
ADD r0,r0,#1 ; i++
ADD r2,r2,r3 ;
r2<--a[i]+sum
CMP r0,#6 ; Compare i and 6
STR r2,[sp,#0x18] ; r2 -->sum
BLT |L1.16| ; Is i<6 then jump to L1.16
ADD sp,sp,#0x1c ; Un allocate space
BX lr ; return
ENDP
2.To compute the
value of f(x) = x3 + 3x2 + 3x + 1 using function.
C code:
//
To perform f(x) = x^3+3x^2+3x+1
int func(int);
void main()
{
volatile int x,p;
/* volatile keyword prevents the
compiler from performing optimizations on variables */
p=func(x);
}
int func(int x)
{
int f;
f=x*x*x +3*x*x +3*x +1;
return f;
}
Equivalent
Assembly Code:
AREA ||.text||, CODE,
READONLY, ALIGN=2
func PROC
ADD r2,r0,#3
; r2 =
r0 +3 x+3
MUL r2,r0,r2
; r2 =
r0 * r2 x*(x+3)
ADD r1,r0,r0,LSL #1 ; r1 = r0 + 2*r0 3x
MLA r0,r2,r0,r1 ; r0 = r2 * r0 + r1 x*x(x+3)+3x ie x^3+3x^2+3x
ADD r0,r0,#1
; r0 =
r0 + r1 x^3 + 3x^2 + 3x + 1
BX lr
;
Return to main
ENDP
main PROC
PUSH {r3,lr}
; Push
r3 and lr to stack
LDR r0,[sp,#0] ; r0 <-- x
BL func
; Call
func
STR r0,[sp,#0] ; x is no more
needed,So save f in same location occupied by x
POP {r3,pc}
ENDP
3.Switch case statement
C code:
void main()
{
volatile int a;
volatile char b;
switch(a)
{
case 1: b= 'R';
break;
case 2: b= 'A';
break;
case 3: b= 'M';
break;
default: b='Y';
}
}
Equivalent
Assembly Code:
AREA ||.text||, CODE, READONLY, ALIGN=2
main PROC
PUSH {r3,lr}
LDR r0,[sp,#0] ; r0 <-- a
CMP r0,#1 ; compare r0 and 1
MOVEQ r0,#0x52 ; r0 <--'R' if
(r0 = 1 ie a = 1)
BEQ |L0.44| ; Jump to |L0.44|
CMP r0,#2 ; compare r0 and 2
MOVEQ r0,#0x41 ; r0 <--'A' if
(r0 = 2 ie a = 2)
BEQ |L0.44| ; Jump to |L0.44|
CMP r0,#3 ; compare r0 and 3
MOVNE r0,#0x59 ; default case r0
<-- 'Y'
MOVEQ r0,#0x4d ; r0 <--'M' if
(r0 = 3 ie a = 3)
|L0.44|
STRB r0,[sp,#0] ; r0 --> b
POP {r3,pc}
ENDP
ASCII values R-0x52 , A - 0x41 , M -0x4d ,Y-0x49
You
can see from the examples that some variables go out of scope and memory is
de-allocated even when the function doesn't go out scope.
In
example 2 , variable x loses its scope and gets de-allocated as soon as
it is loaded to r0 register.The location is used by f later.
In
example 3, variable a loses its scope and gets de-allocated as soon as it
is loaded to r0 register. The location is used by b later.
The
ARM compiler optimizes the code to a great extent to minimize memory usage.
ARM
is based on load-store architecture unlike Intel x86 and hence when any
operation needs to be performed ,
>Operands are loaded into the
registers
>Operations are performed and
stored in registers
>From registers the result is
stored in the memory
Big
calculations involving numerous operations are performed in this way.ARM
compiler effectively uses only 16 registers out of which 3 registers are
reserved for Stack pointer,Link register and Program Counter.
ARM instruction set is covered elaborately in this PPT