You are on page 1of 116

http://www.jourlin.

com

page 1 sur 116

http://www.jourlin.com

Table des matires


Avant-Propos.........................................................................................5 1. Introduction.......................................................................................5 1.1. Petits rappels sur la structure des ordinateurs...................................6 1.3. Qu'est-ce qu'un programme en assembleur ?.................................10 2. Arithmtique entire........................................................................15 2.1. Un mot sur les registres gnraux et leur capacit.........................15 2.2. Entiers signs et non signs............................................................17 2.3. Dbordements.................................................................................21 3. Instructions de branchement ...........................................................24 3.1. Le registre RIP (Re-Extended Instruction Pointer)........................24 3.2. Branchement inconditionnel : instruction jmp (de jump : sauter) ................................................................................................................25 3.3. Branchements conditionnels ..........................................................26 4. Structure des donnes : pointeurs, tableaux, matrices, etc. .............28 4.1. Registres pointeurs ....................................................................28 4.2. Mode d'adressage indirect .........................................................29 4.3 Mode d'adressage indirect index ..............................................30 4.4. Indexations complexes....................................................................32 5. Comparaisons et autres branchements conditionnels.......................34 6. quivalents des structures algorithmiques avances........................39 7. Utilisation de la pile.........................................................................42 8. Procdures.......................................................................................44 8.1 Les instructions call et ret................................................................44 8.2 Les interruptions et les exceptions ..................................................48 9. Autres instructions d'arithmtique entire.......................................52 9.1 Multiplication et division sur des entiers non signs.......................52
9.1.1. Multiplication non signe.................................................................52 9.1.2. Division non signe .........................................................................53

9.2 Multiplication et division sur des entiers signs..............................54

10. Oprateurs logiques.......................................................................55


page 2 sur 116

9.2.1 Multiplication signe.........................................................................54 9.2.2 Division signe..................................................................................55

http://www.jourlin.com

11. Calculs en virgule flottante............................................................57 11.1 Introduction....................................................................................57 11.2. La norme IEEE 754......................................................................58 11.3 Les registres du processeur virgule flottante (x87).....................60 11.4 Principales instructions de calcul...................................................61 11.5 Comparaisons et branchements conditionnels...............................63 12. Paralllisme (MMX, SSE, 3DNow!).............................................64 12.1 Registres MMX..............................................................................65 12.2 Instructions MMX..........................................................................66 13. Bibliographie.................................................................................69 14. Exercices........................................................................................70 14.1 Capacit des cases mmoires (valide section 1.1.)........................70 14.2. Poids des chiffres, parties hautes et basses d'un nombre (valide section 1.1.1)..........................................................................................71 14.3 combien de cases mmoires distinctes peut-on accder avec des adresses hexadcimales allant de 0 FFFF ?........................................72 14.4 Oprandes (valide section 1.3)......................................................73 14.5 Registres gnraux (section 2.1)...................................................73 14.6 Arithmtique entire (sections 2.2 et 2.3)......................................75 14.7 La pile (section 7)..........................................................................78 15. Travaux dirigs..............................................................................80 15.1. Instructions de copie.....................................................................80 15.2. Instructions additives....................................................................81 15.3. Sauts conditionnels et inconditionnels (chapitre 3 du cours).......83 15.4. Adressage indirect (section 4.2 du cours).....................................86 15.5. Adressage indirect index ( 4.3)...................................................89 15.6. Indexations complexes (section 4.4 du cours)..............................92 15.7. Algorithme de tri Bulle (chapitres 5 et 6 comparaisons et structures)...............................................................................................94 15.8. Tri bulle procdural (sections 7 et 8)......................................98 15.9. Plus Petit Commun Multiple (Chapitre 8.2, 9 et 10)..................101 15.10. Calcul arithmtique flottant (chapitre 11).................................110 15.11. Produit scalaire via instructions SIMD.....................................111 16. Travaux pratiques : Programmer en Assembleur sous GNU/Linux... 114 16.1 Premiers pas.................................................................................114 16.2 Programmes corrigs....................................................................116
page 3 sur 116

http://www.jourlin.com

page 4 sur 116

http://www.jourlin.com

Avant-Propos
Ce cours est destin des tudiants de 2e anne de licence en informatique. Il est souhaitable pour le suivre dans de bonnes conditions d'avoir quelques prrequis en algbre, en structure des ordinateurs et ventuellement en programmation structure (langage C). Seuls de brefs rappels seront faits lorsque cela sera ncessaire. D'autre part, pour diverses raisons, ce cours prend pour support le jeu d'instructions des processeurs de la famille 80x86 et la syntaxe assembleur AT&T. Toutefois, l'objectif de ce cours est seulement de permettre l'tudiant d'acqurir les concepts fondamentaux de la programmation en assembleur. Le cours est donc loin d'tre exhaustif, mais aprs avoir suivi ce cours, l'apprentissage d'autres syntaxes, d'autres instructions et ventuellement d'autres jeux d'instructions devrait tre trivial.

1. Introduction
Tous les langages de programmation dits volus ( structurs , orients objet , etc.), qu'ils soient compils ou interprts doivent d'une manire ou d'une autre tre traduits en langage machine avant de pouvoir tre excuts par un ordinateur. Or, s'il est indiscutable que ces langages ont un grand nombre d'atouts pour le dveloppement de logiciels complexes, en termes de conception, de lisibilit, de portabilit et de maintenance, le revers de la mdaille est qu'ils masquent fortement sinon totalement, les limitations de la machine. Ne pas avoir conscience de ces limitations peut pourtant avoir des consquences fcheuses :
1

en terme de performance, dans le cas d'une sous-exploitation des capacits du processeur1.

voir, par exemple, l'apparition de processeurs 64 bits et des Single Instruction Multiple Data (dont font partie les jeux d'instructions MMX, SSE et 3D now!) dans les dernires versions des processeurs Intel et AMD. Les SIMD permettent de raliser jusqu' 16 oprations arithmtiques en parallle. Rares sont les compilateurs exploitent ces instructions, encore plus rares sont les programmes qui en tirent profit. La plupart des ordinateurs personnels prsents sur le march ont un
page 5 sur 116

http://www.jourlin.com

en terme de fiabilit, dans le cas d'une surestimation des capacits (programmes qui dysfonctionnent ou qui doivent tre entirement rcrits ds que le cadre d'utilisation s'largit).

Or, s'il est presque impossible d'crire un programme consquent directement en langage machine, il est tout fait possible de l'crire en assembleur , qui n'en est qu'une version lgrement humanise . Connatre les rudiments de la programmation en assembleur peut donc donner des atouts considrables pour la comprhension et la matrise de tous les autres langages de programmation.

1.1. Petits rappels sur la structure des ordinateurs


1.1.1. Contenu des cases mmoires
La mmoire d'un ordinateur est dcoupe en particules lmentaires appeles bits (pour binary units). Chacune de ces particules peut prendre seulement 2 valeurs possibles (la valeur est dite binaire). La valeur d'une particule lmentaire peut avoir une quantit indnombrable d'interprtations diffrentes : logique (ex : vrai ou faux), numrique (0 ou 1), couleur (ex. : blanc ou noir), gographique (ex. : gauche ou droite), etc. Dans la suite de ce cours, nous allons utiliser le plus souvent une interprtation numrique entire. Notons qu'une interprtation numrique entire peut tre relie n'importe quel type d'interprtation. Le nombre d'interprtations diffrentes d'un ensemble de bits est infini. Cependant, dans une interprtation donne, le nombre d'lments distincts que cet ensemble de bits peut reprsenter est lui dtermin. Par exemple, un ensemble non ordonn de n bits peut reprsenter seulement n+1 valeurs diffrentes. Par exemple si l'on dispose de seulement 2 bits, nous aurons seulement 3 valeurs possibles : 00, 10 et 11 (01 tant quivalent 10 en l'absence de notion d'ordre). En revanche, si nous considrons non plus des ensembles de bits, mais des suites de bits, alors la capacit de reprsentation est considrablement plus
processeur 64 bits, mais l'immense majorit est vendue avec un systme d'exploitation 32 bits !
page 6 sur 116

http://www.jourlin.com

importante. C'est ainsi qu'est structure la mmoire d'un ordinateur : des suites de cases mmoires, qui sont elles-mmes des suites de bits. Le nombre de bits qui composent une case mmoire dtermine sa capacit de reprsentation. Pour bien comprendre la relation entre nombre de bits et capacit de reprsentation, nous pouvons prendre comme exemple la formation des nombres entiers naturels en base dcimale : un chiffre dcimal peut reprsenter 10 valeurs diffrentes et une suite (c.--d. un nombre) de 2 chiffres dcimaux peut reprsenter 100 valeurs diffrentes. Lorsque nos particules lmentaires peuvent prendre les valeurs entires de 0 9, la capacit de reprsentation d'une suite de x particules se calcule simplement : a) 1 particule = 10 valeurs possibles (de 0 9) b) 2 particules = 100 valeurs possibles (de 0 99) c) x particules = 10x valeurs possibles (de 0 10x-1) Mais puisque nous sommes ici dans une base binaire, nous avons : a) 1 particule = 2 valeurs possibles (de 0 1) b) 2 particules = 4 valeurs possibles (nombres binaires 00, 01, 10 et 11) c) x particules = 2x valeurs possibles (de 0 2x-1) La mmoire tant une suite de bits, la capacit de reprsentation n'est dpendante que de la taille de la mmoire disponible : pour reprsenter un objet qui peut prendre X valeurs diffrentes, il nous faudra utiliser ncessairement un nombre Y de bits tel que X 2 Y. Pour reprsenter Z objets pouvant prendre chacun X valeurs diffrentes, il nous faudra utiliser ncessairement un nombre Y de bits tel que X 2 YZ, etc. Nous pouvons voir ici qu' moins de disposer d'une capacit mmoire infinie, il nous sera impossible de reprsenter compltement l'espace des nombres entiers, rationnels, rels, complexes, etc. En fait, quel que soit le langage de programmation utilis, le programmeur devra toujours reprsenter les ensembles infinis et/ou indnombrables du monde rel par des ensembles finis et dnombrables. Les erreurs de programmation issues d'un oubli de cet axiome sont trs frquentes. Elles peuvent ne se rvler que dans des situations exceptionnelles, mais leurs consquences peuvent tre trs importantes.
page 7 sur 116

http://www.jourlin.com

Pour des raisons de visibilit et pour faciliter la reprsentation des donnes, nous utiliserons assez souvent la base hexadcimale (base 16) pour reprsenter les valeurs. Dans cette base, les chiffres sont (par valeur croissante) : 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F. Le gros avantage de cette base par rapport la base dcimale est que chaque nombre binaire de 4 bits peut tre reprsent par un seul chiffre hexadcimal. La valeur d'un nombre peut tre calcule comme tant la somme du produit de chaque chiffre par une puissance de la base. Par exemple, la valeur du nombre de 3 chiffres C2C1C0 exprim en base b , sera C2 * b2 + C1 * b1 + C0 * b0. Chaque chiffre a donc un poids diffrent dans la somme. Par convention, on dira que les chiffres les plus gauche sont de poids fort et les chiffres les plus droite sont de poids faible. En parlant d'une partie d'un nombre, on parlera de partie haute pour la partie la plus gauche et de partie basse pour la partie la plus droite. Par exemple, dans la base hexadcimale, pour un nombre de valeur : F9E8D7C6, nous appellerons partie haute de 8 bits la valeur F9, partie haute de 16 bits la valeur F9E8, partie haute de 24 bits, la valeur F9E8D7, partie basse de 8 bits la valeur C6, partie basse de 16 bits la valeur D7C6 et partie basse de 24 bits la valeur E8D7C6. On utilisera ainsi les termes poids fort, poids faible, partie haute, partie basse pour dsigner les sous-parties du contenu d'une case mmoire. Les tailles de cases mmoires ou de partie de case mmoire les plus utilises sont : L'octet : c'est une case mmoire de 8 bits Le mot de 16 bits : une suite de 2 octets Le mot de 24 bits : une suite de 3 octets Le mot de 32 bits : une suite de 4 octets Le mot de 64 bits : une suite de 8 octets

1.1.2. Adresse des cases mmoires


Il faut maintenant pouvoir dsigner une particule lmentaire prcise parmi toutes celles qui sont disponibles. Une premire ide est de donner chacune une adresse unique. Bien entendu, cette adresse doit tre elle-mme reprsente sous la forme d'une suite de bits. Or, comme nous l'avons vu plus
page 8 sur 116

http://www.jourlin.com

haut, pour pouvoir donner X adresses diffrentes X bits diffrents, nous avons besoin d'un espace mmoire de Y bits, tels que X 2Y. Par exemple, pour pouvoir donner 4 adresses diffrentes 4 bits diffrents, il nous faudra 2 bits pour stocker les adresses. Plus gnralement, l'espace mmoire ncessaire pour stocker les adresses est inversement proportionnel la taille minimale des cases mmoires adressables. Dans la plupart des architectures de processeurs, cette taille minimale sera de 8 bits : chaque octet de la mmoire possde donc une adresse (physique) unique. On aura donc besoin de 1 octet (8 bits) pour pouvoir faire rfrence 256 (2 8) adresses diffrentes d'octets. Avec 2 octets (16 bits) on pourra accder 65536 (2 16) octets (soit 64 kilooctets), avec 4 octets (32 bits) on pourra accder 4 giga-octets (1 kilo-octet= 210 octets, 1 mega-octet = 210 kilo-octets et 1 giga-octet = 210 mega-octet). Les microprocesseurs peuvent manipuler directement des adresses sur 64 bits et peuvent donc grer directement un espace adressable de 2 64 octets, soit 16 exa-octet (16384 pta-octet, soit environ 17 milliards de giga-octets). 1.2. Qu'est-ce qu'un programme en langage machine ?

Un programme en langage machine est une suite d'instructions-machine. Une instruction-machine est une suite de bits qui contient toutes les informations ncessaires l'excution de l'instruction.

D'un point de vue logique, c'est--dire du point de vue du programmeur, le microprocesseur va excuter chaque instruction les unes aprs les autres. De fait, chaque instruction possde une adresse. Idalement, cette adresse pourrait tre simplement le rang de l'instruction dans la suite, c'est dire : 1re instruction du programme, 2e instruction du programme, etc. Or, les instructions sont stockes dans la mmoire de l'ordinateur. Le microprocesseur y accde par blocs de 2 3x2n bits, le plus souvent 8 bits (1 octet), 16 bits (un mot), 32 bits (un mot long) et plus rcemment 64 bits. Plus rarement, on manipulera des donnes de 24 bits (par exemple pour le traitement d'images)
page 9 sur 116

http://www.jourlin.com

ou des donnes de 80 bits (nombres flottants tendus). Les instructions sont de taille variable. En langage machine, l'adresse d'une instruction est donc l'adresse mmoire ou commence cette instruction. Voici par exemple, un petit programme en langage machine totalement factice, crit en binaire avec une mmoire dcoupe en octets :

Adresse 00000000 00000001 00000010 00000011 00000100

Contenu de la mmoire 01010101 11111111 00000000 11001100 00110011 dbut de la 3e instruction 1re instruction 2e instruction

Figure 1 : programme en langage machine (factice)

1.3. Qu'est-ce qu'un programme en assembleur ?


L'assembleur permet simplement de rendre le langage machine un peu plus lisible :

Chaque ligne d'assembleur contient une seule instruction, l'adresse d'une instruction est essentiellement constitue par son numro de ligne. Le programmeur peut donner des noms aux adresses importantes pour lui. Les instructions s'crivent sous la forme mnmonique suite_d'oprandes o la mnmonique est un mot qui rappelle le rle de
page 10 sur 116

http://www.jourlin.com

l'instruction. Par exemple, la mnmonique MOV indique une instruction de transfert de donnes (en anglais dplacer se dit move). Les oprandes eux aussi peuvent avoir des noms dfinis par le programmeur. Par exemple, l'instruction assembleur MOV ORIGINE, DESTINATION va copier la donne qui se trouve l'adresse ORIGINE dans le contenu de l'adresse DESTINATION. Un exemple de programme assembleur est donn en figure 2. La premire colonne contient les adresses symboliques (appeles aussi tiquettes, par exemple nb1). Le programme charg de l'assemblage (c'est--dire la translation assembleur vers langage machine) fera la transformation en adresses binaires.
Figure 2 : Programme en assembleur (syntaxe AT&T2)

La deuxime colonne contient les mnmoniques (ex.: movb) et les directives


.data nb1: .byte1 #(1)zonededonnes #(2)premieroctet: #valeur1enbasedcimale #(3)troisimeoctet: #valeur0enbasedcimale #(4)zoned'instructions #(5)copierlavaleurcontenuedansl'octet #d'adressenb1dansleregistreal nb1, %al #(6)ajouterlaconstanteentirede #valeur5enbasedcimaleauregistreal $5, %al #(7)copierlavaleurcontenuedansle #registreal(1octet)l'adresseres %al, res

res:.byte0 .text Addition: movb addb movb

Les 2 principales syntaxes de l'assembleur sont la syntaxe Intel et la syntaxe AT&T. Dans ce cours, nous utilisons la syntaxe AT&T. C'est celle qui est utilise par le compilateur-assembleur GCC/GAS de GNU (Free Software Foundation). Ceci nous
page 11 sur 116

http://www.jourlin.com

(ex: .data). Les directives sont des instructions qui s'adressent au programme d'assemblage et qui lui permettent de produire un code excutable compatible avec le systme d'exploitation cibl. Par exemple .data demande la cration d'une zone de donne, .byte demande la cration d'un ou plusieurs espaces mmoire de 1 octet qui contiendront des nombres entiers prinitialiss. Les colonnes suivantes contiennent les oprandes spars les uns des autres, le cas chant, par des virgules. Enfin, la dernire colonne contient gnralement les commentaires du programme; ils dcrivent instruction par instruction les oprations qui seront excutes par le microprocesseur. L'exemple en figure 2 nous permet d'introduire plusieurs notions :

1.3.1 Type d'un oprande


Nous voyons dans l'exemple ci-dessus, deux types d'oprandes :

nb1 et res sont des tiquettes. Elles dsignent de faon symbolique des adresses mmoire. $5 est une constante entire, en base dcimale. On pourra crire par exemple $0b010101 pour exprimer la valeur d'une constante en base binaire et $0x9ABC01 pour la base hexadcimale (base 16). al est un registre, pour distinguer un registre d'une tiquette, nous les faisons prcder par un signe %

Les registres sont des emplacements de mmoire internes au processeur. Les oprations arithmtiques ne peuvent tre ralises directement sur des emplacements en mmoire externe. Dans l'exemple prcdent, il aurait t plus simple d'crire une seule instruction addb nb1, 5, res ralisant la mme opration. Malheureusement, les microprocesseurs de la famille 80x86 ne permettent pas ce type d'oprations trois oprandes distinctes.

1.3.2. Taille d'un oprande ou d'une opration


La lettre b qui termine les mnmoniques mov et add indique la taille en bits de l'opration raliser.
permet de raliser les travaux pratiques aussi bien sur GNU/Linux que sur Windows.
page 12 sur 116

http://www.jourlin.com

b indique qu'il s'agit d'une opration sur 8 bits. Les oprandes indiquent donc des emplacements d'octets (en anglais : byte). w indique une opration sur 16 bits. Les emplacements contiennent donc des mots (en anglais : word). l indique une opration sur des cases mmoires de 32 bits, soit des mots longs (en anglais long words). On peut aussi utiliser le suffixe d pour double-word. q indique une opration sur des cases mmoires de 64 bits, soit des mots quadruples (en anglais quad-word).

1.3.3. Sens des oprations


Avec la syntaxe AT&T, lorsqu'il y a 2 oprandes, la premire reprsente la source et la deuxime reprsente la destination. Voyons maintenant l'volution de la mmoire interne et externe au fur et mesure que notre programme est excut ligne aprs ligne :

