Friday, 25 December 2015

C to Assembly conversion using ARM DS-5

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


Build project and open the .axf file in Debug folder.Click Disassembly Tab


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



ARM DS-5 Tutorial

Getting started with  ARM DS-5 Development Studio

Download ARM DS-5 Development Studio from http://ds.arm.com/downloads/

Installation is pretty straight forward. ARM Ltd. grants a free 30 day  trial license for ARM DS-5.The software is fully functional with no limitations.

Keil MDK is a good software for developing code for Cortex M and ARM7 family of processors. When it comes with Cortex A processors found in tablets and smartphones, Keil MDK is unsuitable and hence ARM DS-5.ARM processors are not generally found as standalone chips but as SoCs(System On Chips).

In this tutorial I use ARM DS-5 Ultimate Edition v5.22 . ARM DS-5 is an enterprise class software which enables development and debugging software for low end ARM7 to Cortex M ,Cortex R, Cortex A processors and high end 64 bit ARM Cortex A53 and A57 processors found in Samsung Galaxy Note 5 and Galaxy S6.
To know more in detail, visit www.arm.com and ds.arm.com

This tutorial is for people who would like take a dive to ARM programming without prior knowledge in working with IDEs.

To start working ARM DS-5

1.Click Start ---> ARM DS-5 vx.xx ---> Eclipse for DS-5 vx.xx


2.You'll be prompted to select a workspace,change location or leave it as such and click OK



3.Click File ---> New ---> C Project


4.The C project window opens up. Type the Project Name, Choose Project type as Executable -->Empty Project and Toolchain as ARM Compiler (DS-5 built-in) and click Next.



5. Check the Debug , Release option checkboxes and click Finish.

6.Now to add a Assembly source file to the project, right click on the Project , select New ---> File.



7.Create a file named main.s or any name you want but ensure that is extension is s(Assembly source file) and click Finish.

8.Now the project is setup and it is time to start coding.

9.Nearly all assembly languages irrespective of architecture(be it Intel x86,8051,AVR or ARM) have an instruction named MOV. Type the below code in the IDE.

AREA anyname,CODE,READONLY
ENTRY
MOV r0,#0x22
END

The keywords AREA , ENTRY ,END are specific to ARM assembler developed by ARM Ltd.GNU ARM assembler uses different directives. For time being understand them as a ritual followed while writing ARM assembly programs as we do write void main() in C language.


10.Now right click the Project and click Build Project.



11. Now you see that errors are thrown by the assembler.

Expanded Problems 


The code is right and but why does the assembler throw error. The ARM assembler behaves in a similar way like Python language .It expects the indentations to be right.Indent the code by pressing TAB key.
Indent the code with TAB key(preferably 2 TABs) like the one below.





12.Now right click the Project and click Build Project. The Project has been built successfully except for the warnings. The 2 warnings are related to licensing and can be ignored .A Debug folder is also created.


Expand the Debug folder,you can see object files(*.o) ,an AXF file(also known as ELF) and some  files which are related with the makeutility(*.d ,makefile,*.mk)



AXF file is something like EXE file.Double clicking AXF will reveal the information regarding the file.Using a text editor to open AXF will not give you a clear picture.Click Sections,Segments,Symbol Table,Disassembly Tabs to know more.


There is no need of hardware to test the program.ARM DS-5 has inbuilt emulator for ARM processors. We will use that to debug the code.

14.Native application debugging is simpler since the host and target processor are same. But things get slightly difficult when the code is native(x86_64 should run an emulator to run ARM code).We have to configure the debugger.
Click on Debug   icon and select Debug configuration.Debug configuration opens up.Click DS-5 debugger and click New button.


15.A new configuration is created .Name it as you want. I name it ARM Cortex Debugger. Select Target as ARM FVP(Installed with DS-5) ---> VE_CortexA9x1 ---> Bare Metal Debug ---> Debug Cortex A9 . We choose Bare Metal since we don't use any OS. The processor is a single core processor and hence the suffix x1. Cortex A9 processors were featured in former flagship phone such as Samsung Galaxy Note(Dual Core Cortex A9) and Note2 (Quad core Cortex A9).


16.Click the Files Tab. Click Workspace button in Target Configuration.


17.Choose the .axf file in Debug folder of the project. In my case it was Simple.axf



Finally click Apply Button and Debug button
18.Debug Perspective Switch Window opens up.Click Yes.

Also allow Windows Firewall to unblock the DS-5 debugger by clicking  Allow Access.

19.Now you can start executing code. On the right hand side you see the ARM  core registers. Pressing F5 will execute the code instruction by instruction.


After executing  MOV r0,#0x22 ,PC(Program Counter) gets incremented by 4. A single ARM instruction is 32 bit wide and memory architecture is 8-bit, 4 locations are required to store 32 bits and hence PC+4.The changes in registers are highlighted by yellow color.


To close Debug perspective Debug icon --> Close.You can modify code,rebuild and debug again. IT IS NOT REQUIRED TO SETUP DECONFIGURATION AGAIN !!!!


20.Now we'll write a program to add two numbers 0x22 and 0x06

              AREA anyname,CODE,READONLY
              ENTRY
              MOV r0,#0x22
              MOV r1,#0x06
              ADD r2,r0,r1
       stop   B stop
              END


 The code performs the following    r0  <--- 0x22
                                                                  r1 <--- 0x06
                                                                  r2 <--- r0 + r1

stop B stop will put a hold to the program counter.'stop' is just a label.It can be anything like cat,dog,etc.,.B is unconditional branch  instruction like SJMP and LJMP in 8051.while(1); statement in C is an equivalent of this assembly statement.
Build the code.Click Debug icon and select ARM Cortex Debugger configuration we created earlier



The Register Window showing the results after execution


Numerous assembly programs like can be tried out in ARM DS-5 to get good understanding of ARM architecture.

To get help for any instruction or assembler directive , just place the cursor on the instruction and press F3





Good books for learning ARM assembly language and C programming are

1.ARM Assembly Language ,Fundamentals and Techniques by William Hohl and Christopher Hinds

2.ARM System Developer's guide by Andrew N Sloss, Dominic Symes and Chris Wright