Académique Documents
Professionnel Documents
Culture Documents
com
http://www.jourlin.com
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
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
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.
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
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 :
Contenu de la mmoire 01010101 11111111 00000000 11001100 00110011 dbut de la 3e instruction 1re instruction 2e instruction
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)
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 :
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.
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).
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.
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 :
%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
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.
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
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 :
http://www.jourlin.com
ou ngatif (SF=1).
(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 ,
http://www.jourlin.com
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.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
# 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
# # # # #
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
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:
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)
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.
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.
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
http://www.jourlin.com
fiche+6
fiche+7
6(%rsi) an.naiss.
7(%rsi)
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.
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).
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).
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:
http://www.jourlin.com
Tableau rcapitulatif des sauts conditionnels pour les entiers signs et non signs :
Condition de branchement
ZF=1 ZF=0
http://www.jourlin.com
((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
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:
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:
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:
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
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
1
.... call procA ... call procB ... 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} {}
http://www.jourlin.com
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.
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
http://www.jourlin.com
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.
div <x8> effectue la division de ax par <x8> (voir mul). Le quotient est stock dans al et le reste dans ah.
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.
Ici <destination> doit tre un registre gnral dans lequel sera stock le produit de <source> (valeur immdiate, registre ou emplacement mmoire) et de <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.
http://www.jourlin.com
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
http://www.jourlin.com
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
http://www.jourlin.com
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 :
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.
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).
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.
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
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
January 1994.
http://www.gnu.org/software/binutils/manual/gas2.9.1/
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.
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.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.
http://www.jourlin.com
http://www.jourlin.com
Taille opration 8 bits 8 bits 8 bits 8 bits 16 bits 16 bits 16 bits 16 bits
rsultat sign ? ? ? ? ?
?
CF = ? ? ? ? ? ? ? ? ?
OF = ? ? ? ? ? ? ? ? ?
? ?
? ?
http://www.jourlin.com
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).
http://www.jourlin.com
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 -> {}
http://www.jourlin.com
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.
http://www.jourlin.com
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.
http://www.jourlin.com
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
%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
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
http://www.jourlin.com
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
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.
(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?
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 :
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
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.
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.
(3)
movq
$0,
%rcx
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
%bx
(7)
addq
%rbx
%rax
(8)
jnz
Boucle
(9)
Fin:
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
(7)
addq
%rbx
%rax
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.
http://www.jourlin.com
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,
(5) (6)
jg
(7) (8)
2(%rsi, %rcx, 2)
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
jmp cmpb
Rponse :
http://www.jourlin.com
(9) (14)
REPETER:
movb movq
$0, $0,
%bl %rcx
%rcx
movw
%bx
(%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
http://www.jourlin.com
Boucle_Po ur $0, REPETER %bl # compare bl FAUX # TANT QUE permut = VRAI
http://www.jourlin.com
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
# 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
# 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]
http://www.jourlin.com
%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
http://www.jourlin.com
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 :
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
http://www.jourlin.com
SinonP 3 GCD: movq $0,%rdx #initialise partiehautede64 bitsdunumrateur divq %rbx #diviserdx:rax parrbx,%rax contient
cmpq je
popq 5
16(%rsp)
retq
$8
http://www.jourlin.com
pushq 6 pushq
%rbx %rdx
#empileb #empilelereste r
call
PGCD
#appelPGCD(b,r)
PPCM :
movq 1
$0,%rdx
divq
%rbx
#%rax:quotient dea*b/PGCD(a,b)
movq
%rbx,16(%rsp)
http://www.jourlin.com
movq movq
16(%rsp) 8(%rsp)
#Lenombrea #Lenombreb
PPCM: 3
#Lenombrea #Lenombreb
retq
$8
mulq
%rbx
#raxcontient a*b
popq
%rbx
#rbxcontient PGCD(a,b)
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
http://www.jourlin.com
popq 5
16(%rsp)
retq
$8
AlorsP 1 GCD: movq %rax,16(%rsp) #remplacele1er paramtreparle rsultatduPGCD retq $8 #retourneen dpilantle2e paramtre
PPCM: 3
#Lenombrea #Lenombreb
http://www.jourlin.com
movq movq
mulq
popq
%rbx
#rbxcontient PGCD(a,b)
movq 1
$0,%rdx
divq
%rbx
#%rax:quotient dea*b/PGCD(a,b)
movq
%rbx,16(%rsp)
retq
$8
http://www.jourlin.com
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
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
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)
(%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
Exercice : Remplacez A, B, C et D par les registres ou les mnmoniques SIMD qui permettent d'obtenir un programme correct.
http://www.jourlin.com
Rponse :
http://www.jourlin.com
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.
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.
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
http://www.jourlin.com jae FinSi call Permute movb $1,%bl addq $1,%rcx jmpPourTest
FinSi:
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
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.
http://www.jourlin.com
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.
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
#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
########################################################################### ###########################
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
subb $'a',%dl addb $0xA,%dl jmpCopieCarDecale CarChiffreDecale: subb $'0',%dl CopieCarDecale: shlq $4,(%rdi) orb addq cmpq jbe %dl,(%rdi)
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
subb $'a',%bl addb $0xA,%bl jmpCopieCarMul CarChiffreMul: subb $'0',%bl CopieCarMul: mulq Base addb addq cmpq jbe movq ret$16 %bl, %al
########################################################################### #### 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
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
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
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
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
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
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
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 ###########################################################################
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)
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
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
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,%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
%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
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
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