ligne/instruction nb1 1/.data 2/nb1:.byte1 3/res:.byte0 5/movbnb1,%al 6/addb$5,%al 7/movb%al,res ? 1 1 1 1 1

contenus %al ? ? ? 1 6 6 res ? ? 0 0 0 6

page 13 sur 116

http://www.jourlin.com

Le code assembleur que nous venons d'crire est donc susceptible d'tre produit par la compilation d'une instruction simple en langage C ou C++ : short int nb1=1, res=0; res=nb1+5; En assembleur, il n'y a pas de notion de variable , les notions d'adresse symbolique, de registre, de taille, etc. sont trs primitives compares aux notions de variables (types, structures, de porte dfinie, etc.) telles qu'on peut les trouver dans les langages volus.

page 14 sur 116

http://www.jourlin.com

2. Arithmtique entire
2.1. Un mot sur les registres gnraux et leur capacit
En ralit, le registre al, que nous avons vu rapidement en section 1.3, n'est que la partie basse (accumulator low en anglais) de 8 bits d'un registre de 16 bits nomm ax (accumulator extended). La partie haute de 8 bits de ce registre ax se nomme ah (pour accumulator high). La version 32 bits de ce registre se nomme eax (extended accumulateur extended, bits numrots de 0 31) et la version 64 bits du mme registre se nomme rax. Autrement dit, eax reprsente les 32 bits de poids faible de rax (bits 0 31), ax reprsente les 16 bits de poids faible de eax (bits 0 15), al reprsente ses 8 bits de poids faible (bits 0 7) et ah reprsente les bits de numrots 8 15 dans la figure suivante :

%rax %eax %ax %ah


bit 63..........................................bit 32 b31...............b26 b15...b8

%al
b7...b0

Figure 2 : Le registre rax (64 bits) Les processeurs rcents de la famille 80x86 (AMD et Intel) possdent 3 autres registres de 64 bits similaires rax : rbx, rcx et rdx. Ils sont eux aussi dcomposables en registres de 8, 16 et 32 bits : ebx, ecx, edx, bx, cx, dx, bh, ch, dh, bl, cl et dl. Il est important de bien comprendre la structure imbrique des registres et sous-registres : seul ah et al sont indpendants. Dans les autres cas, une modification d'un des registres a des rpercussions sur les autres registres. On peut lgitimement se demander pourquoi les registres ont une structure aussi complexe. En fait, ce choix technologique dcoule simplement de

page 15 sur 116

http://www.jourlin.com

ncessits conomiques : Les premiers processeurs de la famille 80x86 3 avaient un bus de donnes de 8 bits et un bus d'adresse de 16 bits. Mais chaque volution de la taille des bus de donnes et d'adresses, il fallait garantir que les logiciels crits pour la gnration de processeurs prcdente fonctionnent parfaitement avec les nouveaux processeurs. Sans cette garantie, il aurait t ncessaire de recompiler l'ensemble du parc logiciel. De ceci a driv une contrainte simple : chaque nouveau jeu d'instructions et de registres doit tre un sur-ensemble des anciens jeux d'instructions et de registres.

2.2. Entiers signs et non signs


Revenons sur les questions de capacit de reprsentation des donnes. Si l'on considre les entiers naturels, la valeur la plus basse est bien entendu 0 et la valeur maximale est donn par la formule 2n-1, n tant le nombre de bits utiliss pour le stockage. Autrement dit, un octet (8 bits) peut contenir des valeurs entires allant de 0 255, un mot (16 bits) des valeurs allant de 0 65.535, un mot long (32 bits) des valeurs allant de 0 4.294.967.295 et un mot quadruple (64 bits) des valeurs allant de 0 18.446.744.073.709.551.615. Qu'en est-il pour les entiers relatifs ? Il est vident que d'une faon ou d'une autre, nous devrons utiliser 1 bit pour stocker le signe du nombre. Nous pourrions naturellement utiliser 1 bit pour stocker le signe et tous les autres bits pour stocker la valeur absolue. Mais il est aussi vident que cela nous obligerait modifier l'algorithme que nous utilisons pour l'addition et la soustraction des entiers naturels. Par exemple, sur un octet, 0b1 0b11 devrait donner comme rsultat : 0b10000001 alors que l'algorithme de soustraction pour les entiers naturels nous donne : 0b11111111. Nous allons plutt rechercher un type de reprsentation qui modifie le moins possible cet algorithme additif. La premire constatation est que si l'on choisi le bit de poids fort comme bit de signe avec sa valeur 0 comme indication d'un nombre positif, hormis la question des capacits, l'addition et la soustraction fonctionneront comme avant.

Le 8086, prsent par Intel en1978 tait un processeur 16 bits. Le 80386, prsent par Intel en 1985 permit de passer 32 bits. Et il a fallu attendre les annes 2000 pour voir apparatre les premiers 64 bits de la famille x86 par AMD.
page 16 sur 116

http://www.jourlin.com

Par exemple, l'addition des deux entiers relatifs 64 et 63 (rsultat 127), s'crira en binaire :

0 1 0 0 0 0 0 0 + 0 0 1 1 1 1 1 1 = 0 1 1 1 1 1 1 1

En ce qui concerne les nombres ngatifs, regardons ce qu'il se passe si l'on reprsente les nombre sur 3 bits : La valeur positive maximale est 3 (0b011), en soustrayant successivement 1 cette valeur, nous obtenons : Binaire 011 010 001 000 111 110 101 100 Dcimal 3 2 1 0 -1 -2 -3 -4

En thorie, pour l'opration 0b0-0b1, notre algorithme naturel de


page 17 sur 116

http://www.jourlin.com

soustraction par propagation des retenues donne un nombre binaire compos d'une infinit de bits 1. Mais seuls les 3 bits de poids faible du rsultat peuvent tre stocks. Ce mode de reprsentation des nombres ngatifs se nomme le complment 2. Pour obtenir l'oppos d'un nombre, il suffit d'inverser valeur chacun des bits de ce nombre et de lui ajouter 1. Par exemple, l'oppos de 0b011 (3) sur 3 bits est 0b100+0b1 = 0b101 (-3). Notez que si on ajoute naturellement un nombre son oppos, on obtient bien la valeur 0 sur 3 bits. Lorsqu'il s'agit d'entiers signs, les valeurs seront donc comprises entre -2n-1 et 2n-1-1, n tant le nombre de bits utiliss pour le stockage. Notez que l'oppos de la valeur minimale (-2n-1) ne donne pas un rsultat valide (2n-1>2n-1-1). Par exemple, l'oppos de 0b100 (-4) est 0b100. Ici, on voit bien apparatre la diffrence entre les ensembles des entiers naturels (), des entiers relatifs (), des entiers non signs sur x bits (x) et entiers signs sur x bits (x). On a les relations suivantes :, x et x, mais il faut faire attention, car xx, par contre xx+1. Et bien entendu, toutes les oprations arithmtiques sont susceptibles d'amener nos valeurs dpasser les bornes minimales ou maximales de nos entiers...

2.3.Dbordements
Comment le programmeur peut-il savoir si le calcul a donn un rsultat valide ? En effet, si l'on ajoute 1 255 sur un octet, pour le microprocesseur, le rsultat (invalide) est 0. De mme, toujours sur un octet, -128 1 donne 127 (0b10000000 0b1 = 0b01111111). En plus des registres gnraux rax, rbx, rcx, rdx, le microprocesseur possde un registre spcial : le registre d'tat RFLAGS (FLAG signifie Drapeau en anglais). Chacun des 64 bits (drapeaux) de ce registre indique une caractristique binaire de l'tat du processeur. Mais seule une petite partie de ces bits concernent les oprations arithmtiques. Aprs excution d'une instruction arithmtique, le bit :

page 18 sur 116

http://www.jourlin.com

ZF (Zero Flag, bit n6 de RFLAGS) indique si le rsultat est nul (ZF=1) ou

non nul (ZF=0).


SF (Sign Flag, bit n7 de RFLAGS) indique si le rsultat est positif (SF=0)

ou ngatif (SF=1).

PF (Parity Flag, bit n2 de RFLAGS) indique que le rsultat est pair

(PF=1) ou impair (PF=0).


CF (Carry Flag, bit n0 de RFLAGS) indique une retenue (CF=1) sur les

entiers non signs.

OF (Overflow Flag, bit n11 de RFLAGS), indique un dbordement

(OF=1) sur les entiers signs. Lors d'une opration sur n bits, le drapeau CF contiendra le bit numro n (ou n-1me bit) du rsultat. Par exemple, sur 8 bits :

0b11111111 + 0b1 = 0b00000000 avec CF = 1 0b00000000 0b1 = 0b11111111 avec CF = 1 0b11111110 + 0b1 = 0b11111111 avec CF = 0

On voit donc aisment, que si pour le programmeur, les oprandes sont des entiers non signs, le rsultat est valide si et seulement si CF =0 et il est invalide si et seulement si CF = 1. Si pour le programmeur, les oprandes sont des entiers signs, c'est le drapeau OF qui indiquera la validit du rsultat. Par exemple, sur 8 bits (non-signs : 8, signs : 8) :
0b11111111 + 0b1 = 0b00000000 avec CF=1 et OF=0 ,

(255+1) est invalide dans 8, mais (-1+1) est valide dans 8.


0b00000000 0b1 = 0b11111111 avec CF=1, mais OF=0,

(0-1) est invalide dans 8, mais (0-1) est valide dans 8.

page 19 sur 116

http://www.jourlin.com

0b10000000 + 0b1 = 0b10000001 avec CF=0 et OF=0,

(128 + 1) est valide dans 8 et (-128 + 1) est valide dans 8.


0b10000000 0b1 = 0b01111111 avec CF=0, mais OF =1,

(1281) est valide dans 8, mais (-1281) est invalide dans 8.


0b01111111 + 0b1 = 0b10000000 avec CF=0, mais OF =1,

(127+1) est valide dans 8, mais (127+1) est invalide dans 8.

page 20 sur 116

http://www.jourlin.com

3.Instructionsdebranchement
Nous sommes maintenant en mesure de programmer des formules arithmtiques simples. Laissons pour le moment les instructions de calcul et la reprsentation des nombres pour nous pencher sur ce qui va nous permettre d'aborder des algorithmes un peu plus complexes : les instructions de branchement.

3.1. Le registre RIP (Re-Extended Instruction Pointer)


Nous avons vu que les instructions de taille variable taient excutes par le processeur les unes aprs les autres. Pour cela, le processeur dispose d'un registre spcial, nomm RIP d'une capacit de 64 bits (nomm EIP pour sa partie basse de 32 bits et IP pour sa partie basse de 16 bits 4) qui contient l'adresse de l'instruction courante excuter. La valeur contenue dans ce registre est automatiquement augmente lors de l'excution d'une instruction afin que le registre pointe sur l'instruction suivante (c'est--dire la ligne suivante dans un programme assembleur crit proprement). Nous allons maintenant tudier des instructions qui modifient directement le contenu du registre RIP.

3.2.Branchementinconditionnel:instruction jmp(dejump:sauter)
L'instruction jmp <adresse> permet de remplacer le contenu de RIP par une adresse symbolique ou une constante. Par consquent, l'instruction excute aprs l'instruction jmp n'est plus celle qui se trouve sur la ligne suivante, mais celle situe l'adresse donne en oprande de la mnmonique jmp. Par exemple, dans le programme ci-dessous, l'instruction addl $2, %eax ne sera jamais excute et la fin de l'excution eax et ebx contiendront la valeur 2 et non la valeur 4. debut:
4

movl $2, %eax

# 2 -> eax

Les anciens processeurs de la famille 80x86 avaient un bus d'adresse de 16 bits. La compatibilit ascendante a t respecte, mais le registre IP est n'est utilisable que dans des conditions trs particulires que nous n'aborderons pas dans ce cours
page 21 sur 116

http://www.jourlin.com

jmp suite addl $2, %eax suite: movl %eax, %ebx

# # # # #

on saute l' instruction suivante cette instruction n'est pas excute eax -> %ebx

Le saut peut avoir avoir lieu en avant ou en arrire ainsi la ligne suivante est ce qu'on appelle une boucle infinie : debut: jmp debut # mets l'adresse debut # dans le registre RIP

3.3. Branchements conditionnels


Les instructions de saut conditionnel testent un ou plusieurs drapeaux du registre d'tat et en fonction de leur valeur, effectuent le branchement ou passent l'instruction suivante. La syntaxe de ces instructions est la mme que pour la mnmonique jmp : il y a un seul oprande ; c'est l'adresse ou a lieu de branchement si la condition du branchement est remplie. Par exemple avec les drapeaux ZF, CF et OF que nous avons vu, nous pouvons utiliser les instructions suivantes :

jz : jump if zero , c'est dire saut si rsultat nul. Le branchement est effectu si ZF =1. jnz : jump not zero , c'est--dire saut si rsultat non nul . Le branchement est effectu si ZF =0. jc : jump if carry , c'est--dire saut si retenue . Le branchement est effectu si CF =1. jnc : jump if not carry , c'est--dire saut si pas de retenue . Le branchement est effectu si CF =0. jo : jump if overflow , c'est--dire saut si dbordement . Le branchement est effectu si OF =1. jno : jump if not overflow , c'est--dire saut si pas de dbordement . Le branchement est effectu si OF =0.
page 22 sur 116

http://www.jourlin.com

Il est ais de voir que ces 4 dernires instructions seront trs utiles pour diriger le programme vers un traitement particulier lorsqu'un calcul rend un rsultat invalide dans les entiers naturels ou relatifs. Par exemple : Init: Boucle: movw $0, %cx addw $1, %cx jnc Boucle # # # # 0 -> cx cx+1 -> cx boucle tant que cx+1 est valide (de 1 65535)

Ce programme est une boucle qui va incrmenter le registre cx de 1 chaque itration. On sortira de la boucle lorsque CF sera gal 1, c'est dire lorsque l'incrmentation de cx provoquera une retenue d'entier non sign. Notons que toutes les structures de contrle des langages volus (if-then-else, while, for, repeat-until, do, case, etc.) peuvent tre ralises avec les seules instructions que vous avez vues jusqu' maintenant. Par exemple, l'instruction Pascal if a>b then c:=a else c:=b; , a, b et c tant des variables globales entires non signes 64 bits, pourrait s'crire : maximum: movq var_a, %rax movq var_b, %rbx subq %rax, %rbx jc amax movq var_b, %rax movq %rax, var_c # # # # # # # # a -> rax b -> rbx rbx-rax -> rbx CF=1 => b-a<0 => a>b CF=0 => b-a>=0 => a <= b, on copie b dans rax max(a,b) -> c

bmax: amax:

4. Structure des donnes : pointeurs, tableaux, matrices, etc.


4.1. Registres pointeurs
Toutes les structures de donnes avances reposent sur le concept de pointeur. Un pointeur est une case mmoire qui contient l'adresse d'une autre case mmoire. Comme on peut modifier le contenu du pointeur, on pourra accder
page 23 sur 116

http://www.jourlin.com

diffrentes zones de la mmoire partir d'une position initiale. Toutes les structures de donnes complexes reposent sur ce principe : tableaux, structures, matrices, etc. Le seul registre pointeur que nous avons vu jusqu' maintenant est rip. Il a un rle trs particulier et il n'est pas facilement utilisable pour d'autres tches que celle de pointer sur l'instruction courante. Les registres gnraux rax, rbx, rcx et rdx peuvent servir comme pointeurs. Deux autres registres rsi (reextended source index) et rdi (re-extended destination index) sont souvent utiliss respectivement comme pointeur indiquant la source et comme pointeur indiquant la destination dans des oprations de copie de zones mmoires. Enfin, nous verrons plus loin le pointeur rsp (re-extended stack pointer) et rbp (re-extended base pointer) qui seront utiliss pour la gestion de la pile.

4.2.Moded'adressageindirect
Nous avons vu : 1. 2. 3. L'adressage immdiat (constante), par exemple : $12, $0b010, $0x1ABC, etc. L'adressage registre, par exemple : %rax, %rcx, etc. L'adressage direct, par exemple : caseB, var_a, etc.

Pour manipuler des structures de donnes plus complexes, nous aurons besoin au minimum du mode d'adressage indirect, par exemple :

movq $Tableau, %rsi movw (%rsi), %ax addw $1, %ax movw %ax, (%rsi) addl $2, %rsi

# # # # # # # # #

