mirror of
https://github.com/pkivolowitz/asm_book.git
synced 2026-06-23 01:47:21 +08:00
better looking plus edits
This commit is contained in:
parent
bd1fe10183
commit
5c6fde83a1
2 changed files with 143 additions and 129 deletions
|
|
@ -4,7 +4,10 @@ Calling functions, passing parameters to them and receiving back return
|
||||||
values is basic to using `C` and and `C++`. Calling methods (which are
|
values is basic to using `C` and and `C++`. Calling methods (which are
|
||||||
functions connected to objects) is similar but with enough differences
|
functions connected to objects) is similar but with enough differences
|
||||||
to warrant its own discussion to be provided later in the chapter on
|
to warrant its own discussion to be provided later in the chapter on
|
||||||
[structs](../struct/structs.md).
|
[structs](../structs/using.md).
|
||||||
|
|
||||||
|
Be sure to read [this](./README2.md) for information about
|
||||||
|
passing parameters to functions.
|
||||||
|
|
||||||
## Bottom Line Concept
|
## Bottom Line Concept
|
||||||
|
|
||||||
|
|
@ -62,8 +65,9 @@ allows the function to `ret`urn.
|
||||||
## **bl**
|
## **bl**
|
||||||
|
|
||||||
Branch-with-link computes the address of the instruction following it.
|
Branch-with-link computes the address of the instruction following it.
|
||||||
It places this address into `x30` and then branches to the label
|
|
||||||
provided. It makes one link of breadcrumbs to follow to get back
|
It places this address into register `x30` and then branches to the
|
||||||
|
label provided. It makes one link of breadcrumbs to follow to get back
|
||||||
following a `ret`.
|
following a `ret`.
|
||||||
|
|
||||||
**This is why it is absolutely essential to backup `x30` inside your
|
**This is why it is absolutely essential to backup `x30` inside your
|
||||||
|
|
@ -72,23 +76,23 @@ functions if they call other functions themselves.**
|
||||||
How about this trivial program:
|
How about this trivial program:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
.text // 1
|
.text // 1
|
||||||
.global main // 2
|
.global main // 2
|
||||||
.align 2 // 3
|
.align 2 // 3
|
||||||
// 4
|
// 4
|
||||||
main: ldr x0, =hw // 5
|
main: ldr x0, =hw // 5
|
||||||
bl puts // 6
|
bl puts // 6
|
||||||
ret // 7
|
ret // 7
|
||||||
// 8
|
// 8
|
||||||
.data // 9
|
.data // 9
|
||||||
hw: .asciz "Hello World!" // 10
|
hw: .asciz "Hello World!" // 10
|
||||||
// 11
|
// 11
|
||||||
.end // 12
|
.end // 12
|
||||||
```
|
```
|
||||||
|
|
||||||
What could possibly go wrong?
|
What could possibly go wrong?
|
||||||
|
|
||||||
Here is a listing from `gdb` since running the program simply
|
Here is a listing from `gdb` since running the program
|
||||||
hangs:
|
hangs:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|
@ -128,31 +132,33 @@ return.
|
||||||
So, when line 7 executes it puts the contents of `x30` into the
|
So, when line 7 executes it puts the contents of `x30` into the
|
||||||
program counter and branches to it.
|
program counter and branches to it.
|
||||||
|
|
||||||
And the problem with this is? Hint: notice where `gdb` put us after
|
And the problem with this is?
|
||||||
|
|
||||||
|
Hint: notice where `gdb` put us after
|
||||||
the control-C. Still on line 7. An infinite loop of returning to the
|
the control-C. Still on line 7. An infinite loop of returning to the
|
||||||
return statement.
|
return statement.
|
||||||
|
|
||||||
Here is a fixed version of the code:
|
Here is a fixed version of the code:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
.text // 1
|
.text // 1
|
||||||
.global main // 2
|
.global main // 2
|
||||||
.align 2 // 3
|
.align 2 // 3
|
||||||
// 4
|
// 4
|
||||||
main: str x30, [sp, -16]! // 5
|
main: str x30, [sp, -16]! // 5
|
||||||
ldr x0, =hw // 6
|
ldr x0, =hw // 6
|
||||||
bl puts // 7
|
bl puts // 7
|
||||||
ldr x30, [sp], 16 // 8
|
ldr x30, [sp], 16 // 8
|
||||||
ret // 9
|
ret // 9
|
||||||
// 10
|
// 10
|
||||||
.data // 11
|
.data // 11
|
||||||
hw: .asciz "Hello World!" // 12
|
hw: .asciz "Hello World!" // 12
|
||||||
// 13
|
// 13
|
||||||
.end // 14
|
.end // 14
|
||||||
```
|
```
|
||||||
|
|
||||||
The address to which `main()` should return is pushed onto the stack on
|
The address to which `main()` should return is pushed onto the stack on
|
||||||
line 5. It is safe there.
|
line 5. It should be safe there.
|
||||||
|
|
||||||
It is recovered from the stack on line 8 and used by line 9's `ret`.
|
It is recovered from the stack on line 8 and used by line 9's `ret`.
|
||||||
|
|
||||||
|
|
@ -161,14 +167,16 @@ It is recovered from the stack on line 8 and used by line 9's `ret`.
|
||||||
First, let's take a trip back in time to the early days of C.
|
First, let's take a trip back in time to the early days of C.
|
||||||
|
|
||||||
[Stephen Bourne](https://en.wikipedia.org/wiki/Stephen_R._Bourne) was
|
[Stephen Bourne](https://en.wikipedia.org/wiki/Stephen_R._Bourne) was
|
||||||
writing `sh` the first shell for Unix. He noticed that every function
|
writing `sh`, the first shell for Unix. He noticed that every function
|
||||||
had to return a value - even functions that had no reason to return
|
had to return a value - even functions that had no reason to return
|
||||||
a value. In these early days, `void` functions did not yet exist.
|
a value.
|
||||||
|
|
||||||
Bourne argued that an instruction could be saved if the concept of
|
In these early days, `void` functions did not yet exist.
|
||||||
`void` functions were added to C. Saving one instruction per function
|
|
||||||
was really valuable - so that's how we get `void` functions that
|
Bourne argued that an instruction could be saved per function if the
|
||||||
return no value.
|
concept of `void` functions were added to C. Saving one instruction per
|
||||||
|
function was really valuable - so that's how we get `void` functions
|
||||||
|
that return no value.
|
||||||
|
|
||||||
What about functions that do return a value?
|
What about functions that do return a value?
|
||||||
|
|
||||||
|
|
@ -179,47 +187,52 @@ Note that `x0` and `x1` could also be `w0` and `w1` or even the first
|
||||||
and second floating point registers if the function is returning a
|
and second floating point registers if the function is returning a
|
||||||
`float` or `double`.
|
`float` or `double`.
|
||||||
|
|
||||||
Here are samples, first in C++ then in the corresponding assembly
|
Here are samples, first in C / C++ then in the corresponding assembly
|
||||||
language:
|
language:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
// 1
|
// 1
|
||||||
int ReturnsAnInt() { // 2
|
int ReturnsAnInt() { // 2
|
||||||
return 16; // 3
|
return 16; // 3
|
||||||
} // 4
|
} // 4
|
||||||
// 5
|
// 5
|
||||||
long ReturnsALong() { // 6
|
long ReturnsALong() { // 6
|
||||||
return 16; // 7
|
return 16; // 7
|
||||||
} // 8
|
} // 8
|
||||||
// 9
|
// 9
|
||||||
float ReturnsAFloat() { // 10
|
float ReturnsAFloat() { // 10
|
||||||
return 16.0f; // 11
|
return 16.0f; // 11
|
||||||
} // 12
|
} // 12
|
||||||
// 13
|
// 13
|
||||||
double ReturnsADouble() { // 14
|
double ReturnsADouble() { // 14
|
||||||
return 16.0; // 15
|
return 16.0; // 15
|
||||||
} // 16
|
} // 16
|
||||||
```
|
```
|
||||||
|
|
||||||
Here it is in assembly language:
|
Here it is in assembly language:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
ReturnsAnInt: // 1
|
ReturnsAnInt: // 1
|
||||||
mov w0, 16 // 2
|
mov w0, 16 // 2
|
||||||
ret // 3
|
ret // 3
|
||||||
// 4
|
// 4
|
||||||
ReturnsALong: // 5
|
ReturnsALong: // 5
|
||||||
mov x0, 16 // 6
|
mov x0, 16 // 6
|
||||||
ret // 7
|
ret // 7
|
||||||
// 8
|
// 8
|
||||||
ReturnsAFloat: // 9
|
ReturnsAFloat: // 9
|
||||||
fmov s0, 16 // 10
|
fmov s0, 16 // 10
|
||||||
ret // 11
|
ret // 11
|
||||||
// 12
|
// 12
|
||||||
ReturnsADouble: // 13
|
ReturnsADouble: // 13
|
||||||
fmov d0, 16 // 14
|
fmov d0, 16 // 14
|
||||||
ret // 15
|
ret // 15
|
||||||
```
|
```
|
||||||
|
|
||||||
Note, the use of the floating point move instruction as well as the
|
Note, the use of the floating point move instruction as well as the
|
||||||
single precision and double precision registers.
|
single precision and double precision registers.
|
||||||
|
|
||||||
|
## Repeating the TL;DR
|
||||||
|
|
||||||
|
If your functions call *any* other functions, `x30` must be backed
|
||||||
|
up on the stack and then restored into `x30` before returning.
|
||||||
|
|
|
||||||
|
|
@ -6,22 +6,22 @@ For the purposes of the present discussion, we assume all parameters are `long i
|
||||||
|
|
||||||
Up to 8 parameters are passed in the scratch registers (of which there are 8). These are `x0` through `x7`. *Scratch* means the value of the register can be changed at will without any need to backup or restore their values.
|
Up to 8 parameters are passed in the scratch registers (of which there are 8). These are `x0` through `x7`. *Scratch* means the value of the register can be changed at will without any need to backup or restore their values.
|
||||||
|
|
||||||
**This also means that you cannot count on the contents of the scratch registers maintaining their value if your function makes any function calls itself.**
|
**This means that you cannot count on the contents of the scratch registers maintaining their value if your function makes any function calls.**
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
long func(long p1, long p2) // 1
|
long func(long p1, long p2) // 1
|
||||||
{ // 2
|
{ // 2
|
||||||
return p1 + p2; // 3
|
return p1 + p2; // 3
|
||||||
} // 4
|
} // 4
|
||||||
```
|
```
|
||||||
|
|
||||||
is implemented as:
|
is implemented as:
|
||||||
|
|
||||||
```asm
|
```asm
|
||||||
func: add x0, x0, x1 // 1
|
func: add x0, x0, x1 // 1
|
||||||
ret // 2
|
ret // 2
|
||||||
```
|
```
|
||||||
|
|
||||||
The first parameter (`p1`) goes in the first scratch register (`x0`). It's an `x` because the parameter's type is `long int`. It is the `0` register, because that is the first scratch register.
|
The first parameter (`p1`) goes in the first scratch register (`x0`). It's an `x` because the parameter's type is `long int`. It is the `0` register, because that is the first scratch register.
|
||||||
|
|
@ -42,10 +42,10 @@ A pointer is an address of something. The word *pointer* is scary. The words *ad
|
||||||
Here is a function which *also* adds two parameters together but this time using pointers to `long int` rather than the values themselves.
|
Here is a function which *also* adds two parameters together but this time using pointers to `long int` rather than the values themselves.
|
||||||
|
|
||||||
```c
|
```c
|
||||||
void func(long * p1, long * p2) // 1
|
void func(long * p1, long * p2) // 1
|
||||||
{ // 2
|
{ // 2
|
||||||
*p1 = *p1 + *p2; // 3
|
*p1 = *p1 + *p2; // 3
|
||||||
} // 4
|
} // 4
|
||||||
```
|
```
|
||||||
|
|
||||||
`Line 1` passes the *address of* `p1` and `p2` as parameters. That is, the addresses of `p1` and `p2` are passed in registers `x0` and `x1` rather than their contents. The contents of the underlying
|
`Line 1` passes the *address of* `p1` and `p2` as parameters. That is, the addresses of `p1` and `p2` are passed in registers `x0` and `x1` rather than their contents. The contents of the underlying
|
||||||
|
|
@ -60,11 +60,11 @@ The values are added together and the result overwrites the value pointed to by
|
||||||
Here it is in assembly language:
|
Here it is in assembly language:
|
||||||
|
|
||||||
```asm
|
```asm
|
||||||
func: ldr x2, [x0] // 1
|
func: ldr x2, [x0] // 1
|
||||||
ldr x3, [x1] // 2
|
ldr x3, [x1] // 2
|
||||||
add x2, x2, x3 // 3
|
add x2, x2, x3 // 3
|
||||||
str x2, [x0] // 4
|
str x2, [x0] // 4
|
||||||
ret // 5
|
ret // 5
|
||||||
```
|
```
|
||||||
|
|
||||||
The `add` instruction cannot operate on values in memory. With little exception, all the *action* takes place in registers, not memory. Therefore, the underlying values pointed to by the parameters must be fetched from memory.
|
The `add` instruction cannot operate on values in memory. With little exception, all the *action* takes place in registers, not memory. Therefore, the underlying values pointed to by the parameters must be fetched from memory.
|
||||||
|
|
@ -105,10 +105,10 @@ So, as the smart *human*, we decided to use `x2` and `x3` because, well, they're
|
||||||
Suppose we had:
|
Suppose we had:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
long func(const long p1, const long p2) // 1
|
long func(const long p1, const long p2) // 1
|
||||||
{ // 2
|
{ // 2
|
||||||
return p1 + p2; // 3
|
return p1 + p2; // 3
|
||||||
} // 4
|
} // 4
|
||||||
```
|
```
|
||||||
|
|
||||||
how would the assembly language change?
|
how would the assembly language change?
|
||||||
|
|
@ -122,10 +122,10 @@ Answer: no change at all!
|
||||||
Suppose we had:
|
Suppose we had:
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
long func(long & p1, long & p2) // 1
|
long func(long & p1, long & p2) // 1
|
||||||
{ // 2
|
{ // 2
|
||||||
return p1 + p2; // 3
|
return p1 + p2; // 3
|
||||||
} // 4
|
} // 4
|
||||||
```
|
```
|
||||||
|
|
||||||
how would the assembly language change?
|
how would the assembly language change?
|
||||||
|
|
@ -150,9 +150,10 @@ Here is a sample function that requires 9 parameters (for who knows what reason)
|
||||||
```c++
|
```c++
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
void SillyFunction(long p1, long p2, long p3, long p4, long p5, long p6,
|
void SillyFunction(long p1, long p2, long p3, long p4,
|
||||||
long p7, long p8, long p9) {
|
long p5, long p6, long p7, long p8,
|
||||||
printf("This example hurts my brain: %ld %ld\n", p8, p9);
|
long p9) {
|
||||||
|
printf("This example hurts: %ld %ld\n", p8, p9);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
@ -169,38 +170,38 @@ This example hurts my brain: 8 9
|
||||||
In assembly language, this program could be written as:
|
In assembly language, this program could be written as:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
.text // 1
|
.text // 1
|
||||||
.global main // 2
|
.global main // 2
|
||||||
// 3
|
// 3
|
||||||
SillyFunction: // 4
|
SillyFunction: // 4
|
||||||
str x30, [sp, -16]! // 5
|
str x30, [sp, -16]! // 5
|
||||||
ldr x0, =fmt // 6
|
ldr x0, =fmt // 6
|
||||||
mov x1, x7 // 7
|
mov x1, x7 // 7
|
||||||
ldr x2, [sp, 16] // 8
|
ldr x2, [sp, 16] // 8
|
||||||
bl printf // 9
|
bl printf // 9
|
||||||
ldr x30, [sp], 32 // 10
|
ldr x30, [sp], 32 // 10
|
||||||
ret // 11
|
ret // 11
|
||||||
// 12
|
// 12
|
||||||
main: // 13
|
main: // 13
|
||||||
str x30, [sp, -16]! // 14
|
str x30, [sp, -16]! // 14
|
||||||
mov x0, 9 // 15
|
mov x0, 9 // 15
|
||||||
str x0, [sp, -16]! // 16
|
str x0, [sp, -16]! // 16
|
||||||
mov x0, 1 // 17
|
mov x0, 1 // 17
|
||||||
mov x1, 2 // 18
|
mov x1, 2 // 18
|
||||||
mov x2, 3 // 19
|
mov x2, 3 // 19
|
||||||
mov x3, 4 // 20
|
mov x3, 4 // 20
|
||||||
mov x4, 5 // 21
|
mov x4, 5 // 21
|
||||||
mov x5, 6 // 22
|
mov x5, 6 // 22
|
||||||
mov x6, 7 // 23
|
mov x6, 7 // 23
|
||||||
mov x7, 8 // 24
|
mov x7, 8 // 24
|
||||||
bl SillyFunction // 25
|
bl SillyFunction // 25
|
||||||
ldr x30, [sp], 32 // 26
|
ldr x30, [sp], 32 // 26
|
||||||
ret // 27
|
ret // 27
|
||||||
// 28
|
// 28
|
||||||
.data // 29
|
.data // 29
|
||||||
fmt: .asciz "This example hurts my brain: %ld %ld\n" // 30
|
fmt: .asciz "This example hurts: %ld %ld\n" // 30
|
||||||
// 31
|
// 31
|
||||||
.end
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Notice how `main()` puts the first 8 parameters into the scratch registers `x0` through `x7` using `Lines 17` to `24`. But first, it put the ninth parameter on the stack. It did the stack parameter first so that the stack pointer could be manipulated in a scratch register.
|
Notice how `main()` puts the first 8 parameters into the scratch registers `x0` through `x7` using `Lines 17` to `24`. But first, it put the ninth parameter on the stack. It did the stack parameter first so that the stack pointer could be manipulated in a scratch register.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue