> Le langage assembleur expliqué : registres, instructions, pile et un exemple x86-64 concret, pour comprendre le plus bas niveau et pourquoi l'apprendre.

*Source : https://coder-studio.com/tutoriels/langage-assembleur/*

---

[// Assembleur](https://coder-studio.com/tutoriels/)

# Le langage assembleur : comprendre le plus bas niveau

Par **Kévin Papot** · publié le 30 juillet 2025

Le **langage assembleur** est le langage le plus proche du processeur qu’un humain puisse raisonnablement écrire. Chaque instruction y correspond, ou presque, à une opération que le CPU exécute directement. Le comprendre, c’est lever le capot sur ce qui se passe réellement quand votre code Python ou C tourne. Ce tutoriel pose les fondations : registres, instructions, pile, avec un petit exemple x86-64 et, surtout, une réponse honnête à la question « pourquoi l’apprendre encore aujourd’hui ? ».

## De quoi parle-t-on ?

Un processeur ne comprend que du **code machine** : des suites de bits. L’assembleur en est la représentation lisible : à chaque instruction machine correspond un **mnémonique** (`mov`, `add`, `jmp`…). Un programme appelé **assembleur** (au sens de l’outil, _assembler_) traduit ce texte en code machine.

Point crucial : l’assembleur est **spécifique à une architecture**. L’assembleur x86-64 (PC, serveurs) diffère de l’ARM (téléphones, Mac récents, Raspberry Pi). Apprendre l’un rend les autres plus faciles, mais ils ne sont pas interchangeables. Nous utiliserons ici x86-64.

## Les registres

Les **registres** sont de minuscules cases mémoire situées _dans_ le processeur. Ils sont extrêmement rapides — bien plus que la RAM — mais peu nombreux. C’est là que le CPU manipule ses données. Sur x86-64, on trouve notamment des registres généraux de 64 bits :

-   `rax`, `rbx`, `rcx`, `rdx` : usage général (calculs, valeurs temporaires) ;
-   `rsi`, `rdi` : souvent source et destination, notamment pour passer des arguments ;
-   `rsp` : le **pointeur de pile** (_stack pointer_) ;
-   `rbp` : le pointeur de base de la trame (_base pointer_) ;
-   `r8` à `r15` : registres supplémentaires.

Chaque registre 64 bits a des sous-parties accessibles : `eax` désigne les 32 bits de poids faible de `rax`, `ax` les 16 bits, `al` les 8 bits. Pratique pour travailler sur des données plus petites sans registre dédié.

## Les instructions

Une instruction assembleur suit typiquement la forme `opération destination, source` (syntaxe Intel, la plus lisible pour débuter). Quelques instructions fondamentales :

-   **`mov`** — copie une valeur : `mov rax, 5` place 5 dans `rax` ;
-   **`add` / `sub`** — addition / soustraction : `add rax, rbx` fait `rax = rax + rbx` ;
-   **`cmp`** — compare deux valeurs (en soustrayant sans stocker), ce qui met à jour les _drapeaux_ ;
-   **`jmp`, `je`, `jne`, `jg`…** — sauts conditionnels ou non, qui permettent boucles et branchements ;
-   **`call` / `ret`** — appeler une routine et en revenir ;
-   **`push` / `pop`** — empiler / dépiler une valeur sur la pile.

Il n’y a pas de `if`, de `for`, ni de fonction au sens des langages de haut niveau : on les **reconstruit** à partir de comparaisons et de sauts. C’est déroutant, puis révélateur : on voit enfin de quoi sont faites ces abstractions.

## La pile

La **pile** (_stack_) est une zone de mémoire organisée en « dernier entré, premier sorti ». Le processeur s’en sert pour stocker les variables locales, sauvegarder des registres, et surtout mémoriser **où revenir** après un appel de fonction. Le registre `rsp` pointe en permanence sur son sommet.

-   `push rax` décrémente `rsp` et y écrit `rax` ;
-   `pop rbx` lit le sommet dans `rbx` et incrémente `rsp` ;
-   `call` empile automatiquement l’adresse de retour ; `ret` la dépile pour y sauter.

Comprendre la pile, c’est comprendre comment les fonctions s’imbriquent — et pourquoi une récursion infinie finit par la faire déborder (_stack overflow_).

## Un petit exemple x86-64

Voici une fonction qui additionne deux entiers, en syntaxe AT&T (celle de GNU `as`, où l’ordre est `source, destination` et les registres sont préfixés de `%`). Elle suit la convention d’appel Linux : premier argument dans `rdi`, deuxième dans `rsi`, retour dans `rax`.

```
    .text
    .globl addition
addition:                 # int addition(int a, int b)
    movl %edi, %eax       # eax = a (1er argument)
    addl %esi, %eax       # eax = eax + b (2e argument)
    ret                   # la valeur de retour est dans eax
```

Chaque ligne fait exactement une chose. On copie le premier argument dans le registre de retour, on y ajoute le second, on revient. Il n’y a rien de caché : c’est là toute la force — et la lourdeur — de l’assembleur. Pour appeler ce genre de routine depuis du C, voyez notre tutoriel dédié à [l’interfaçage entre assembleur et C](https://coder-studio.com/tutoriels/interfacage-assembleur-c/).

## Pourquoi l’apprendre aujourd’hui ?

Personne, ou presque, n’écrit d’application entière en assembleur en 2026. Les compilateurs modernes produisent du code souvent plus optimisé qu’un humain, et la productivité serait catastrophique. Alors pourquoi s’y intéresser ? Pour des raisons très concrètes :

-   **Comprendre en profondeur** — pointeurs, pile, débordements de tampon, passage d’arguments : ces notions deviennent limpides quand on a vu leur mécanique sous-jacente.
-   **Déboguer et rétro-ingénierie** — lire un désassemblage, analyser un crash, inspecter un binaire sans code source, en sécurité informatique notamment.
-   **Optimiser des points critiques** — quelques routines choisies (SIMD, cryptographie, systèmes embarqués contraints) peuvent justifier de l’assembleur, après mesure.
-   **Systèmes embarqués et bas niveau** — noyaux, pilotes, micro-contrôleurs frôlent parfois le métal.

Autrement dit, on apprend l’assembleur moins pour l’écrire au quotidien que pour **mieux écrire tout le reste**. C’est un investissement de compréhension, pas d’usage.

## Par où commencer

Nul besoin de tout retenir. Installez GCC, écrivez de petites fonctions en C, puis regardez l’assembleur généré avec `gcc -S -masm=intel fichier.c`. Comparer votre C et l’assembleur produit est la façon la plus efficace de progresser : vous verrez concrètement comment une boucle `for` devient une suite de `cmp` et de `jmp`. Le reste vient avec la pratique et la curiosité.

* * *

**À lire ensuite**

-   [Interfaçage entre assembleur et C](https://coder-studio.com/tutoriels/interfacage-assembleur-c/) — faire dialoguer les deux mondes en pratique
-   [Les bases du langage C](https://coder-studio.com/tutoriels/langage-c/) — le langage qui se compile presque directement en assembleur
-   [Rust vs C++](https://coder-studio.com/tutoriels/rust-vs-cpp/) — deux langages de bas niveau qui compilent en code machine natif