fait pointer %rsi sur le 1er lment du tableau Copie l'lment courant du tableau dans ax Ajoute 1 ax Remplace l'lment courant du tableau par ax Ajoute 2 rsi (passe l'lment suivant)

page 24 sur 116

http://www.jourlin.com

Dans cet exemple, nous avons un tableau qui contient des valeurs de 16 bits (2 octets). Cet extrait de programme ajoute la valeur 1 au premier lment et fait pointer rsi sur l'lment suivant. On comprend bien ici le rle des parenthses qui entourent le nom du registre : %rsi dsigne le contenu du registre rsi alors que (%rsi) dsigne le contenu de la case mmoire dont l'adresse est contenue dans rsi. Ce concept est fondamental pour la comprhension du fonctionnement des structures de donnes dans tous les langages de programmation. Nous pouvons maintenant crire presque tout algorithme avec les seuls concepts, registres et instructions que nous avons vu jusqu' prsent. Autrement dit, on peut concevoir un compilateur pour n'importe quel langage volu qui produirait du code compos uniquement des instructions, registres et mode d'adressages expliqus ci-dessus. Nous aurions mme pu nous passer de certaines instructions ou certains registres. Ce qui va suivre peut malgr tout permettre d'crire des excutables considrablement plus compacts et efficaces.

4.3 Mode d'adressage indirect index


Il est possible en utilisant ce mode d'adressage, d'effectuer un dplacement relatif par rapport au pointeur. En reprenant l'exemple de la section 4.2, on pourrait copier directement le 2e lment du tableau dans le registre bx avec l'instruction :

movw2(%rsi),%bx
Avec ce mode d'adressage, le processeur rcupre l'adresse contenue dans rsi, ajoute cette adresse la constante qui prcde la parenthse ouvrante et copie le contenu de l'adresse ainsi obtenue dans le registre bx. Il est important de noter que :
Le contenu de rsi reste inchang. si %rsi contient la valeur

$0x0102030405060708 avant l'excution de movw 2(%rsi), %bx , il contient toujours $0x0102030405060708 aprs l'excution de cette mme instruction et non $0x010203040506070A bien que ce soit les 16 bits trouvs cet emplacement qui sont copis dans %bx.

Le dplacement relatif peut tre positif ou ngatif, mais il ne peut tre


page 25 sur 116

http://www.jourlin.com

qu'une constante. Par exemple, si rsi contient l'adresse du dernier lment du tableau, alors -2(%rsi) dsignera l'avant-dernier lment de 16 bits du tableau. Par contre, le mode d'adressage %bx(%rsi) n'est pas autoris puisque %bx n'est pas une constante. Nous imaginons facilement comment ce mode d'adressage va se rvler utile pour traiter des donnes du type record (en pascal) ou structure (en C) :

movq $fiche, %rsi movl (%rsi), %eax movb 4(%rsi), %bl movw 5(%rsi), %cx

# rsi pointe sur une fiche # copie le numro de # tlphone (4 octets) dans eax # copie l'ge de la personne #(1 octet) dans bl # copie l'anne de naissance

rsi $fiche

fiche

fiche+1

fiche+2

fiche+3

0d490843500 (%rsi) 1(%rsi) 2(%rsi) 3(%rsi)

page 26 sur 116

http://www.jourlin.com

fiche+4 0d24 4(%rsi) ge

fiche+5 0d1980 5(%rsi) an.naiss.

fiche+6

fiche+7

6(%rsi) an.naiss.

7(%rsi)

4.4. Indexations complexes


Dans le mme esprit, on pourra crire -5(%rsi, %rax) pour dsigner le contenu de la case mmoire pointe par rsi+rax-5. Comme nous avons vu ci-dessus, le calcul est ralis par le processeur : rsi et rax restent inchangs lors d'une telle opration. Il faut noter que les deux registres doivent tre de mme taille. Ici %rsi est appel base et %rax est appel index . La taille des deux registres dpend donc du type de processeur (16, 32 ou 64) et du type de systme d'exploitation : un systme GNU/Linux ou Windows 32 bits ne permettra pas d'utiliser les caractristiques 64 bits d'un processeur 64 bits. Il existe un mode d'adressage encore plus puissant : 5(%rdi, %rbx, 8) dsigne ainsi le contenu de la mmoire dont l'adresse est calcule par : rdi+(rbx*8)+5. Reprenons l'exemple de la section 4.3. et supposons que rsi pointe sur la premire fiche d'un tableau de fiche et que rbx contient le numro de la fiche courante : movb 4(%rsi, %rbx, 8), %al # Fiche[rbx].age -> %al

Cette instruction aura pour effet de copier dans al, la valeur pointe par rsi+ (rbx*8)+4, c'est--dire l'adresse du champ ge de la fiche numro %rbx. Ici, la valeur 8 est appele scale factor . Ce champ peut prendre uniquement les valeurs 1, 2, 4 ou 8 qui correspondent aux tailles octets, mot de 16 bits, mot de 32 bits et mot de 64 bits.

5. Comparaisons et autres branchements conditionnels


Nous avons vu ci-dessus comment raliser une comparaison arithmtique en
page 27 sur 116

http://www.jourlin.com

utilisant la soustraction. Pour des raisons de lisibilit et d'efficacit, il est en fait prfrable d'utiliser une instruction prvue spcialement pour a : la mnmonique cmp. cmp oprande1, oprande2 est une sorte de soustraction virtuelle. Plus prcisment, les contenus des deux oprandes restent inchangs, mais le registre RFLAGS est modifi exactement comme lors de la soustraction oprande2-oprande1. Les drapeaux OF, CF, ZF, SF et PF donnent les caractristiques du rsultat. Aprs une instruction de comparaison, nous pouvons bien sr faire un branchement conditionnel en fonction des valeurs de RFLAGS. Pour plus de lisibilit, chaque mnmonique de branchement conditionnel possde plusieurs synonymes. Par exemple :

Saut si CF = 1 : jc (jump5 if carry6), jb (jump if below7), jnae (jump if not above8 or equal9) Saut si CF = 0 : jnc (jump if no carry), jae (jump if above or equal), jnb (jump if not below) Saut si ZF = 1 : jz (jump if zero), je (jump if equal) Saut si ZF = 0 : jnz (jump if not zero), jne (jump if not equal) Saut si CF=0 et ZF=0 : ja ( jump if above), jnbe (jump if not below or equal). Saut si CF=1 ou ZF=1: jbe (jump if below or equal), jna (jump if not above)

Bien entendu, jb, jnae, jae, jnb, ja, jnbe, jbe et jna n'ont de sens que si l'instruction prcdente est une comparaison d'entiers non signs (naturels). Si l'on veut comparer des entiers relatifs (signs), on utilisera :
5 6 7 8 9 en franais : saute en franais : retenue en franais : en dessous en franais : au-dessus en franais : gal
page 28 sur 116

http://www.jourlin.com

Saut si ((SF oux10 OF) ou ZF) = 0 : jg (jump if greater11), jnle (jump if not less12 or equal) Saut si ((SF oux OF) ou ZF) =1 : jle (jump if less or equal), jng (jump if not greater) Saut si (SF oux OF) =0 : jge (jump if greater or equal), jnl (jump if not less) Saut si (SF oux OF) = 1 : jl (jump if less), jnge (jump if not greater or equal) Saut si OF = 0 : jno(jump if not overflow) Saut si OF = 1 : jo(jump if overflow) Saut si SF = 0 : js (jump if sign : saut si rsultat ngatif) Saut si SF = 0 : jns (jump if not sign : saut si rsultat positif).

Enfin, 4 instructions de saut conditionnel un peu particulires :


Saut si PF =0 : jnp, jpo (not parity ou parity odd : le rsultat est impair) Saut si PF =1 : jp, jpe (parity ou parity even : le rsultat est pair).

Et comme le registre rcx est souvent utilis comme compteur de boucle :


Saut si %cx = 0 : jcxz (le registre cx contient 0) Saut si %ecx =0 : jecxz (le registre ecx contient 0) Saut si %ecx =0 : jrcxz (le registre rcx contient 0)

Par exemple, l'instruction Pascal if a>b then c:=a else c:=b; , a, b et c tant
10 ou exclusif : (SF oux OF) = 1 lorsque soit SF=1 soit OF=1, mais est gal 0 si SF=1 et OF=1 11 en franais : plus grand 12 en franais : plus petit
page 29 sur 116

http://www.jourlin.com

des variables globales entires non signes 32 bits, peut maintenant s'crire : maximum: movl var_b, %eax # var_b -> eax cmpl var_a, %eax # compare var_a eax # (en fait : eax moins var_a) jae bmax # jump if above or # equal (vers bmax si b>=a) movl var_a, %eax # a > b, on copie a dans eax movl %eax, var_c # max(a,b) -> c

amax: bmax:

page 30 sur 116

http://www.jourlin.com

Tableau rcapitulatif des sauts conditionnels pour les entiers signs et non signs :

Condition aprs : cmp a, b a=b ab

Mnmoniqu e JE/JZ JNE/JNZ

Condition de branchement

ZF=1 ZF=0

Tableau rcapitulatif pour les entiers non signs :


Condition aprs : cmp a, b a>b ab a<b ab Mnmoniqu e JA/JNBE JAE/JNB/JN C JB/JNAE/JC JBE/JNA Condition de branchement

CF=0 et ZF=0 CF=0 CF=1 CF=1 ou ZF=1

page 31 sur 116

http://www.jourlin.com

Tableau rcapitulatif pour les entiers signs :


Condition aprs : cmp a, b a>b ab a<b ab a-b > min_Z13 a-b<min_Z a-b<0 a-b>0 Mnmonique Condition de branchement

JG/JNLE JGE/JNL JL/JNGE JLE/JNG JNO JO JNS JS

((SF oux OF) ou ZF) = 0 (SF oux OF) = 0 (SF oux OF) = 1 ((SF oux OF) ou ZF) = 1 OF = 0 OF =1 SF = 0 SF =1

13 Pour rappel, min_z = -2n-1 , n tant le nombre de bits utiliss lors de la comparaison prcdente.
page 32 sur 116

http://www.jourlin.com

6. quivalents des structures algorithmiques avances


Il n'y a pas, en assembleur de structures de boucles ou de choix multiples comme on peut les trouver dans les langages structurs de type C ou Pascal. Nous pouvons les raliser, par exemple de la faon suivante (quivalent assembleur de structures ralises en C) :

If(var_a

>

var_b)

{<instructions-alors>}

else

{<instructions-sinon>} Si: # b>=a Alors: Sinon: FinIf: cmp a, b jae Sinon --> saut vers sinon <instructions-alors> jmp FinIf <instructions-sinon>

While(var_a > var_b) {<instructions>} TantQue: # b>=a cmp a, b jae FinTantQue ---> saut vers FinTantQue <instructions> jmp TantQue
page 33 sur 116

http://www.jourlin.com

FinTantQue:

Do {<instructions>} While(a>b) Faire: <instructions> cmp a, b jb Faire

# b<a --> boucle

FinFaire:

for(i=0; i<=10; i++) {<instructions>} PourInit: PourTest: movq $0, %rax cmpq $10, %rax ja FinPour # i>10 <instructions> addq $1, %rax jmp PourTest

FinPour:

page 34 sur 116

http://www.jourlin.com

switch (a) { case 'a': <instructionsA> break; case 'a': <instructionsB> break; default: <instructionsC> } CasA: cmpb $'a', %al jne CasB <instructionsA> jmp FinCas: cmpb $'b', %al jne Defaut <instructionsB> jmp FinCas: <instructionsC>

CasB:

Defaut: FinCas:

page 35 sur 116

http://www.jourlin.com

7. Utilisation de la pile
Le CPU possde un registre spcial nomm %rsp14 (pour re-extended stack pointer, soit pointeur de pile tendu). La pile est un tableau de cases mmoires contigus. %rsp pointe en permanence sur le sommet de la pile, c'est--dire le dernier lment empil. L'empilement d'une valeur (64 bits) se fait par l'instruction pushq et le dpilement par l'instruction popq. En fait, push oprande commence par dcrmenter %rsp et remplace le contenu de la case pointe par %rsp par oprande . pop se contente d'incrmenter %rsp. L'incrmentation et la dcrmentation se font par groupe de 8 octets, soit 64 bits. L'utilisation de la pile va se rvler trs utile, par exemple pour simuler l'utilisation de variables locales ou de paramtres de procdures. D'autre part, nous verrons qu'il est possible d'accder directement des lments de la pile sans passer par les instructions push et pop. Si le processeur fonctionne en mode 32 bits (systmes d'exploitation 32 bits), la pile est pointe par %esp, et les valeurs sont empiles (pushl) ou dpiles (popl) par groupes de 4 octets. Si le processeur fonctionne en mode 16 bits (systmes d'exploitation 16 bits), la pile est pointe par %sp, et les valeurs sont empiles (pushw) ou dpiles (popw) par groupes de 2 octets.

8.Procdures
8.1 Les instructions call et ret
Les instructions call et ret permettent respectivement d'appeler et de sortir d'une procdure. L'oprande qui suit call est l'adresse symbolique de la procdure. Traditionnellement, les paramtres de la procdure sont passs par la pile. call empile tout d'abord l'adresse de la prochaine instruction (le contenu de %rip) et remplace le contenu de rip par l'adresse donne en oprande. ret dpile et place la valeur dpile dans rip, ce qui provoque (sauf erreur de programmation) le retour l'endroit o la procdure t appele (instruction qui suit le call adresse ). Comme la pile n'est pas uniquement utilise pour sauvegarder l'adresse de retour, mais aussi pour stocker les paramtres et les
14 Il existe une version 16 bits de ce registre (sp), mais nous ne pouvons l'utiliser que dans un mode particulier du processeur que nous ne verrons pas ici.
page 36 sur 116

http://www.jourlin.com

variables locales de la procdure, il est important de s'assurer que le haut de la pile contient bien l'adresse de retour avant l'excution de ret. ret peut avoir une oprande n de type constante entire . Dans ce cas, ret dpilera l'adresse de retour, puis n octets avant de retourner la procdure appelante. Le registre rbp (re-extended base pointer) est souvent utilis par le programmeur pour accder aux diffrentes zones de la pile, il pointe gnralement sur l'adresse de retour de la procdure. Voici un exemple de procdure qui prend 2 entiers non signs comme paramtres (par la pile) et renvoie le maximum des 2 valeurs :
appel: # Empilage des paramtres de la procdure Maximum push UnsignedQuadWord_a push UnsignedQuadWord_b call Maximum # appel procdure Maximum popq %rax # rsultat dans rax # <<suite de la procdure appelante >> retq # retour la procdure appelante Maximum: movq %rsp, %rbp # rbp pointe sur l'adresse de retour pushq %rax # sauve ancienne valeur de rax pushq %rbx # sauve ancienne valeur de rbx movq 8(%rbp), %rbx # copie le paramtre # b dans rbx movq 16(%rbp), %rax # copie le paramtre # a dans rax cmpq %rax, %rbx # compare le 1er et le 2e # paramtre jae bmax # Si b >= a alors saute bmax amax: pushq %rax # empile a jmp FinMax bmax: pushq %rbx # empile b FinMax: popq %rax # valeur max -> rax movq %rax, 8(%rbp) # valeur max -> 1er paramtre popq %rbx # rcupre ancienne valeur de %rbx popq %rax # rcupre ancienne valeur de %rax retq $8 # dpile le 2e paramtre avant de # retourner la procdure appelante

page 37 sur 116

http://www.jourlin.com

Voici un schma qui illustre l'excution de procdures imbriques. Les chiffres reprsentent l'ordre d'excution du code : procA : ....

2 3

procC : ... ret

1
.... call procA ... call procB ... ret

call C .... ret

5
procB : ...

ret

Et voici l'volution de la pile lors de l'excution schmatise ci-dessus : 1. 2. 3. 4. 5. 6. 7. {} {adresse de 5} {adresse de 5, adresse de 4} {adresse de 5} {} {adresse de 7} {}

page 38 sur 116

http://www.jourlin.com

8.2 Les interruptions et les exceptions


Il nous manque encore un dispositif pour permettre au processeur de ragir en temps rel divers vnements, lis aux priphriques (appui d'une touche sur le clavier, clic ou mouvement de la souris, etc.) ou l'tat du processeur luimme (division par zro, accs une zone mmoire protge, etc.). Un simple balayage permanent de ces trs nombreuses donnes gaspillerait un montant considrable de ressources. Heureusement, tous les processeurs actuels possdent un jeu d'instructions qui permettent une gestion vnementielle de cette problmatique. Par exemple, un priphrique, lorsqu'il est sollicit par l'utilisateur, envoie un signal (qu'on appelle interruption) au microprocesseur, qui peut alors interrompre l'excution du programme courant, excuter une routine du systme d'exploitation et revenir au programme courant lorsque l'interruption a t traite. En ralit, n'importe quel programme peut gnrer des interruptions logicielles et des exceptions. Mais nous ne rentrerons pas ici dans des dtails qui relveraient plus d'un cours sur les systmes d'exploitation que d'un cours sur l'assembleur et le langage machine. Nous allons limiter cette section la description de l'instruction int, qui va nous permettre de communiquer avec le systme d'exploitation (pour les exemples, nous prendrons GNU/Linux). L'instruction int a un comportement similaire l'instruction call. Le registre rip va tre modifi pour permettre l'excution d'une procdure, mais l'adresse de l'instruction int est mmorise, de telle sorte qu' la sortie de la procdure, rip prend pour valeur l'adresse de l'instruction qui suit l'instruction int. La syntaxe de l'instruction int est la suivante :

intConstante
Constante est un entier qui dsigne le numro du gestionnaire d'interruption appeler. La valeur de certains registres sera utilise par le gestionnaire d'interruptions afin de slectionner la procdure approprie et de lui transmettre ses paramtres.

page 39 sur 116

http://www.jourlin.com

Sous GNU/Linux, par exemple, int $0x80 va appeler le gestionnaire d'interruption du systme d'exploitation (le gestionnaire de numro hexadcimal 80, soit 128 en dcimal) . Le numro de fonction systme est donn par %eax et les paramtres sont transmis via les registres %ebx,%ecx, %edx,%esi,%edi (dans cet ordre). Lorsqu'il y a plus de 5 paramtres, %ebx contient tout simplement l'adresse des paramtres. Voici par exemple, un petit programme en assembleur, destin tre assembl, li et excut sous GNU/Linux :
.data #directivedecrationd'unezonededonne btlm: #adressesymboliquepointantsurlachane: .string"Bonjourtoutlemonde!\n" .text #directivedecrationd'unezone #d'instructions .globlmain #directivedecrationd'unetiquette #deporteglobale main: #mainestl'adressededbutduprogramme movl$4,%eax #slectiondelafonction #writedusystme movl$1,%ebx #dernierparamtredewrite: #stdout movl$btlm,%ecx #premierparamtre #dewrite:l'adressede #lachanedecaractres #afficher movl$23,%edx #lenombredecaractres #afficher:23 int$0x80 #appeldel'interruption #128>GNU/Linux movl$1,%eax #slectiondelafonction #exitdusystme xorl%ebx,%ebx #misezrodu1erparamtre #enutilisant #xor,c'estdireouexclusif int$0x80 #appeldel'interruption #128>GNU/Linux ret #finduprogrammeetretourausystme

page 40 sur 116

http://www.jourlin.com

9. Autres instructions d'arithmtique entire


9.1 Multiplication et division sur des entiers non signs
9.1.1. Multiplication non signe

mul <x8> effectue la multiplication du contenu du registre al avec le contenu de x8 qui est soit l'emplacement d'un octet, soit un registre de 8 bits. Le rsultat est stock dans le registre 16 bits ax. mul <x16> effectue la multiplication du contenu du registre ax avec le contenu de x16 qui est soit l'emplacement d'un mot de 16 bits, soit un registre 16 bits. Le rsultat est stock dans dx:ax. Autrement dit, le mot (16 bits) de poids faible du rsultat est stock dans ax et le mot (16 bits) de poids fort dans dx. On peut se demander pourquoi le rsultat n'est pas stock dans eax. C'est pour une simple raison historique conserve par les contraintes de compatibilit : le registre eax n'existait pas dans le 8086. mul <x32> effectue la multiplication du contenu du registre eax avec le contenu de x32 qui est soit l'emplacement d'un mot de 32 bits, soit un registre 32 bits. Le rsultat est stock dans edx:eax. Autrement dit, le mot (32 bits) de poids faible du rsultat est stock dans eax et le mot (32 bits) de poids fort dans edx. mul <x64> effectue la multiplication du contenu du registre rax avec le contenu de x64 qui est soit l'emplacement d'un mot de 64 bits, soit un registre 64 bits. Le rsultat est stock dans rdx:rax. Autrement dit, le mot de 64 bits de poids faible du rsultat est stock dans rax et le mot de 64 bits de poids fort dans rdx. Les drapeaux OF et CF du registre d'tat RFLAGS sont mis 0 si la moiti suprieure des bits du rsultat (ah, dx, edx ou rdx) est 0, les deux drapeaux sont mis 1 dans le cas contraire.

9.1.2. Division non signe

div <x8> effectue la division de ax par <x8> (voir mul). Le quotient est stock dans al et le reste dans ah.

page 41 sur 116

http://www.jourlin.com

div <x16> effectue la division de dx:ax par <x16> (voir mul). Le quotient est stock dans ax et le reste dans dx. div <x32> effectue la division de edx:eax par <x32> (voir mul). Le quotient est stock dans eax et le reste dans edx. div <x64> effectue la division de rdx:rax par <x64> (voir mul). Le quotient est stock dans rax et le reste dans rdx. Les drapeaux de RFLAGS ne sont pas affects. En cas de dpassement, l'exception #DE (division par zro) est gnre 15.

9.2 Multiplication et division sur des entiers signs


9.2.1 Multiplication signe
imul peut prendre :

1 oprande : Comportement similaire mul. 2 oprandes : imul <source>, <destination>

Ici <destination> doit tre un registre gnral dans lequel sera stock le produit de <source> (valeur immdiate, registre ou emplacement mmoire) et de <destination>.

3 oprandes : imul <source1>, <source2>, <destination>

Ici <destination> doit tre un registre gnral dans lequel sera stock le produit de <source1> (registre ou emplacement mmoire) et de <source2> (valeur immdiate). CF et OF sont affects de la mme faon que pour mul.

9.2.2 Division signe


idiv fonctionne de la mme faon que div.
15 Pour simplifier, disons qu'une exception est une erreur qui par dfaut, sera gre par le systme d'exploitation.
page 42 sur 116

http://www.jourlin.com

10. Oprateurs logiques


1. and <source>, <destination> : Il s'agit du ET bits bits 2. or <source>, <destination> : Il s'agit du OU bits bits

3. xor <source>, <destination> : Il s'agit du OU EXCLUSIF bits bits 4. not <source-destination> : Il s'agit du NON bits bits

Comme pour les additions et les soustractions, l'oprande <destination> dsigne aussi le rsultat de l'opration. Par exemple :
movb xorb $0b11110000, %al $0b01010101, %al #0b11110000>al #0b11110000ouexclusif #0b01010101>al #icialcontient0b10100101

ou encore :
movb notb $0b10101010, %al #0b10101010>al %al #non(0b10101010)>al #icialcontient0b01010101

page 43 sur 116

http://www.jourlin.com

11. Calculs en virgule flottante


11.1 Introduction
Nous avons vu que le microprocesseur n'est pas capable de travailler directement dans l'ensemble des nombres entiers naturels ou relatifs. Le processeur peut en revanche manipuler des sous-ensembles finis : entiers signs ou non signs reprsents le plus souvent sur 8x2 n bits. Par consquent, nos sous-ensembles sont dfinis trs simplement par leurs bornes infrieures et suprieures. Malheureusement, lorsque nous cherchons reprsenter les nombres rels, les choses se compliquent : nous avons une infinit de valeurs entre une borne infrieure et une borne suprieure. Ceci pose videmment un problme lorsqu'il s'agit de reprsenter ces nombres avec des valeurs binaires de taille fixe. Le choix de notre reprsentation va donc dterminer en plus des limites infrieures et suprieures, la prcision maximale de nos calculs. De mme qu'un simple dbordement lors d'une addition de nombres entiers peut produire un rsultat final incorrect, une trs lgre erreur de prcision un moment donn dans un calcul sur des nombres virgule flottante peut mener des rsultats ultimes totalement errons.

11.2. La norme IEEE 754


Dans cette norme, les nombres sont reprsents en plusieurs parties : le signe, l'exposant et la mantisse. C'est la reprsentation que nous adoptons naturellement en base dcimale quand nous crivons : -1234.10 -9. Or, -1234.10-9 peut s'crire aussi -12340.10-10 ou -123,4.10-8 . Dans la premire criture, la mantisse a une taille de 4 chiffres et l'exposant s'crit avec 1 chiffre, dans le 2e cas la mantisse a une taille de 5 chiffres et l'exposant s'crit avec 2 chiffres, etc. Pour simplifier les choses et gagner de la prcision taille mmoire constante, nos nombres seront normaliss chaque fois que cela sera possible : notre mantisse aura toujours en base binaire la forme de 1 virgule fraction binaire . Notre exposant sera ngatif pour les valeurs infrieures 0 et positif ou nul pour les valeurs suprieures ou gales 1.

page 44 sur 116

http://www.jourlin.com

Cette normalisation n'est bien entendu plus possible lorsque nous atteignons les bornes suprieures ou infrieures de l'exposant. Lorsque nous atteignons la limite infrieure de l'exposant, c'est--dire lorsque notre valeur (positive ou ngative) est trs proche de 0, nous dirons que le nombre est dnormalis. L'exposant prendra la valeur 0 et la mantisse aura la forme 0 virgule fraction binaire . Lorsque nous dpassons la limite suprieure de l'exposant, notre nombre prendra une valeur qui symbolise +infini ou -infini. Aprs une opration invalide dans les rels (racine d'un nombre ngatif, division par 0), le rsultat aura galement une valeur remarquable. On peut voir ci-dessous la reprsentation 32 bits des nombres virgule flottante. L'exposant (entier sign) n'est pas reprsent en complment 2 mais sous la forme biaise : sur 8 bits, la valeur biaise 127 correspond au zro (non biais). Les valeurs biaises qui vont de 1 126 correspondent aux valeurs non biaises de -126 -1 et les valeurs biaises qui vont de 128 254 correspondent aux valeurs non biaises de 1 126.
nombre plus zro moins zro dnormalis positif dnormalis ngatif normalis positif normalis ngatif +infini -infini NaN (not a number) Signe (1bit) 0 1 0 1 0 1 0 1 0 ou 1 Exposant (8bits) 0 0 0 0 1..0d254 1..0d254 0d255 0d255 0d255 Mantisse (23 bits) 0 0 0b0,xxxx...xxxx 0b0,xxxx...xxxx 0b1,xxxx...xxxx 0b1,xxxx...xxxx 0 0 x0

page 45 sur 116

http://www.jourlin.com

11.3 Les registres du processeur virgule flottante (x87)


Du point de vue du programmeur, le processeur virgule flottante (FPU : floating point unit) est une unit distincte du processeur central (CPU: central processing unit). Il existe bien entendu des ponts entre les deux, mais chacun a ses propres registres et son propre jeu d'instructions. Ceci est encore un hritage du du 8086 qui tait limit aux instructions sur les entiers signs et non signs, auquel on pouvait adjoindre un second processeur, le 8087 que l'on appelait coprocesseur arithmtique. Lorsque l'on a intgr les instructions et registres virgule flottante, il a t dcid de prserver la compatibilit avec les logiciels crits pour des machines 2 processeurs 8086 et 8087. Le FPU possde 8 registres de 80 bits chacun (1 bit pour le signe, 15 bits pour l'exposant, 64 bits pour la mantisse). Ces 8 registres ne sont pas adressables directement comme dans le cas de l'unit d'arithmtique entire. Ils constituent une pile de registres, que l'on nommera %st(0), %st(1), ..., %st(7). %st(0) dsigne le sommet de la pile (c'est--dire le dernier lment empil) et %st(i) dsigne le i-me lment de la pile de registres.

