(anonymous)

Published on
Embed video
Share video
Ask about this video

Scene 1 (0s)

GUIDE COMPLET Architecture Y86 Programmation en langage machine simplifiée Version détaillée pour débutants Créé le 28/01/2026.

Scene 2 (8s)

I Table des matières 1. Introduction à Y86 2. Les concepts de base 3. La mémoire 4. Les registres 5. Les instructions de copie 6. Les opérations arithmétiques 7. Les branchements et tests 8. La pile (stack) 9. Les appels de fonction 10. Les tableaux 11. Exemples complets 12. Astuces et bonnes pratiques.

Scene 3 (24s)

1II Introduction à Y86 Qu'est-ce que Y86 ? Y86 est un processeur simplifié créé à des fins pédagogiques. C'est comme une version "allégée" de l'architecture Intel x86, conçue pour permettre aux étudiants d'apprendre les concepts fondamentaux de la programmation en langage machine sans être submergés par la complexité. Pourquoi apprendre Y86 ? I Comprendre comment fonctionne un ordinateur au niveau le plus bas I Apprendre à programmer directement le processeur I Comprendre l'exécution des programmes compilés I Optimiser son code en comprenant le matériel II IMPORTANT : Y86 utilise une syntaxe AT&T; (comme GNU Assembler), où la source est à gauche et la destination à droite. Exemple : addl %eax, %ebx signifie ebx = ebx + eax.

Scene 4 (50s)

2II Les concepts de base Le binaire et les bits Les ordinateurs ne comprennent que deux états : 0 et 1. C'est ce qu'on appelle le système binaire. Un bit est la plus petite unité d'information : il vaut soit 0, soit 1. Décimal Binaire Hexadécimal 0 0000 0x0 1 0001 0x1 2 0010 0x2 5 0101 0x5 10 1010 0xA 15 1111 0xF 255 11111111 0xFF Les unités de mesure I 1 bit = une seule valeur (0 ou 1) I 1 octet (byte) = 8 bits = peut représenter 256 valeurs (0-255) I 1 mot (word) en Y86 = 32 bits = 4 octets = peut représenter 4,3 milliards de valeurs I ASTUCE : En hexadécimal, on utilise le préfixe 0x. Exemple : 0xFF = 255 en décimal = 11111111 en binaire.

Scene 5 (1m 16s)

3II La mémoire Comment fonctionne la mémoire ? Imaginez la mémoire comme un immense tableau de cases numérotées. Chaque case peut stocker un octet (8 bits). Le numéro de chaque case s'appelle une adresse. Exemple visuel de la mémoire : Adresse Contenu Commentaire 0x0000 30 Première case de la mémoire 0x0001 50 0x0002 04 0x0003 04 0x0004 00 ... ... ... 0x0100 02 Variable "a" 0x0101 00 0x0102 00 0x0103 00 0x0104 05 Variable "b" Les mots mémoire En Y86, on travaille principalement avec des mots de 32 bits (4 octets). Pour stocker un mot, on utilise 4 cases consécutives de la mémoire. Little Endian vs Big Endian Y86 utilise le format Little Endian : l'octet de poids faible (les bits les moins significatifs) est stocké en premier. Exemple : Pour stocker le nombre 0x12345678 Format Adresse 0 Adresse 1 Adresse 2 Adresse 3 Little Endian (Y86) 78 56 34 12 Big Endian 12 34 56 78 II IMPORTANT : Quand vous voyez le nombre 0x12345678 en mémoire Y86, il est stocké comme 78 56 34 12 (dans cet ordre) !.

Scene 6 (1m 49s)

