Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .github/workflows/examples.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
name: "Test examples"

"on":
pull_request:
branches:
- "**"
push:
branches:
- "master"

concurrency:
cancel-in-progress: true
group: "${{ github.workflow }}-${{ github.ref }}"

permissions:
contents: read

jobs:
build-and-run:
name: "${{ matrix.example }}"
runs-on: ubuntu-latest
timeout-minutes: 5
strategy:
# Build and run every example even if one of them is broken.
fail-fast: false
matrix:
example:
- hello
- sum
- stack
- strings
- float
- casm1
- casm2
- casm3
steps:
- name: "Checkout repository"
uses: "actions/checkout@v7"
- name: "Install NASM"
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends --yes nasm
- name: "Build and run the ${{ matrix.example }} example"
run: ./scripts/ci/test-examples.sh "${{ matrix.example }}"
2 changes: 1 addition & 1 deletion .github/workflows/link-check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name: "Link Check"
- "**"
push:
branches:
- "main"
- "master"

concurrency:
cancel-in-progress: true
Expand Down
16 changes: 8 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ Mkfile.old
dkms.conf

# Our binaries
hello/hello
sum/sum
stack/stack
strings/reverse
float/dot_product
casm/casm1/casm
casm/casm2/casm
casm/casm3/casm
examples/hello/hello
examples/sum/sum
examples/stack/stack
examples/strings/reverse
examples/float/dot_product
examples/casm/casm1/casm
examples/casm/casm2/casm
examples/casm/casm3/casm
20 changes: 20 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,26 @@ If you want to directly contribute to the project, create a pull reguest with th

4. Open a pull request in GitHub. Fill the pull request template with the reason and description for the provided changes. Link your pull request with the existing issue, if applicable. After submitting your PR, wait for the review from the project maintainers.

## Code examples

The programs that accompany the chapters live in the [examples](./examples/) directory. Each of them is a standalone program with its own `Makefile` and `README.md`.

If you change an example, or add a new one, check that it still builds and prints what the related chapter says it should print:

```bash
./scripts/ci/test-examples.sh
```

To check only some of the examples, pass their names:

```bash
./scripts/ci/test-examples.sh hello casm3
```

