So we will now write the following very simple program in ARM assembler:
int a = 4; // a,b,c are all int typed variables
int b = 5;
int c;
main( )
{
c = a + b;
}
|
Read that page if you don't follow what I will do next.
.data
a: .4byte 4 // a contains the value 4
b: .4byte 5 // b contains the value 5
c: .skip 4 // c is not initialized (will contain 0)
|
c = a + b; |
is writting in the .text section of the assembler program.
The meaning of the statement c = a + b is:
1. Compute the value in the expression in the RHS of the assignment
2. Store the result value in memory at the address of the variable given in the LHS
|
In the assignment c = a + b, we must do these actions:
|
From what you have learned in ARM assembler programming, the following ARM instructions will perform the stated tasks above:
// Move a into r1
movw r0, #:lower16:a // Moves the address of memory
movt r0, #:upper16:a // variable a into register r0
ldr r1,[r0] // Move int value from var into r1
// Move b into r2
movw r0, #:lower16:b // Moves the address of memory
movt r0, #:upper16:b // variable b into register r0
ldr r2,[r0] // Move int value from var into r2
// Add them up
add r2, r1, r2 // r2 = a + b
// Move sum in r2 to c
movw r0, #:lower16:c // Moves the address of memory
movt r0, #:upper16:c // variable c into register r0
str r2,[r0]
|
/* --------------------------------------------------
Define required labels for EGTAPI
-------------------------------------------------- */
.global main, Stop, CodeEnd
.global DataStart, DataEnd
.global a, b, c
/* --------------------------------------------------
Begin of the program instructions
-------------------------------------------------- */
.text
main:
// Move a into r1
movw r0, #:lower16:a // Moves the address of memory
movt r0, #:upper16:a // variable a into register r0
ldr r1,[r0] // Move int value from var into r1
// Move b into r2
movw r0, #:lower16:b // Moves the address of memory
movt r0, #:upper16:b // variable b into register r0
ldr r2,[r0] // Move int value from var into r2
// Add them up
add r2, r1, r2 // r2 = a + b
// Move sum in r2 to c
movw r0, #:lower16:c // Moves the address of memory
movt r0, #:upper16:c // variable c into register r0
str r2,[r0] // Move sum in r2 to var c
Stop:
CodeEnd:
nop
/* --------------------------------------------------
Begin of the permanent program variables
-------------------------------------------------- */
.data
DataStart:
a: .4byte 4 // int a contains the value 4
b: .4byte 5 // int b contains the value 5
c: .skip 4 // int c is not initialized (will contain 0)
DataEnd:
.end
|
How to run the program:
|
When you write assembler programs, it is very important that:
|
Data type Load instruction Store instruction
------------- ------------------ -------------------
int ldr str
short ldrsh strh
byte ldrsb strb
|
If you use a wrong instruction for a given data type, your program may not work correctly (because incorrect data may be copied between the CPU and the memory)
I will do 2 examples with mixed data type operations.
byte a = 4; // 3 different data types
short b = 5;
int c = 256;
main( )
{
c = a + b;
}
|
We need to align some variables because of alignment requirements (see: click here)
.data
a: .byte 4 // a contains the value 4 in 8 bits
.align 1
b: .2byte 5 // b contains the value 5 in 16 bits
.align 2
c: .4byte 256 // c contains the value 256 in 32 bits
|
The assembler program must use the correct load and store instructions for each variable.
Here are the changes (highlight in red):
main:
// Move a into r1
movw r0, #:lower16:a // Moves the address of memory
movt r0, #:upper16:a // variable a into register r0
ldrsb r1,[r0] // Move byte value from var into r1
// ldrsb also sign-extend to int for computation !!
// Move b into r2
movw r0, #:lower16:b // Moves the address of memory
movt r0, #:upper16:b // variable b into register r0
ldrsh r2,[r0] // Move short value from var into r2
// ldrsh also sign-extend to int for computation !!
// Add them up
add r2, r1, r2 // r2 = a + b
// Move sum in r2 to c
movw r0, #:lower16:c // Moves the address of memory
movt r0, #:upper16:c // variable c into register r0
str r2,[r0] // Move sum in r2 to int var c
|
How to run the program:
|
byte a = 4; // 3 different data types
short b = 5;
int c = 256;
main( )
{
a = b + c; // a is a byte typed variable !!!
}
|
Because the variable types have not changed, we can use the same .data segment (I won't repeat it here).
The statement has changed.
Now we must get the values from b and c, add them up and store the sum in the variable a.
When you write the assembler code, we need to make sure we use the correct load/store instruction for each data type.
The assembler program is as follows:
main:
// Move short b into r1
movw r0, #:lower16:b // Moves the address of memory
movt r0, #:upper16:b // variable b into register r0
ldrsh r1,[r0] // Move short value from var into r1
// ldrsb also sign-extend to int !!
// Move c into r2
movw r0, #:lower16:c // Moves the address of memory
movt r0, #:upper16:c // variable c into register r0
ldr r2,[r0] // Move int value from var into r2
// Add them up
add r2, r1, r2 // r2 = b + c
// Move sum in r2 to a
movw r0, #:lower16:a // Moves the address of memory
movt r0, #:upper16:a // variable a into register r0
strb r2,[r0] // Move sum in r2 to byte var c
|
How to run the program:
|
Can you explan why a = 5 after the execution ???
|
Try c = 100 - you will see that there is no overflow and the program is correct
|