4II Les registres Qu'est-ce qu'un registre ? Les registres sont des mémoires ultra-rapides situées directement dans le processeur. Ils sont beaucoup plus rapides que la mémoire RAM, mais il y en a très peu (seulement 8 en Y86). I Analogie : Si la mémoire RAM est comme une bibliothèque, les registres sont comme votre bureau de travail. Vous ne pouvez mettre que quelques livres sur votre bureau, mais vous pouvez les consulter instantanément ! Les 8 registres de Y86 Registre Numéro Usage typique %eax 0 Accumulator - Résultats de calculs %ecx 1 Counter - Compteurs de boucles %edx 2 Data - Données générales %ebx 3 Base - Adresses de base %esp 4 I Stack Pointer - Pointeur de pile (SPÉCIAL) %ebp 5 Base Pointer - Pointeur de base %esi 6 Source Index - Index source %edi 7 Destination Index - Index destination Convention d'utilisation I Caller-save (l'appelant doit sauvegarder) : %eax, %ecx, %edx Ces registres sont "sacrifiables". Une fonction peut les modifier librement. Si vous voulez garder leur valeur après un appel de fonction, c'est à VOUS de les sauvegarder avant. I Callee-save (l'appelé doit sauvegarder) : %ebx, %esi, %edi, %ebp Ces registres sont "précieux". Si une fonction veut les utiliser, elle DOIT d'abord sauvegarder leur valeur, puis la restaurer avant de se terminer. Les registres cachés PC (Program Counter) Contient l'adresse de l'instruction en cours d'exécution. Il s'incrémente automatiquement après chaque instruction. PSW (Program Status Word) - Les drapeaux Contient 3 bits d'état qui sont modifiés après chaque opération arithmétique : • Z (Zero) : vaut 1 si le résultat est zéro.

Scene 7 (2m 41s)

• S (Sign) : vaut 1 si le résultat est négatif • O (Overflow) : vaut 1 en cas de débordement.

Scene 8 (2m 50s)

5II Les instructions de copie La famille movl Les instructions movl permettent de copier des données. Le 'l' signifie 'long' (32 bits, soit 4 octets). Les deux premières lettres indiquent la source et la destination : • i = immediate (valeur constante) • r = register (registre) • m = memory (mémoire) Instruction Signification Exemple Effet irmovl V, rB Immediate → Register irmovl $42, %eax eax = 42 rrmovl rA, rB Register → Register rrmovl %eax, %ebx ebx = eax mrmovl M, rA Memory → Register mrmovl 0x100, %eax eax = Mem[0x100] rmmovl rA, M Register → Memory rmmovl %eax, 0x100 Mem[0x100] = eax II RÈGLE IMPORTANTE : On ne peut PAS faire de copie directe mémoire → mémoire, ni mettre une valeur immédiate directement en mémoire. Il faut TOUJOURS passer par un registre intermédiaire ! Les modes d'adressage mémoire 1. Adressage direct L'adresse est spécifiée directement dans l'instruction. mrmovl 0x100, %eax → Lit le mot à l'adresse 0x100 et le met dans eax 2. Adressage indirect par registre L'adresse est contenue dans un registre (entre parenthèses). mrmovl (%esi), %eax → Lit le mot à l'adresse contenue dans esi 3. Adressage indexé L'adresse = contenu du registre + un déplacement. mrmovl 4(%esi), %eax → Lit le mot à l'adresse (esi + 4) Exemple complet expliqué pas à pas : # Initialisation .pos 0x100 a: .long 12 # À l'adresse 0x100, on stocke 12 # Programme irmovl a, %esi # esi = 0x100 (l'ADRESSE de a) mrmovl (%esi), %eax # eax = Mem[esi] = Mem[0x100] = 12 mrmovl 4(%esi), %ebx # ebx = Mem[esi+4] = Mem[0x104].

Scene 9 (3m 38s)

I ASTUCE : L'adressage indexé est très utile pour parcourir des tableaux ! Si esi pointe vers le début d'un tableau, 4(%esi) est le 2e élément, 8(%esi) le 3e, etc..

Scene 10 (3m 50s)