11.4 Principales instructions de calcul


fld, fst

L'instruction fld (float load) permet d'empiler un nombre virgule flottante dans la pile des registres. La lettre qui suit indique la taille de l'oprande : flds pour empiler un nombre flottant de 32 bits, fldl pour empiler un nombre flottant de 64 bits et fldt pour empiler un flottant de 80 bits. Lorsqu'une valeur est empile, elle est automatiquement convertie en nombre flottant de 80 bits. L'oprande qui suit la mnmonique fld, c'est--dire la valeur empiler, peut tre soit un registre st(i) soit un emplacement en mmoire. fild et fist (avec i pour integer ) sont utiliss pour les conversions virgule flottante/entiers. L'instruction fst (float store) copie la valeur contenue dans le sommet de la pile, st(0) l'emplacement donn en oprande. fstp ralise la mme opration en dpilant le sommet de la pile.
fadd, fsub, fmul, fdiv

Ces instructions ralisent respectivement l'addition, la soustraction, la multiplication et la division de deux oprandes dont une au moins est un
page 46 sur 116

http://www.jourlin.com

emplacement de la pile de registres, l'autre oprande tant soit un registre de la pile, soit un emplacement en mmoire. Lorsqu'un seul oprande est spcifi, l'opration est ralise entre l'oprande et le sommet de la pile st(0). Le suffixe p peut tre ajout pour permettre un postdpilement des registres. Le suffixe r peut tre ajout aux mnmoniques fsub et fdiv afin d'inverser l'ordre des oprandes. Le rsultat d'une opration est toujours empil et se retrouve donc toujours dans %st(0). Ici aussi, les suffixes s , l ou t peuvent tre ajouts pour spcifier la taille des nombres flottants.

Par exemple :

fdivrps diviseur va prendre 32 bits l'adresse

symbolique diviseur , convertir ce nombre flottant sur 80 bits, diviser cette valeur par le sommet de la pile, remplacer le sommet de la pile par le rsultat de la division et enfin dpiler la pile de registres.
fiadd, fisub, fimul, fidiv

Ces instructions sont utiliser lorsque l'emplacement mmoire contient un entier. Il sera converti en nombre flottant de 80 bits avant l'opration.

11.5 Comparaisons et branchements conditionnels


Le FPU possde son propre registre d'tat et donc ses propres drapeaux. Lors d'une comparaison de 2 nombres virgule flottante, les drapeaux C0, C1, C2 et C3 indiquent le rsultat de la comparaison. Pour pouvoir utiliser les instructions classiques de branchement conditionnel, il faudra transfrer les valeurs des drapeaux C0,...,C3 dans les drapeaux ZF, CF et PF du registre RFLAGS. Cependant pour les microprocesseurs rcents, l'instruction fcomi effectue la comparaison et affecte directement les registres ZF, PF et CF, ce qui permet d'utiliser immdiatement aprs les instructions de branchement conditionnel classiques (jc, jnc, jo, jz, etc.). Aprs l'instruction fcomi %st(i) les trois registres de EFLAGS sont modifis de la faon suivante :

page 47 sur 116

http://www.jourlin.com

Condition st(0) > st(i) st(0) < st(i) st(0) = st(i) non comparable

ZF 0 0 1 1

PF 0 0 0 1

CF 0 1 0 1

PF=1 par exemple lorsqu'un des deux registres a NaN16 pour valeur. Si la comparaison s'est bien droule, alors PF =0 et les branchements conditionnels je, ja, jb, jae, jbe, jna, jnb, jnae et jnbe prennent la mme signification que lorsqu'il suivent une comparaison de valeurs entires (voir chapitre 5).

12. Paralllisme (MMX, SSE, 3DNow!)


Les instructions de type MMX, SSE et 3DNow sont ce que l'on apelle des SIMD, pour Single Instruction Multiple Data. Une instruction va lancer une srie d'oprations sur une srie de donnes en parallle. Nous allons voir les principaux concepts de programmation avec la technologie MMX. Les technologies SSE et 3DNow! tant de simples ajouts ou amliorations de MMX, nous n'allons pas les tudier ici.

12.1 Registres MMX


Les registres MMX se superposent aux registres du FPU. Le programmeur ne pourra donc pas utiliser les instructions du FPU et celle de MMX simultanment. Il y a 8 registres MMX, nomms mm0..., mm7. Ils ont chacun une taille de 64 bits. Dans chacun de ces registres, nous pouvons stocker, au choix :

1 nombre entier de 64 bits

16 Not a Number (rsultat d'une opration invalide)


page 48 sur 116

http://www.jourlin.com

2 nombres entiers de 32 bits 4 nombres entiers de 16 bits 8 nombres entiers sur 8 bits

Par exemple, si l'on veut stocker 8 entiers dans le registre mm0, ses bits numrots de 0 7 contiendront le 1er nombre, ses bits numrots de 8 15 contiendront le 2e nombre, etc.

12.2 Instructions MMX


Avec le CPU ou le FPU, les diffrents problmes qui pouvaient se produire lors d'un calcul (dbordement, division par 0, etc.) taient indiqus par des drapeaux ou par des exceptions. Dans le cas des instructions MMX, soit la retenue est ignore (on obtient le modulo du rsultat), soit les valeurs sont satures : lorsqu' la suite d'une opration, le rsultat est suprieur la borne maximale, il sera rendu gal la borne maximale. Si le rsultat est infrieur la borne minimale, il sera rendu gal la borne minimale. Cette manire de traiter les dbordements est particulirement bien adapte aux algorithmes traitant de l'image, du son ou de la vido. Les mnmoniques utilises sont trs proches de celles utilises pour les oprations d'arithmtique entire classique. Pour les copies de donnes, on utilisera les instructions movd (32 bits) et movq (64 bits). Pour additionner de 8 octets 8 autres octets en parallle sans saturation s'crira paddb <source>, <destination>. La destination doit tre un registre MMX, la source est soit un registre MMX, soit un emplacement. Pour additionner en parallle 4 nombres de 16 bits 4 autres nombres de 16 bits, on crira paddw. Enfin, une addition de 2x2 nombres de 32 bits en parallle s'crira paddd. L'addition avec saturation d'entiers signs s'crira padds (avec b ou w en suffixe) et l' addition avec saturation d'entiers non signs s'crira paddus (avec b ou w en suffixe). Les mnmoniques correspondantes pour la soustraction sont psubb, psubw, psubd, psubsb, psubsw, psubusb et psubusw. pmull ralise la multiplication en parallle des 4 entiers signs de 16 bits de la source aux 4 entiers signs de 16 bits de la destination et stocke la partie basse (16 bits) du rsultat dans la destination. pmulh ralise le mme calcul, mais stocke la partie haute du rsultat. pmullw et pmulhw ralisent les mmes
page 49 sur 116

http://www.jourlin.com

oprations avec 2x2 entiers signs de 32 bits. Le jeu d'instructions MMX contient galement des oprations de comparaison, de conversion, d'opration logiques et de dcalage, mais nous avons vu les concepts principaux. Supposons qu' l'adresse photo, nous ayons une image de 256x256 pixels en 256 niveaux de gris (0-> noir, 255 -> blanc). Voici un petit programme parallle, 8 fois plus rapide que son quivalent squentiel, qui augmente d'un cran la luminosit de l'image : movq$photo, %rsi movq$0x0101010101010101, %mm0 #8octetsdevaleur1>mm0 movw $0, %cx #pixelcourant boucle: movq (%rsi),%mm1 paddusb%mm0,%mm1 movq %mm1,(%rsi) addl $8,%rsi addw $8,%cx jncboucle #charge8octets>mm1 #ajoute18pixelsen// #transfre8pixels>photo #avancede8pixels #MAJducompteurdepixels #boucledansquecx<=65535

page 50 sur 116

http://www.jourlin.com

13. Bibliographie
AMD64 Architecture Programmer's Manual. Volume 1 : application

programming, http://www.amd.com/usen/assets/content_type/white_papers_and_tech_docs/24592.pdf
Intel Extended Memory 64 Technology Porting IA-32 Applications

to the Intel Xeon Processor http://www.intel.com/cd/ids/developer/asmona/eng/171850.htm? page=1


Dean Elsner, Jay Fenlason & friends, Using as : The GNU Assembler,

January 1994.

http://www.gnu.org/software/binutils/manual/gas2.9.1/

page 51 sur 116

http://www.jourlin.com

14. Exercices
14.1 Capacit des cases mmoires (voir section 1.1.)
14.1.1. QCM : Quel est le nombre maximal de valeurs distinctes que peut reprsenter une case mmoire de 3 bits ?
a. 3 b. 6 (2x3) c. 7 (2-1) d. 8 (2) e. 9 (3) Rponse : d. avec une case mmoire de 3 bits, on pourra reprsenter 2 valeurs distinctes. Reprsentes sous la forme de nombres binaires, il s'agit de 000, 001, 010, 011, 100, 101, 110 et 111.

14.1.2. QCM : Quelle est la taille minimale pour une case mmoire qui pourra reprsenter un mois particulier parmi les douze que compte un calendrier grgorien ?
a. 4 bits b. 12 bits b. 24 bits (2x12) c. 144 bits (12) d. 4096 bits (2)

Rponse : a. Une case mmoire de 3 bits peut contenir une valeur parmi
page 52 sur 116

http://www.jourlin.com

seulement 8 (2) valeurs distinctes. Avec un bit supplmentaire (soit 4 bits), on peut par stocker une valeur parmi 16 valeurs distinctes. Pour stocker un mois particulier parmi 12 possibles, une taille de 4 bits est donc ncessaire et suffisante.

14.2. Poids des chiffres, parties hautes et basses d'un nombre (voir section 1.1.1)
14.2.1 quelle est la valeur en base dcimale du nombre hexadcimal B105 ?
a. 11x16 + 1x32 + 0x48 + 5x64= 528 b. 11x16 + 1x16 + 0x16 + 5x16=45333 c. 11x16x3 + 1x16x2 + 0x16x1 + 5x16x0= 560 d. 16 x (11 + 1 + 0 + 5) = 21344 Rponse : b.

14.2.2. Quel est le chiffre de poids fort et la partie basse de 3 chiffres du nombre AZ35T6 (exprim dans une base numrique inconnue) ?
a. On ne peut pas rpondre cette question sans connatre la base numrique utilise b. chiffre de poids fort : 6 ; partie basse de 3 chiffres : AZ3 c. chiffre de poids fort : A ; partie basse de 3 chiffres : 5T6 d. chiffre de poids fort : A ; partie basse de 3 chiffres : AZ3

Rponse : a.

page 53 sur 116

http://www.jourlin.com

14.2.3. Quelle est la partie haute de 12 bits du nombre FEDCBA9 exprim en base hexadcimale ?
a. FEDC b. FED c. FE d. BA9 Rponse : b. (chaque chiffre hexadcimal ncessite 4 bits et seulement 4 bits pour son stockage).

14.2.4. Combien d'octets contient une case mmoire de 64 bits ?


a. 64 b. 6,4 (64/10) c. 8 (64/8) d. 512 (8*64) Rponse : c. Un octet est un groupe ou plutt une squence de 8 bits.

14.3 combien de cases mmoires distinctes peut-on accder avec des adresses hexadcimales allant de 0 FFFF ?
a. 2=65536 cases mmoires b. 2=256 bits c. 2=16 octets d. 2-1=65535 kilo-octets Rponse : a. chaque chiffre hexadcimal tant reprsent sur 4 bits, l'adresse
page 54 sur 116

http://www.jourlin.com

est donc code sur 16 bits. Il y a donc 2 valeurs possibles pour une adresse.

14.4 Oprandes (voir section 1.3)


14.4.1 movlnb1,al est une instruction qui :
a) copie la valeur de 8 bits correspondant l'tiquette nb1 dans le registre al b) copie la valeur 32 bits contenue l'adresse de l'tiquette nb1 dans le registre al c) copie la valeur 32 bits contenue l'adresse de l'tiquette nb1 dans la case mmoire dont l'adresse est reprsente par l'tiquette al d) copie la valeur du registre al dans le registre nb1 Rponse : c. Le registre 8 bits al est dsign par %al . La valeur de l'tiquette nb1 est dsigne par $nb1 . S'il existait un registre nomm nb1 , il faudrait s'y rfrer par %nb1 . En l'absence de signe indiquant un type d'oprande constante ($) ou registre (%), les tiquettes font rfrence des cases mmoires dfinies symboliquement par le programmeur. Mov est la mnmonique qui correspond une instruction de copie. Le l qui suit mov indique que les oprandes ont une capacit de 32 bits. L'oprande source est celle qui est gauche de la virgule et l'oprande destination , celle qui est droite de la virgule (syntaxe AT&T).

page 55 sur 116

http://www.jourlin.com

14.5 Registres gnraux (voir section 2.1)


14.5.1 parmi les propositions suivantes, lesquelles sont exactes ?
a) %bh et %bl sont des registres gnraux d'une capacit de 8 bits ? b) Toute modification du contenu de %cl entraine une modification du contenu de %ch. c) Toute modification du contenu de %bl entraine une modification du contenu de %bx, %ebx et %rbx d) Toute modification de %rdx entraine une modification du contenu de %edx, %dx, %dh et %dl e) Une modification de %rax peut entrainer une modification de %eax, %ax, %ah ou %al Rponse : a) Exact, par dfinition. b) Faux. %cl et %ch font tous deux partie des registres %cx, %ecx et %rcx mais ils correspondent des emplacements mmoires distincts. c) Exact : %bl dsigne les 8 bits de poids faible de %bx (partie basse de 16 bits de %rbx), mais aussi de %ebx (partie basse de 32 bits de %rbx) et donc de %rbx. d) Faux: Par exemple, une modification qui n'affecterait que la partie haute de 32 bits de %rdx, laisserait inchanges les valeurs contenues dans %edx (partie basse de 32 bits de %rdx), dans %dx (partie basse de 16 bits de %rdx), dans %dh (partie haute de 8 bits de %dx) et dans %dl (partie basse de 8 bits de %rdx). e) Exact : Si la modification de %rax affecte ses 16 bits de poids faible, alors elle affecte les registres %eax (32 bits de poids faible de %rax), %ax (16 bits de poids faibles), %ah (8 bits de poids fort de %ax) et %al (8 bits de poids faible de %rax).

page 56 sur 116

http://www.jourlin.com

14.6 Arithmtique entire (sections 2.2 et 2.3)


Donnez le rsultat (en dcimal) et les valeurs de CF (Carry Flag entiers non signs) et OF (Overflow Flag entiers signs) aprs les oprations ci-dessous. Pour vous viter d'avoir recours une calculatrice, voici les valeurs de quelques puissances de 2 : 27 = 128 ; 28 = 256 ; 215=32768 ; 216 = 65536 .

