Architecture des ordinateurs (2 ème partie : assembleur 80x86) Vincent Vidal IUT Lens Architecture des ordinateurs p. 1
Organisation Durée : 8 semaines. Cours 1h, TP 3h Intervenants TP : Coste, Loukil, Porquet, Roussel, Vidal Évaluation : 1 DS 1 Partiel x contrôles de TP Environnement : sous linux, assembleur 32bits gnu assembleur (as) Architecture des ordinateurs p. 2
Qu est-ce que l assembleur? (1) Langage de (très) bas niveau permettant l écriture de programmes rapides et d interfaces vers des périphériques (drivers) Utilisé à l heure actuelle pour : Écriture de drivers Écriture des portions de programmes optimisées Écriture de programmes embarqués Démos graphiques pour les concours Bien sûr : on peut tout faire en assembleur... Architecture des ordinateurs p. 3
Qu est-ce que l assembleur? (2) Mémoire Lang. machine Assembleur 00002D4A 6C insb 00002D4B 6962635F63 imul sp,[bp+si+0x63],word 0x635f 00002D50 7375 jnc 0x2dc7 00002D52 5F pop di 00002D53 696E697400 imul bp,[bp+0x69],word 0x74 00002D58 5F pop di 00002D59 5F pop di 00002D5A 627373 bound si,[bp+di+0x73] 00002D5D 5F pop di Architecture des ordinateurs p. 4
Plan du cours (1) Algorithmique en C++, ou comment se passer des : Instructions de boucle (for, while,... ) Instructions de condition (if, switch... ) Paramètres des procédures Variables locales Basiques de l assembleur : Les variables Les registres La pile Les instructions Les appels système Architecture des ordinateurs p. 5
Plan du cours (2) Compilation en assembleur, ou comment traduire : Les boucles Les conditions Les procédures Optimisation, ou comment exploiter au mieux : Les registres La mémoire Les instructions Architecture des ordinateurs p. 6
Algorithmique pour l assembleur (1) Objectif : montrer comment on peut, en C++, se passer : des structures de contrôle habituelles (for, while, if, switch... ) des arguments des procedures, en utilisant une pile des variables locales, au moyen de la pile Moyen : définition d un langage simple basé sur C++, que nous appellerons C : définition des labels définition d instructions de saut et de comparaison définition d une pile et des procédures associées définition de procédures d affichage simples Architecture des ordinateurs p. 7
Algorithmique pour l assembleur (2) ATTENTION Ce que nous allons voir par la suite en C (en particulier les instructions de saut) ne doit en aucun cas être utilisé ailleurs qu en cours et TP d assembleur!!! Architecture des ordinateurs p. 8
Les boucles en C Les structures de contrôle for, while et do{...}while seront remplacées par l utilisation conjointe : des labels des instructions de saut des structures conditionnelles Architecture des ordinateurs p. 9
Les labels Identificateur suivi de :, se plaçant devant une ligne de code. Chaque label doit être unique. (...) cout << "Ceci est "; mon_label_1: cout << "un exemple "; mon_label_2: cout << "pas très long."; (...) Architecture des ordinateurs p. 10
Les sauts inconditionnels (1) jmp(<label>) Cette instruction (JuMP) effectue un saut vers le label passé en argument : la prochaine instruction à exécuter sera celle qui suit ce label. Le saut est dit inconditionnel car il est effectué dès que l instruction jmp est rencontrée. Nous verrons par la suite des instructions de saut conditionnel. Architecture des ordinateurs p. 11
Les sauts inconditionnels (2) Exemple : cout << "Un "; jmp(ici); cout << "petit "; ici: cout << "saut."; produit l affichage suivant : Un saut. Architecture des ordinateurs p. 12
Les boucles for en C (1) En C++, la somme des entiers de 1 à 10 : int somme=0; for(int i=1;i<=10;i++) {somme=somme+i;} cout << somme; Architecture des ordinateurs p. 13
Les boucles for en C (2) En C, la somme des entiers de 1 à 10 : int somme=0; int i=1; boucle: if (i>10) jmp(fin_boucle); somme=somme+i; i++; jmp(boucle); fin_boucle: cout << somme; Architecture des ordinateurs p. 14
Les boucles while en C (1) En C++, le calcul de la plus grande puissance de 17 strictement inférieure à 5423 : int n=1; while (n*17 < 5423) { n=n*17; } Architecture des ordinateurs p. 15
Les boucles while en C (2) En C, le calcul de la plus grande puissance de 17 strictement inférieure à 5423 : int n=1; boucle: if (n*17 >= 5423) jmp(fin_boucle); n=n*17; jmp(boucle); fin_boucle: Architecture des ordinateurs p. 16
Les boucles do{...}while en C (1) En C++, la saisie d un entier non nul : int entier; do { cin >> entier; } while (entier==0); Architecture des ordinateurs p. 17
Les boucles do{...}while en C (2) En C, la saisie d un entier non nul : int entier; boucle: cin >> entier; if (entier==0) jmp(boucle); Architecture des ordinateurs p. 18
Les structures conditionnelles en C Les structures conditionnelles if et switch seront remplacées par l utilisation conjointe : de l instruction de comparaison des labels des sauts conditionnels Architecture des ordinateurs p. 19
Instruction de comparaison (1) cmp(<operande1>,<operande2>) Cette instruction compare les deux opérandes et met à jour une variable appelée flag. Cette variable est : négative si la première opérande est strictement inférieure à la seconde nulle si les deux opérandes sont égales positive si la première opérande est strictement supérieure à la seconde Nous l utiliserons uniquement pour comparer des entiers ou des caractères. Architecture des ordinateurs p. 20
Instruction de comparaison (2) Exemple : cmp(12,56); cout << "Le flag est "; if (flag<0) cout << "négatif."; else if (flag>0) cout << "positif."; else cout << "nul."; produit l affichage suivant : Le flag est négatif. Architecture des ordinateurs p. 21
Instruction de comparaison (3) Exemple : cmp(56,12); cout << "Le flag est "; if (flag<0) cout << "négatif."; else if (flag>0) cout << "positif."; else cout << "nul."; produit l affichage suivant : Le flag est positif. Architecture des ordinateurs p. 22
Instruction de comparaison (4) Exemple : cmp(12,12); cout << "Le flag est "; if (flag<0) cout << "négatif."; else if (flag>0) cout << "positif."; else cout << "nul."; produit l affichage suivant : Le flag est nul. Architecture des ordinateurs p. 23
Les sauts conditionnels (1) je(<label>) Cette instruction (Jump if Equal) effectue un saut vers le label si flag = 0 jne(<label>) Cette instruction (Jump if Not Equal) effectue un saut vers le label si flag 0. Architecture des ordinateurs p. 24
Les sauts conditionnels (2) jl(<label>) Cette instruction (Jump if Less) effectue un saut vers le label si flag < 0 jle(<label>) Cette instruction (Jump if Less or Equal) effectue un saut vers le label si flag 0. Architecture des ordinateurs p. 25
Les sauts conditionnels (3) jg(<label>) Cette instruction (Jump if Greater) effectue un saut vers le label si flag > 0 jge(<label>) Cette instruction (Jump if Greater or Equal) effectue un saut vers le label si flag 0. Architecture des ordinateurs p. 26
Les sauts conditionnels (4) Exemple : int x=12; cout << "Un "; cmp(x,12) jle(ici); cout << "petit "; ici: cout << "saut."; produit l affichage suivant : Un saut. Architecture des ordinateurs p. 27
Les sauts conditionnels (5) Exemple : int x=12; cout << "Un "; cmp(56,x) jl(ici); cout << "petit "; ici: cout << "saut."; produit l affichage suivant : Un petit saut. Architecture des ordinateurs p. 28
Les sauts conditionnels (6) Exemple : int x= Z,y= A ; cout << "Un "; cmp(x,y) jg(ici); cout << "petit "; ici: cout << "saut."; produit l affichage suivant : Un saut. Architecture des ordinateurs p. 29
La conditionnelle if-then-else en C (1) En C++, calculer le plus grand de deux entiers : int a=4,b=10,max; if (a>b) {max=a;} else {max=b;} Architecture des ordinateurs p. 30
La conditionnelle if-then-else en C (2) En C, calculer le plus grand de deux entiers : int a=4,b=10,max; cmp(a,b); jle(cas_else); cas_then: max=a; jmp(fin_if); cas_else: max=b; fin_if: (...) Architecture des ordinateurs p. 31
La conditionnelle switch en C (1) En C++, associer les codes 2,5,3 aux lettres a,b,c, sinon 666 : char lettre= b ; int code; switch (lettre) { case a : code=2; break; case b : code=5; break; case c : code=3; break; default: code=666;} Architecture des ordinateurs p. 32
La conditionnelle switch en C (2) En C, associer les codes 2,5,3 aux lettres a,b,c, sinon 666 : char lettre= b ; int code; cmp(lettre, a ); je(cas_a); cmp(lettre, b ); je(cas_b); cmp(lettre, c ); je(cas_c); jmp(default); Architecture des ordinateurs p. 33
La conditionnelle switch en C (3) cas_a: code=2; jmp(fin_cas); cas_b: code=5; jmp(fin_cas); cas_c: code=3; jmp(fin_cas); default: code=666; fin_cas: (...) Architecture des ordinateurs p. 34
Synthèse boucles+conditionnelles (1) Reprenons l exemple de la somme des entiers de 1 à 10. En C++ : int somme=0; for(int i=1;i<=10;i++) {somme=somme+i;} cout << somme; Architecture des ordinateurs p. 35
Synthèse boucles+conditionnelles (2) Ce qui nous donne en C : int somme=0; int i=1; boucle: cmp(i,10); jg(fin_boucle); somme=somme+i; i++; jmp(boucle); fin_boucle: cout <<somme; Architecture des ordinateurs p. 36
Synthèse boucles+conditionnelles (3) Reprenons l exemple du calcul de la plus grande puissance de 17 strictement inférieure à 5423. En C++ : int n=1; while (n*17 < 5423) { n=n*17; } Architecture des ordinateurs p. 37
Synthèse boucles+conditionnelles (4) Ce qui nous donne en C : int n=1; boucle: cmp(n*17,5423); jge(fin_boucle); n=n*17; jmp(boucle); fin_boucle: (...) Architecture des ordinateurs p. 38
Connecteurs logiques (1) Les connecteurs && (et logique), (ou logique) et! (négation) seront remplacés par l utilisation conjointe : de l instruction de comparaison des labels des sauts conditionnels Il est impératif de bien connaître le sens des connecteurs : cond1 && cond2 : vrai si cond1 et cond2 sont vrais cond1 cond2 : vrai si cond1 est vrai, ou cond2 est vrai, ou les deux sont vrais!cond : vrai si cond est faux Architecture des ordinateurs p. 39
Connecteurs logiques (2) En C++, tester si un entier est plus grand que deux autres entiers : if (x <= z && y <= z) cout << "z est le + grand"; else cout << "z n est pas le + grand"; Architecture des ordinateurs p. 40
Connecteurs logiques (3) En C, tester si un entier est le plus grand de trois entiers : cmp(x,z); jg(cas_else); cmp(y,z); jg(cas_else); cout << "z est le + grand"; jmp(fin_if); cas_else: cout << "z n est pas le + grand"; fin_if: Architecture des ordinateurs p. 41
Connecteurs logiques (4) En C++, tester si un entier n est pas le plus petit de trois entiers : if (x <= z y <= z) cout << "z n est pas le + petit"; else cout << "z est le + petit"; Architecture des ordinateurs p. 42
Connecteurs logiques (5) En C++, tester si un entier n est pas le plus petit de trois entiers : cmp(x,z); jle(cas_then); cmp(y,z); jg(cas_else); cas_then: cout << "z n est pas le + petit"; jmp(fin_if); cas_else: cout << "z est le + petit"; fin_if: Architecture des ordinateurs p. 43
Connecteurs logiques (6) Une autre possibilité : cmp(x,z); jle(cas_then); cmp(y,z); jle(cas_then); cout << "z est le + petit"; jmp(fin_if); cas_then: cout << "z n est pas le + petit"; fin_if: Architecture des ordinateurs p. 44
Les sous-programmes en C Les appels aux sous-programmes ne se feront plus directement. On utilisera l instruction suivante : call(<nom-sous-programmme>) Cette instruction exécute le sous-programme passé en paramètre. Elle place une information sur la pile correspondant en assembleur à l adresse de la prochaine instruction à exécuter après l exécution du sous-programme (en C : cette valeur est 666). Architecture des ordinateurs p. 45
La pile (1) Pile : partie de la mémoire de l ordinateur, permettant de sauvegarder temporairement des informations, passer des paramètres aux sous-programmes, créer des variables locales, sauvegarder le contexte lors de l appel aux sous-programmes,... Architecture des ordinateurs p. 46
La pile (2) push(<valeur>) Cette instruction dépose la valeur passée en argument au sommet de la pile. Cette valeur peut être une valeur immédiate (un entier ou un caractère), ou bien une variable dont le contenu sera placé sur la pile (variable entier, caractère, pointeur). Le pointeur esp indiquant le sommet de la pile est automatiquement décrémenté de 1. Architecture des ordinateurs p. 47
La pile (3) pop(<variable>) Cette instruction retire la valeur placée au sommet de la pile et la stocke dans la variable passée en paramètre. Le pointeur esp indiquant le sommet de la pile est automatiquement incrémenté de 1. Architecture des ordinateurs p. 48
La pile (4) adresse contenu pointeur de pile 2C56 2C57 2C58 2C59 2C5A 2C5B int x=523 ; char c= A ; push(x) ; Architecture des ordinateurs p. 49
La pile (5) adresse contenu pointeur de pile 2C56 2C57 2C58 2C59 2C5A 2C5B 523 esp int x=523 ; char c= A ; push(x) ; push(c) ; Architecture des ordinateurs p. 49
La pile (6) adresse contenu pointeur de pile 2C56 2C57 2C58 2C59 2C5A A esp 2C5B 523 esp+1 int x=523 ; char c= A ; push(x) ; push(c) ; push(1200) ; Architecture des ordinateurs p. 49
La pile (7) adresse contenu pointeur de pile 2C56 2C57 2C58 2C59 1200 esp 2C5A A esp+1 2C5B 523 esp+2 int x=523 ; char c= A ; push(x) ; push(c) ; push(1200) ; push( F ) ; Architecture des ordinateurs p. 49
La pile (8) adresse contenu pointeur de pile 2C56 2C57 2C58 F esp 2C59 1200 esp+1 2C5A A esp+2 2C5B 523 esp+3 int x=523 ; char c= A ; push(x) ; push(c) ; push(1200) ; push( F ) ; pop(c) ; /*c == F */ Architecture des ordinateurs p. 49
La pile (9) adresse contenu pointeur de pile 2C56 2C57 2C58 F 2C59 1200 esp 2C5A A esp+1 2C5B 523 esp+2 int x=523 ; char c= A ; push(x) ; push(c) ; push(1200) ; push( F ) ; pop(c) ; /*c == F */ pop(x) ; /*x = 1200*/ Architecture des ordinateurs p. 49
La pile (10) adresse contenu pointeur de pile 2C56 2C57 2C58 F 2C59 1200 2C5A A esp 2C5B 523 esp+1 int x=523 ; char c= A ; push(x) ; push(c) ; push(1200) ; push( F ) ; pop(c) ; /*c == F */ pop(x) ; /*x = 1200*/ Architecture des ordinateurs p. 49
Le passage des paramètres en C (1) Les passage des paramètres pour les sous-programmes va maintenant être effectué au moyen de la pile : Avant l appel : on empile les paramètres. Pendant l appel : on accède aux paramètres au moyen du pointeur de pile esp. Après l appel : on dépile les paramètres avec pop ou en incrémentant le pointeur de pile esp. Architecture des ordinateurs p. 50
Le passage des paramètres en C (2) Pendant un appel à call(sous-programme) : 666 esp Paramètre N esp+1... Paramètre 2 esp+n-1 Paramètre 1 esp+n Architecture des ordinateurs p. 51
Le passage des paramètres en C (3) void print_char(char c) { cout << c; } int main() { print_char( A ); } Architecture des ordinateurs p. 52
Le passage des paramètres en C (4) void print_char() { cout << (char) esp[1]; } int main() { push( A ); // on empile le param call(print_char); esp++; // on dépile le param } Architecture des ordinateurs p. 53
Le passage des paramètres en C (5) Pendant l appel à call(print_char) : 666 esp A esp+1 Architecture des ordinateurs p. 54
Le passage des paramètres en C (6) void soustraction(int a, int b) { cout << (a - b); } int main() { soustraction(123,44); } Architecture des ordinateurs p. 55
Le passage des paramètres en C (7) void soustraction() { cout << esp[2] - esp[1]; } int main() { push(123); // empile les params push(44); call(soustraction); esp+=2; // dépile les 2 params} Architecture des ordinateurs p. 56
Le passage des paramètres en C (8) Pendant l appel à call(soustraction) : 666 esp 44 esp+1 123 esp+2 Architecture des ordinateurs p. 57
Les variables locales en C (1) Les variables locales seront maintenant définies elles aussi au moyen de la pile, en décrémentant le pointeur de sommet de pile esp d autant de cases mémoire que nécessaire. En plus de esp dénotant le sommet de la pile, on utilisera un second pointeur pour retenir l emplacement de esp au moment de l appel à la procédure. Ce second pointeur est appelé ebp. Architecture des ordinateurs p. 58
Les variables locales en C (2) Pendant l appel à un sous-programme, avec N paramètres et M variables locales (en ayant fait ebp=esp avant de créer les var. locales) : Var loc M ebp-m esp... Var loc 1 ebp-1 esp+m-1 666 ebp esp+m Paramètre N ebp+1 esp+m +1... Paramètre 1 ebp+n esp+m +N Architecture des ordinateurs p. 59
Les variables locales en C (3) void moyenne(int a, int b, int c) { int somme=(a + b + c); cout << somme / 3; } int main() { moyenne(3,12,56);} Architecture des ordinateurs p. 60
Les variables locales en C (4) void moyenne() { ebp=esp; esp--; // crée une var locale ebp[-1]=ebp[1]+ebp[2]+ebp[3]; cout << ebp[-1] / 3; esp++; //dépile la var locale} int main() { push(3); push(12); push(56); call(moyenne); esp+=3;} Architecture des ordinateurs p. 61
Les variables locales en C (5) Pendant l appel à call(moyenne) et création d une variable locale : 71 ebp-1 esp 666 ebp esp+1 56 ebp+1 esp+2 12 ebp+2 esp+3 3 ebp+3 esp+4 Architecture des ordinateurs p. 62
Les variables locales en C (6) void moyenne() { ebp=esp; esp--; // crée une var locale ebp[-1]=ebp[1]+ebp[2]+ebp[3]; cout << ebp[-1]/3; esp++; //dépile la var locale} int main() { push(3); push(12); push(56); call(moyenne); esp+=3;} Architecture des ordinateurs p. 63
Les variables locales en C (7) Quelques recommandations : Toujours effectuer ebp=esp en entrant dans un sous programme utilisant des arguments. Toujours (sauf cas spéciaux) dépiler les arguments en sortie d un sous-programme, en effectuant ebp+=n, où N est le nombre de paramètres. Architecture des ordinateurs p. 64