6II Les opérations arithmétiques Les 4 opérations de base Instruction Signification Exemple Effet addl rA, rB Addition addl %eax, %ebx ebx = ebx + eax subl rA, rB Soustraction subl %eax, %ebx ebx = ebx - eax andl rA, rB ET logique andl %eax, %ebx ebx = ebx AND eax xorl rA, rB OU exclusif xorl %eax, %ebx ebx = ebx XOR eax II ATTENTION : Syntaxe AT&T; ! La destination est à DROITE. Exemple : addl %eax, %ebx signifie ebx = ebx + eax (et PAS eax = eax + ebx) Opérations avec valeur immédiate (extension) Le simulateur Y86 que vous utiliserez supporte les opérations avec des valeurs immédiates. C'est une extension pratique qui n'existe pas dans le Y86 de base : iaddl $5, %eax → eax = eax + 5 isubl $1, %ecx → ecx = ecx - 1 (décrémentation) Modification des drapeaux Toutes les opérations arithmétiques modifient automatiquement les 3 drapeaux du PSW : Drapeau Nom Valeur 1 si... Exemple Z Zero Résultat = 0 subl %eax, %eax → Z=1 S Sign Résultat < 0 isubl $10, %eax (si eax=5) → S=1 O Overflow Débordement Addition de 2 grands nombres Astuces de programmation 1. Mettre un registre à zéro xorl %eax, %eax → eax = 0 (car X XOR X = 0 toujours) Cette astuce économise 4 octets par rapport à irmovl $0, %eax 2. Tester un registre sans le modifier andl %eax, %eax → eax reste inchangé, mais Z et S sont mis à jour Utile avant un branchement conditionnel ! (car X AND X = X toujours).

Scene 11 (4m 34s)

7II Les branchements et tests Le concept de branchement Un branchement (jump) modifie le compteur ordinal (PC) pour sauter à une autre instruction. C'est comme tourner les pages d'un livre dans le désordre. Branchement inconditionnel jmp etiquette → Saute TOUJOURS à l'étiquette Branchements conditionnels Ces instructions regardent les drapeaux Z, S et O pour décider si elles sautent ou non. On les utilise après une opération arithmétique (souvent une soustraction pour comparer). Instruction Condition Utilisation typique Drapeaux je == Jump if Equal Z = 1 jne != Jump if Not Equal Z = 0 jl < Jump if Less S^O = 1 jle <= Jump if Less or Equal (S^O)=1 ou Z=1 jg > Jump if Greater S^O = 0 jge >= Jump if Greater or Equal (S^O)=0 ou Z=1 I ASTUCE : Pour comparer A et B, on fait subl %eax, %ebx (avec A dans eax et B dans ebx). Les drapeaux indiquent ensuite si B > A, B = A, etc. Réaliser un IF en Y86 II PIÈGE IMPORTANT : En C, on dit QUAND exécuter le code. En Y86, on dit QUAND SAUTER PAR-DESSUS (le contraire) ! Exemple 1 : IF simple # En C : # if (eax != 3) # En Y86 (on INVERSE la condition !) : rrmovl %eax, %ecx # Copie eax dans ecx (pour ne pas perdre eax) isubl $3, %ecx # ecx = ecx - 3 je apres # Si égal (Z=1), SAUTER le code ... code ... # Code exécuté si eax != 3 apres: ... suite ... # Suite du programme Exemple 2 : IF...ELSE # En C : # if (eax != 3) else.

Scene 12 (5m 29s)

# En Y86 : rrmovl %eax, %ecx isubl $3, %ecx je sinon # Si égal, aller au ELSE ... code A ... # Code du IF jmp apres # Sauter le ELSE sinon: ... code B ... # Code du ELSE apres: ... suite ... Réaliser une boucle WHILE # En C : # while (eax > 0) # En Y86 : boucle: andl %eax, %eax # Teste eax (met à jour Z et S) jle fin # Si eax <= 0, sortir ... code ... # Corps de la boucle isubl $1, %eax # eax-- jmp boucle # Recommencer fin: ... suite ... I OPTIMISATION : Dans l'exemple ci-dessus, on peut décrémenter AVANT le test pour économiser une instruction : isubl $1, %eax; jl fin.

Scene 13 (6m 0s)