Taille opration 8 bits 8 bits 8 bits 8 bits 16 bits 16 bits 16 bits 16 bits

Opration 0-1 127+1 0xFF+1 -128-1 0-1 32767+1 0xFFFF+1 -32768-1

rsultat sign ? ? ? ? ?
?

rsultat non sign ? ? ? ? ?


?

CF = ? ? ? ? ? ? ? ? ?

OF = ? ? ? ? ? ? ? ? ?

? ?

? ?

page 57 sur 116

http://www.jourlin.com

Rponse: Taille opration Opration rsultat sign rsultat non sign CF = ? OF = ?

8 bits 8 bits 8 bits 8 bits 16 bits 16 bits 16 bits 16 bits

0-1 127+1 0xFF+1 -128-1 0-1 32767+1 0xFFFF+1 -32768-1

-1 -128 0 127 -1 -32768 0 32767

255 128 0 127 65535 32768 0 32767

1 0 1 0 1 0 1 0

0 1 0 1 0 1 0 1

Explication : - Ligne 1 : 0b00000000 - 0b1 = 0b11111111, valeur qui est interprte comme 28 -1 dans les entiers non signs (avec un Carry Flag 1 qui indique une retenue, donc un rsultat invalide) ou comme -1 dans les entiers signs (avec un Overflow Flag 0 qui indique un rsultat valide). - Ligne 2 : 0b01111111 + 0b1 = 0b10000000, valeur qui est interprte comme 27 dans les entiers non signs (avec un Carry Flag 0 qui indique l'absence de retenue, donc un rsultat valide) ou comme -27 dans les entiers signs (avec un Overflow Flag 1 qui indique un rsultat invalide). - Ligne 3 : 0b11111111 + 0b1 = 0b00000000, valeur qui est interprte comme 0 dans les entiers non signs (avec un Carry Flag 1 qui indique une retenue, donc un rsultat invalide) et galement comme 0 dans les entiers
page 58 sur 116

http://www.jourlin.com

signs (mais cette fois avec un Overflow Flag 0 qui indique un rsultat valide). - Ligne 4 : 0b10000000 - 0b1 = 0b01111111, valeur qui est interprte comme 27-1 dans les entiers non signs (avec un Carry Flag 0 qui indique l'absence de retenue, donc un rsultat valide) et galement comme 2 7-1 dans les entiers signs (mais cette fois avec un Overflow Flag 1 qui indique un rsultat invalide). - Ligne 5 : 0b0000000000000000-0b1 = 0b1111111111111111, valeur qui est interprte comme 216 -1 dans les entiers non signs (avec un Carry Flag 1 qui indique une retenue, donc un rsultat invalide) ou comme -1 dans les entiers signs (avec un Overflow Flag 0 qui indique un rsultat valide). - Ligne 6 : 0b0111111111111111+0b1 = 0b1000000000000000, valeur qui est interprte comme 215 dans les entiers non signs (avec un Carry Flag 0 qui indique l'absence de retenue, donc un rsultat valide) ou comme - 215 dans les entiers signs (avec un Overflow Flag 1 qui indique un rsultat invalide). - Ligne 7 : 0b1111111111111111+0b1 = 0b0000000000000000, valeur qui est interprte comme 0 dans les entiers non signs (avec un Carry Flag 1 qui indique une retenue, donc un rsultat invalide) et galement comme 0 dans les entiers signs (mais cette fois avec un Overflow Flag 0 qui indique un rsultat valide). - Ligne 8 : 0b1000000000000000 0b1 = 0b0111111111111111, valeur qui est interprte comme 215-1 dans les entiers non signs (avec un Carry Flag 0 qui indique l'absence de retenue, donc un rsultat valide) et galement comme 215-1 dans les entiers signs (mais cette fois avec un Overflow Flag 1 qui indique un rsultat invalide).

page 59 sur 116

http://www.jourlin.com

14.7 La pile (section 7)


Remplacez les points d'interrogation par les valeurs adquates : movq $0x89, %rax movq $0x67, %rbx pushq %rax pushq %rbx pushq $0x45 %rax = ????? %rax = ????? %rax = ????? %rax = ????? %rax = ????? %rbx= <indfini> %rsp -> {} %rbx = ????? %rbx = ????? %rbx = ????? %rbx = ????? %rsp -> {} %rsp-> {?????} %rsp-> {?????, ?????} %rsp -> {?????, ?????, ??? ??} %rsp-> {?????, ?????} %rsp-> {?????} %rsp -> {}

popq %rax popq %rax popq %rbx

%rax = ????? %rax = ????? %rax = ?????

%rbx = ????? %rbx = ????? %rbx = ?????

page 60 sur 116

http://www.jourlin.com

Rponse : movq $0x89, %rax movq $0x67, %rbx pushq %rax pushq %rbx pushq $0x45 popq %rax popq %rax popq %rbx %rax = $0x89 %rbx= <indfini> %rsp -> {} %rax = $0x89 %rbx = $0x67 %rax = $0x89 %rbx = $0x67 %rax = $0x89 %rbx = $0x67 %rax = $0x89 %rbx = $0x67 %rax = $0x45 %rbx = $0x67 %rax = $0x67 %rbx = $0x67 %rax = $0x67 %rbx = $0x89 %rsp -> {} %rsp-> {$0x89} %rsp-> {$0x67, $0x89} %rsp -> {$0x45, $0x67, $0x89} %rsp-> {$0x67, $0x89} %rsp-> {$0x89} %rsp -> {}

page 61 sur 116

http://www.jourlin.com

15. Travaux dirigs


15.1. Instructions de copie
Prrequis : structure d'un programme, structure d'une instruction, type et taille des oprandes, direction des instructions, instruction mov Donnez la valeur du registre %eax aprs l'excution conscutive de chacune de ces instructions. 1. movl $0xEFFACE, %eax Rponse : 0xEFFACE

explication : il s'agit d'une simple copie d'une constante (adressage immdiat) de 32 bits dans un registre de 32 bits. 2. movb $0b10, %ah Rponse : 0xEF02CE

explication : %ah est la partie haute de 8 bits (2 chiffres hexadcimaux) du registre de 16 bits (4 chiffres hexadcimaux) %ax. %ax est lui-mme la partie basse de 16 bits du registre %eax (32 bits, soit 8 chiffres hexadcimaux). La valeur binaire 0b10 contenue sur 1 octet (movb) correspond un nombre hexadcimal de deux chiffres (0x02) qui va tre copi et donc remplacer l'ancienne valeur (0xFA) contenue dans la partie %ah du registre %eax. 3. movw $10, %bx Rponse : 0xEF02CE

explication : on copie la valeur 0x000A dans le registre %bx. %ax et %bx tant des registres spars et indpendants, la valeur de %ax reste inchange. 4. movw %bx, %ax Question: %ah=? Rponse : 0x00

explication : Le registre %bx contient la valeur hexadcimale 000A. Cette valeur est copie dans le registre %ax. Le registre %al tant la partie basse de 8 bits (2 chiffres hexadcimaux) de %ax, sa valeur devient 0A. Le registre %ah tant la partie haute de 8 bits de %ax, sa valeur devient 0x00.

page 62 sur 116

http://www.jourlin.com

15.2. Instructions additives


Prrequis : Ceux du 15.1 + 2.2 (entiers signs et non signs) + dbordements Donnez la valeur du registre %eax et des drapeaux OF, CF et ZF aprs l'excution conscutive de chacune de ces instructions. 1. subl %eax, %eax CF=0, OF=0 Rponse : 0x00000000, ZF=1,

explication : %eax contient une valeur de 32 bits quelconque. L'instruction subl %eax, %eax ralise la soustraction de cette valeur et d'elle-mme. Le rsultat, zro est copi dans l'oprande destination : %eax. Le drapeau ZF (Zero Flag) du registre d'tat (RFLAGS) prend la valeur 1, indiquant que le rsultat est gal zro. 2. subw $1, %ax Rponse : 0x0000FFFF, ZF=0, CF=1, OF=0

explication : 0 1 = -1 . L'opration est ralise sur 16 bits et sur le registre %ax, soit la partie basse de 8 chiffres hexadcimaux de %eax. 0xFFFF correspond la valeur -1 code en complment deux. l'issue de l'opration, le drapeau OF (Overflow) du registre d'tat RFLAGS prend la valeur 0, indiquant la validit du rsultat sur les entiers signs. Le drapeau CF (Carry Flag) prend quant lui la valeur 1, indiquant un dbordement sur les entiers non signs. 3. addl $2, %eax Rponse : 0x00010001, ZF=0, CF=0, OF=0

explication : L'opration tant ralis sur 32 bits et sur %eax, la retenue est propage jusqu'au 5e chiffre le plus droite de %eax. L'opration est valide sur les entiers signs et non signs. 4. addl $0FFEFFFF, %eax

Rponse : 0x10000000, ZF=0, CF=0, OF=0. Explication : La retenue est propage jusqu'au chiffre hexadcimal le plus gauche du registre 32 bits %eax. Le rsultat est non nul, valide pour les entiers non signs et valide pour les entiers signs (ajout de 2 nombres positifs et rsultat positif bit de poids fort 0). OF est donc positionn 0.

page 63 sur 116

http://www.jourlin.com

15.3. Sauts conditionnels et inconditionnels (chapitre 3 du cours)


Quelle est la valeur de %ecx aprs l'excution de ces petits programmes ? Il est dans certains cas impossible de dterminer cette valeur. Si c'est le cas, rpondez par indfini .

15.3.1
movl Boucle: addl jmp Rponse : indfini. Explication : l'entre de la boucle, %ecx contient la valeur 0. La valeur 1 est ajoute %ecx chaque itration. Mais jmp tant un saut inconditionnel, nous avons faire une boucle infinie. Il est donc impossible de connatre la valeur de %ecx aprs l'instruction jmp puisque le pointeur d'instruction n'atteindra jamais cette adresse. $0, $1, Boucle %ecx %ecx

15.3.2
movl Boucle: addl jnc Rponse : 0. Explication : l'entre de la boucle, %ecx contient la valeur 0. La valeur 1 est ajoute %ecx chaque itration. jnc Boucle ralise un saut vers addl $1, %ecx lorsque le drapeau CF du registre d'tat RFLAGS est 0. Autrement dit, le saut sera effectu tant que l'opration addl $1, %ecx ne
page 64 sur 116

$0, $1, Boucle

%ecx %ecx

http://www.jourlin.com

provoque pas de dbordement sur les entiers non signs. Lorsque %ecx passe de 0xFFFFFFFF 0, CF passe 1 et l'instruction jnc ne ralise pas le saut. L'excution se poursuit donc l'instruction qui suit immdiatement jnc Boucle . La valeur de %ecx est alors 0.

15.3.3
movl Boucle: addl jno Rponse : 0. Explication : l'entre de la boucle, %ecx contient la valeur 0. La valeur 1 est ajoute %ecx chaque itration. jnc Boucle ralise un saut vers addl $1, %ecx lorsque le drapeau OF du registre d'tat RFLAGS est 0. Autrement dit, le saut sera effectu tant que l'opration addl $1, %ecx ne provoque pas de dbordement sur les entiers signs. Lorsque %ecx passe de 0x0FFFFFFF 0x10000000, 0F passe 1 et l'instruction jno ne ralise pas le saut. L'excution se poursuit donc l'instruction qui suit immdiatement jnc Boucle . La valeur de %ecx est alors 0x10000000. $0, $1, Boucle %ecx %ecx

page 65 sur 116

http://www.jourlin.com

15.3.4
(1) (2) (3) (4) (5) (6) (7) Rponse : 212. Explication : l'entre de la boucle (1), %ecx contient la valeur 0. La premire instruction l'intrieur de la boucle (2) consiste copier la valeur dcimale 0d210 dans le registre %eax. L'instruction suivante (3) soustrait la valeur courante de %ecx au registre %eax. Si la valeur courante de %ecx est strictement suprieure 0d210, alors le drapeau CF est mis 1 et l'instruction suivante (4) effectue un saut vers Sortie (7). Sinon, la valeur de %ecx est augmente (5) de 2 et l'instruction jmp (6) renvoie l'excution au dbut de boucle (2). %ecx ayant t initialis 0, il aura chaque itration des valeurs paires. Par consquent, l'opration de la ligne 3, provoquera la mise 1 de CF (dbordement d'un entier non sign) lorsque %ecx aura comme valeur 0d212. Sortie: Boucle: movl movl subl jc addl jmp $0, $210, %ecx Sortie $2, Boucle %ecx %ecx %eax %eax

page 66 sur 116

http://www.jourlin.com

15.4. Adressage indirect (section 4.2 du cours)


Ce petit programme permet de copier, octet par octet, une chaine de caractres termine par 0, dont l'adresse est contenue dans le registre rsi vers une deuxime chaine de caractres dont l'adresse est contenue dans le registre %rdi. Remplacez chaque point d'interrogation par un caractre pertinent. Si ncessaire, vous pouvez aussi supprimer des points d'interrogation. (1) (2) (3) (4) (5) (6) (7) Rponse : (1) (2) (3) (4) (5) (6) (7) FinCopie: Boucle: movb movb jz addq addq jmp (%rsi), %al, FinCopie $1, $1, Boucle %rsi %rdi %al (%rdi) ????????? ??? ??????? mov? mov? j? add? add? jmp ?%rsi?, ?%al?, ???????? ??, ??, ?????? ?%rsi? ?%rdi? ?%al? ?%rdi?

page 67 sur 116

http://www.jourlin.com

Correction :

Ligne 1, Colonne 1 : Il faut une tiquette, car c'est le dbut de l'itration (copie d'un caractre) Ligne 1, Colonne 2 : Suffixe b, car la valeur copie est un octet (8bits) Ligne 1, Colonne 3 : Il faut des parenthses pour indiquer que la donne copier ne se trouve pas directement dans %rsi mais l'adresse contenue dans %rsi Ligne 1, Colonne 4 : Il faut supprimer les ? : l'octet point par %rsi est copi directement dans %al Ligne 2, Colonne 2 : Suffixe b, car la valeur copie est un octet (8bits) Ligne 2, Colonne 3 : Il faut supprimer les ? : la valeur copier est contenue directement dans %al Ligne 2, Colonne 4 : Il faut des parenthses pour indiquer que la valeur ne doit pas tre copie directement dans %rdi mais l'adresse contenue dans %rdi Ligne 3, Colonne 2 : 'jz' : si l'octet qui vient d'tre copi est gal 0, la chaine de caractre d'origine est termine, il faut donc sortir de la boucle. Ligne 3, Colonne 3 : Adresse symbolique dfinie la ligne 7, c'est--dire la sortie de la boucle. Ligne 4, Colonne 1 : Supprimer les ? : cette adresse indique le dbut de l'incrmentation des registres, mais n'tant pas utilise comme cible d'un saut, il est parfaitement inutile de lui donner un nom. Ligne 4, Colonne 2 : Suffixe b, car la valeur ajoute est sur un octet (8bits) Ligne 4, Colonne 3 : $1 : valeur constante dsignant la taille (1 octet) de l'objet que l'on vient de copier, qu'il faut ajouter au registre %rsi pour qu'il pointe sur le prochain octet copier

page 68 sur 116

http://www.jourlin.com

Ligne 4, Colonne 4 : Supprimer les ? : C'est bien l'adresse qu'il faut augmenter et cette adresse est contenue directement dans %rsi Ligne 5, Colonne 2 : Suffixe b, car la valeur ajoute est sur un octet (8bits) Ligne 5, Colonne 3 : $1 : valeur constante dsignant la taille (1 octet) de l'emplacement o l'on a copi, qu'il faut ajouter au registre %rdi pour qu'il pointe sur le prochain emplacement de 8 bits Ligne 5, Colonne 4 : Supprimer les ? : C'est bien l'adresse qu'il faut augmenter et cette adresse est contenue directement dans %rdi Ligne 6, Colonne 3 : Etiquette dfinie en ligne 1

Explication : (1) Copie de l'octet [movb] point [()] par le registre 64 bits %rsi dans le registre 8 bits %al. (2) Copie de l'octet contenu dans %al l'emplacement point [()] par le registre 64 bits %rdi. Note : %al joue le rle de registre intermdiaire, car la copie directe d'une valeur pointe vers une valeur pointe n'est pas implmente dans les processeurs de la famille 80x86 (3) Si l'octet courant, contenu dans %al est gal zro (ZF=1), la copie est termine (poursuite de l'excution du programme la ligne 7). Sinon (ZF=0), l'excution se poursuit la ligne 4. (4) L'adresse d'origine (contenue dans %rsi) de la copie est augmente de 1. Autrement dit, %rsi pointe sur le prochain octet copier. (5) L'adresse de destination (contenue dans %rdi) de la copie est augmente de 1. Autrement dit, %rdi pointe sur le prochain octet modifier. (6) Retour la ligne 1, c'est dire copie de l'octet point par la nouvelle valeur de %rsi la nouvelle adresse contenue dans %rdi. (7) Fin de la copie. Cette ligne ne peut tre atteinte que lorsque ZF=1 la
page 69 sur 116

http://www.jourlin.com

ligne 3.

15.5. Adressage indirect index ( 4.3)


Dans cet exercice, nous voulons calculer le poids total d'une suite d'objets dont les caractristiques sont stockes en mmoire sous la forme d'une suite de cases mmoires contigus : n, t, p, v... t, p, v o n est un entier non sign 8 bits reprsentant le nombre d'objets, t est un entier non sign 32 bits reprsentant la taille de l'objet, p est un entier non sign 16 bits reprsentant le poids de l'objet, et v est un entier non sign de 64 bits reprsentant le volume de l'objet. Cette suite de cases mmoires est pointe par %rsi et le poids total devra tre stock dans %rax. Remplacez les points d'interrogation pour obtenir un programme correct.

(1) (2)

movq movq

$0, $0,

??%rax? ??%rbx?

# Poids total zro # Poid courant zro # copie le nombre total d'objets dans le registre %cl # %rsi pointe sur l'objet courant # copie le poids de l'objet

(3)

movb

??%rsi?,

??%cl?

(4)

addq

$1,

??%rsi?

(5)

Boucle:

movw

??%rsi?,

??%bx?

page 70 sur 116

http://www.jourlin.com

courant dans %rbx (6) addq ??%rbx? ??%rax? # ajoute le poids de l'objet courant %rax # fait pointer %rsi sur l'objet suivant # %cl contient le nombre d'objets restant # nouvelle itration s'il reste des objets traiter

(7)

addq

???,

??%rsi?

(8)

subb

$1,

??%cl?

(9)

jnz

Boucle

(10)

Fin:

Rponse :

page 71 sur 116

http://www.jourlin.com

(1) (2)

movq movq

$0, $0,

%rax %rbx

# Poids total zro # Poid courant zro # copie le nombre total d'objets dans le registre %cl # %rsi pointe sur l'objet courant # copie le poids de l'objet courant dans %rbx # ajoute le poids de l'objet courant %rax # fait pointer %rsi sur l'objet suivant

(3)

movb

(%rsi),

%cl

(4)

addq

$1,

%rsi

(5)

Boucle:

movw

4(%rsi),

%bx

(6)

addq

%rbx

%rax

(7)

addq

$14,

%rsi

page 72 sur 116

http://www.jourlin.com

(8)

subb

$1,

%cl

# %cl contient le nombre d'objets restant # nouvelle itration s'il reste des objets traiter

(9)

jnz

Boucle

(10) Correction :

Fin:

Ligne 1 : Il s'agit d'un accs direct %rax, qui par ailleurs n'est pas initialis. Utiliser ici les parenthses reviendrait mettre 0 une case mmoire alatoirement pointe par %rax.

Ligne 2 : Il s'agit d'un accs direct %rbx, qui par ailleurs n'est pas initialis. Utiliser ici les parenthses reviendrait mettre 0 une case mmoire alatoirement pointe par %rbx.

Ligne 3 : %rsi doit tre entour de parenthses (sans index) puisqu'il contient l'adresse prcise dans laquelle est stocke le nombre d'objets. %cl ne doit quant lui pas tre entour de parenthses puisqu'il doit stoker directement cette valeur.

Ligne 4 : Pas de parenthses puisque %rsi doit contenir directement l'adresse de l'objet courant.

Ligne 5 : %rsi pointe sur l'objet courant, c'est--dire sur un entier de 32 bits (4 octets) indiquant la taille de l'objet. 4(%rsi) est donc le contenu de la case mmoire pointe par %rsi+4, c'est--dire le poids de l'objet courant. Il ne faut pas de parenthses autour de %bx puisque ce registre doit contenir directement le poids de l'objet courant.

Ligne 6 : Aucune parenthse puisque %rbx contient directement le


page 73 sur 116

http://www.jourlin.com

poids de l'objet courant et que %rax doit contenir directement le poids global des objets jusqu' l'objet courant. Ligne 7 : Pour faire pointer %rsi sur l'objet suivant, il faut ajouter l'adresse qu'il contient, la taille en octets de l'lment courant, soit 4 (taille) + 2 (poids) + 8 (volume) = 14 octets. %rsi doit contenir directement cette adresse et il ne doit donc pas tre entour de parenthses.

Ligne 8 : %cl contient directement le nombre d'objets restants traiter et il ne doit donc pas tre entour de parenthses.

Explication : voir commentaires dans le programme

15.6. Indexations complexes (section 4.4 du cours)


Dans cet exercice, la question pose est la mme que dans l'exercice 15.5, mais le programme est plus court, car il utilise les possibilits d'indexation complexe du processeur. Nous voulons calculer le poids total d'une suite d'objets dont les caractristiques sont stockes en mmoire sous la forme d'une suite de cases mmoires contigus : n, t, p, v, ... t, p, v o n est un entier non sign 8 bits reprsentant le nombre d'objets, t est un entier non sign 32 bits reprsentant la taille de l'objet, p est un entier non sign 16 bits reprsentant le poids de l'objet, et v est un entier non sign de 16 bits reprsentant le volume de l'objet. Cette suite de cases mmoires est pointe par %rsi et le poids total devra tre stock dans %rax. Remplacez les points d'interrogation pour obtenir un programme correct. (1) (2) movq movq $0, $0, %rax %rbx # Poids total zro # Poid courant zro # mets zro la partie haute de %rcx

(3)

movq

$0,

%rcx

page 74 sur 116

http://www.jourlin.com

(4)

movb

(%rsi),

%rcx

# copie le nombre total d'objets dans le registre %rcx # %rcx contient le numro courant de l'objet traiter (0 si un seul objet) # copie le poids de l'objet courant dans %rbx # ajoute le poids de l'objet courant %rax # nouvelle itration s'il reste des objets traiter

(5)

Boucle:

????

??,

%rcx

(6)

movw

?(%rsi, %rcx, ?),

%bx

(7)

addq

%rbx

%rax

(8)

jnz

Boucle

(9)

Fin:

page 75 sur 116

http://www.jourlin.com

Rponse : (1) (2) movq movq $0, $0, %rax %rbx # Poids total zro # Poid courant zro # mets zro la partie haute de %rcx # copie le nombre total d'objets dans le registre %rcx # %rcx contient le numro courant de l'objet traiter (0 si un seul objet) # copie le poids de l'objet courant dans %rbx # ajoute le poids de

(3)

movq

$0,

%rcx

(4)

movb

(%rsi),

%rcx

(5)

Boucle:

subq

$1,

%rcx

(6)

movw

5(%rsi, %rcx, 8), %bx

(7)

addq

%rbx

%rax

page 76 sur 116

http://www.jourlin.com

l'objet courant %rax (8) jnz Boucle # nouvelle itration s'il reste des objets traiter

(9)

Fin:

Explication : %rsi pointe sur le nombre d'objets (1 octet), %rsi+1 pointe donc sur la taille du premier objet. Si l'on considre que le premier objet correspond %rcx=0, le deuxime %rcx=1, etc et puisque l'on sait que la taille de chaque objet est de 8 octets (taille sur 4 octets, poids sur 2 octets et volume sur 2 octets), l'adresse de l'objet courant est donne par %rsi+1+8*%rcx. Pour avoir l'adresse du poids de l'objet courant, il suffit de rajouter les 4 octets pris par la taille de l'objet, soit : %rsi+1+4+8*%rcx. Pour accder au contenu de cette adresse, il suffit donc d'crire 5(%rsi, %rcx, 8). En ligne 4, il faut s'assurer que le premier objet traiter corresponde bien une valeur de 0 pour %rcx. C'est pourquoi la dcrmentation de %rcx est ralise en dbut d'itration.

page 77 sur 116

http://www.jourlin.com

15.7. Algorithme de tri Bulle (chapitres 5 et 6 comparaisons et structures).


On considre un tableau d'entiers signs sur 16 bits, point par a et l'algorithme de tri suivant : PROGRAMME Tri_Bulle VARIABLE permut : Booleen; REPETER permut = FAUX POUR i VARIANT DE 0 N-1 FAIRE SI a[i] > a[i+1] ALORS echanger a[i] et a[i+1] permut = VRAI FIN SI FIN POUR TANT QUE permut = VRAI FIN PROGRAMME Il s'agit maintenant de traduire ce programme en assembleur (64 bits). 1. Combien d'tiquettes symboliques nous faudra traduire en assembleur ? [QCM : 0, 1, 2, 3 , 4, 5] Rponse : 4 : a, i, N, permut 2. Quel registre pour a ? [QCM : %rax, %bx, %cl, %rsi, %edi ?] Rponse : N'importe quel registre 64 bits peut stocker l'adresse du tableau, mais par convention, on choisira %rsi (source index). 3. Quel registre pour i ? [QCM : %rax, %bx, %rcx, %dl, %esi, %di] Rponse : N'importe quel registre gnral pourrait faire l'affaire, mais si l'on veut pouvoir traiter des tableaux de grande taille, on choisira un registre 64 bits. Aussi, par convention on choisira %rcx (compteur).

page 78 sur 116

http://www.jourlin.com

4. Quel registre pour N ? [QCM : %eax, %bx, %rcx, %dl, %rsi, %di, valeur immdiate] Rponse : N est une constante. Nous n'utiliserons pas un registre, mais simplement une valeur immdiate. 5, Quel registre pour permut ? [QCM : %al, %bl, %cx, %dl, %esi] Rponse : Nous n'avons besoin que d'un seul bit, mais quitte gaspiller 7 bits, il sera plus simple d'utiliser un registre gnral 8 bits . %rcx est dj utilis et %rax nous servira trs probablement de stockage temporaire. Nous pouvons donc choisir %bl ou %dl. 6. Les lignes du programme assembleur qui correspond l'algorithme de tri on t mlanges. Donnez l'ordre correct des lignes : (1) (2) (3) (4) Boucle_Pour: cmpq $49, %rcx 2(%rsi, %rcx, 2) # N-1 # compare a[i] et a[i+1] # bx contient a[i+1] # SI a[i] > a[i+1] ALORS

cmpw %ax,

movw 2(%rsi, %rcx, 2), %bx jge Fin_Si

(5) (6)

jg

Fin_Pour # echanger a[i] et a[i+1] :

(7) (8)

movw %ax, jnz REPETER

2(%rsi, %rcx, 2)

# ancien a[i] -> a[i+1] # TANT QUE permut =

page 79 sur 116

http://www.jourlin.com

VRAI (9) (10) (11) (12) Fin_Si: (13) (14) REPETER: movb $0, %bl # permut = FAUX

movw %bx, movb addq movq movq $1, $1, (%rsi, %rcx, 2), $0,

(%rsi, %rcx, # a[i+1] -> 2) a[i] %bl %rcx %ax %rcx # permut = VRAI # i=i+1 # ax contient a[i] # POUR i VARIANT DE 0

(15) (16) Fin_Pour:

jmp cmpb

Boucle_Pour $0, %bl # compare bl FAUX

Rponse :

page 80 sur 116

http://www.jourlin.com

(9) (14)

REPETER:

movb movq

$0, $0,

%bl %rcx

# permut = FAUX # POUR i VARIANT DE 0 # N-1

(1) (5) (13) (2) (4)

Boucle_Pour: cmpq jg movq cmpw jge

$49, Fin_Pour (%rsi, %rcx, 2), %ax, Fin_Si

%rcx

%ax 2(%rsi, %rcx, 2)

# ax contient a[i] # compare a[i] et a[i+1] # SI a[i] > a[i+1] ALORS

(3) (6) (10) (7) (11) (12) Fin_Si:

movw

2(%rsi, %rcx, 2),

%bx

# bx contient a[i+1] # echanger a[i] et a[i+1] :

movw movw movb addq

%bx, %ax, $1, $1,

(%rsi, %rcx, # a[i+1] -> a[i] 2) 2(%rsi, %rcx, 2) %bl %rcx # ancien a[i] -> a[i+1] # permut = VRAI # i=i+1

page 81 sur 116

http://www.jourlin.com

(15) (16) (8) Fin_Pour:

jmp cmpb jnz

Boucle_Po ur $0, REPETER %bl # compare bl FAUX # TANT QUE permut = VRAI

page 82 sur 116

http://www.jourlin.com

15.8. Tri bulle procdural (sections 7 et 8).


En reprenant l'algorithme du tri bulle , nous crivons une procdure qui ralise ce tri. Les paramtres de la procdure sont passs par la pile. La procdure TriBulle fait elle-mme appel une sous-procdure nomme FaitPermutations qui fait elle-mme appel une procdure Permute . Le programme principal appelle la procdure TriBulle pour classer par poids croissant les objets dcrits dans l'exercice 15.6. Insrer dans le programme cidessous, les instructions : call TriBulle, call FaitPermutations, call Permute. (1) (2) (3) (4) (5) (6) (7) (8) (9) (10) Repeter: cmpb jne retq $0, Repeter $8 %bl # %bl = 0 si pas de nouvelle permutation # tant que permut = VRAI # retour l'appel avec dpilement du paramtre d'appel main: fin: TriBulle: pushq retq movq movq movb subq 8(%rsp), %rsi $0, (%rsi), $1, %rdx %dl %rdx $Stock # empile l'adresse du tableau d'objets # retour au systme d'exploitation # %rsi pointe sur le tableau trier # pour mettre zro la partie haute de %rdx # %rdx contient le nombre d'objets trier (N) # %rdx est la limite de la boucle Pour (N-1)

page 83 sur 116

http://www.jourlin.com

(11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25)

FaitPermu movb tations: PourInit: PourTest: movq cmpq jae Si: movw cmpw jae movb FinSi: add jmp FinPour: Permute: retq pushq movq movq movq

$0, $0, %rdx, FinPour

%bl %rcx %rcx

# permut = FAUX # Pour i=0 # Jusqu' n-1 # on sort de la boucle quand i>=n-1 # compare T[i].poids

5(%rsi, %ax %rcx, 8), %ax, FinSi $1, $1, PourTest %bl %rcx

13(%rsi, # avec T[i+1].poids %rcx, 8)

# permut = VRAI # lment suivant

# retour l'appel %rdx 1(%rsi, %rax %rcx, 8), 9(%rsi, %rdx %rcx, 8), %rdx, # sauve le nombre d'objets trier # sauve T[i] # sauve T[i+1]

1(%rsi, # copie T[i+1] dans T[i] %rcx, 8)

page 84 sur 116

http://www.jourlin.com

(26) (27) (28) Question :


movq popq retq

%rax, %rdx

9(%rsi, # copie ancien T[i] dans %rcx, 8) T[i+1] # restaure le nombre d'objets trier

call TriBulle : entre la ligne ? et la ligne ? call FaitPermutations : entre la ligne ? et la ligne ? call Permute : entre la ligne ?? et la ligne ??

Rponse :

call TriBulle : entre la ligne 1 et la ligne 2 call FaitPermutations : entre la ligne 7 et la ligne 8 call Permute : entre la ligne 17 et la ligne 18

page 85 sur 116

http://www.jourlin.com

15.9. Plus Petit Commun Multiple (Chapitre 8.2, 9 et 10)


Le plus petit commun multiple (P.P.C.M) de deux entiers non nuls a et b peut tre calcul comme la valeur absolue du produit de a et b divis par le plus grand commun diviseur (ou P.G.C.D.) de a et b. En utilisant l'algorithme d'Euclide pour le calcul du PGCD, nous avons l'algorithme suivant :
FonctionPGCD(a:nombre,b:nombre):nombre Sib=0 |alorsPGCD=a Sinon |regalaurestedeladivision(entire)deaparb |PGCD=PGCD(b,r) Finsi

et FonctionPPCM(a:nombre,b:nombre):nombre PPCM=a*b SiPPCM<0 |alorsPPCM=PPCM FinSi PPCM=PPCM*PGCD(a,b) Remettez dans l'ordre les lignes des programmes assembleur correspondant ces 2 fonctions :

page 86 sur 116

http://www.jourlin.com

PGCD : AlorsP 1 GCD: movq %rax,16(%rsp) #remplacele1er paramtreparle rsultatduPGCD retq $8 #retourneen dpilantle2e paramtre PGCD: 2 movq movq 16(%rsp),%rax 8(%rsp),%rbx #Lenombrea #Lenombreb

page 87 sur 116

http://www.jourlin.com

SinonP 3 GCD: movq $0,%rdx #initialise partiehautede64 bitsdunumrateur divq %rbx #diviserdx:rax parrbx,%rax contient

cmpq je

$0,%rbx AlorsPGCD #sib=0onsort enrenvoyanta

popq 5

16(%rsp)

#rsultatdu PGCDlaplace du1erparamtre

retq

$8

#retourneen dpilantle2e paramtre

page 88 sur 116

http://www.jourlin.com

pushq 6 pushq

%rbx %rdx

#empileb #empilelereste r

call

PGCD

#appelPGCD(b,r)

PPCM :

movq 1

$0,%rdx

#initialise partiehautede 64bitsdu numrateur

divq

%rbx

#%rax:quotient dea*b/PGCD(a,b)

movq

%rbx,16(%rsp)

#remplacele1er paramtreparle rsultatduPGCD

page 89 sur 116

http://www.jourlin.com

movq movq

16(%rsp) 8(%rsp)

#Lenombrea #Lenombreb

PPCM: 3

pushq pushq call

16(%rsp) 8(%rsp) PGCD

#Lenombrea #Lenombreb

retq

$8

#sorten dpilantle2e paramtre

mulq

%rbx

#raxcontient a*b

popq

%rbx

#rbxcontient PGCD(a,b)

page 90 sur 116

http://www.jourlin.com

Rponse : 2 PGCD: movq movq 4 cmpq je 16(%rsp),%rax 8(%rsp),%rbx $0,%rbx AlorsPGCD #sib=0onsort enrenvoyanta 3 SinonP GCD: movq $0,%rdx #initialise partiehautede 64bitsdu numrateur divq %rbx #diviserdx:rax parrbx,%rax contient pushq 6 pushq %rbx %rdx #empileb #empilelereste r call PGCD #appelPGCD(b,r) #Lenombrea #Lenombreb

page 91 sur 116

http://www.jourlin.com

popq 5

16(%rsp)

#rsultatdu PGCDlaplace du1erparamtre

retq

$8

#retourneen dpilantle2e paramtre

AlorsP 1 GCD: movq %rax,16(%rsp) #remplacele1er paramtreparle rsultatduPGCD retq $8 #retourneen dpilantle2e paramtre

PPCM: 3

pushq pushq call

16(%rsp) 8(%rsp) PGCD

#Lenombrea #Lenombreb

page 92 sur 116

http://www.jourlin.com

movq movq

16(%rsp) 8(%rsp) %rbx

#Lenombrea #Lenombreb #raxcontient a*b

mulq

popq

%rbx

#rbxcontient PGCD(a,b)

movq 1

$0,%rdx

#initialise partiehautede 64bitsdu numrateur

divq

%rbx

#%rax:quotient dea*b/PGCD(a,b)

movq

%rbx,16(%rsp)

#remplacele1er paramtreparle rsultatduPGCD

retq

$8

#sorten dpilantle2e paramtre

page 93 sur 116

http://www.jourlin.com

15.10. Calcul arithmtique flottant (chapitre 11)


Vous trouverez ci-aprs la formule mathmatique de calcul d'une mensualit de crdit immobilier :

t 12 t n 11 12 K
o : m est la mensualit K est le capital emprunt t est le taux annuel proportionnel n est le nombre de mensualits

Le programme assembleur suivant est une procdure qui permet de raliser ce calcul en considrant que m, K, t et n sont des flottants cods sur 64 bits. Vous devez simplement remplacer les lettres en rouge par des valeurs entires comprises entre 0 et 7.
main: ################ fldl fildl fdivrp fldl fmul fld1 fadd Calcul de m ###### t # st(0) = t NBMOISDANSANNEE # st(0)=12 ; st(1)=t %st(a),%st(b) # st(0)=t/12 k # st(0)=K ; st(1) = t/12 %st(c), %st(d) # st(0)=K.t/12 ; st(1) = t/12 # st(0)=1.0 ; st(1)=K.t/12 ; st(2) = t/12 %st(e), %st(f) # st(0)=1.0+t/12 ; st(1)=K.t/12 ; #st(2) = t/12 movq n, %rcx # le nombre de mensualits fld1 # st(0)=1.0 ; st(1)=1.0+t/12 ; st(2)=K.t/12 ; st(3) = t/12 PuissanceN: fdiv %st(g), %st(h) # st(0)=(1.0+t/12)^-rcx ; st(1)=1.0+t/12 ; st(2)=K.t/12 ; st(3) = t/12 subq $1, %rcx # rcx=rcx-1 jnz PuissanceN # vers puissance suivante fld1 # st(0)=1.0 ; st(1)=(1.0+t/12)^n ; st(2)=1.0+t/12 ; st(3)=K.t/12 ; st(4) = t/12 fsubp %st(i), %st(j) # st(0)=1.0-(1.0+t/12)^n ; st(1)=1.0+t/12 ; st(2)=K.t/12 ; st(3) = t/12 fdivrp %st(k), %st(l) # st(0)=1.0+t/12 ; st(1)= # (K.t/12)/(1.0-(1.0+t/12)^n) ; # st(2) = t/12

page 94 sur 116

http://www.jourlin.com fstp fstpl fstp %st(m) m %st(n) # # # # st(0)=(K.t/12)/(1.0-(1.0+t/12)^n) ; st(1) = t/12 stocke le rsultat ; st(0)=t/12 vide la pile des registres flottants

################ Affiche le rsultat ####### pushq m movsd m, %xmm0 movl $AfficheM, %edi movl $1, %eax call printf addq $8, %rsp systme movq $1,%rax %rbx,%rbx $0x80 # sauve m # chaine de format # stdout # libre m # slection de la fonction exit du # mise zro du 1er paramtre en # xor, c'est dire ou exclusif # appel de l'interruption # 128 -> GNU/Linux

xorq utilisant int fin: ret

Rponse : a=0; b=1; c=1; d=0; e=2; f=0; g=1; h=0; i=0; j=1; k=0; l=2; m=0; n=0 (voir aussi correction complte en section (16.2.4)

15.11. Produit scalaire via instructions SIMD.


Il s'agit ici d'crire une procdure ralisant le produit scalaire de 2 matrices 4x4 d'entiers signs sur 16 bits. Le rsultat de l'opration est une matrice 4x4 d'entiers signs sur 32 bits. Afin d'obtenir les meilleures performances en temps de calcul, nous utilisons les instructions SIMD (single instructions multiple data ; instruction unique donnes multiples). Voici la principale procdure de ce programme :
ProduitScalaire: movq 16(%rsp), %rdi pushq %rdi vecteurs call movq movq movq movq Pivote 8(%rsp), %rcx 16(%rsp), %rdi 24(%rsp), %rsi $0, %rax $0, %rbx # adresse de la deuxime matrice # pivote la 2e matrice pour avoir les # colonnes en lignes # # # # adresse de la matrice produit adresse de la deuxime matrice adresse de la premire matrice indice de la ligne de la premire

matrice TraiteLigne: movq

# indice de la ligne de la transpose # de la seconde matrice

page 95 sur 116

http://www.jourlin.com TraiteColonne: movq movq C call movw movq D call shll

(%rsi, %rax, 8), A (%rdi, %rbx, 8), B B, A SommeDesComposantes %dx, (%rcx) (%rsi, %rax, 8), A B, A SommeDesComposantes $16, %edx

# # # # # # # # # # # # # # # # # # # # # # # # # # # #

charge la ligne de la 1ere matrice dans A charge la colonne de la 2e matrice (ou ligne de sa transpose) dans B A : partie basse du produit composante par composante rdx contient la somme des composantes 8 bits de A copie du rsultat dans la matrice produit charge la ligne de la 1ere matrice dans A A : partie haute du produit composante par composante rdx contient la somme des composantes 8 bits de %mm0 dcale le rsultat de 16 bits vers la gauche pour le mettre en partie haute ajoute la partie haute du rsultat dans la matrice produit pointe sur l'lment suivant calculer indice de colonne suivant fin de colonne ?

addl addq addq cmpq jne movq addq cmpq jne ret $24

%edx, (%rcx) $4, %rcx $1, %rbx $4, %rbx TraiteColonne $0, %rbx $1, %rax $4, %rax TraiteLigne

# on recommence la colonne 0 # ligne suivante # dernire ligne ?

Exercice : Remplacez A, B, C et D par les registres ou les mnmoniques SIMD qui permettent d'obtenir un programme correct.

page 96 sur 116

http://www.jourlin.com

Rponse :

= %mm0 B = %mm1 C = pmullw D = pmulhw

page 97 sur 116

http://www.jourlin.com

16. Travaux pratiques : Programmer en Assembleur sous GNU/Linux


Les programmes ci-dessous pourront tre excuts avec une distribution de GNU/Linux 64 bits, avec les logiciels g++ et insight. Ils pourront tre facilement adapts pour fonctionner sou GNU/Linux 32 bits.

16.1 Premiers pas


1. Ouvrez un terminal. Vous pouvez y accder en cliquant sur le menu principal de votre distribution. Il se trouve dans la catgorie applications et dans la sous-catgorie systme. Vous taperez toutes vos commandes dans la fentre du terminal. Commencez par crer un rpertoire de travail mkdir tp_asm et choisissez-le comme rpertoire courant cd tp_asm . ce stade, vous avez probablement un choix faire parmi une grande quantit d'diteurs disponibles. Je choisis emacs pour mes exemples. Pour diter votre premier programme assembleur, lancez l'diteur : emacs btlm.s . Faites un copier-coller du code donn en section 8.2 :

2.

3.

.data #directivedecrationd'unezonededonne btlm: #adressesymboliquepointantsurlachane: .string"Bonjourtoutlemonde!\n" .text #directivedecrationd'unezone #d'instructions .globlmain #directivedecrationd'unetiquette #deporteglobale main: #mainestl'adressededbutduprogramme movl$4,%eax #slectiondelafonctionwritedusystme movl$1,%ebx #dernierparamtredewrite:stdout movl$btlm,%ecx#premierparamtredewrite:l'adressede #lachanedecaractresafficher movl$23,%edx #lenombredecaractresafficher:23 int$0x80 #appeldel'interruption128>GNU/Linux movl$1,%eax #slectiondelafonctionexitdusystme xorl%ebx,%ebx#misezrodu1erparamtreenutilisant #xor,c'estdireouexclusif int$0x80 #appeldel'interruption128>GNU/Linux ret #finduprogrammeetretourausystme

4.

Sauvez le fichier et quittez l'diteur.


page 98 sur 116

http://www.jourlin.com

5. 6. 7. 8.

Assemblez le fichier en tapant gcc btlm.s -o btlm Excutez le programme ./btlm Sauf problmes lis votre configuration logicielle, vous devez voir apparatre dans votre terminal la phrase Bonjour tout le monde! Vous pouvez aussi excuter votre programme au pas--pas en utilisant un dbogueur, par exemple en tapant insight ./btlm . Le dboguer vous permet aussi d'observer au pas--pas l'volution des registres, de la mmoire, etc.

16.2 Programmes corrigs


En prenant exemple sur l'exercice prcdent, essayer de programmer sans modle les programmes des travaux dirigs. Vous trouverez quelques corrections ci-dessous.

16.2.1 Correction de l'exercice 14.7 :


.text .globlmain main: movq movq pushq pushq pushq popq popq popq fin:ret $0x89, $0x67, %rax %rbx $0x45 %rax %rax %rbx #directivedecrationd'unezoned'instructions #directivedecrationd'unetiquette #deporteglobale %rax #copiedes64bitsde0x89dans%rax %rbx #copiedes64bitsde0x67dans%rbx #empile0x89 #empile0x67 #empile0x45 #dpileetcopie0x45dans%rax #dpileetcopie0x67dans%rax #dpileetcopie0x89dans%rax

16.2.2 Correction de l'exercice 15.8 :


Stock: #nombre(8bits)d'objets #danslaliste .long 0x12345678 #taille(32bits)dupremier #objet .word 0x1234 #poids(16bits)dupremier #objet .word 0x5678 #volume(16bits)dupremierobjet .long 0x87654321 #taille(32bits)dudeuximeobjet .word 0x0123 #poids(16bits)dudeuximeobjet .word0x4321 #volume(16bits)dudeuximeobjet .data .byte 5

page 99 sur 116

http://www.jourlin.com .long .word .word .long .word .word .long .word .word .text .globlmain 0x01234567 0x1123 0x2345 0x98765432 0x0012 0x7654 0x09876543 0x1234 0x8765 #taille(32bits)dutroisimeobjet #poids(16bits)dutroisimeobjet #volume(16bits)dutroisimeobjet #taille(32bits)duquatrimeobjet #poids(16bits)duquatrimeobjet #volume(16bits)duquatrimeobjet #taille(32bits)ducinquimeobjet #poids(16bits)ducinquimeobjet #volume(16bits)ducinquimeobjet #directivedecrationd'unezone #d'instructions #directivedecrationd'unetiquette #deporteglobale

######################ProgrammePrincipal##################### main: fin: pushq$Stock callTriBulle retq #empilel'adressedutableau #d'objets #appeldelaprocduretribulle #retourausystme #d'exploitation

#######################ProcedureTribulle####################### TriBulle: movq movq movb subq Repeter: call cmpb permutation jne retq Repeter $8 #tantquepermut=VRAI #retourl'appelavec #dpilementduparamtred'appel FaitPermutations #appeldelaprocdure $0,%bl #%bl=0sipasdenouvelle 8(%rsp),%rsi $0,%rdx (%rsi),%dl $1,%rdx #%rsipointesurletableau #trier #pourmettrezrola #partiehautede%rdx #%rdxcontientlenombre #d'objetstrier(N) #%rdxestlalimitedela #bouclePour(N1)

#########################ProcedureFaitPermutations ###################### FaitPermutations: movb $0, %bl PourInit: movq PourTest: cmpq jae Si: movw cmpw #permut=FAUX $0, %rcx %rdx, %rcx FinPour 5(%rsi,%rcx,8),%ax %ax,13(%rsi,%rcx,8) #Pouri=0 #Jusqu'n1 #onsortdelaboucle #quandi>=n1 #compareT[i].poids #avecT[i+1].poids

page 100 sur 116

http://www.jourlin.com jae FinSi call Permute movb $1,%bl addq $1,%rcx jmpPourTest

FinSi:

#permut=VRAI #lmentsuivant #BouclePour

FinPour: retq ############################ProcedurePermute ############################ Permute: pushq %rdx d'objetstrier movq movq movq movq popq retq #sauvelenombre 1(%rsi,%rcx,8),%rax 9(%rsi,%rcx,8),%rdx %rdx,1(%rsi,%rcx,8) %rax,9(%rsi,%rcx,8) %rdx #sauveT[i] #sauveT[i+1] #copieT[i+1]dansT[i] #copieancienT[i]dans #T[i+1] #restaurelenombre #d'objetstrier

16.2.3 Correction de l'exercice 15.9 :


Dans le but de lire au clavier et d'afficher l'cran des nombres hexadcimaux, il est ncessaire de pouvoir passer d'une valeur numrique contenue dans une case mmoire une chane de caractres et vice-versa. Une chane de caractres est une suite compose des codes ASCII (8 bits) de chacun de ses caractres, termine par le code ASCII 0 . On sait que les codes ASCII des chiffres ('0' '9') sont conscutifs et infrieurs aux codes ASCII des lettres ('a' 'z '), galement conscutifs :
car. ASCII valeur '0' 48 0 '1' 49 1 '2' 50 2 '3' 51 3 '4' 52 4 '5' 53 5 '6' 54 6 '7' 55 7 '8' 56 8 '9' 57 9 ...... ...... 'a' 97 10 'b' 98 11 'c' 99 12 'd' 100 13 'e' 101 14 'f' 102 15

Il est donc facile de convertir un seul caractre compris entre '0' et '9' ou entre 'a' et 'f' en valeur numrique comprise entre 0 et 15 : si le code ASCII du chiffre hexadcimal est suprieur ou gal au code ASCII de 'a', il suffit de lui soustraire le code ASCII de 'a' pour obtenir une valeur comprise entre 0 et 5 et de lui ajouter 10 pour obtenir une valeur comprise entre 10 et 15. Si le code ASCII du chiffre hexadcimal est infrieur au code ASCII de 'a', il s'agit donc d'un caractre compris entre '0' et '9'. Il suffit alors de lui soustraire le code ASCII de '0' pour obtenir une valeur comprise entre 0 et 9.

page 101 sur 116

http://www.jourlin.com

On crira donc la conversion caractre -> valeur de cette faon :


cmpb $'a',%dl#%dlcontientlecaractreconvertir jb CarChiffre#codeASCII<'a':c'estunchiffre subb $'a',%dl#c'estunelettre addb $0xA,%dl#conversionenentierde4bits jmpSuite #caractresuivant CarChiffre: subb Suite: $'0',%dl #%dlcontientlechiffrehexadcimal

l'inverse, la conversion valeur -> caractre s'crira :


cmpb $0xA,%dl #%dlcontientlechiffreconvertir jb ValeurChiffre#valeur<10 subb $0xA,%dl #c'estunelettre addb $'a',%dl #%dlcontientsoncodeASCII jmp Suite #chiffresuivant ValeurChiffre: addb $'0',%dl Suite: #%dlcontientlecaractrehexadcimal

Ce sous-problme tant rgl, il nous reste crire le code pour traiter tous les chiffres/caractres du nombre. Dans le cas d'une conversion valeur -> chane de caractres , il nous trouver un moyen d'extraire chacun des chiffres de la valeur. Dans le cas d'une conversion chane de caractres -> valeur , il nous faut trouver un moyen d'assembler chacun des chiffres de la valeur. Pour l'extraction des chiffres d'une valeur, on peut procder par divisions par 16 successives, le reste de la division donnant le chiffre hexadcimal de poids faible. On peut raliser la mme chose avec des dcalages droite de 4 bits. Pour l'assemblage des chiffres d'une valeur, on peut procder par multiplication par 16 par dcalages gauche de 4 bits.

.data Nombre: .quad Base: .quad NombreASCII: 0 16

#directivedecrationd'unezonededonne #unnombrede64bits #labase16(dite"hexadcimale") #adressesymboliquepointantsurlenombre

page 102 sur 116

http://www.jourlin.com .string"XXXXXXXXXXXXXXXX\n" #encaractresASCII .text #directivedecrationd'unezoned'instructions .globlmain #directivedecrationd'unetiquette #deporteglobale main: #mainestl'adressededbutduprogramme pushq $NombreASCII #empilel'adressedunombresous #formedechaneASCII #Appellelaprocduredelecture #empilel'adressedunombreobtenir #empilel'adressedelachane

callLitChaine pushq $Nombre pushq $NombreASCII convertir callHexaVersEntierMul

#appellelaprocduredeconversionpar #multiplications addq $0x11111111,Nombre #Faituneaddition pushq Nombre #empilelenombre pushq $NombreASCII #empilel'adressedelachane #crire call EntierVersHexaDivise #appellelaprocdure #deconversionpardivisions pushq $NombreASCII #empilel'adressedunombre #sousformedechaneASCII pushq $17 #empilelenombredecaractres #afficher callAfficheChaine #Appellelaprocdured'affichage pushq $NombreASCII #empilel'adressedunombresousforme #dechaneASCII #Appellelaprocduredelecture #empilel'adressedunombreobtenir #empilel'adressedelachane

callLitChaine pushq $Nombre pushq $NombreASCII convertir callHexaVersEntierDecale#appellelaprocduredeconversion #pardcalages subq pushq pushq call $0x111111,Nombre#Faitunesoustraction

Nombre #empilelenombre $NombreASCII #empilel'adressedelachanecrire EntierVersHexaDecale #appellelaprocdurede #conversionpardcalages pushq $NombreASCII #empilel'adressedunombre #sousformedechaneASCII pushq $17 #empilelenombredecaractresafficher callAfficheChaine #Appellelaprocdured'affichage movl$1,%eax xorl%ebx,%ebx int$0x80 fin:ret #slectiondelafonctionexitdusystme #misezrodu1erparamtreenutilisant #xor,c'estdireouexclusif #appeldel'interruption128>GNU/Linux

########################################################################### ###########################

page 103 sur 116

http://www.jourlin.com

LitChaine: movl$3,%eax #slectiondelafonctionreaddusystme movl$2,%ebx #dernierparamtredewrite:stdin movl8(%rsp),%ecx #premierparamtredewrite: #l'adressedelachanedecaractres #lire int$0x80 #appeldel'interruption128>GNU/Linux ret $16 #retourneendpilantleparamtre ########################################################################### ########################### AfficheChaine: movl$4,%eax #slectiondelafonctionwritedusystme movl$1,%ebx #dernierparamtredewrite:stdout movl16(%rsp),%ecx #premierparamtredewrite:l'adresse #delachanedecaractresafficher movl8(%rsp),%edx #lenombredecaractresafficher int$0x80 #appeldel'interruption #128>GNU/Linux ret $16 #retourneendpilantleparamtre ########################################################################### ############################# HexaVersEntierDecale: movq 8(%rsp),%rsi movq 16(%rsp),%rdi movq movq $0,%rcx $0,%rdx #%rsipointesurlachainedecaractres #%rdipointesurnombreissudela #conversion #positionducaractrecourantdansla #chane #metszrolesbitsde%rdxpour #oprationCopieCarDecale #rcuprationducaractre #courantdans%dl # #codeASCII<'A': #c'estunchiffre #c'estunelettre #conversionenentierde4bits #sautversl'insertiondans #%rax

ConversionCarDecale: movb (%rsi,%rcx),%dl cmpb jb $'a',%dl CarChiffreDecale

subb $'a',%dl addb $0xA,%dl jmpCopieCarDecale CarChiffreDecale: subb $'0',%dl CopieCarDecale: shlq $4,(%rdi) orb addq cmpq jbe %dl,(%rdi)

#%dlcontientlechiffre #dcale%raxd'unchiffre(4bits)vers #ladroite #copie%dldansles4bitsdepoids #faiblesde%rax #chiffresuivant #conversionterminel

$1,%rcx $15,%rcx ConversionCarDecale

page 104 sur 116

http://www.jourlin.com #orsque%rcxestngatif #retourneenenlevantles #2paramtresdelapile ########################################################################### #### ret$16 HexaVersEntierMul: movq 8(%rsp),%rsi movq 16(%rsp),%rdi movq movq $0,%rcx $0,%rbx #%rsipointesurlachainedecaractres #%rdipointesurnombreissudela #conversion #positionducaractrecourantdansla #chane #metszrolesbitsde%rbxpour #oprationCopieCarDecale #Copielenombrecourantdans%rax #rcuprationducaractre #courantdans%bl # #codeASCII<'A':c'estun #chiffre #c'estunelettre #conversionenentierde4bits #sautversl'insertiondans%rax #%blcontientlechiffre #multiplielenombrecourant #parlabase: #rsultatdans%rax #ajoutelechiffrecourantdans #les4bitsdepoidsfaible #chiffresuivant #conversionterminelorsque #%rcxestngatif #copielenombreobtenu #l'adressedemande #retourneenenlevantles2 #paramtresdelapile

movq (%rdi),%rax ConversionCarMul: movb (%rsi,%rcx),%bl cmpb jb $'a',%bl CarChiffreMul

subb $'a',%bl addb $0xA,%bl jmpCopieCarMul CarChiffreMul: subb $'0',%bl CopieCarMul: mulq Base addb addq cmpq jbe movq ret$16 %bl, %al

$1,%rcx $15,%rcx ConversionCarMul %rax,(%rdi)

########################################################################### #### EntierVersHexaDivise: movq 8(%rsp),%rdi movq 16(%rsp),%rax movq $15,%rcx ConversionChiffreDivise: movq $0,%rdx divq Base #%rdipointesurlachainedecaractres #%eaxcontientlenombreconvertir #positionducaractrecourantdansla #chane #divise%rdx:%raxpar16:lerestede #ladivision%rdx #estlechiffredepoidsfaible

page 105 sur 116

http://www.jourlin.com #dunombre #convertiret #lequotient(%rax)contientles #autreschiffresconvertir cmpb $0xA,%dl jb ValeurChiffreDivise #valeur<10 subb $0xA,%dl #c'estunelettre addb $'a',%dl #%dlcontientsoncodeASCII jmp CopieDivise #sautverslacopie ValeurChiffreDivise: addb $'0',%dl #%dlcontientlecodeASCIIduchiffre CopieDivise: movb %dl,(%rdi,%rcx) #copiedanslachainelaposition%rcx subq $1,%rcx #chiffresuivant jns ConversionChiffreDivise #conversionterminelorsque%rcxest #ngatif ret$16 #retourneenenlevantles2paramtresde #lapile ########################################################################### ### EntierVersHexaDecale: movq 8(%rsp),%rdi movq 16(%rsp),%rax movq $15,%rcx ConversionChiffreDecale: movq %rax,%rdx %dl andb $0xF,%dl shrq $4,%rax #%rdipointesurlachainedecaractres #%eaxcontientlenombreconvertir #positionducaractrecourantdansla #chane #rcuprationduchiffrecourantdans #masqueles4bitsdepoidsfortde%dl #dcale%raxd'unchiffre(4bits) #versladroite

cmpb $0xA,%dl jb ValeurChiffreDecale #valeur<10 subb $0xA,%dl #c'estunelettre addb $'a',%dl #%dlcontientsoncodeASCII jmp CopieDecale #sautverslacopie ValeurChiffreDecale: addb $'0',%dl #%dlcontientlecodeASCIIduchiffre CopieDecale: movb %dl,(%rdi,%rcx) #copiedanslachainelaposition%rcx subq $1,%rcx #chiffresuivant jns ConversionChiffreDecale #conversionterminelorsque #%rcxestngatif ret$16 #retourneenenlevantles2 #paramtresdelapile

16.2.4 Correction du 15.10


.data .align 8 # directive de cration d'une zone de donne

page 106 sur 116

http://www.jourlin.com

m: .double 0.0 # m : mensualit k: .double 0.0 # K : capital emprunt t: .double 0.0 # t : taux annuel proportionnel n: .quad 0 # n : nombre de mensualits NBMOISDANSANNEE: .quad 12 EntreeK: .string "Veuillez entrer le capital emprunt\n" EntreeT: .string "Veuillez entrer le taux annuel proportionnel\n" EntreeN: .string "Veuillez entrer le nombre de mensualits\n" AfficheM: .string "Le montant de la mensualit d'lve %lf\n" FormatEntreeDouble: .string "%lf" # Pour les appels de scanf et printf (stdio.h) FormatEntreeQuad: .string "%llu" # llu : unsigned long long (non signs de 64 bits) .text .globl main # directive de cration d'une zone d'instructions # directive de cration d'une tiquette

# de porte globale main: # main est l'adresse de dbut du programme ################ LECTURE DE K ###################### subq $8,%rsp # cre un paramtre obligatoire, # mais inutilis pour printf movl $EntreeK, %edi # chaine de format movl $1, %eax # stdout call printf # affiche l'invitation entrer k addq $8, %rsp # libre le paramtre fictif movq $k, %rsi # l'adresse du nombre lire movq $FormatEntreeDouble, %rdi # chaine de format movq $0, %rax # stdin call scanf # Lit k ################ LECTURE DE T ###################### subq $8,%rsp # cre un paramtre obligatoire, # mais inutilis pour printf movl $EntreeT, %edi # chaine de format movl $1, %eax # stdout call printf # affiche l'invitation entrer t addq $8, %rsp # libre le paramtre fictif movq $t, %rsi # l'adresse du nombre lire movq $FormatEntreeDouble, %rdi # chaine de format movq $0, %rax # stdin call scanf # Lit t ################ LECTURE DE N ###################### subq $8,%rsp # cre un paramtre obligatoire, # mais inutilis pour printf movl $EntreeN, %edi # chaine de format movl $1, %eax # stdout call printf # affiche l'invitation entrer n addq $8, %rsp # libre le paramtre fictif movq $n, %rsi # l'adresse du nombre lire movq $FormatEntreeQuad, %rdi # chaine de format

page 107 sur 116

http://www.jourlin.com movq call ################ fldl fildl fdivrp fldl fmul fld1 fadd movq fld1 PuissanceN: fdiv subq jnz fld1 fsubp fdivrp fstp fstpl fstp ################ pushq m movsd movl movl call addq movq xorq utilisant fin: int ret $0, %rax # stdin scanf # Lit n Calcul de m ######################### t # st(0) = t NBMOISDANSANNEE # st(0)=12 ; st(1)=t %st(0),%st(1) # st(0)=t/12 k # st(0)=K ; st(1) = t/12 %st(1), %st(0) # st(0)=K.t/12 ; st(1) = t/12 # st(0)=1.0 ; st(1)=K.t/12 ; st(2) = t/12 %st(2), %st(0) # st(0)=1.0+t/12 ; st(1)=K.t/12 ; # st(2) = t/12 n, %rcx # le nombre de mensualits # st(0)=1.0 ; st(1)=1.0+t/12 ; # st(2)=K.t/12 ; st(3) = t/12 %st(1), %st(0) $1, %rcx PuissanceN # st(0)=(1.0+t/12)^-rcx ;st(1)=1.0+t/12 ; # st(2)=K.t/12 ; st(3) = t/12 # rcx=rcx-1 # vers puissance suivante # st(0)=1.0 ; st(1)=(1.0+t/12)^n ; # st(2)=1.0+t/12 ; st(3)=K.t/12 ; #st(4) = t/12 # st(0)=1.0-(1.0+t/12)^n ; # st(1)=1.0+t/12 ; st(2)=K.t/12 ; # st(3) = t/12 # st(0)=1.0+t/12 ; # st(1)=(K.t/12)/(1.0-(1.0+t/12)^n) ; # st(2) = t/12 # st(0)=(K.t/12)/(1.0-(1.0+t/12)^n) ; # st(1) = t/12 # stocke le rsultat ; st(0)=t/12 # vide la pile des registres flottants

%st(0), %st(1) %st(0), %st(2) %st(0) m %st(0)

Affiche le rsultat ################# # sauve m m, %xmm0 $AfficheM, %edi # chaine de format $1, %eax # stdout printf $8, %rsp # libre m $1,%rax # slection de la fonction exit du systme %rbx,%rbx # mise zro du 1er paramtre en $0x80 # xor, c'est dire ou exclusif # appel de l'interruption 128 -> GNU/Linux

16.2.5 Calcul sur les nombres complexes (flottants) :


.data Nombre1: .double 12.34 .double5.678 #partierelle:unnombreIEEE754de64bits #partieimaginaire:unnombreIEEE754de

page 108 sur 116

http://www.jourlin.com #64bits Nombre2: .double 901.2 .double0.345 Nombre3: .double 0.0 .double0.0 #partierelle:unnombreIEEE754de64bits #partieimaginaire:unnombreIEEE754de #64bits #partierelle:unnombreIEEE754de64bits #partieimaginaire:unnombreIEEE754de #64bits #directivedecrationd'unezoned'instructions #directivedecrationd'unetiquette #deporteglobale #mainestl'adressededbutduprogramme pushq pushq pushq callq pushq pushq pushq callq pushq pushq pushq callq pushq pushq pushq callq $Nombre1 $Nombre2 $Nombre3 AdditionComplexes $Nombre1 $Nombre2 $Nombre3 SoustractionComplexes $Nombre1 $Nombre2 $Nombre3 MultiplicationComplexes $Nombre1 $Nombre2 $Nombre3 DivisionComplexes

.text .globlmain main:

movl$1,%eax #slectiondelafonctionexitdusystme xorl%ebx,%ebx#misezrodu1erparamtreenutilisant #xor,c'estdireouexclusif int$0x80 #appeldel'interruption128>GNU/Linux fin:ret ########################################################################### ######################## AdditionComplexes: movq 24(%rsp),%rsi #adressedupremiernombre:a+b.i movq 16(%rsp),%rdi #adressedusecondnombre:c+d.i fldl (%rsi) #st(0)=a fldl (%rdi) #st(0)=c;st(1)=a faddp %st(0),%st(1) #st(0)=a+c fldl 8(%rsi) #st(0)=b;st(1)=a+c fldl 8(%rdi) #st(0)=d;st(0)=b;st(1)=a+c faddp %st(0), %st(1) #st(0)=b+d;st(1)=a+c

page 109 sur 116

http://www.jourlin.com #%rdipointesurl'adressedursultat #dpileetstockelapartie #imaginaire(b+d) fstpl (%rdi) #dpileetstockelapartierelle(a+c) ret$24 #retourl'appelavecdpilementdes #3paramtres ########################################################################### ######################## SoustractionComplexes: movq 24(%rsp),%rsi #adressedupremiernombre:a+b.i movq 16(%rsp),%rdi #adressedusecondnombre:c+d.i fldl (%rsi) #st(0)=a fldl (%rdi) #st(0)=c;st(1)=a fsubrp %st(0),%st(1) #st(0)=ac fldl 8(%rsi) #st(0)=b;st(1)=ac fldl 8(%rdi) #st(0)=d;st(0)=b;st(1)=ac fsubrp %st(0), %st(1) #st(0)=bd;st(1)=ac movq 8(%rsp),%rdi #%rdipointesurl'adressedursultat fstpl 8(%rdi) #dpileetstockelapartieimaginaire #(bd) fstpl (%rdi) #dpileetstockelapartierelle(ac) ret$24 #retourl'appelavecdpilementdes #3paramtres ########################################################################### ########################## MultiplicationComplexes: movq 24(%rsp),%rsi #adressedupremiernombre:a+b.i movq 16(%rsp),%rdi #adressedusecondnombre:c+d.i #rappel:(a+bi)(c+di)=(acbd)+(ad+bc)i fldl (%rsi) #st(0)=a fldl (%rdi) #st(0)=c;st(1)=a fmulp %st(0),%st(1) #st(0)=ac fldl 8(%rsi) #st(0)=b;st(1)=ac fldl 8(%rdi) #st(0)=d;st(1)=b;st(2)=ac fmulp %st(0),%st(1) #st(0)=bd;st(1)=ac fsubrp %st(0),%st(1) #st(0)=acbd fldl fldl fmulp fldl fldl fmulp faddp movq fstpl (%rsi) 8(%rdi) %st(0),%st(1) 8(%rsi) (%rdi) %st(0),%st(1) %st(0),%st(1) 8(%rsp),%rdi 8(%rdi) #st(0)=a;st(1)=acbd #st(0)=d;st(1)=a;st(2)=acbd #st(0)=ad;st(1)=acbd #st(0)=b;st(1)=ad;st(2)=acbd #st(0)=c;st(1)=b;st(2)=ad; #st(3)=acbd #st(0)=bc;st(1)=ad;st(2)=acbd #st(0)=ad+bc;st(1)=acbd movq fstpl 8(%rsp),%rdi 8(%rdi)

#%rdipointesurl'adressedursultat #dpileetstockelapartieimaginaire #(bd) fstpl (%rdi) #dpileetstockelapartierelle(ac) ret$24 #retourl'appelavecdpilementdes #3paramtres ###########################################################################

page 110 sur 116

http://www.jourlin.com ########################## DivisionComplexes: movq 24(%rsp),%rsi movq 16(%rsp),%rdi fldl fld fmulp fldl fld fmulp faddp fld fldl fldl fsubp fldl fmulp fdivrp movq fstpl fldl fldl faddp fldl fmulp fdivrp movq fstpl (%rdi) %st(0) %st(0),%st(1) 8(%rdi) %st(0) %st(0),%st(1) %st(0),%st(1) %st(0) 8(%rdi) (%rdi) %st(0),%st(1) (%rsi) %st(0),%st(1) %st(0),%st(1) 8(%rsp),%rdx (%rdx) 8(%rdi) (%rdi) %st(0),%st(1) 8(%rsi) %st(0),%st(1) %st(0),%st(1) 8(%rsp),%rdi 8(%rdi)

#adressedupremiernombre:a+b.i #adressedusecondnombre:c+d.i #rappel:(a+bi)/(c+di)= #a(cd)/(c+d)+b(d+c)/(c+d) #st(0)=c #st(0)=c;st(1)=c #st(0)=c #st(0)=d;st(1)=c #st(0)=d;st(1)=d;st(2)=c #st(0)=d;st(1)=c #st(0)=c+d #st(0)=c+d;st(1)=c+d #st(0)=d;st(1)=c+d;st(2)=c+d #st(0)=c;st(1)=d;st(2)=c+d; #st(3)=c+d #st(0)=cd;st(1)=c+d;st(2)=c+d #st(0)=a;st(1)=cd;st(2)=c+d; #st(3)=c+d #st(0)=a(cd);st(1)=c+d; #st(2)=c+d #st(0)=a(cd)/(c+d);st(1)=c+d #%rdxpointesurl'adressedursultat #dpilea(cd)/(c+d)danslapartie #relledursultat #st(0)=c+d #st(0)=d;st(1)=c+d #st(0)=c;st(1)=d;st(2)=c+d #st(0)=c+d;st(1)=c+d #st(0)=b;st(1)=c+d;st(2)=c+d #st(0)=b(c+d);st(1)= c+d #st(0)=b(c+d)/(c+d)

#%rdipointesurl'adressedursultat #dpileb(c+d)/(c+d)danslapartie #imaginairedursultat ret$24 #retourl'appelavecdpilementdes #3paramtres ########################################################################### ##########################

16.2.6 Correction du TD 15.11


.data .align8 m1: .word .word .word .word #directivedecrationd'unezonededonne 0,1,2,3 4,5,6,7 8,9,10,11 12,13,14,15

page 111 sur 116

http://www.jourlin.com m2: .word .word .word .word .long .long .long .long 0,4,8,12 1,5,9,13 2,6,10,14 3,7,11,15 0,0,0,0 0,0,0,0 0,0,0,0 0,0,0,0

m3:

FormatString16: #pourlesappelsprintf .string "%.3d" FormatString32: #pourlesappelsprintf .string "%.6ld" RetourChariot: .string"\n" Annoncem1: .string"matricem1:\n" Annoncem2: .string"matricem2:\n" Annoncem22: .string"matricem2pivote:\n" Annoncem3: .string"m1xm2=\n" .text .globlmain main: #directivedecrationd'unezoned'instructions #directivedecrationd'unetiquette #mainestl'adressededbutduprogramme pushq $Annoncem1 call AfficheChaineSimple pushq $m1 callAfficheMatrice16 #Affichelapremirematrice pushq $Annoncem2 call AfficheChaineSimple pushq $m2 callAfficheMatrice16 #Afficheladeuximematrice pushq $m1 pushq $m2 pushq $m3 call ProduitScalaire pushq $Annoncem22 call AfficheChaineSimple pushq $m2 callAfficheMatrice16 #Afficheladeuximematriceaprs #inversionligne/colonnes pushq $Annoncem3 call AfficheChaineSimple pushq $m3 callAfficheMatrice32 #Affichelamatricersultat movq$1,%rax xorq%rbx,%rbx #slectiondelafonctionexit #dusystme #misezrodu1erparamtreen

page 112 sur 116

http://www.jourlin.com #utilisantxor,c'estdireouexclusif #appeldel'interruption #128>GNU/Linux

int$0x80 fin:ret ProduitScalaire: movq 16(%rsp),%rdi pushq %rdi call movq movq movq movq TraiteLigne: movq TraiteColonne: movq movq pmullw call movw movq pmulhw call shll addl addq addq cmpq jne movq addq cmpq jne ret$24 Pivote:movq Pivote 8(%rsp),%rcx 16(%rsp),%rdi 24(%rsp),%rsi $0,%rax $0,%rbx

#adressedeladeuximematrice #pivotela2ematricepouravoirles #vecteurscolonnes #enlignes #adressedelamatriceproduit #adressedeladeuximematrice #adressedelapremirematrice #indicedelalignedelapremire #matrice #indicedelalignedelatranspose #delasecondematrice

(%rsi,%rax,8),%mm0

#chargelalignedela1ere #matricedansmm0 (%rdi,%rbx,8),%mm1 #chargelacolonnedela2e #matrice(oulignedesa #transpose)dansmm1 %mm1,%mm0 #%mm0:partiebasseduproduit #composanteparcomposante SommeDesComposantes #rdxcontientlasommedes #composantes8bitsde%mm0 %dx,(%rcx) #copiedursultatdansla #matriceproduit (%rsi,%rax,8),%mm0 #chargelalignedela1ere #matricedansmm0 %mm1,%mm0 #%mm0:partiehauteduproduit #composanteparcomposante SommeDesComposantes #rdxcontientlasommedes #composantes8bitsde%mm0 $16,%edx #dcalelersultatde16bitsversla #gauchepourlemettreenpartiehaute %edx,(%rcx) $4,%rcx $1,%rbx $4,%rbx TraiteColonne $0,%rbx $1,%rax $4,%rax TraiteLigne 8(%rsp),%rsi #ajoutelapartiehautedursultatdans #lamatriceproduit #pointesurl'lmentsuivantcalculer #indicedecolonnesuivant #findecolonne? #onrecommencelacolonne0 #lignesuivante #dernireligne?

#adressedelamatricepivoter

page 113 sur 116

http://www.jourlin.com movq movq TraiteElement: cmpq jbe movq addq addq pushw movq addq addq pushw popw movw popw movw ColSuivante: addq cmpq jne movq addq cmpq jne ret$8 $0, $0, %rax %rbx #indicedeligne(l) #indicedecolonne(c) #lmentsuivantsil<=c #onvachangerl'lment(l,c) #avecl'lment(c,l)

%rax, %rbx ColSuivante %rsi,%rdi

%rax,%rdi %rax,%rdi #rdi:base+l*2 (%rdi,%rbx,8) #lment(l,c)>pile %rsi,%rdx %rbx,%rdx %rbx,%rdx #rdx:base+c*2 (%rdx,%rax,8) #lment(c,l)>pile %cx %cx,(%rdi,%rbx,8)#pile>lment(l,c) %cx %cx,(%rdx,%rax,8)#pile>element(c,l) $1,%rbx $4,%rbx TraiteElement $0,%rbx $1,%rax $4,%rax TraiteElement

#retourlacolonne0 #lignesuivante

SommeDesComposantes: pushq %rax pushq %rbx pushq %rcx movq $0, movq %mm0, movb $4, Somme: movq andq addq shrq subb jne popq popq popq ret

%rdx %rax %cl

#sauvegarde #sauvegarde #sauvegarde #lasomme #boucledecl=4jusqu'0exclu #mot16bitsdepoidsfaible #ajoute%rdx #dcale%raxde16bitsversladroite #compteur

%rax, %rbx $0xFFFF,%rbx %rbx, %rdx $16, %rax $1, %cl Somme %rcx %rbx %rax

AfficheMatrice16: movb $4,%cl #indicecolonne movq 8(%rsp),%rsi #pointeurverslamatrice TraiteLigne16: movb $4,%ch #indiceligne TraiteElement16: movq $FormatString16,%rdi #pointeurverslachanede

page 114 sur 116

http://www.jourlin.com #formatage $0,%rax #stdout %rcx #sauvercxcarmodifiparlafonctionprintf %rsi #sauversicarutiliscommeparamtreparla #fonctionprintf $0,%rbx#pournercupererqu'unoctetdans%rsi (%rsi),%bx #rcuprelemot16bitsafficher %rbx, %rsi #%rsiestleparamtrededonne #pourprintf printf %rsi %rcx $2,%rsi #elementsuivantdelamatrice $1,%ch #indicesuivant TraiteElement16 #traitel'lmentsuivantdanslaligne $RetourChariot,%rdi #pointeurverslachanesaut #laligne $0,%rax #stdout %rcx #sauvercxcarmodifiparlafonctionprintf %rsi #sauversicarutiliscommeparamtreparla #fonctionprintf #afficheunsautlaligne

movq pushq pushq movq movw movq call popq popq addq subb jnz movq movq pushq pushq call popq popq subb jnz movq movq call ret$8

printf %rsi %rcx $1,%cl #lignesuivante TraiteLigne16 $RetourChariot,%rdi #pointeurverslachane #sautlaligne $0,%rax #stdout printf #afficheunsautlaligne

AfficheMatrice32: movb $4,%cl #indicecolonne movq 8(%rsp),%rsi #pointeurverslamatrice TraiteLigne32: movb $4,%ch #indiceligne TraiteElement32: movq $FormatString32,%rdi #pointeurverslachane #deformatage movq $0,%rax #stdout pushq %rcx #sauvercxcarmodifiparla #fonctionprintf pushq %rsi #sauversicarutiliscomme #paramtreparlafonctionprintf movq $0,%rbx #pournercupererqu'unoctetdans%rsi movl (%rsi),%ebx #rcuprel'octetafficher movq %rbx, %rsi #%rsiestleparamtrededonnepour #printf call printf popq %rsi popq %rcx

page 115 sur 116

http://www.jourlin.com addq subb jnz movq movq pushq pushq call popq popq subb jnz movq movq call ret$8 $4,%rsi #elementsuivantdelamatrice $1,%ch #indicesuivant TraiteElement32 #traitel'lmentsuivantdanslaligne $RetourChariot,%rdi #pointeurverslachanesaut #laligne $0,%rax #stdout %rcx #sauvercxcarmodifi #parlafonctionprintf %rsi #sauversicarutiliscomme #paramtreparlafonctionprintf printf #afficheunsautlaligne %rsi %rcx $1,%cl #lignesuivante TraiteLigne32 $RetourChariot,%rdi #pointeurverslachanesaut #laligne $0,%rax #stdout printf #afficheunsautlaligne

AfficheChaineSimple: movq 8(%rsp),%rdi movq $0,%rax call printf ret $8

page 116 sur 116

You might also like