Besides the tools from the [Requirements](./README.md#requirements) section, the script needs [GCC](https://gcc.gnu.org/) to build the examples that interact with C, and [Python](https://www.python.org/) to run the examples that expect a terminal.

When adding a new example, you also need to add a `test_<name>` function in the script and an entry in the `example` list of the [Examples](./.github/workflows/examples.yaml) workflow. The same script runs in continuous integration for every pull request, so a broken example fails the build.

## Review and approval process

After you submit your PR, wait for the review. The project maintainers will evaluate your changes and provide feedback either using [suggested changes](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/incorporating-feedback-in-your-pull-request) or pull request comments. Address the review suggestions and comments as soon as you can. If your PR looks good, the maintainers approve and merge it.
Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Assembly programming

[![CC BY-NC-SA 4.0][cc-by-nc-sa-shield]][cc-by-nc-sa] [![Check Links](https://github.com/0xAX/asm/actions/workflows/link-check.yaml/badge.svg)](https://github.com/0xAX/asm/actions/workflows/link-check.yaml) [![star this repo](https://badgen.net/github/stars/0xAX/asm)](https://github.com/0xAX/asm) [![fork this repo](https://badgen.net/github/forks/0xAX/asm)](https://github.com/0xAX/asm/fork) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/0xAX/asm/issues) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)
[![CC BY-NC-SA 4.0][cc-by-nc-sa-shield]][cc-by-nc-sa] [![Check Links](https://github.com/0xAX/asm/actions/workflows/link-check.yaml/badge.svg)](https://github.com/0xAX/asm/actions/workflows/link-check.yaml) [![Examples](https://github.com/0xAX/asm/actions/workflows/examples.yaml/badge.svg)](https://github.com/0xAX/asm/actions/workflows/examples.yaml) [![star this repo](https://badgen.net/github/stars/0xAX/asm)](https://github.com/0xAX/asm) [![fork this repo](https://badgen.net/github/forks/0xAX/asm)](https://github.com/0xAX/asm/fork) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/0xAX/asm/issues) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com)

This repository contains blog posts that introduce the [assembly](https://en.wikipedia.org/wiki/Assembly_language) programming language. For this moment, all the content and examples cover only the [x86_64](https://en.wikipedia.org/wiki/X86-64) processors and the GNU Linux operating system. In the future, I plan to post learning materials for the [ARM64](https://en.wikipedia.org/wiki/AArch64) architecture.

Expand Down Expand Up @@ -29,6 +29,8 @@ Here are links to each post:
- [Part 6. Floating-point arithmetic](https://github.com/0xAX/asm/blob/master/content/asm_6.md)
- [Part 7. Assembly interaction with high-level programming languages](https://github.com/0xAX/asm/blob/master/content/asm_7.md)

The code examples that accompany the chapters live in the [examples](./examples/) directory.

## Requirements

To run code examples, you need the following tools:
Expand Down
7 changes: 5 additions & 2 deletions content/asm_6.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,13 +397,16 @@ _parse_first_float_vector:
;; Initialize the rsi register with the pointer to the place where
;; the strtod(3) will finish its work.
mov rsi, end_buffer_1
;; Preserve the pointer to the current floating-point value in the r12 register,
;; because the strtod(3) may change it.
mov r12, rdi
;; Call the strtod(3) to convert a floating-point value from the input buffer to double representation.
call strtod
;; Preserve the pointer to the next floating-point value from the input buffer
;; in the rax register.
mov rax, [rel end_buffer_1]
;; Check whether it is the end of the input string.
cmp rax, rdi
cmp rax, r12
;; Proceed with the second vector if we reached the end of the first vector.
je _read_second_float_vector
;; Store the reference to the beginning of the buffer where we will store
Expand Down Expand Up @@ -440,7 +443,7 @@ As soon as we finish parsing the floating-point values for the first vector, we
- To store the number of values within the second vector, we will use the `r15` register instead of `r14`.

> [!TIP]
> For reference, you can find the whole code [here](https://github.com/0xAX/asm/blob/master/float/dot_product.asm).
> For reference, you can find the whole code [here](../examples/float/dot_product.asm).

### Calculation of the dot product

Expand Down
15 changes: 12 additions & 3 deletions content/asm_7.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,12 +216,18 @@ After defining the assembly code, we can specify input and output operands that

For example:

- `a` - tells the compiler to use the `rax` register.
- `b` - tells the compiler to use the `rbx` register.
- `c` - tells the compiler to use the `rcx` register.
- `d` - tells the compiler to use the `rdx` register.
- `S` - tells the compiler to use the `rsi` register.
- `D` - tells the compiler to use the `rdi` register.
- `r` - tells the compiler to use a [general-purpose register](./asm_2.md).
- `g` - tells the compiler to use any register, memory, or immediate integer operand.
- `f` - tells the compiler to use a [floating-point register](./asm_6.md).
- `m` - forces the compiler to use a memory location.

You can find all the supported constraint strings in the [official documentation](https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Output-Operands).
You can find all the supported constraint strings in the official documentation. For more information, see the [Simple Constraints](https://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html) and [Constraints for Particular Machines](https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html) documents.

Let's try to rewrite our `hello world` program using the inline assembly:

Expand All @@ -239,14 +245,17 @@ int main() {
"movq %1, %%rsi \n\t" // rsi = str - Set the second argument of `sys_write` to the reference of the `str` variable.
"movq %2, %%rdx \n\t" // rdx = len(str) - Set the third argument of `sys_write` to the length of the `str` variable's value.
"syscall" // Call the `sys_write` system call.
: "=g"(ret) // Return the result in the `ret` variable.
: "g"(str), "g" (len)); // Put `str` and `len` variables in any general operand (memory, register, or immediate, if possible)
: "=a"(ret) // Return the result of the system call from the `rax` register in the `ret` variable.
: "g"(str), "g" (len) // Put `str` and `len` variables in any general operand (memory, register, or immediate, if possible)
: "rdi", "rsi", "rdx", "memory"); // Tell the compiler about the registers and the memory that the assembly code changes.

printf("Bytes written: %d\n", ret);
return 0;
}
```

The last part of the assembly block is the list of clobbers. Here we tell the compiler which registers and memory our assembly code changes. The compiler does not analyze the assembly code, so without this list it may store its own values in the `rdi`, `rsi`, or `rdx` registers and lose them. We do not add `rax` to this list, because the `=a` output operand already describes it.

> [!NOTE]
> In the example above, we used [GNU `as` assembly](https://sourceware.org/binutils/docs/as.html), which has a slightly different syntax from the [NASM](https://nasm.us/) assembly. The main difference is that the order of operands for the `movq` instruction is reversed, and we move the value of the left operand to the right.

Expand Down
20 changes: 20 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Code examples

These are the sample programs that accompany the book. Each directory is a standalone program with its own `Makefile` and `README.md`.

| Example | Description | Chapter |
| --------------------- | ------------------------------------- | ----------------------------------------------------------------------------------------- |
| [hello](./hello/) | The basic "Hello, World!" program | [Part 1. Introduction](../content/asm_1.md) |
| [sum](./sum/) | Sum of two integer numbers | [Part 2. The `x86_64` concepts](../content/asm_2.md) |
| [stack](./stack/) | Sum of two command line arguments | [Part 3. Journey through the stack](../content/asm_3.md) |
| [strings](./strings/) | Reverse a given input string | [Part 4. Data manipulation](../content/asm_4.md) |
| [float](./float/) | Dot product of two vectors of doubles | [Part 6. Floating-point arithmetic](../content/asm_6.md) |
| [casm](./casm/) | Interaction between assembly and C | [Part 7. Assembly interaction with high-level programming languages](../content/asm_7.md) |

To build an example, go to its directory and run the following command:

```bash
make
```

The tools you need to build and run the examples are listed in the [Requirements](../README.md#requirements) section.
2 changes: 1 addition & 1 deletion casm/README.md → examples/casm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ To build each program, go to its directory and run:
make
```

For more details, read [Part 7. Assembly interaction with high-level programming languages](https://github.com/0xAX/asm/blob/master/content/asm_7.md).
For more details, read [Part 7. Assembly interaction with high-level programming languages](../../content/asm_7.md).
File renamed without changes.
File renamed without changes.
File renamed without changes.
5 changes: 3 additions & 2 deletions casm/casm2/casm.c → examples/casm/casm2/casm.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ int main() {
"movq %1, %%rsi \n\t" // rsi = str - Set the second argument of `sys_write` to the reference of the `str` variable.
"movq %2, %%rdx \n\t" // rdx = len(str) - Set the third argument of `sys_write` to the length of the `str` variable's value.
"syscall" // Call the `sys_write` system call.
: "=g"(ret) // Return the result in the `ret` variable.
: "g"(str), "g" (len)); // Put `str` and `len` variables in any general operand (memory, register, or immediate, if possible)
: "=a"(ret) // Return the result of the system call from the `rax` register in the `ret` variable.
: "g"(str), "g" (len) // Put `str` and `len` variables in any general operand (memory, register, or immediate, if possible)
: "rdi", "rsi", "rdx", "memory"); // Tell the compiler about the registers and the memory that the assembly code changes.

printf("Bytes written: %d\n", ret);
return 0;
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
33 changes: 33 additions & 0 deletions examples/float/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Dot product

This is a simple program that reads two double vectors and calculates the [dot product](https://en.wikipedia.org/wiki/Dot_product) of their values.

To build the program, run:

```bash
make
```

Run the program in a terminal and type the values of the vectors when it asks for them:

```bash
$ ./dot_product
Input the first vector: 2.5 3.17
Input the second vector: 4.22 100.1
Dot product = 327.867000
```

> [!IMPORTANT]
> Do not pipe the input to the program. It expects a terminal and gives either a wrong answer or no answer at all when the standard input and output are a pipe or a file:
>
> ```bash
> $ printf '2.5 3.17\n4.22 100.1\n' | ./dot_product
> Input the first vector: Input the second vector: Error: the number of values in vectors should be the same
> ```
>
> There are two reasons for this behavior:
>
> - The program reads each vector with a single `sys_read` system call. A terminal returns one line per read, but a pipe returns everything that was already written to it. So the first read takes both lines, and the program sees all the values as the first vector and no values as the second one.
> - The program prints the result with the C `printf` function, but exits with the raw `sys_exit` system call. The C library writes to a terminal line by line, but buffers the whole output when it writes to a pipe or a file. Nothing flushes that buffer before `sys_exit` ends the program, so the result never gets printed.

For more details, read [Part 6. Floating-point arithmetic](../../content/asm_6.md).
10 changes: 8 additions & 2 deletions float/dot_product.asm → examples/float/dot_product.asm
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,17 @@ _parse_first_float_vector:
;; Initialize the rsi register with the pointer to the place where
;; the strtod(3) will finish its work.
mov rsi, end_buffer_1
;; Preserve the pointer to the current floating-point value in the r12 register,
;; because the strtod(3) may change it.
mov r12, rdi
;; Call the strtod(3) to convert a floating-point value from the input buffer to double representation.
call strtod

;; Preserve the pointer to the next floating-point value from the input buffer
;; in the rax register.
mov rax, [rel end_buffer_1]
;; Check whether it is the end of the input string.
cmp rax, rdi
cmp rax, r12
;; Proceed with the second vector if we reached the end of the first vector.
je _read_second_float_vector

Expand Down Expand Up @@ -199,14 +202,17 @@ _parse_second_float_vector:
;; Initialize the rsi register with the pointer which to the place where
;; the strtod(3) will finish its work.
mov rsi, end_buffer_2
;; Preserve the pointer to the current floating-point value in the r12 register,
;; because the strtod(3) may change it.
mov r12, rdi
;; Call the strtod(3)
call strtod

;; Preserve the pointer to the next floating-point value from the input buffer
;; in the rax register.
mov rax, [rel end_buffer_2]
;; Check whether it is the end of the input string.
cmp rax, rdi
cmp rax, r12
;; Calculate the dot product after we have both vectors.
je _calculate_dot_product

Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion hello/README.md → examples/hello/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ To build the program, run:
make
```

For more details, read [Part 1. Introduction](https://github.com/0xAX/asm/blob/master/content/asm_1.md).
For more details, read [Part 1. Introduction](../../content/asm_1.md).
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion stack/README.md → examples/stack/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ To build the program, run:
make
```

For more details, read [Part 3. Journey through the stack](https://github.com/0xAX/asm/blob/master/content/asm_3.md).
For more details, read [Part 3. Journey through the stack](../../content/asm_3.md).
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion strings/README.md → examples/strings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ To build the program, run:
make
```

For more details, read [Part 4. Journey through the stack](https://github.com/0xAX/asm/blob/master/content/asm_4.md).
For more details, read [Part 4. Data manipulation](../../content/asm_4.md).
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion sum/README.md → examples/sum/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ To build the program, run:
make
```

For more details, read [Part 2. The `x86_64` concepts](https://github.com/0xAX/asm/blob/master/content/asm_2.md).
For more details, read [Part 2. The `x86_64` concepts](../../content/asm_2.md).
File renamed without changes.
11 changes: 0 additions & 11 deletions float/README.md

This file was deleted.

Loading
Loading