8II La pile (stack) Qu'est-ce que la pile ? La pile est une zone de mémoire spéciale qui fonctionne comme une pile d'assiettes : on ne peut ajouter ou retirer des éléments qu'au sommet. C'est une structure LIFO (Last In, First Out = dernier entré, premier sorti). I Analogie : Imaginez une pile d'assiettes. Vous ne pouvez prendre que l'assiette du dessus, et vous ne pouvez ajouter une nouvelle assiette qu'au-dessus de toutes les autres. Le pointeur de pile %esp Le registre %esp (Stack Pointer) contient toujours l'adresse du sommet de la pile. En Y86, la pile "descend" : quand on empile, esp DIMINUE ! Mémoire haute (0xFFFF...) IIIIIIIIIIIIIIIIIIII I I ← Espace disponible I I I I IIIIIIIIIIIIIIIIIIII I 42 I ← Sommet de pile (ESP pointe ici) IIIIIIIIIIIIIIIIIIII I 12 I IIIIIIIIIIIIIIIIIIII I 07 I IIIIIIIIIIIIIIIIIIII Mémoire basse (0x0000...) Initialisation de la pile II TRÈS IMPORTANT : Avant d'utiliser la pile, il FAUT initialiser %esp ! C'est normalement la PREMIÈRE instruction de votre programme. .pos 0 # Début du programme irmovl pile, %esp # Initialise ESP avec l'adresse de la pile ...votre code... .pos 0x200 # Adresse où on place la pile pile: .long 0 # Réserve l'espace pour la pile Les instructions PUSH et POP Instruction Effet Équivalent Description pushl %eax ESP -= 4 Mem[ESP] = eax isubl $4, %esp rmmovl %eax, (%esp) Empile eax popl %eax eax = Mem[ESP] ESP += 4 mrmovl (%esp), %eax iaddl $4, %esp Dépile dans eax Exemple d'utilisation irmovl pile, %esp # ESP = 0x200 irmovl $42, %eax # eax = 42 pushl %eax # ESP = 0x1FC, Mem[0x1FC] = 42 irmovl $13, %ebx # ebx = 13 pushl %ebx # ESP = 0x1F8, Mem[0x1F8] = 13 popl %ecx # ecx = 13, ESP = 0x1FC popl %edx # edx = 42, ESP = 0x200.

Scene 14 (6m 58s)

I RÈGLE D'OR : On dépile généralement dans l'ordre INVERSE de l'empilement. C'est le principe LIFO !.

Scene 15 (7m 6s)

9II Les appels de fonction Les instructions CALL et RET Instruction Ce qu'elle fait État de la pile call fonction 1. Empile l'adresse de retour 2. Saute à "fonction" ESP diminue de 4 ret 1. Dépile l'adresse 2. Saute à cette adresse ESP augmente de 4 Exemple simple # Appelant irmovl $5, %eax # Paramètre dans eax call triple # Appel de fonction # À ce point, eax contient 15 halt # Fonction triple: addl %eax, %eax # eax = eax + eax (×2) addl %eax, %eax # Non ! Erreur : eax = eax×2 = ×4 total ret # Retour Passage de paramètres par la pile Quand on a plus de paramètres que de registres, on les passe par la pile. Convention : On les empile dans l'ordre INVERSE (le dernier en premier). II POURQUOI L'ORDRE INVERSE ? Pour que le premier paramètre soit le plus proche de ESP, donc facile à trouver à un offset fixe. Exemple complet avec 3 paramètres # En C : fonction(a, 12, b) # APPELANT : mrmovl b, %eax pushl %eax # 3e paramètre EN PREMIER irmovl $12, %eax pushl %eax # 2e paramètre mrmovl a, %eax pushl %eax # 1er paramètre EN DERNIER call fonction iaddl $12, %esp # Nettoie la pile (3 × 4 octets) # APPELÉE : fonction: mrmovl 4(%esp), %eax # 1er param à ESP+4 mrmovl 8(%esp), %ebx # 2e param à ESP+8 mrmovl 12(%esp), %ecx # 3e param à ESP+12 # ... traitement ... ret Pourquoi ESP+4 et pas ESP+0 ? Parce que l'instruction call a empilé l'adresse de retour ! Donc au moment d'entrer dans la fonction, la pile ressemble à ça : IIIIIIIIIIIIIIII I 3e param I ← ESP+12 IIIIIIIIIIIIIIII.

Scene 16 (8m 1s)

