Et maintenant : l’assembleur 80x86 Architecture des ordinateurs – p. 1 Les registres (1) Les registres sont des «variables» du processeur, dont le nom est fixé. Appartenant au CPU, leur utilisation est très rapide (beaucoup plus que la mémoire centrale). Sur les 80x86, on en distingue plusieurs types : Les registres généraux Les registres d’index Les registres de segment Les registres MMX et SSE ... Architecture des ordinateurs – p. 2 Les registres (2) Nous reverrons chacun des registres dans leur usage propre par la suite. La présentation « détaillée » suivante n’a pour but que de vous présenter les noms que vous allez rencontrer très rapidement en TP. Retenez cependant l’usage principal de chacun de ces registres. Architecture des ordinateurs – p. 3 Les registres (3) EAX (32 bits) AX (16 bits) AH (8 bits) AL (8 bits) Accumulateur EBX (32 bits) BX (16 bits) BH (8 bits) BL (8 bits) Base ECX (32 bits) CX (16 bits) CH (8 bits) CL (8 bits) Compteur EDX (32 bits) DX (16 bits) DH (8 bits) DL (8 bits) Données EDI (32 bits) Destination Index ESI (32 bits) Source Index ESP (32 bits) Pointeur de pile EBP (32 bits) Pointeur de base Architecture des ordinateurs – p. 4 Les registres (4) Si EAX contient (en hexa) 0xA512E6FF (32 bits = 4 octets = 1 double-mot (dword)), alors : AX contient 0xE6FF (16 bits = 2 octets = 1 mot (word)) AH contient 0xE6 (8 bits=1 octet (byte)) AL contient 0xFF (8 bits = 1 octets (byte)) Ce comportement est identique pour les registres EBX, ECX et EDX. Il s’agit d’un héritage des premiers processeurs Intel 80x86, qui fonctionnaient en 8 bits puis en 16 bits. Certains processeurs Intel ou AMD récents (Intel Itanium, AMD Opteron) fonctionnent en 64 bits, ainsi que les processeurs plus anciens Sun UltraSparc, DEC Alpha, . . . Architecture des ordinateurs – p. 5 Les registres (5) EAX : Ce registre est utilisé pour récupérer le résultat ou comme paramètre de sous-programmes. Il sert de paramètre aux fonctions mathématiques existantes. EBX : Ce registre sert de base pour l’adressage indexé (accès à la mémoire). Adressage que nous étudierons plus tard. Architecture des ordinateurs – p. 6 Les registres (6) ECX : Ce registre est un registre compteur. Il est utilisé pour gérer des boucles du type : Pour i allant de 10 à 1 Afficher i Nous verrons un peu plus tard comment gérer cette boucle en assembleur. EDX : Registre utilisé pour certaines multiplications / divisions. Architecture des ordinateurs – p. 7 Les registres (7) Le registre ESP est le registre de pile, son usage en tant que variable est donc strictement interdit. Le registre EBP sert comme EBX pour l’adressage indexé (pour accéder à la pile). EDI est le registre de destination, utilisé pour la gestion des chaînes. ESI est l’équivalent de EDI mais cette fois-ci c’est la source. Architecture des ordinateurs – p. 8 Un premier programme en assembleur Architecture des ordinateurs – p. 9 Une première version (1) .intel_syntax noprefix # choix de la syntaxe intel .data # Partie Données hello: long: .ascii .int "Hello, world\n" 13 .global main # une chaı̂ne de caractères # longueur de la chaı̂ne # Partie programme main: mov mov mov mov int eax,4 ebx,1 ecx,offset hello edx,long 0x80 # affichage de la chaı̂ne mov mov int eax,1 ebx,0 0x80 # on quitte le programme Architecture des ordinateurs – p. 10 Une première version (2) .intel_syntax noprefix On choisit la syntaxe Intel avec l’option «noprefix». Ceci permet d’écrire mov eax,4 (signifie eax=4) plutôt que movl $4,%eax. Architecture des ordinateurs – p. 11 Une première version (3) .data hello: long: .ascii .int "Hello, world\n" 13 Dans la partie .data, on peut définir des variables globales initialisées avec un nom symbolique. On définit ici une chaîne de caractères hello terminée par un caractère de saut de ligne et un entier long représentant la longueur de la chaîne. Architecture des ordinateurs – p. 12 Une première version (4) mov mov mov mov int eax,4 ebx,1 ecx,offset hello edx,long 0x80 On va appeler avec l’instruction int la fonction 4 de l’interruption 0x80 du kernel (noyau linux). Elle nécessite 4 paramètres : 4 dans eax : la fonction (affichage de chaîne) 1 dans ebx : fichier de sortie (ici stdout) adresse de la chaîne dans ecx longueur de la chaîne dans edx Architecture des ordinateurs – p. 13 Une première version (5) mov mov int eax,1 ebx,0 0x80 On appelle à nouveau l’instruction int pour la fonction 1 de l’interruption 0x80 du kernel. Elle nécessite 2 paramètres : 1 dans eax : la fonction (sortie de programme) 0 dans ebx : code de retour du programme Architecture des ordinateurs – p. 14 Une deuxième version (1) .intel_syntax noprefix # choix de la syntaxe intel .data # Partie Données hello: .ascii "Hello, world\n\0" .global main # une chaı̂ne de caractères # Partie programme main: push call add offset hello printf esp,4 # affichage de la chaı̂ne push call 0 exit # on quitte le programme # on dépile l’argument (4 octets) Architecture des ordinateurs – p. 15 Une deuxième version (2) On a remplacé les appels directs au noyau par des appels aux fonctions de la librairie C disponible sur tous les systèmes Linux (Unix en général). Les arguments sont passés par la pile. Avantage : on a accès à toutes les fonctions prédéfinies de cette librairie, et de manière générale à toutes les fonctionnalités des librairies installées sur le système ! Et c’est plus rapide à écrire. . . Inconvénient : on ne sait pas comment sont codées ces librairies, et on utilise la pile. Si on souhaite de la rapidité d’exécution, il est déconseillé de les utiliser (et ce n’est pas dans l’«esprit assembleur»). Architecture des ordinateurs – p. 16 Les déclarations de variables en assembleur Architecture des ordinateurs – p. 17 Format général La déclaration de constantes et de variables se fait au moyen de directives précisant le type des données. Les formats les plus courants sont : .equ <nom_constante>,<valeur> <label>: <directive> <v1>,<v2>, ...,<vn> Le label correspond à une adresse mémoire qui sera utilisée pour accéder aux données stockées en mémoire. Valeur immédiate : entier, caractère, constante Architecture des ordinateurs – p. 18 Les directives .byte : entiers sur un octet (8 bits) .word : entiers sur un mot (16 bits) .int : entiers sur un double-mot (32 bits) .ascii : chaînes de caractère .asciz : chaînes de caractère terminées par l’octet 0 .space <rep>,<val> : répète <rep> fois la valeur <val> codée sur 1 octet .fill <rep>,<taille>,<val> : répète <rep> fois la valeur <val> codée sur <taille> octets Architecture des ordinateurs – p. 19 Exemples (1) # une constante .equ N,10 # un octet (8 bits) n: .byte 12 # un tableau de 5 octets tab1: .byte 0,123,24,36,255 # un mot (16 bits) x: .word 23456 # un tableau de 3 mots tab2: .word 876,1336,65535 Architecture des ordinateurs – p. 20 Exemples (2) # une chaı̂ne de caractères ch1: .ascii "Hello World" # équivalent à : ch1: .ascii "Hello ","World" # une chaı̂ne se terminant par 0 ch2: .asciz "Bonjour monde\n" # équivalent à : ch3: .ascii "Bonjour monde\n\0" Architecture des ordinateurs – p. 21 Exemples (3) # un tableau de 15 octets initialisés # à 0 tab3: .space 15,0 # équivalent à : ch4: .fill 15,1,0 # un tableau de 120 caractères # initialisés à ’x’ tab3: .space 120,’x’ # un tableau de 200 entiers (double# -mots) initialisés à 100000 ch2: .fill 200,4,100000 Architecture des ordinateurs – p. 22 Les principales instructions en assembleur Architecture des ordinateurs – p. 23 Généralités La plupart des instructions assembleur (aussi appelées mnémoniques) ont 0, 1 ou 2 arguments (opérandes). Ces opérandes sont soit un label (pour les instructions de saut uniquement), soit un registre, soit un accès à la mémoire (suivant les différents modes d’adressage). En général pour une même instruction, il ne peut y avoir plus d’un accès à la mémoire. En général les opérandes doivent être de même taille (1, 2 ou 4 octets). Architecture des ordinateurs – p. 24 L’instruction mov (1) mov <destination>,<source> Cette instruction sert à faire des transferts d’information entre : un registre et une valeur immédiate la mémoire et une valeur immédiate deux registres un registre et la mémoire la mémoire et un registre Architecture des ordinateurs – p. 25 Accès à la mémoire : modes d’adressage (1) Format général : var[r1*f1+r2*f2+r3*f3+c] Où : var est une variable (son label de définition) r1,r2 et r3 sont des registres (eax, ebx, ecx, edx, edi, esi, ebp) f1,f2 et f3 sont des entiers représentant un facteur d’échelle (val. possibles : 0, 1, 2, 4, 8) c est une valeur immédiate (entier ou constante) Architecture des ordinateurs – p. 26 Accès à la mémoire : modes d’adressage (2) Lorsqu’on fait un accès à la mémoire, il peut être nécessaire de préfixer cet accès pour indiquer la taille des données considérées : byte ptr var[...] : on considère une donnée sur un octet (8 bits) word ptr var[...] : on considère une donnée sur deux octets (16 bits) dword ptr var[...] : on considère une donnée sur quatre octets (32 bits) Ce ne sera pas nécessaire lorsque le compilateur pourra déterminer tout seul cette taille (si un autre opérande est un registre par ex.). Architecture des ordinateurs – p. 27 Accès à la mémoire : modes d’adressage (3) Nous allons voir quelques exemples. Soit la déclaration de variables suivante : x: y: chaine: tab: .int .int .ascii .word 345 15 "Hello world" 12,65,43,98,2 Architecture des ordinateurs – p. 28 Accès à la mémoire : modes d’adressage (4) mov eax,12 Met dans le registre eax la valeur 12 (eax=12). mov ebx,x # ou bien (équivalent) mov ebx,x[0] Met dans le registre ebx la valeur contenue dans la variable x, càd 345. Architecture des ordinateurs – p. 29 Accès à la mémoire : modes d’adressage (5) mov eax,ebx Met dans le registre eax la valeur contenue dans ebx (eax=ebx). mov al,chaine[4] Met dans le registre 8 bits al la valeur contenue dans la cinquième case de la variable chaine (chaine de caractères=tableau de caractères=tableau d’octets), càd ’o’. Architecture des ordinateurs – p. 30 Accès à la mémoire : modes d’adressage (6) mov ebx,4 mov al,chaine[ebx] Met dans al la valeur contenue dans la cinquième case de la variable chaine, càd ’o’. mov ebx,2 mov al,chaine[ebx*4] Met dans al la valeur contenue dans la neuvième case de la variable chaine, càd ’r’. Architecture des ordinateurs – p. 31 Accès à la mémoire : modes d’adressage (7) mov eax,4 mov ebx,2 mov al,chaine[eax+ebx*2] Met dans al la valeur contenue dans la neuvième case de la variable chaine, càd ’r’. mov eax,1 mov al,chaine[eax+eax*2+eax*4+1] Met dans al la valeur contenue dans la neuvième case de la variable chaine, càd ’r’. Architecture des ordinateurs – p. 32 Accès à la mémoire : modes d’adressage (8) mov ax,tab[3*2] Met dans le registre 16 bits ax la valeur contenue dans la quatrième case de la variable tab (tableau d’entiers de 2 octets chacun), càd 98. mov ebx,2 mov ax,tab[ebx*2] Met dans le registre 16 bits ax la valeur contenue dans la troisième case de la variable tab, càd 43. Architecture des ordinateurs – p. 33 Accès à la mémoire : modes d’adressage (9) mov dword ptr y,234 Met dans la variable y la valeur 234. Il est nécessaire ici de préfixer y, pour indiquer que la donnée est sur 4 octets. mov ax,20 mov tab[4],ax Met dans la troisième case du tableau tab la valeur 20. Il n’est pas nécessaire de préfixer tab, car ax est un registre 16 bits. Architecture des ordinateurs – p. 34 Accès à la mémoire : modes d’adressage (10) mov al,’A’ mov ebx,3 mov chaine[ebx+1],al Met dans la cinquième case de la variable chaine le caractère ’A’. Il n’est pas nécessaire de préfixer chaine, car al est un registre 8 bits. # ceci est interdit !!!!! mov chaine[1],chaine[2] On est obligé d’utiliser des registres pour faire des transferts de mémoire avec mov. Architecture des ordinateurs – p. 35 Intel et les petits indiens. . . (1) . . . ou la difficile traduction de little endian, par opposition à big endian, middle endian. . . Il s’agit de la façon dont les données d’un type de base sont rangées dans la mémoire. Sur les processeurs Intel, les octets de poids faible sont rangés en premier dans la mémoire (dans les adresses les plus faibles). Architecture des ordinateurs – p. 36 Intel et les petits indiens. . . (2) Par exemple : # variable sur 16 bits var: .word 0 # mise à jour de var mov word ptr var,0x4142 L’octet de poids faible est 0x42 (code ascii de ’B’). Il est rangé dans var[0]. L’octet de poids fort est 0x41 (code ascii de ’A’). Il est rangé dans var[1]. La variable var contient donc la chaîne "BA". Architecture des ordinateurs – p. 37 Intel et les petits indiens. . . (3) # variable sur 16 bits var: .int 0 # mise à jour de var mov dword ptr var,0x41424344 # est donc équivalent à mov byte ptr var[0],0x44 mov byte ptr var[1],0x43 mov byte ptr var[2],0x42 mov byte ptr var[3],0x41 La variable var contient donc la chaîne "DCBA”. Architecture des ordinateurs – p. 38 Les instructions add et inc (1) add <destination>,<source> Cette instruction effectue l’addition des deux opérandes, et place le résultat dans destination. Source et destination peuvent être des registres ou des accès à la mémoire, source peut être aussi une valeur immédiate. inc <source> Cette instruction incrémente (ajoute 1) à source, qui est obligatoirement un registre. Architecture des ordinateurs – p. 39 Les instructions add et inc (2) Exemple : .data x: .int 25 main: mov eax,x mov ebx,10 add eax,ebx # eax contient 35 inc eax add x,eax # x contient 61 Architecture des ordinateurs – p. 40 Les instructions sub et dec (1) sub <destination>,<source> Cette instruction soustrait source de destination, et place le résultat dans destination. Source et destination peuvent être des registres ou des accès à la mémoire, source peut être aussi une valeur immédiate. dec <source> Cette instruction décrémente (enlève 1) à source, qui est obligatoirement un registre. Architecture des ordinateurs – p. 41 Les instructions sub et dec (2) Exemple : .data x: .int 25 main: mov eax,x mov ebx,10 sub eax,ebx # eax contient 15 dec eax sub x,eax # x contient 11 Architecture des ordinateurs – p. 42 L’instruction mul (1) mul <source> Cette instruction multiplie (avec source étant un registre ou une opérande memoire) : eax par source si source est sur 32 bits. Le résultat est dans edx+eax. ax par source si source est sur 16 bits. Le résultat est dans dx+ax. al par source si source est sur 8 bits. Le résultat est dans ah+al=ax. Architecture des ordinateurs – p. 43 L’instruction mul (2) Exemple : .data x: .int 25 main: mov eax,x mov ebx,10 mul ebx # eax contient 250 # edx contient 0 Architecture des ordinateurs – p. 44 L’instruction div (1) div <source> Cette instruction divise (avec source étant un registre ou une opérande memoire) : edx+eax par source si source est sur 32 bits. Le quotient est dans eax et le reste dans edx. ax+dx par source si source est sur 16 bits. Le quotient est dans ax et le reste dans dx. ax par source si source est sur 8 bits. Le quotient est dans al et le reste dans ah. Architecture des ordinateurs – p. 45 L’instruction div (2) Exemple : .data x: .int 25 main: mov eax,x mov edx,0 # essentiel ! mov ebx,10 div ebx # eax contient 2 (quotient) # edx contient 5 (reste) Architecture des ordinateurs – p. 46 L’instruction and and <destination>,<source> Cette instruction effectue un “et” logique entre destination et source, et place le résultat dans destination. Exemple : mov ah,0b11001101 mov al,0b01100100 and ah,al #ah contient 0b01000100 Architecture des ordinateurs – p. 47 L’instruction or or <destination>,<source> Cette instruction effectue un “ou” logique entre destination et source, et place le résultat dans destination. Exemple : mov ah,0b11001101 mov al,0b01100100 or ah,al #ah contient 0b11101101 Architecture des ordinateurs – p. 48 L’instruction xor xor <destination>,<source> Cette instruction effectue un “ou” exclusif entre destination et source, et place le résultat dans destination. Exemple : mov ah,0b11001101 mov al,0b01100100 xor ah,al #ah contient 0b10101001 Architecture des ordinateurs – p. 49 L’instruction not not <source> Cette instruction effectue un “non” logique sur destination et place le résultat dans destination. Exemple : mov ah,0b11001101 not ah #ah contient 0b00110010 Architecture des ordinateurs – p. 50 L’instruction push push <source> Cette instruction place une donnée contenue dans un registre ou une opérande mémoire sur la pile et décrémente esp : de 4 octets si source est un registre 32 bits (eax, ebx, . . . ) de 2 octets si source est un registre 16 bits (ax, bx, . . . ) On ne peut pas placer directement sur la pile un registre 8 bits. Architecture des ordinateurs – p. 51 L’instruction pusha pusha Cette instruction place les registres suivants sur la pile, dans l’ordre : eax, ecx, edx, ebx, esp (valeur avant le début de l’instruction), ebp, esi, edi. Après l’exécution de l’instruction, esp est donc décrémenté de 8 × 4 = 32 octets. Architecture des ordinateurs – p. 52 L’instruction pop pop <destination> Cette instruction récupère la donnée placée au sommet de la pile dans un registre et incrémente esp : de 4 octets si destination est un registre 32 bits (eax, ebx, . . . ) de 2 octets si destination est un registre 16 bits (ax, bx, . . . ) On ne peut pas retirer directement de la pile une donnée sur 8 bits. Architecture des ordinateurs – p. 53 L’instruction popa popa Cette instruction restaure le contenu des registres qui avaient été placés sur la pile par exemple par l’instruction pusha, dans l’ordre : esi, edi, ebp, esp, ebx, edx, ebx, eax. Après l’exécution de l’instruction, esp est donc incrémenté de 8 × 4 = 32 octets. Architecture des ordinateurs – p. 54 L’instruction cmp cmp <destination>,<source> Cette instruction compare les deux opérandes et place le résultat de la comparaison dans les bits CF et ZF du registre eflags (nous étudierons plus tard en détail ce registre particulier). Le résultat de cette comparaison sera en particulier utilisé par les instructions de saut conditionnel. Architecture des ordinateurs – p. 55 L’instruction jmp jmp <label> Cette instruction 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. Architecture des ordinateurs – p. 56 Les instructions jcond jcond <label> Ces instructions effectuent un saut conditionnel vers le label passé en argument, en fonction des valeurs CF et ZF du registre eflags. Les conditions possibles sont : je : saut si égalité jne : saut si différent jl : saut si inférieur strict jle : saut si inférieur ou égal jg : saut si supérieur strict jge : saut si supérieur ou égal Architecture des ordinateurs – p. 57 L’instruction loop (1) loop <label> Cette instruction décrémente le registre ecx et effectue un saut vers le label passé en argument, si la valeur contenue dans ecx est differente de 0. Cette instruction permet de simplifier l’écriture des boucles, en effectuant automatiquement la décrémentation, la comparaison, et le saut conditionnel. Architecture des ordinateurs – p. 58 L’instruction loop (2) Exemple : la somme des 10 premiers carrés. mov ebx,0 # somme mov ecx,10 # compteur mov edx,0 # pour la mult. boucle: mov eax,ecx mul ecx add ebx,eax loop boucle # ebx contient la somme # des 10 premiers carrés Architecture des ordinateurs – p. 59 Les sous-programmes Architecture des ordinateurs – p. 60 Deux possibilités On peut définir deux types de sous-programmes, les macros et les procédures : Les macros ne sont pas réellement des sous-programmes : ils permettent de simplifier l’écriture d’un programme et disparaissent après la compilation. Les procédures utilisent le mécanisme des procédures/fonctions des langages de haut niveau au moyen d’instructions du microprocesseur particulières. Architecture des ordinateurs – p. 61 Les macros (1) Le contenu d’une macro est recopié à l’endroit ou on l’appelle. La macro peut être placée n’importe où dans le code, mais doit précéder l’endroit où on l’appelle. Le code de la macro disparaît après la compilation. Architecture des ordinateurs – p. 62 Les macros (2) Une macro possède un nom, et accepte des arguments. Forme générale d’une macro : .macro nom_macro arg1,arg2,...,argn ......... Instructions ......... .endm Architecture des ordinateurs – p. 63 Les macros (3) Les arguments peuvent être ce que l’on veut : ce qui est passé en paramètre sera simplement recopié dans le code à l’endroit spécifié. Ainsi, on peut faire passer en paramètre un registre, une opérande mémoire, voire même une instruction assembleur ! Attention : dans le corps de la macro, les paramètres doivent être précédés du caractère “\”. Architecture des ordinateurs – p. 64 Les macros (4) Exemple : une macro qui effectue l’addition de trois valeurs dans le registre eax. .macro add3 val1,val2,val3 mov eax,\val1 add eax,\val2 add eax,\val3 .endm ......... add3 10,15,30 add3 eax,notes[10],45 Architecture des ordinateurs – p. 65 Les macros (5) Le code sera en réalité remplacé par : mov add add mov add add eax,10 eax,15 eax,30 eax,eax eax,notes[10] eax,45 Architecture des ordinateurs – p. 66 Les macros (6) Même chose, mais avec un argument supplémentaire indiquant dans quel registre doit être placé le résultat : .macro add3 reg,val1,val2,val3 mov \reg,\val1 add \reg,\val2 add \reg,\val3 .endm ......... add3 eax,10,15,30 add3 ebx,eax,notes[10],45 Architecture des ordinateurs – p. 67 Les macros (7) Le code sera en réalité remplacé par : mov add add mov add add eax,10 eax,15 eax,30 ebx,eax ebx,notes[10] ebx,45 Architecture des ordinateurs – p. 68 Les macros (8) Exemple : additionner le contenu de deux variables, placer le résultat dans une troisième. .macro addv var1,var2,var3 push eax mov eax,\var2 add eax,\var3 mov \var1,eax pop eax .endm Architecture des ordinateurs – p. 69 Les macros (9) .data x y res ......... addv .int .int .int 4 5 0 res,x,y est remplacé par : push mov add mov pop eax eax,x eax,y z,eax eax Architecture des ordinateurs – p. 70 Les procédures (1) Les trois composantes essentielles d’une procédure sont : Un label définissant le point d’entrée de la procédure, L’instruction ret permettant de quitter la procédure, L’instruction call permettant de faire un appel à la procédure. Architecture des ordinateurs – p. 71 Les procédures (2) Une procédure peut-être placée n’importe où dans le code. Mais attention : si elle est placée au milieu de lignes de code, par exemple dans la partie principale, rien n’empêche que l’on y entre. . . Conseil : placez-les soit au début du programme (avant le label main :), soit aprés les instructions de terminaison du programme (call exit). Architecture des ordinateurs – p. 72 Les procédures (3) Squelette d’un appel à une procédure : main: ......... call sous-prog ......... call exit sous_prog: ......... ret Architecture des ordinateurs – p. 73 Les procédures (4) Fonctionnement de l’instruction call : L’instruction call empile le contenu du registre eip, qui représente l’adresse de la prochaine instruction à exécuter. Elle effectue ensuite un saut inconditionnel vers le label d’entrée de la procédure. Architecture des ordinateurs – p. 74 Les procédures (5) Fonctionnement de l’instruction ret : L’instruction ret dépile la valeur qui est au sommet de la pile, qui doit normalement être la valeur sauvegardée de eip. Elle effectue un saut inconditionnel vers un label virtuel placé devant cette instruction. Architecture des ordinateurs – p. 75 Les procédures : passage des paramètres (1) Les deux méthodes principales de passage des arguments sont : Par la pile : semblable à ce qui a été vu en C–. C’est la façon classique de faire des languages de haut niveau. Par les registres : plus simple à mettre en oeuvre, mais plus difficile à utiliser. En effet, il faut prévoir quels registres utiliser pour les calculs et quels registres utiliser pour les paramètres. Architecture des ordinateurs – p. 76 Passage des paramètres par la pile (1) Le passage des paramètres par la pile s’effectue de la façon suivante : Avant l’appel : on empile les paramètres avec l’instruction push. Pendant l’appel : on accède aux paramètres au moyen des registres de pile esp et ebp. 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. 77 Passage des paramètres par la pile (2) Attention : Lorsqu’on incrémente le registre esp pour libérer la place occupée sur la pile par les arguments, on doit indiquer le nombre d’octets que l’on libère, et non pas le nombre d’arguments. Par exemple, si on avait empilé les registres eax, ebx et ecx, on incrémentera esp de 12 car ces trois registres font chacun 4 octets. Si on avait empilé les registres eax, bx et cx, on incrémentera la pile de 8 car eax fait 4 octets, bx en fait 2, cx en fait 2. Architecture des ordinateurs – p. 78 Passage des paramètres par la pile (3) L’accés aux arguments se fait au moyen des registres esp et ebp. On prendra soin d’écrire : sous_prog: push ebp mov ebp,esp ......... pop ebp ret afin que si on utilise la pile dans cette procédure, les arguments soient repérés par le même déplacement par rapport à ebp. Architecture des ordinateurs – p. 79 Passage des paramètres par la pile (4) A l’adresse ebp+4, se trouve le contenu du registre eip sauvegardé par call. Le premier paramètre p1 (le dernier a avoir été empilé) se trouve à l’adresse ebp+8. Le deuxième paramètre p2 se trouve à l’adresse ebp+12 si p1 est codé sur 4 octets, sinon à l’adresse ebp+10 si p1 est codé sur deux octets. Le troisième paramètre p3 se trouve à l’adresse ebp+16 si p1 et p2 sont codés sur 4 octets, sinon à l’adresse ebp+14 si p1 est codé sur deux octets et p2 sur 4 octets (ou inversement), sinon à l’adresse ebp+12 si p1 et p2 sont codés sur deux octets. ......... Architecture des ordinateurs – p. 80 Passage des paramètres par la pile (5) esp contient l’adresse mémoire du sommet de la pile. ebp contient l’adresse mémoire du sommet de la pile au moment où on est entré dans la procédure. Les registres esp et ebp contenant des adresses mémoire, pour accéder aux paramètres, on doit procéder ainsi : mov eax,[ebp+8] # charge le premier paramètre # dans eax Architecture des ordinateurs – p. 81 Passage des paramètres par registres (1) C’est le mode le plus simple car on n’utilise pas la pile. Les paramètres sont simplement stockés dans les registres avant l’appel du sous-programme. Ils sont récupérés par le sous-programme directement dans les registres et éventuellement modifiés en sortie si besoin. Inconvénients : nombre limité des regitres et problème des effets de bord. Architecture des ordinateurs – p. 82 Passage des paramètres par registres (2) Squelette d’un appel à une procédure : main: ......... mov eax,4 call sous_prog ......... sous_prog: # dans eax : valeur 4 ......... ret Architecture des ordinateurs – p. 83 Les variables locales (1) Comme en C–, on utilise la pile pour créer des variables locales. On décrémente esp d’autant d’octets que l’on a besoin, ou on utilise push pour initialiser les variables. On utilise ebp pour accéder aux variables sur la pile. Attention au déplacement, si on a sauvegardé des registres pour les restaurer en sortie du sous-programme. Architecture des ordinateurs – p. 84 Les variables locales (2) sous_prog: push ebp mov ebp,esp push eax # sauvegarde eax push ebx # sauvegarde ebx sub esp,8 # réserve 8 octets mov [ebp-12],21 # 1ère var locale mov [ebp-16],34 # 2ème var locale ......... add esp,8 # libère les vars locales pop ebx # récupère ebx pop eax # récupère eax pop ebp # récupère ebp ret Architecture des ordinateurs – p. 85 Les variables locales (3) 34 21 old ebx old eax old ebp old eip Paramètre 2 Paramètre 1 ←− ←− ←− ←− ←− ←− ←− ←− esp esp+4 esp+8 esp+12 esp+16 esp+20 esp+24 esp+28 ←− ←− ←− ←− ←− ←− ←− ←− ebp-16 ebp-12 ebp-8 ebp-4 ebp ebp+4 ebp+8 ebp+12 Architecture des ordinateurs – p. 86