Reputation: 25
This quarter I am taking an ARM assembly class and I am having trouble understanding it completely. There's a lab tomorrow and we are allowed to get a head start on the lab. The task for this lab is to convert the following C functions into assembly:
int32_t Less1(int32_t a)
{
return a - 1 ;
}
int32_t Add(int32_t a, int32_t b)
{
return a + b ;
}
int32_t Square2x(int32_t x)
{
return Square(x + x) ;
}
int32_t Last(int32_t x)
{
return x + SquareRoot(x) ;
}
Here are the exact instructions:
Your task is to create assembly language replacements for each of these C functions...
Functions Square and SquareRoot are provided in the main program.
We were provided a main program that I can provide all or part of if necessary. I don't understand like 90% of it and I'm not expected to as a lot of it is dedicated to making the functions display on a test board with a touch screen. It also has the square and squareroot functions as it seems like assembly doesn't have these operations. I'm not sure if I called them correctly but they are named "Square" and "Squareroot".
Here's what I wrote in ARM assembly:
Add: PUSH {LR}
MOV r0, a
MOV r1, b
ADD r2, r1, r0
BX r2
Less1: PUSH {LR}
MOV r0, a
MOV r1, 1
SUB r2, r0, r1
BX r2
Square2x: PUSH {LR}
MOV r0, x
MOV r1, r0
ADD r2, r0, r1
Square r3, r2
BX r3
Last: PUSH {LR}
MOV r0, x
Squareroot r1, r0
ADD r2, r0, r1
BX r2
I have no idea if this is right as I know C++ and have just started on assembly so I'd appreciate any input. I don't fully understand the registers and instructions yet but this is what I came up with based on my textbook and what I could find online.
Thank you!
Upvotes: 0
Views: 769
Reputation: 71536
Which ARM instruction set are you working with, based on your code snippets it is either ARM (armv7 or older). ARM or thumb instructions
int32_t Add(int32_t a, int32_t b)
{
return a + b ;
}
I will let you try godbolt or a command line compiler, etc. The teacher has to know you have access to the internet and tools. But you had better be able to understand and defend code from the compiler otherwise it is just plagiarism and it will become obvious.
So int32_t means 32 bits which is the size of registers. compiled languages have a "calling convention" or sometimes abi (application binary interface). Basically a set of rules for each function. The first parameter is passed this way with some exceptions for specific types. The second parameter is passed this way based in part on first and its type, and so on. the return value is handled this way.
There are instruction set things like a call to a function (look up bl) puts the return address in the link register, r14 so you typically but are not limited to using bx lr to return, it will be bx something, there are cases where it is not lr but it is the contents that were in lr.
if function a calls function b, the return to function a will be in the link register (assume that there is only one link register). if b calls function c though that changes the link register to return to function b, how do we get back to function a? You save the link register on the stack and pop it back off. Everything you add to the stack you need to remove before you return.
Does the Add function call another function? (no) That is the first test for needing to use the stack to save the link register. Does the Add function have more than a few intermediate variables in flight at any one time? That may require some of them to remain on the stack. (no)
You probably do not need to use the stack for the Add function, does not mean you cannot, you are more than welcome to.
I will tell you that the arm compilers I have used for a function like this the two parameters are passed using registers, you can figure out which ones as this is part of your assignment I will call them ra and rb.
so you need a label which is assembly language dependent (assembler, the tool, not target) so it may or may not have a colon as an example
Add:
...
bx lr
the bx lr I gave you
In the middle you need to add the two numbers
rc = ra + rb
Or perhaps
ra = ra + rb
You need to figure out the input register names, and the output register names (it is a register not the stack for the C compilers I have seen for ARM).
so it will be something along the lines of
Add:
rc = ra + rb (a + b)
rd = rc (put the result in the return register)
bx lr
And you might be able to optimize that, you have to figure out what those instructions and registers are. And the rules for those instructions.
The subtract one should be even easier once you do the Add function.
int32_t Square2x(int32_t x)
{
return Square(x + x) ;
}
Is it calling a function, yes, so what does that mean with respect to preserving the return address? That should define your skeleton or frame of your function.
You need to deal with x + x before calling the function. As you would expect the calling convention has a specific register that x will show up in every time for a 32 bit parameter like this. And that is the same register used to call Square2X as well as Square as well as Less1 and Last and one of the two operands to Add. So if I call that register rx, then before calling Square you need to compute rx = rx + rx yes? Or there is another way that gives the same result. Then you call Square. Note you can optimize here and save on something.
int32_t Last(int32_t x)
{
return x + SquareRoot(x) ;
}
You are calling a function, that triggers some code. You are passing x that came in to the function called, probably does not need to be moved or modified. But I will tell you that that specific register based on the conventions I know about can be modified by the called function. So when SquareRoot returns that x register might not contain x anymore. So before calling SquareRoot how can you preserve it for later, so you can add it to the result that comes back from SquareRoot?
From Add and Less1 you should already know the passed in register for a single 32-bit parameter like this as well as the return register for a result like this, let's assume that SquareRoot is passed and returns an int32_t otherwise this gets a whole lot harder. All functions use the same rules so you know what register x is passed to SquareRoot and you know what register it comes back in. So you need to figure out how to preserve the x passed to Last so you can add it later. You are already preserving another register that gets messed up and you need to save later. But you will see compilers might have a twist as to how they do this to avoid an extra instruction or two. Depending on the ARM instruction set this one should be 5.
Add you are looking for two instructions.
Less1, two instructions
Square2X first pass four instructions. I think you can make this two.
Last, five instructions
Depending on the instruction set and architecture version there might be more for some of these.
Upvotes: 3
Reputation: 193
You don't need to load argument into register one by one.
Because it automatically set at register(use r0-r3) when pass arguments.
For example:
int32_t Add(int32_t a, int32_t b)
{
return a + b ;
}
a
is automatically set at r0
, b is automatically set at r1
.
This is an example of Add function:
Add: stmfd sp!,{lr}
ADD r0, r0, r1 // return value store into r0
ldmfd sp!,{lr}
mov pc,lr
Upvotes: 0