I 2e param I ← ESP+8 IIIIIIIIIIIIIIII I 1er param I ← ESP+4 IIIIIIIIIIIIIIII I Adr retour I ← ESP+0 (sommet) IIIIIIIIIIIIIIII Sauvegarde de registres Rappel des conventions : I Caller-save (%eax, %ecx, %edx) : L'appelant les sauve s'il veut les garder I Callee-save (%ebx, %esi, %edi, %ebp) : La fonction les sauve si elle les modifie Structure complète d'une fonction fonction: # === PROLOGUE === pushl %esi # Sauve callee-save si on les utilise pushl %edi isubl $8, %esp # Réserve 8 octets pour variables locales # === CORPS === mrmovl 20(%esp), %eax # Lit 1er param (décalé par sauvegardes) mrmovl 24(%esp), %ebx # Lit 2e param # ... traitement ... rmmovl %eax, 0(%esp) # Stocke var locale à ESP+0 rmmovl %ebx, 4(%esp) # Stocke var locale à ESP+4 # === ÉPILOGUE === iaddl $8, %esp # Libère variables locales popl %edi # Restaure callee-save popl %esi ret # Retour I ATTENTION AUX DÉCALAGES ! Chaque PUSH décale tous les paramètres ! Si vous sauvegardez 2 registres (8 octets), les paramètres sont à ESP+12, ESP+16, etc..

Scene 17 (8m 38s)

I Les tableaux Deux approches pour parcourir un tableau Approche 1 : Avec un index On calcule l'adresse comme : adresse_base + (index × 4) # t[i] = i pour i de 0 à 9 xorl %eax, %eax # i = 0 xorl %ebx, %ebx # index en octets = 0 boucle: rmmovl %eax, t(%ebx) # t[ebx] = eax iaddl $4, %ebx # index += 4 octets iaddl $1, %eax # i++ rrmovl %eax, %ecx isubl $10, %ecx jl boucle # Si i < 10, continue .pos 0x100 t: .long 0 # Début du tableau (40 octets pour 10 éléments) Approche 2 : Avec un pointeur On utilise un registre qui pointe directement vers l'élément courant. # Même chose avec un pointeur irmovl t, %ebx # ebx = adresse de début xorl %eax, %eax # i = 0 boucle: rmmovl %eax, (%ebx) # *ebx = eax iaddl $4, %ebx # ebx += 4 (passe à l'élément suivant) iaddl $1, %eax # i++ rrmovl %eax, %ecx isubl $10, %ecx jl boucle .pos 0x100 t: .long 0 I QUELLE APPROCHE CHOISIR ? Le pointeur est souvent plus rapide (une addition au lieu d'un calcul d'offset à chaque fois). Mais l'index est plus flexible si vous devez accéder aux éléments dans le désordre. Tableaux multidimensionnels Pour un tableau 2D t[i][j], l'adresse est : base + (i × nb_colonnes + j) × 4 # Accès à t[2][3] dans un tableau 5×5 irmovl $2, %eax # i = 2 irmovl $5, %ebx # nb_colonnes = 5 # Calcul : i × nb_colonnes xorl %ecx, %ecx mult: addl %ebx, %ecx # ecx += 5 isubl $1, %eax jg mult # Répète 2 fois # ecx = 10 maintenant iaddl $3, %ecx # ecx += j (3) # ecx = 13 irmovl $4, %eax # On devrait faire ecx × 4, mais simplifions: # ... calcul final de l'offset ... irmovl t, %esi mrmovl 52(%esi), %eax # 52 = 13 × 4.

Scene 18 (9m 41s)

1II1II Exemples complets commentés Exemple 1 : Multiplication par additions Programme qui calcule c = a × b en faisant des additions successives. .pos 0 irmovl pile, %esp # Initialise la pile mrmovl a, %ebx # ebx = a (multiplicateur) mrmovl b, %ecx # ecx = b (multiplicande) call mult # Appel : résultat dans eax rmmovl %eax, c # Stocke le résultat halt mult: xorl %eax, %eax # Résultat = 0 boucle: andl %ebx, %ebx # Teste ebx je fin # Si 0, terminé addl %ecx, %eax # Ajoute b au résultat isubl $1, %ebx # Décrémente le compteur jmp boucle fin: ret # Retourne eax .pos 0x100 a: .long 7 # Premier nombre b: .long 6 # Deuxième nombre c: .long 0 # Résultat (sera 42) .pos 0x200 pile: .long 0 Exemple 2 : Somme d'un tableau .pos 0 irmovl pile, %esp irmovl tab, %esi # Pointeur vers le tableau irmovl $5, %ecx # Nombre d'éléments call somme rmmovl %eax, resultat halt somme: xorl %eax, %eax # Somme = 0 loop: andl %ecx, %ecx # Teste le compteur je done # Si 0, fini mrmovl (%esi), %ebx # Lit l'élément courant addl %ebx, %eax # Ajoute à la somme iaddl $4, %esi # Passe à l'élément suivant isubl $1, %ecx # Décrémente le compteur jmp loop done: ret .pos 0x100 tab: .long 10 .long 20 .long 30 .long 40 .long 50 resultat: .long 0 # Sera 150 .pos 0x200 pile: .long 0 Exemple 3 : Factorielle récursive Calcul de n! de manière récursive. Cet exemple montre l'importance de la pile !.

Scene 19 (10m 38s)

.pos 0 irmovl pile, %esp irmovl $5, %eax # Calcul de 5! pushl %eax # Paramètre call fact iaddl $4, %esp # Nettoie la pile rmmovl %eax, resultat # Stocke 120 halt fact: # Lit n mrmovl 4(%esp), %eax # Cas de base : si n <= 1, retourne 1 rrmovl %eax, %ecx isubl $1, %ecx jg recursion # Si n > 1, cas récursif irmovl $1, %eax # Sinon retourne 1 ret recursion: # Sauvegarde n pushl %eax # Appel récursif avec n-1 isubl $1, %eax pushl %eax call fact iaddl $4, %esp # Nettoie paramètre # Multiplie n × fact(n-1) popl %ebx # Récupère n # Multiplication simple (eax = eax × ebx) rrmovl %eax, %ecx # Sauve résultat xorl %eax, %eax mult_loop: andl %ebx, %ebx je mult_done addl %ecx, %eax isubl $1, %ebx jmp mult_loop mult_done: ret .pos 0x100 resultat: .long 0 .pos 0x400 pile: .long 0 # Pile plus grande pour la récursion.

Scene 20 (11m 14s)

1II2II Astuces et bonnes pratiques Optimisations courantes 1. Mettre à zéro I irmovl $0, %eax (6 octets) I xorl %eax, %eax (2 octets) 2. Doubler une valeur I irmovl $2, %ebx; ... multiplication ... I addl %eax, %eax (2 octets, instantané) 3. Tester sans modifier I andl %eax, %eax puis tester Z et S 4. Copier avec test Si vous copiez un registre et devez ensuite le tester, utilisez rrmovl puis andl sur la destination (ça ne modifie pas la valeur). Erreurs fréquentes à éviter I Oublier d'initialiser %esp Votre programme va crasher dès le premier push/call ! I Ne pas nettoyer la pile après call La pile va grandir indéfiniment et déborder I Inverser source et destination Rappelez-vous : syntaxe AT&T, destination à DROITE I Oublier que les drapeaux changent Une opération arithmétique écrase Z, S, O I Accéder à ESP au mauvais offset Pensez à l'adresse de retour et aux sauvegardes ! I Confondre adresse et valeur irmovl a, %eax (adresse) vs mrmovl a, %eax (valeur) Conseils de débogage I Dessinez la pile ! Sur papier, tracez ESP et ce qu'il y a à chaque adresse I Commentez TOUT ! Une ligne de commentaire par instruction minimum I Testez par étapes ! Commencez simple, ajoutez progressivement I Vérifiez les drapeaux ! Utilisez le simulateur pour voir Z, S, O Liste de contrôle avant exécution.

Scene 21 (12m 0s)

 ESP est initialisé en début de programme  La pile est à une adresse suffisamment haute (0x200+)  Tous les branchements ont des étiquettes valides  Les données sont alignées sur 4 octets (.align 4)  Chaque CALL a son IADDL de nettoyage correspondant  Les fonctions sauvegardent/restaurent les registres callee-save  Les paramètres sont empilés dans l'ordre inverse  Le programme se termine par HALT I CONSEIL FINAL : Y86 est difficile au début, mais c'est normal ! Avec de la pratique, vous commencerez à "penser en assembleur" et tout deviendra plus clair. Bon courage ! I.

Scene 22 (12m 23s)

Fin du guide Ce guide a été créé pour vous aider à maîtriser la programmation en Y86. N'hésitez pas à y revenir aussi souvent que nécessaire ! Pour aller plus loin, consultez le livre "Computer Systems: A Programmer's Perspective" de Bryant & O'Hallaron, disponible sur csapp.cs.cmu.edu.