Académique Documents
Professionnel Documents
Culture Documents
Paul A. Carter
20 mars 2005
1 Introduction
1.1
1.2
1.3
1.4
Systmes Numriques
. . . . . . . . . . . . . . . . . . . . . .
1.1.1
Dcimal . . . . . . . . . . . . . . . . . . . . . . . . . .
1.1.2
Binaire
1.1.3
Hexadecimal
. . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
Organisation de l'Ordinateur
. . . . . . . . . . . . . . . . . .
1.2.1
Mmoire . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2.2
Le CPU (processeur) . . . . . . . . . . . . . . . . . . .
1.2.3
. . . . . . . . . . . .
1.2.4
1.2.5
1.2.6
Mode Rel
1.2.7
. . . . . . . . . . . . . . . . . .
1.2.8
. . . . . . . . . . . . . . . . . .
10
1.2.9
Interruptions
. . . . . . . . . . . . . . . . . . . . . . .
11
Langage Assembleur
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
11
1.3.1
Langage Machine . . . . . . . . . . . . . . . . . . . . .
11
1.3.2
Langage d'Assembleur . . . . . . . . . . . . . . . . . .
12
1.3.3
Oprandes d'Instruction . . . . . . . . . . . . . . . . .
13
1.3.4
Instructions de base
. . . . . . . . . . . . . . . . . . .
13
1.3.5
Directives . . . . . . . . . . . . . . . . . . . . . . . . .
14
1.3.6
Entres et Sorties . . . . . . . . . . . . . . . . . . . . .
17
1.3.7
Dbogage
. . . . . . . . . . . . . . . . . . . . . . . . .
18
Crer un Programme . . . . . . . . . . . . . . . . . . . . . . .
19
1.4.1
Premier programme
. . . . . . . . . . . . . . . . . . .
19
1.4.2
23
1.4.3
Assembler le code . . . . . . . . . . . . . . . . . . . . .
24
1.4.4
Compiler le code C . . . . . . . . . . . . . . . . . . . .
24
1.4.5
24
1.4.6
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
25
ii
1.5
Fichier Squelette
. . . . . . . . . . . . . . . . . . . . . . . . .
2.2
2.3
2.4
29
. . . . . . . . . . . . . . . . . . . .
29
2.1.1
29
2.1.2
Extension de signe
32
2.1.3
2.1.4
Programme exemple
2.1.5
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . .
35
. . . . . . . . . . . . . . . . . . .
37
. . . . . . . . . . .
39
Structures de Contrle . . . . . . . . . . . . . . . . . . . . . .
40
2.2.1
Comparaison
. . . . . . . . . . . . . . . . . . . . . . .
40
2.2.2
Instructions de branchement . . . . . . . . . . . . . . .
41
2.2.3
44
44
2.3.1
Instructions if . . . . . . . . . . . . . . . . . . . . . . .
44
2.3.2
Boucles while . . . . . . . . . . . . . . . . . . . . . . .
45
2.3.3
Boucles do while
45
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . .
3.2
26
46
49
. . . . . . . . . . . . . . . . . . . . .
49
. . . . . . . . . . . . . . . . . . . .
49
3.1.2
50
3.1.3
Dcalages arithmtiques . . . . . . . . . . . . . . . . .
50
3.1.4
Dcalages circulaires . . . . . . . . . . . . . . . . . . .
51
3.1.5
Application simple . . . . . . . . . . . . . . . . . . . .
51
Oprations de Dcalage
3.1.1
Dcalages logiques
. . . . . . . . . . . . . . .
52
. . . . . . . . . . . . . . . . . . . . .
52
. . . . . . . . . . . . . . . . . . . . .
53
. . . . . . . . . . . . . . . . . . . . .
53
. . . . . . . . . . . . . . . . . . . . .
53
3.2.4
ET .
L'opration OU .
L'opration XOR
L'opration NOT
3.2.5
L'instruction
. . . . . . . . . . . . . . . . . . . .
54
3.2.6
54
3.3
55
3.4
58
3.2.1
3.2.2
3.2.3
L'opration
TEST
. . . . . . . . . . . . . . . . . . . . .
3.4.1
3.4.2
. . . . . . . . . . . . .
58
. . . . . . . . .
59
3.5
60
3.5.1
61
3.6
. . . . . . . . . . . . . . . . . . . . . . . . .
63
3.6.1
Mthode une
. . . . . . . . . . . . . . . . . . . . . . .
63
3.6.2
Mthode deux . . . . . . . . . . . . . . . . . . . . . . .
64
3.6.3
Mthode trois . . . . . . . . . . . . . . . . . . . . . . .
66
iii
4 Sous-Programmes
69
4.1
Adressage Indirect
. . . . . . . . . . . . . . . . . . . . . . . .
69
4.2
70
4.3
La pile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
72
4.4
. . . . . . . . . . . . . . . . .
73
4.5
Conventions d'Appel . . . . . . . . . . . . . . . . . . . . . . .
74
4.5.1
74
4.5.2
. . . . . . . . . . . . . . .
80
. . . . . . . . . . . . . . . . . . .
82
4.6
Programme Multi-Modules
4.7
4.8
. . . . . . . . . . . . . .
85
4.7.1
86
4.7.2
Etiquettes de fonctions . . . . . . . . . . . . . . . . . .
86
4.7.3
87
4.7.4
4.7.5
4.7.6
4.7.7
Exemples
4.7.8
. . . . . . . . . . . . . . . . . .
. . . . . . .
87
. . . . . . . . . . . . . . . . . .
88
. . . . . . . . . . . . . . .
88
. . . . . . . . . . . . . . . . . . . . . . . . .
89
93
. . . . . . . . . . .
93
4.8.1
Sous-programmes rcursifs . . . . . . . . . . . . . . . .
94
4.8.2
96
5 Tableaux
5.1
5.2
99
Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . .
99
5.1.1
99
5.1.2
5.1.3
5.1.4
Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . 103
5.1.5
Tableaux Multidimensionnels
. . . . . . . . . . . . . . . . . . .
6.2
. . . . . . . . . . . . . 102
. . . . . . . . . . . . . . 107
. . . . . . . . . . . . . . . . 111
5.2.2
Le prxe d'instruction
5.2.3
5.2.4
5.2.5
Exemple . . . . . . . . . . . . . . . . . . . . . . . . . . 115
REP
. . . . . . . . . . . . . . . 113
REPx
6 Virgule Flottante
6.1
. . . . . . . . . . . 100
. . . . . . . . . . . . . 114
121
. . . . . . . . . . . . . . 121
6.1.1
. . . . . . . . . . . . . . 121
6.1.2
. . . . . . . . . . . . . . . 126
6.2.1
Addition . . . . . . . . . . . . . . . . . . . . . . . . . . 127
6.2.2
Soustraction . . . . . . . . . . . . . . . . . . . . . . . . 127
iv
6.3
6.2.3
Multiplication et division
6.2.4
Le Coprocesseur Arithmtique
. . . . . . . . . . . . . . . . 128
6.3.1
Matriel . . . . . . . . . . . . . . . . . . . . . . . . . . 129
6.3.2
Instructions . . . . . . . . . . . . . . . . . . . . . . . . 130
6.3.3
Exemples
6.3.4
6.3.5
6.3.6
. . . . . . . . . . . . . . . . . . . . . . . . . 136
7 Structures et C++
7.1
7.2
. . . . . . . . . . 128
. . . . . . . . . . . . . . . . . 129
. . . . . . . . . . . . 139
149
Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
7.1.1
Introduction . . . . . . . . . . . . . . . . . . . . . . . . 149
7.1.2
Alignement en mmoire
7.1.3
7.1.4
Assembleur et C++
. . . . . . . . . . . . . . . . . 150
. . . . . . . . . . . . . . . . . . . . . . . 157
7.2.1
7.2.2
Rfrences . . . . . . . . . . . . . . . . . . . . . . . . . 161
7.2.3
7.2.4
Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
7.2.5
Hritage et Polymorphisme
7.2.6
A Instructions 80x86
. . . . . . . . . . . . . . . 172
181
A.1
A.2
Index
190
Prface
Objectif
L'objectif de ce livre est de permettre au lecteur de mieux comprendre
comment les ordinateurs fonctionnent rellement un niveau plus bas que les
langages de programmation comme Pascal. En ayant une comprhension plus
profonde de la faon dont fonctionnent les ordinateurs, le lecteur peu devenir
plus productif dans le dveloppement de logiciel dans des langages de plus
haut niveau comme le C et le C++. Apprendre programmer en assembleur
est un excellent moyen d'atteindre ce but. Les autres livres d'assembleur
pour PC apprennent toujours programmer le processeur 8086 qu'utilisait
le PC originel de 1981 ! Le processeur 8086 ne supportait que le mode
rel.
protg
vi
PRFACE
exemples pour toutes ces plateformes sont disponibles sur mon site Web :
tous
Remerciements
L'auteur voutrait remercier les nombreux programmeurs qui ont contribu au mouvement Libre/Open Source. Tous les programmes et mme ce
livre lui-mme ont t crs avec des logiciels gratuits. L'auteur voudrait
remercier en particulier John S. Fine, Simon Tatham, Julian Hall et les
autres dveloppeurs de l'assembleur NASM sur lequel tous les exemples de ce
livre sont bass ; DJ Delorie pour le dveloppement du compilateur C/C++
DJGPP utilis ; les nombreuses personnes qui ont contribu au compilateur
GNU gcc sur lequel DJGPP est bas ; Donald Knuth et les autres pour avoir
vii
Page de l'auteur
Page NASM sur SourceForge
DJGPP
Assembleur Linux
The Art of Assembly
USENET
Documentation Intel
http://www.drpaulcarter.com/
http://sourceforge.net/projects/nasm/
http://www.delorie.com/djgpp
http://www.linuxassembly.org/
http://webster.cs.ucr.edu/
comp.lang.asm.x86
http://developer.intel.com/design/Pentium4/documentation.htm
Ractions
L'auteur accepte toute raction sur ce travailThe author welcomes any
feedback on this work.
E-mail: pacman128@gmail.com
WWW: http://www.drpaulcarter.com/pcasm
viii
PRFACE
Chapitre 1
Introduction
1.1
Systmes Numriques
1.1.1 Dcimal
Les nombres en base 10 sont constitus de 10 chires possibles (0-9).
Chaque chire d'un nombre est associ une puissance de 10 selon sa position
dans le nombre. Par exemple :
1.1.2 Binaire
Les nombres en base 2 sont composs de deux chires possibles (0 et
1). Chaque chire est associ une puissance de 2 selon sa position dans le
nombre (un chire binaire isol est appel bit). Par exemple :
110012 = 1 24 + 1 23 + 0 22 + 0 21 + 1 20
= 16 + 8 + 1
= 25
Cet exemple montre comment passer du binaire au dcimal. Le tableau 1.1
montre la reprsentation binaire des premiers nombres.
i.e., des
CHAPITRE 1.
INTRODUCTION
Decimal
Binary
Decimal
Binary
0000
1000
0001
1001
0010
10
1010
0011
11
1011
0100
12
1100
0101
13
1101
0110
14
1110
0111
15
1111
No previous carry
Previous carry
+0
+1
+0
+1
+0
+1
+0
+1
c
Fig. 1.1 Addition binaire (c signie
carry, retenue)
110112
+100012
1011002
Si l'on considre la division dcimale suivante :
1234 10 = 123 r 4
on peut voir que cette division spare le chire le plus droite du nombre
et dcale les autres chires d'une position vers la droite. Diviser par deux
eectue une opration similaire, mais pour les chires binaires du nombre.
Considrons la division binaire suivante
1 :
bit le moins
signicatif (lsb, least signicant bit). Le chire le plus gauche est appel
le bit le plus signicatif (msb, most signicant bit). L'unit de base de la
mmoire consiste en un jeu de 8 bits appel octet (byte).
1
L'indice 2 est utilis pour indiquer que le nombre est reprsent en binaire, pas en
dcimal
1.1.
SYSTMES NUMRIQUES
Decimal
Binary
25 2 = 12 r 1 11001 10 = 1100 r 1
12 2 = 6 r 0
1100 10 = 110 r 0
62=3r 0
110 10 = 11 r 0
32=1r 1
11 10 = 1 r 1
12=0r 1
1 10 = 0 r 1
Donc
2510 = 110012
589 16 = 36 r 13
36 16 = 2 r 4
2 16 = 0 r 2
Donc
589 = 24D16
Fig. 1.3
1.1.3 Hexadecimal
Les nombres hexadcimaux utilisent la base 16. L'hexadcimal (ou
hexa
en abrg) peut tre utilis comme notation pour les nombres binaires. L'hexa
a 16 chires possibles. Cela pose un problme car il n'y a pas de symbole
utiliser pour les chires supplmentaires aprs 9. Par convention, on utilise
des lettres pour les reprsenter. Les 16 chires de l'hexa sont 0-9 puis A, B,
C, D, E et F. Le chire A quivaut 10 en dcimal, B 11, etc. Chaque chire
d'un nombre en hexa est associ une puissance de 16. Par exemple :
Pour convertir depuis le dcimal vers l'hexa, utilisez la mme ide que celle
utilise pour la conversion binaire en divisant par 16. Voyez la Figure 1.3
pour un exemple.
CHAPITRE 1.
INTRODUCTION
La raison pour laquelle l'hexa est utile est que la conversion entre l'hexa et
le binaire est trs simple. Les nombres binaires deviennent grands et incomprhensibles rapidement. L'hexa fournit une faon beaucoup plus compacte
pour reprsenter le binaire.
Pour convertir un nombre hexa en binaire, convertissez simplement chaque
chire hexa en un nombre binaire de 4 bits. Par exemple,
en
24D16
est converti
24D16
est faux. Le conversion du binaire vers l'hexa est aussi simple. On eectue
la mme chose, dans l'autre sens. Convertissez chaque segment de 4 bits du
binaire vers l'hexa. Commencez droite, pas gauche, du nombre binaire.
Cela permet de s'assurer que le procd utilise les segments de 4 bits cor-
rects . Exemple :
110
6
0000
0
0101
5
1010
0111
7
11102
E16
1.2
Organisation de l'Ordinateur
1.2.1 Mmoire
La mmoire est mesure en
L'unit mmoire de base est l'octet. Un ordinateur avec 32 mega octets
10
kilo octets ( 2
= 1024 oc- de mmoire peut stocker jusqu' environ 32 millions d'octets d'informations.
20
tets), mega octets (2
= Chaque octet en mmoire est tiquet par un nombre unique appel son
oc-
oc-
2A
45
B8
20
8F
CD
12
2E
Fig. 1.4
Adresses Mmoire
Souvent, la mmoire est utilise par bouts plus grand que des octets
isoles. Sur l'architecture PC, on a donn des noms ces portions de mmoire
plus grandes, comme le montre le Tableau 1.2.
2
S'il n'est pas clair que le point de dpart est important, essayez de convertir l'exemple
en partant de la gauche
1.2.
ORGANISATION DE L'ORDINATEUR
mot
2 octets
double mot
4 octets
quadruple mot
8 octets
paragraphe
16 octets
Tab. 1.2
Units Mmoire
Toutes les donnes en mmoire sont numriques. Les caractres sont stocks en utilisant un
code caractre
ASCII
(Ameri-
004116 .
registres. Le processeur
peut accder aux donnes dans les registres plus rapidement qu'aux donnes
en mmoire. Cependant, le nombre de registres d'un processeur est limit,
donc le programmeur doit faire attention n'y conserver que les donnes
actuellement utilises.
Les instructions que peut excuter un type de processeur constituent
le
langage machine.
plus basique que les langages de plus haut niveau. Les instructions du langage machine sont encodes en nombres bruts, pas en format texte lisible.
Un processeur doit tre capable de dcoder les instructions trs rapidement
pour fonctionner ecacement. Le langage machine est conu avec ce but en
tte, pas pour tre facilement dchirable par les humains. Les programmes
crits dans d'autres langages doivent tre convertis dans le langage machine
3
En fait, l'ASCII n'utilise que les 7 bits les plus faible et donc n'a que 128 valeurs
utiliser.
CHAPITRE 1.
INTRODUCTION
compilateur
est un
programme qui traduit les programmes crits dans un langage de programmation en langage machine d'une architecture d'ordinateur particulire. En
gnral, chaque type de processeur a son propre langage machine unique.
C'est une des raisons pour lesquelles un programme crit pour Mac ne peut
pas tre excut sur un PC.
Les ordinateurs utilisent une
GHz
horloge
vitesse d'horloge ). Lorsque vous achetez un ordinateur 1,5GHz, 1,5GHz est la frquence
seconde. Un processeur de cette horloge. L'horloge ne dcompte pas les minutes et les secondes. Elle
1,5GHz a 1,5 milliards
bat simplement un rythme constant. Les composants lectroniques du prod'impulsions horloge par
cesseur utilisent les pulsations pour eectuer leurs oprations correctement,
seconde.
comme le battement d'un mtronome aide jouer de la musique un rythme
8088,8086:
identiques. Ils taient les processeurs utiliss dans les tous premiers
PC. Il orent plusieurs registres 16 bits : AX, BX, CX, DX, SI, DI,
BP, SP, CS, DS, SS, ES, IP, FLAGS. Ils ne supportent que jusqu'
1Mo de mmoire et n'oprent qu'en
Il apporte
bits.
mode protg 16
80386:
tend la plupart des registres 32 bits (EAX, EBX, ECX, EDX, ESI,
EDI, EBP, ESP, EIP) et ajoute deux nouveaux registres 16 bits : FS
1.2.
ORGANISATION DE L'ORDINATEUR
AX
AH
AL
80486/Pentium/Pentium Pro:
Pentium MMX:
eXentions) au Pentium. Ces instructions peuvent acclrer des oprations graphiques courantes.
Pentium II:
indiquent quelle zone de la mmoire est utilise pour les direntes parties
d'un programme. CS signie Code Segment, DS Data Segment, SS Stack
Segment (segment de pile) et ES Extra Segment. ES est utilis en tant que
CHAPITRE 1.
INTRODUCTION
registre de segment temporaire. Des dtails sur ces registres se trouvent dans
les Sections 1.2.6 et 1.2.7.
Le registre de pointeur d'instruction (IP) est utilis avec le registre CS
pour mmoriser l'adresse de la prochaine instruction excuter par le processeur. Normalement, lorsqu'une instruction est excute, IP est incrment
pour pointer vers la prochaine instruction en mmoire.
Le registre FLAGS stocke des informations importantes sur les rsultats
d'une instruction prcdente. Ces rsultats sont stocks comme des bits individuels dans le registre. Par exemple, le bit Z est positionn 1 si le rsultat
de l'instruction prcdente tait 0 ou 0 sinon. Toutes les instructions ne
modient pas les bits dans FLAGS, consultez le tableau dans l'appendice
pour voir comment chaque instruction aecte le registre FLAGS.
mot
mot
mot
1.2.
ORGANISATION DE L'ORDINATEUR
des
640Ko
du
octet). Les adresses valides vont de 00000 FFFFF (en hexa). Ces adresses limite
DOS
?
Le
BIOS
requiert
ncessitent un nombre sur 20 bits. Cependant, un nombre de 20 bits ne
tiendrait dans aucun des registres 16 bits du 8086. Intel a rsolu le problme,
en utilisant deux valeurs de 16 bits pour dterminer une adresse. La premire
valeur de 16 bits est appele le
slecteur.
tre stockes dans des registres de segment. La seconde valeur de 16 bits est
16 selecteur + deplacement
Multiplier par 16 en hexa est facile, il sut d'ajouter un 0 la droite du
nombre. Par exemple, l'adresse physique rfrence par 047C:0048 est obtenue de la faon suivante :
047C0
+0048
04808
De fait, la valeur du slecter est un numro de paragraphe (voir Tableau 1.2).
Les adresses relles segmentes ont des inconvnients :
Une seule valeur de slecteur peut seulement rfrencer 64Ko de mmoire (la limite suprieure d'un dplacement de 16 bits). Que se passet-il si un programme a plus de 64Ko de code ? Une seule valeur de CS
ne peut pas tre utilise pour toute l'excution du programme. Le programme doit tre divis en sections (appeles
segments )
de moins de
Dans les deux modes, les programmes sont diviss en segments. En mode rel,
ces segments sont des positions xes en mmoire et le slecteur indique le
10
CHAPITRE 1.
INTRODUCTION
de base d'un systme de mmoire virtuelle est de ne garder en mmoire que les
programmes et les donnes actuellement utiliss. Le reste des donnes et du
code sont stocks temporairement sur le disque jusqu' ce qu'on aie nouveau
besoin d'eux. Dans le mode protg 16 bits, les segments sont dplacs entre
la mmoire et le disque selon les besoins. Lorsqu'un segment est recharg
en mmoire depuis le disque, il est trs probable qu'il sera un endroit
en mmoire dirent de celui o il tait avant d'tre plac sur le disque.
Tout ceci est eectu de faon transparente par le systme d'exploitation. Le
programme n'a pas a tre crit diremment pour que la mmoire virtuelle
fonctionne.
En mode protg, chaque segment est assign une entre dans un tableau de descripteurs. Cette entre contient toutes les informations dont le
systme a besoin propos du segment. Ces informations indiquent : s'il est
actuellement en mmoire ; s'il est en mmoire, o il se trouve ; les droits
d'accs (
journaliste
PC
bien
connu a baptis le proces- sont toujours des quantits sur 16 bits. En consquence, Les tailles de segseur 286 cerveau mort ment sont toujours limites au plus 64Ko. Cela rend l'utilisation de grands
(brain dead)
tableaux problmatique.
pages .
avec des pages plutt qu'avec des segments. Cela implique que seules
certaines parties d'un segment peuvent tre prsentes en mmoire un
instant donn. En mode 16 bits du 286, soit le segment en entier est
en mmoire, soit rien n'y est. Ce qui n'aurait pas t pratique avec les
segments plus grands que permet le mode 32 bits.
1.3.
11
LANGAGE ASSEMBLEUR
1.2.9 Interruptions
Quelques fois, le ot ordinaire d'un programme doit tre interrompu pour
traiter des vnements qui requirent une rponse rapide. Le matriel d'un
ordinateur ore un mcanisme appel
interruptions
etc.). Les interruptions provoquent le passage du contrle un gestionnaire d'interruptions. Les gestionnaires d'interruptions sont des routines
curseur
teurs d'interuptions
vec-
p.e.,
CD-ROM et les cartes son). Les interruptions internes sont souleves depuis le processeur, cause d'une erreur ou d'une instruction d'interruption.
Les interruptions erreur sont galement appeles
traps.
Les interruptions
1.3
Langage Assembleur
4
Cependant, ils peuvent galement utiliser une interface de plus bas niveau au niveau
du noyau
12
CHAPITRE 1.
INTRODUCTION
tions des processeurs 80x86 varient en taille. L'opcode est toujours au dbut
de l'instruction. Beaucoup d'instructions comprennent galement les donnes
Le langage machine est trs dicile programmer directement. Dchiffrer la signication d'instructions codes numriquement est fatigant pour
des humains. Par exemple, l'instruction qui dit d'ajouter les registres EAX
et EBX et de stocker le rsultat dans EAX est encode par les codes hexadcimaux suivants :
03 C3
C'est trs peu clair. Heureusement, un programme appel un
assembleur
beaucoup plus
mnmonique oprande(s)
Un
assembleur
compilateurs
sont des programmes qui font des conversions similaires pour les langages de
programmation de haut niveau. Un assembleur est beaucoup plus simple
Cela a pris plusieurs an- qu'un compilateur. Chaque instruction du langage d'assembleur reprsente
nes aux scientiques de directement une instruction machine. Les instructions d'un langage de plus
l'informatique pour conce- haut niveau sont
plus complexes et peuvent requrir beaucoup
voir le simple fait d'crire
d'instructions machine.
un compilateur !
Une autre dirence importante entre l'assembleur et les langages de
beaucoup
haut niveau est que comme chaque type de processeur a son propre langage
machine, il a galement son propre langage d'assemblage. Porter des programmes assembleur entre direntes architectures d'ordinateur est
beaucoup
1.3.
13
LANGAGE ASSEMBLEUR
registre :
du processeur.
mmoire :
immdiat :
Ce sont des valeurs xes qui sont listes dans l'instruction elle-
implicite :
src
dest .
les deux oprandes ne peuvent pas tre tous deux des oprandes mmoire.
Cela nous montre un autre caprice de l'assembleur. Il y a souvent des rgles
quelque peu arbitraires sur la faon dont les direntes instructions sont
utilises. Les oprandes doivent galement avoir la mme taille. La valeur de
AX ne peut pas tre stocke dans BL.
Voici un exemple (les points-virgules marquent un commentaire) :
mov
mov
L'instruction
add
add
eax, 4
al, ah
ADD
; eax = eax + 4
; al = al + ah
14
CHAPITRE 1.
L'instruction
sub
sub
SUB
INTRODUCTION
bx, 10 ; bx = bx - 10
ebx, edi ; ebx = ebx - edi
Les instructions
INC
et
DEC
inc
dec
ecx
dl
ADD
et
SUB
INC
et
DEC
quivalentes.
; ecx++
; dl--
1.3.5 Directives
Une
directive
la dnition de constantes
la dnition de mmoire pour stocker des donnes
grouper la mmoire en segment
inclure des codes sources de faon conditionnelle
inclure d'autres chiers
La directive equ
La directive
equ
sont des constantes nommes qui peuvent tre utilises dans le programme
assembleur. Le format est le suivant :
pas
La directive %dene
Cette directive est semblable la directive
1.3.
15
LANGAGE ASSEMBLEUR
Unit
Lettre
octet
mot
double mot
quadruple mot
dix octets
MOV.
RESX
SIZE
et
DX
deux faons. Elles peuvent tre rednies et peuvent tre plus que de simples
nombres constants.
Directives de donnes
Les directives de donnes sont utilises dans les segments de donnes
pour rserver de la place en mmoire. Il y a deux faons de rserver de la
mmoire. La premire ne fait qu'allouer la place pour les donnes ; la seconde
alloue la place et donne une valeur initiale. La premire mthode utilise une
des directives
RESX.
Le
de l'objet (ou des objets) qui sera stock. Le Tableau 1.3 montre les valeurs
possibles.
La seconde mthode (qui dnit une valeur initiale) utilise une des directives
DX.
Les lettres
labels
(tiquettes). Les labels permettent de faire rfrence facilement aux emplacements mmoire dans le code. Voici quelques exemples :
L1
L2
L3
L4
L5
L6
L7
L8
db
dw
db
db
db
dd
resb
db
0
1000
110101b
12h
17o
1A92h
1
"A"
;
;
;
;
;
;
;
;
Les doubles et simples quotes sont traites de la mme faon. Les dnitions de donnes conscutives sont stockes squentiellement en mmoire.
C'est dire que le mot L2 est stock immdiatement aprs L1 en mmoire.
Des squences de mmoire peuvent galement tre dnies.
L9
db
0, 1, 2, 3
; definit 4 octets
16
L10
L11
CHAPITRE 1.
db
db
DD
INTRODUCTION
DQ ne peut tre utilise que pour dnir des constantes virgule ottante en
double prcision.
Pour les grandes squences, la directive de NASM
Cette directive rpte son oprande un certain nombre de fois. Par exemple :
L12
L13
times 100 db 0
resw 100
Souvenez vous que les labels peuvent tre utiliss pour faire rfrence
des donnes dans le code. Il y a deux faons d'utiliser les labels. Si un
label simple est utilis, il fait rfrence l'adresse (ou oset) de la donne.
Si le label est plac entre crochets ([]), il est interprt comme la donne
cette adresse. En d'autres termes, il faut considrer un label comme un
pointeur
1
2
3
4
5
6
7
mov
mov
mov
mov
add
add
mov
al, [L1]
eax, L1
[L1], ah
eax, [L6]
eax, [L6]
[L6], eax
al, [L6]
;
;
;
;
;
;
;
La ligne 7 de cet exemple montre une proprit importante de NASM. L'assembleur ne garde
pas
C'est au programmeur de s'assurer qu'il (ou elle) utilise un label correctement. Plus tard, il sera courant de stocker des adresses dans les registres et
utiliser les registres comme un pointeur en C. L encore, aucune vrication
n'est faite pour savoir si le pointeur est utilis correctement. A ce niveau,
l'assembleur est beaucoup plus sujet erreur que le C.
Considrons l'instruction suivante :
mov
[L6], 1
; stocke 1 en L6
quoi ? Parce que l'assembleur ne sait pas s'il doit considrer 1 comme un
5
Les nombres virgule ottante en simple prcision sont quivalent aux variables float
en C.
1.3.
17
LANGAGE ASSEMBLEUR
octet, un mot ou un double mot. Pour rparer cela, il faut ajouter un spcicateur de taille :
mov
dword [L6], 1
; stocke 1 en L6
L6.
et
TWORD6 .
%include.
7 :
routines d'E/S de l'auteur
%include "asm_io.inc"
Pour utiliser une de ces routines d'achage, il faut charger EAX avec la
Elle saute une autre portion de code mais revient son origine une fois
6
TWORD dnit une zone mmoire de 10 octets. Le coprocesseur virgule ottante utilise
ce type de donnes.
7
Le chier asm_io.inc (et le chier objet asm_io que recquiert asm_io.inc)
sont dans les exemples de code tlcharger sur la page web de ce tutorial,
http://www.drpaulcarter.com/pcasm
18
CHAPITRE 1.
print_int
print_char
INTRODUCTION
print_string
stock dans AL
adresse
print_nl
read_int
read_char
EAX.
lit un caractre au clavier et stocke son code ASCII
dans le registre EAX.
Tab. 1.4 Routine d'E/S de l'assembleur
1.3.7 Dbogage
La bibliothque de l'auteur contient galement quelques routines utiles
pour dboguer les programmes. Ces routines de dbogage achent des informations sur l'tat de l'ordinateur sans le modier. Ces routines sont en fait
des
macros
asm_io.inc
dont
nous avons parl plus haut. Les macros sont utilises comme des instructions
ordinaires. Les oprandes des macros sont spars par des virgules.
Il y a quatre routines de dbogage nommes
et
dump_math ;
dump_regs
de l'ordinateur sur
stdout (i.e.
ZF
prend en argument un entier qui est ach galement. Cela peut aider
distinguer la sortie de direntes commandes
dump_mem
dump_regs.
dump_regs).
Le second
1.4.
19
CRER UN PROGRAMME
l'adresse. La mmoire ache commencera au premier multiple de paragraphe avant l'adresse demande.
dump_stack
pile sera prsente dans le Chapitre 4). La pile est organise en double
mots et cette routine les ache de cette faon. Elle prend trois arguments dlimits par des virgules. Le premier est un entier (comme pour
dump_regs).
EBP
dump_math
aprs
avant
EBP.
1.4
dump_regs.
Crer un Programme
beaucoup
plus simple de programmer dans un langage de plus haut niveau qu'en assembleur. De plus, utiliser l'assembleur rend le programme trs dur porter
sur d'autres plateformes. En fait, il est rare d'utiliser l'assembleur tout court.
Alors, pourquoi apprendre l'assembleur ?
1. Quelques fois, le code crit en assembleur peut tre plus rapide et plus
compact que le code gnr par un compilateur.
2. L'assembleur permet l'accs des fonctionnalits matrielles du systme directement qu'il pourrait tre dicile ou impossible utiliser
depuis un langage de plus haut niveau.
3. Apprendre programmer en assembleur aide acqurir une comprhension plus profonde de la faon dont fonctionne un ordinateur.
4. Apprendre programmer en assembleur aide mieux comprendre comment les compilateurs et les langage de haut niveau comme C fonctionnent.
Ces deux dernier points dmontrent qu'apprendre l'assembleur peut tre utile
mme si on ne programme jamais dans ce langage plus tard. En fait, l'auteur
programme rarement en assembleur, mais il utilise les leons qu'il en a tir
tous les jours.
20
1
2
3
4
5
6
CHAPITRE 1.
INTRODUCTION
int main()
{
int ret_status ;
ret_status = asm_main();
return ret_status ;
}
Fig. 1.6 Code de
nomme
asm_main.
driver.c
1
2
3
4
5
6
7
;
;
;
;
;
;
;
first.asm
fichier: first.asm
Premier programme assembleur. Ce programme attend la saisie de deux
entiers et affiche leur somme.
Pour creer l'executable en utilisant djgpp :
nasm -f coff first.asm
gcc -o first first.o driver.c asm_io.o
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
%include "asm_io.inc"
;
; Les donnees initialisees sont placees dans le segment .data
;
segment .data
;
; Ces labels referencent les chaines utilisees pour l'affichage
;
prompt1 db
"Entrez un nombre : ", 0
; N'oubliez pas le 0 final
prompt2 db
"Entrez un autre nombre : ", 0
outmsg1 db
"Vous avez entre ", 0
outmsg2 db
" et ", 0
outmsg3 db
", leur somme vaut ", 0
1.4.
23
24
25
26
27
28
29
30
31
CRER UN PROGRAMME
21
;
; Les donnees non initialisees sont placees dans le segment .bss
;
segment .bss
;
; Ces labels referencent les doubles mots utilises pour stocker les entrees
;
input1 resd 1
input2 resd 1
32
33
34
35
36
37
38
39
40
;
; Le code est place dans le segment .text
;
segment .text
global _asm_main
_asm_main:
enter 0,0
; initialisation
pusha
41
42
43
mov
call
eax, prompt1
print_string
; affiche un message
call
mov
read_int
[input1], eax
; lit un entier
; le stocke dans input1
mov
call
eax, prompt2
print_string
; affiche un message
call
mov
read_int
[input2], eax
; lit un entier
; le stocke dans input2
mov
add
mov
eax, [input1]
eax, [input2]
ebx, eax
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
dump_regs 1
dump_mem 2, outmsg1, 1
;
; Ce qui suit affiche le message resultat en plusieurs etapes
;
mov
eax, outmsg1
call
print_string
; affiche le premier message
22
65
66
67
68
69
70
71
72
73
74
75
CHAPITRE 1.
mov
call
mov
call
mov
call
mov
call
mov
call
call
INTRODUCTION
eax, [input1]
print_int
eax, outmsg2
print_string
eax, [input2]
print_int
eax, outmsg3
print_string
eax, ebx
print_int
print_nl
eax, 0
; affiche input1
; affiche le second message
; affiche input2
; affiche le troisieme message
76
77
78
79
80
popa
mov
leave
ret
first.asm
.data).
Seules les
donnes initialises doivent tre dnies dans ce segment. Dans les lignes 17
21, plusieurs chanes sont dclares. Elle seront aches avec la bibliothque
C et doivent donc se terminer par un caractre
vous qu'il y a une grande dirence entre
et
'0'.
Les donnes non initialises doivent tre dclares dans le segment bss
(appel
.bss
.text
instructions sont places. Notez que le label de code pour la routine main
convention
d'appel C . Cette convention spcie les rgles que le C utilise lorsqu'il com-
pile le code. Il est trs important de connatre cette convention lorsque l'on
interface du C et de l'assembleur. Plus loin, la convention sera prsente
dans son intgralit ; cependant, pour l'instant, il sut de savoir que tous
les symboles C (
soulignement qui leur est ajout par le compilateur C (cette rgle s'applique
spciquement pour DOS/Windows, le compilateur C Linux n'ajoute rien
du tout aux noms des symboles).
La directive
global
par
dfaut. Cela signie que seul le code du mme module peut utiliser le label.
La directive
global
porte externe.
Ce
1.4.
23
CRER UN PROGRAMME
type de label peut tre accd par n'importe quel module du programme.
Le module
gratuit DJGPP
Internet. Il ncessite un PC base de 386 ou plus et tourne sous DOS, Windows 95/98 ou NT. Ce compilateur utilise des chiers objets au format COFF
(Common Object File Format). Pour assembler le chier au format COFF,
utilisez l'option
Le compilateur C Linux est galement un compilateur GNU. Pour convertir le code ci-dessus an qu'il tourne sous Linux, retirez simplement les prxes de soulignement aux lignes 37 et 38. Linux utilise le format ELF (Executable and Linkable Format) pour les chiers objet. Utilisez l'option
pour Linux. Il produit galement un chier objet avec l'extension
o.
-f elf
Les chiers spciques au
disponibles
Borland C/C++ est un autre compilateur populaire. Il utilise le for- compilateur,
sur le site de l'auteur,
mat Microsoft OMF pour les chier objets. Utilisez l'option -f obj pour les
ont
dj
t
modis
compilateurs Borland. L'extension du chier objet sera obj. Le format OMF
pour fonctionner avec le
utilise des directives segment direntes de celles des autres formats objet.
compilateur appropri.
Le segment data (ligne 13) doit tre chang en :
-f win32
9
10
obj.
http://www.delorie.com/djgpp
24
CHAPITRE 1.
INTRODUCTION
format-objet est soit co , soit elf , soit obj soit win32 selon le compilateur
C utilis (Souvenez vous que le chier source doit tre modi pour Linux
et pour Borland).
utilisez :
gcc -c driver.c
L'option
marrage
beaucoup
code de d-
first.exe
(ou juste
first
sous Linux).
first.exe.
gcc
compilera
driver.c
puis liera.
1.4.
25
CRER UN PROGRAMME
-l fichier-listing
nasm
48
49
50
51
52
00000000
00000009
00000011
0000001A
00000023
456E7465722061206E756D6265723A2000
456E74657220616E6F74686572206E756D6265723A2000
prompt1 db
prompt2 db
"Entrez un nombre : ne
", sont
0
pas
places dans le programme termin. Chaque module peut dnir ses propres
labels dans le segment de donnes (et les autres segments galement). Lors
de l'tape d'dition de liens (voir Section 1.4.5), toutes ces dnitions de
labels de segment de donnes sont combines pour ne former qu'un segment
de donnes. Les dplacements naux sont alors calculs par l'diteur de liens.
Voici une petite portion (lignes 54 56 du chier source) du segment text
dans le chier listing :
94 0000002C A1[00000000]
95 00000031 0305[04000000]
96 00000037 89C3
mov
add
mov
eax, [input1]
eax, [input2]
ebx, eax
La troisime colonne montre le code machine gnr par l'assembleur. Souvent le code complet pour une instruction ne peut pas encore tre calcul.
Par exemple, ligne 94, le dplacement (ou l'adresse) de
input1
n'est pas
connu avant que le code ne soit li. L'assembleur peut calculer l'opcode pour
l'instruction
entre crochets car la valeur exacte ne peut pas encore tre calcule. Dans
ce cas, un dplacement temporaire de 0 est utilis car
input1
est au dbut
du segment bss dclar dans ce chier. Souvenez vous que cela ne signie
pas
ractres.
26
CHAPITRE 1.
INTRODUCTION
input2
est
indian.
big endian
et
little endian.
i.e.
etc.
le plus signicatif )
00000004 serait stock sous la forme des quatre octets suivants : 00 00 00 04.
Les mainframes IBM, la plupart des processeurs RISC et les processeur Motorola utilisent tous cette mthode big endian. Cependant, les processeurs de
type Intel utilisent la mthode little endian ! Ici, l'octet le moins signicatif
est stock en premier. Donc, 00000004 est stock en mmoire sous la forme
04 00 00 00. Ce format est cod en dur dans le processeur et ne peut pas
tre chang. Normalement, le programmeur n'a pas besoin de s'inquiter du
format utilis. Cependant, il y a des circonstances o c'est important.
1. Lorsque des donnes binaires sont transfres entre dirents ordinateurs (soit via des chiers, soit via un rseau).
2. Lorsque des donnes binaires sont crites en mmoire comme un entier
multioctet puis relues comme des octets individuels ou
vice versa.
1.5
Fichier Squelette
La Figure 1.7 montre un chier squelette qui peut tre utilis comme
point de dpart pour l'criture de programmes assembleur.
1.5.
1
2
3
4
5
27
FICHIER SQUELETTE
skel.asm
%include "asm_io.inc"
segment .data
;
; Les donnees initialisees sont placees dans ce segment de donnees
;
6
7
8
9
10
segment .bss
;
; Les donnees non initialisees sont placees dans le segment bss
;
11
12
13
14
15
16
segment .text
global _asm_main
_asm_main:
enter 0,0
pusha
; initialisation
17
18
19
20
21
;
; Le code est place dans le segment text. Ne modifiez pas le code
; place avant ou apres ce commentaire.
;
22
23
24
25
26
popa
mov
leave
ret
eax, 0
; retour au C
skel.asm
28
CHAPITRE 1.
INTRODUCTION
Chapitre 2
111000,
bit de signe.
Ce bit vaut 0 si le
Grandeur signe
La premire mthode est la plus simple, elle est appele
grandeur signe.
Elle reprsente l'entier en deux parties. La premire partie est le bit de signe
et la seconde est la grandeur entire. Donc 56 serait reprsent par l'octet
00111000
on inverse le bit de signe. Cette mthode est intuitive mais a ses inconvnients. Tout d'abord, il y a deux valeurs possibles pour 0 :
0 (10000000).
+0 (00000000)
et
30
CHAPITRE 2.
l'arithmtique pour le processeur. De plus, l'arithmtique gnrale est galement complique. Si l'on ajoute 10
56,
Complment 1
La seconde mthode est appele reprsentation en
complment un.
le complment un de
56.
00111000
11000111
est la reprsenta-
00000000 (+0)
et
11111111 (0).
+56
hexa. pour trouver le complment un, tez chaque chire de F pour obtenir
C7 en hexa. Cela concorde avec le rsultat ci-dessus.
Complment deux
Les deux premires mthodes dcrites ont t utilises sur les premiers
ordinateurs. Les ordinateurs modernes utilisent une troisime mthode appele reprsentation en
complment deux.
On trouve le e complment
11000111.
00111000
Puis, on ajoute un :
11000111
+
1
11001000
Dans la notation en complment deux, calculer le complment deux
est quivalent trouver l'oppos d'un nombre. Donc,
sentation en complment deux de
56.
11001000
est la repr-
2.1.
31
Nombre
Reprsentation Hexa
00
01
127
7F
-128
80
-127
81
-2
FE
-1
FF
11001000
en ajoutant un
au complment un.
00110111
+
1
00111000
Lorsque l'on eectue l'addition dans l'opration de complmentation
deux, l'addition du bit le plus gauche peut produire une retenue. Cette retenue n'est
pas utilise. Souvenez vous que toutes les donnes sur un ordinateur
sont d'une taille xe (en terme de nombre de bits). Ajouter deux octets produit toujours un rsultat sur un octet (comme ajouter deux mots donne un
mot,
etc .)
11111111
+
1
c 00000000
o
cette retenue, mais elle n'est pas stocke dans le rsultat). Donc, en notation
en complment deux, il n'y a qu'un zro. Cela rend l'arithmtique en
complment deux plus simple que les mthodes prcdentes.
En utilisant la notation en complment deux, un octet sign peut tre
utilis pour reprsenter les nombres
128
+127.
32, 768
+32, 767 peuvent tre reprsents. +32, 767 est reprsent par 7FFF, 32, 768
par 8000, -128 par FF80 et -1 par FFFF. Les nombres en complment deux
sur 32 bits vont de
milliards
+2
milliards environ.
32
CHAPITRE 2.
+255
1 sign ou
mov
mov
ax, 0034h
cl, al
Bien sr, si le nombre ne peut pas tre reprsent correctement dans une
plus petite taille, rduire la taille ne fonctionne pas. Par exemple, si
0134h (ou 308 en dcimal) alors le code ci-dessus mettrait quand
AX valait
mme CL
34h. Cette mthode fonctionne la fois avec les nombres signs et non
signs. Considrons les nombres signs, si
alors
dans
La rgle pour les nombres non signs est que tous les bits retirs soient
0 an que la convertion soit correcte. La rgle pour les nombres signs est
que les bits retirs doivent soit tous tre des 1 soit tous des 0. De plus, Le
premier bit ne pas tre retir doit valoir la mme chose que ceux qui l'ont
t. Ce bit sera le bit de signe pour la valeur plus petite. Il est important
qu'il ait la mme valeur que le bit de signe original !
tendre
que les nouveaux bits deviennent des copies du bit de signe. Comme le bit
2.1.
33
de signe de FF est 1, les nouveaux bits doivent galement tre des uns,
pour donner FFFF. Si le nombre sign 5A (90 en dcimal) tait tendu, le
rsultat serait 005A.
Il y a plusieurs instructions fournies par le 80386 pour tendre les nombres.
Souvenez vous que l'ordinateur ne sait pas si un nombre est sign ou non.
C'est au programmeur d'utiliser l'instruction adquate.
Pour les nombres non signs, on peut placer des 0 dans les bits de poids
fort de faon simple en utilisant l'instruction
mov
ah, 0
le mot non sign de AX en un double mot non sign dans EAX. Pourquoi
pas ? Il n'y a aucune faon d'accder aux 16 bits de poids fort de EAX
dans un
truction :
movzx
movzx
movzx
movzx
eax, ax
eax, al
ax, al
ebx, ax
;
;
;
;
tend
tend
tend
tend
ax
al
al
ax
eax
eax
ax
ebx
Pour les nombres signs, il n'y a pas de faon simple d'utiliser l'instruction
MOV
CBW
Octet en Mot) tend le signe du registre AL dans AX. Les oprandes sont
implicites. L'instruction
CWD
CWDE (Convert
MOVZX
34
1
2
3
4
CHAPITRE 2.
char ch;
while ( (ch = fgetc(fp )) != EOF ) {
/ faire qqch avec ch /
}
Fig. 2.2 Utilisation de la fonction fgetc
Application la programmation en C
L'extension d'entiers signs et non signs a galement lieu en C. Les
ou variables en C peuvent tre dclares soit comme signes soit comme non
non, c'est chaque compi- signes (les int sont signs). Considrons le code de la Figure 2.1. A la
lateur de le dcider. C'est
ligne 3, la variable a est tendue en utilisant les rgles pour les valeurs non
pourquoi on dnit le type
signes (avec MOVZX), mais la ligne 4, les rgles signes sont utilises pour
explicitement dans la Fib (avec MOVSX).
gure 2.1.
Il existe un bug de programmation courant en C qui est directement
fgetc()est
int
int
puis-
char
char
EOF (qui
un
int
FFFFFFFF en
(qui
ressemble
hexa).
int,
char.
fgetc() renvoie
Le C tronquera alors
les bits de poids fort pour faire tenir la valeur de l'int dans le
000000FF et
FF. Donc, la boucle while ne peut pas
FF
char. Le
FFFFFFFF seront
char est
ch est compar avec EOF. Comme
2.1.
35
EOF est un int1 , ch sera tendu un int an que les deux valeurs compares
2
char n'est pas sign, FF est tendu et donne 000000FF. Cette valeur
compare EOF (FFFFFFFF) et est dirente. Donc la boucle ne nit
Si le
est
jamais !
So le
char
est sign,
FF
FFFFFFFF.
FF
peut
char.
ch
comme un
int,
ch doit
sub
carry ag
overow
add
(dpassement de
rel de l'opration est trop grand et ne tient pas dans la destination pour
l'arithmtique signe. Le drapeau de retenue est 1 s'il y a une retenue dans
le bit le plus signicatif d'une addition d'une soustraction. Donc, il peut
tre utilis pour dtecter un dpassement de capacit en arithmtique non
signe. L'utilisation du drapeau de retenue pour l'arithmtique signe va tre
vu sous peu. Un des grands avantages du complment 2 est qu les rgles
pour l'addition et la soustraction sont exactement les mmes que pour les
entiers non signs. Donc,
add
et
sub
signs ou non.
002C
+ FFFF
002B
44
+ (1)
43
MUL
MUL
ou
IMUL. L'instruction
IMUL est utili-
Il est courant de penser que les chiers ont un carctre EOF comme dernier caractre.
Ce n'est pas vrai !
2
La raison de cette ncessit sera expose plus tard.
36
CHAPITRE 2.
dest
source1
source2
reg/mem8
Action
AX = AL*source1
reg/mem16
DX:AX = AX*source1
reg/mem32
EDX:EAX = EAX*source1
reg16
reg/mem16
dest *= source1
reg32
reg/mem32
dest *= source1
reg16
immed8
dest *= immed8
reg32
immed8
dest *= immed8
reg16
immed16
dest *= immed16
reg32
immed32
dest *= immed32
reg16
reg/mem16
immed8
dest = source1*source2
reg32
reg/mem32
immed8
dest = source1*source2
reg16
reg/mem16
immed16
dest = source1*source2
reg32
reg/mem32
immed32
dest = source1*source2
Tab. 2.2
Instructions imul
Considrons la multiplication de l'octet FF avec lui-mme donnant un rsultat sur un mot. En utilisant la multiplication non signe, il s'agit de 255
fois 255 soit 65025 (ou FE01 en hexa). En utilisant la multiplication signe,
il s'agit de
fois
mul
La
source
source
est soit un registre soit une rfrence mmoire. Cela ne peut pas
IMUL
MUL,
imul
imul
dest, source1
dest, source1, source2
DIV
et
IDIV.
Il eectuent respec-
tivement une division entire non signe et une division entire signe. Le
format gnral est :
div
source
2.1.
37
Si la source est sur 8 bits, alors AX est divis par l'oprande. Le quotient est
stock dans AL et le reste dans AH. Si la source est sur 16 bits, alors DX:AX
est divis par l'oprande. Le quotient est stock dans AX et le reste dans
DX. Si la source est sur 32 bits, alors EDX:EAX est divis par l'oprande,
IDIV
IDIV spciales comme
IMUL.
Si le quotient est trop grand pour tenir dans son registre ou que
NEG
%include "asm_io.inc"
segment .data
prompt
db
square_msg
db
cube_msg
db
cube25_msg
db
quot_msg
db
rem_msg
db
neg_msg
db
math.asm
; Chaines affichees
"Entrez un nombre : ", 0
"Le carre de l'entree vaut ", 0
"Le cube de l'entree vaut ", 0
"Le cube de l'entree fois 25 vaut ", 0
"Le quotient de cube/100 vaut ", 0
"Le reste de cube/100 vaut ", 0
"La ngation du reste vaut ", 0
10
11
12
segment .bss
input resd 1
13
14
15
16
17
18
segment .text
global _asm_main
_asm_main:
enter 0,0
pusha
; routine d'initialisation
19
20
21
mov
call
eax, prompt
print_string
call
mov
read_int
[input], eax
imul
mov
eax
ebx, eax
22
23
24
25
26
27
38
28
29
30
31
32
CHAPITRE 2.
mov
call
mov
call
call
eax, square_msg
print_string
eax, ebx
print_int
print_nl
mov
imul
mov
call
mov
call
call
ebx, eax
ebx, [input]
eax, cube_msg
print_string
eax, ebx
print_int
print_nl
imul
mov
call
mov
call
call
ecx, ebx, 25
eax, cube25_msg
print_string
eax, ecx
print_int
print_nl
mov
cdq
mov
idiv
mov
mov
call
mov
call
call
mov
call
mov
call
call
eax, ebx
ecx, 100
ecx
ecx, eax
eax, quot_msg
print_string
eax, ecx
print_int
print_nl
eax, rem_msg
print_string
eax, edx
print_int
print_nl
neg
mov
call
mov
call
edx
eax, neg_msg
print_string
eax, edx
print_int
33
34
35
36
37
38
39
40
; ebx *= [entree]
41
42
43
44
45
46
47
; ecx = ebx*25
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
;
;
;
;
64
65
66
67
68
69
; inverse le reste
2.1.
70
39
call
print_nl
71
72
73
74
75
popa
mov
leave
ret
eax, 0
; retour au C
math.asm
ADD
et
SUB
modient le drapeau
de retenue si une retenue est gnre. Cette information stocke dans le drapeau de retenue peut tre utilise pour additionner ou soustraire de grands
nombres en morcelant l'opration en doubles mots (ou plus petit).
Les instructions
ADC
et
SBB
L'instruction
SBB
eectue :
1
2
add
adc
eax, ecx
edx, ebx
1
2
sub
sbb
eax, ecx
edx, ebx
vraiment
Section 2.2). Pour une boucle de somme, il serait pratique d'utiliser l'instruction
ADC
CLC
ADD
et
ADC.
40
CHAPITRE 2.
2.2
Structures de Contrle
niveau (
et
goto
est
2.2.1 Comparaison
Les structures de contrle dcident ce qu'il faut faire en comparant des
donnes. En assembleur, le rsultat d'une comparaison est stock dans le
registre FLAGS pour tre utilis plus tard. Le 80x86 fournit l'instruction
CMP pour eectuer des comparaisons. Le registre FLAGS est positionn selon
la dirence entre les deux oprandes de l'instruction CMP. Les oprandes
sont soustraites et le registre FLAGS est positionn selon le rsultat, mais
ce rsultat n'est stock
l'instruction
SUB
nulle part.
au lieu de
CMP.
Pour les entiers non signs, il y a deux drapeaux (bits dans le registre
FLAGS) qui sont importants : les drapeaux zro (ZF) et retenue (CF) . Le
drapeau zero est allum (1) si le rsultat de la dirence serait 0. Considrons
une comparaison comme :
cmp
vleft, vright
La dirence
vleft - vright
i.e.
ZF est allum(
1) et CF est teint (
teint et CF galement. Si
CMP
i.e.
vaut 0,
0). Si
allum (retenue).
Pour les entiers signs, il y a trois drapeaux importants : le drapeau
Pourquoi
SF
OF
S'il n'y peau d'overow est allum si le rsultat d'une opration dpasse la capaa pas d'overow, alors cit (de trop ou de trop peu). Le drapeau de signe est allum si le rsultat
la dirence aura la vad'une opration est ngatif. Si vleft = vright, ZF est allum (comme pour
leur correcte et doit tre
les entiers non signs). Si vleft > vright, ZF est teint et SF = OF. Si
positive ou nulle. Donc,
vleft < vright, ZF est teint et SF 6= OF.
SF = OF = 0. Par contre,
N'oubliez pas que d'autres instructions peuvent aussi changer le registre
s'il y a un overow, la
dirence
valeur
en
fait
n'aura
correcte
pas
(et
ngative).
SF = OF = 1.
sera
Donc,
CMP.
2.2.
41
STRUCTURES DE CONTRLE
branchement inconditionnel est exactement comme un goto, il eectue toujours le branchement. Un branchement conditionnel peut eectuer le branchement ou non selon les drapeaux du registre FLAGS. Si un branchement
conditionnel n'eecute pas le branchement, le contrle passe l'instruction
suivante.
L'instruction
JMP
(abbrviation de
jump )
tiquette de code
de
l'instruction laquelle se brancher. L'assembleur ou l'diteur de liens remplacera l'tiquette par l'adresse correcte de l'instruction. C'est une autre des
oprations pnibles que l'assembleur fait pour rendre la vie du programmeur
plus simple. Il est important de raliser que l'instruction immdiatement
aprs l'instruction
SHORT
dplacement
NEAR
JMP.
SHORT
Ce saut est le saut par dfaut, que ce soit pour les branchements
WORD
FAR
JMP.
Les tiquettes de code valides suivent les mmes rgles que les tiquettes
de donnes. Les tiquettes de code sont dnies en les plaant dans le segment
de code au dbut de l'instruction qu'ils tiquettent. Deux points sont placs
la n de l'tiquette lors de sa dnition. Ces deux points ne font
du nom.
pas
partie
42
CHAPITRE 2.
JZ
JNZ
JO
JNO
JS
JNS
JC
JNC
JP
JNP
drapeau de parit
bits dans les 8 bits de poids faible du rsultat est pair ou impair).
Le pseudo-code suivant :
if ( EAX == 0 )
EBX = 1;
else
EBX = 2;
peut tre crit de cette faon en assembleur :
1
2
3
4
5
6
7
cmp
jz
mov
jmp
thenblock:
mov
next:
eax, 0
thenblock
ebx, 2
next
;
;
;
;
ebx, 1
; partie THEN du IF
if ( EAX >= 5 )
EBX = 1;
else
EBX = 2;
2.2.
43
STRUCTURES DE CONTRLE
Sign
JE
saute si
JNE
saute si
JL, JNGE
saute si
JLE, JNG
saute si
JG, JNLE
saute si
JGE, JNL
saute si
vleft
vleft
vleft
vleft
vleft
vleft
= vright
6= vright
< vright
vright
> vright
vright
Non Sign
JE
saute si
JNE
saute si
JB, JNAE
saute si
JBE, JNA
saute si
JA, JNBE
saute si
JAE, JNB
saute si
vleft
vleft
vleft
vleft
vleft
vleft
= vright
6= vright
< vright
vright
> vright
vright
Si EAX est plus grand ou gal 5, ZF peut tre allum ou non et SF sera
gal OF. Voici le code assembleur qui teste ces conditions (en supposant
que EAX est sign) :
1
2
3
4
5
6
7
8
9
10
11
12
cmp
js
jo
jmp
signon:
jo
elseblock:
mov
jmp
thenblock:
mov
next:
eax, 5
signon
elseblock
thenblock
; goto signon si SF = 1
; goto elseblock si OF = 1 et SF = 0
; goto thenblock si SF = 0 et OF = 0
thenblock
; goto thenblock si SF = 1 et OF = 1
ebx, 2
next
ebx, 1
coup
beau-
Tableau 2.4 montre ces instructions. Les branchements gal et non gal (JE
et JNE) sont les mmes pour les entiers signs et non signs (en fait, JE et
JNE sont rellement identiques JZ et JNZ, respectivement). Chacune des
autres instructions de branchement a deux synonymes. Par exemple, regardez JL (jump less than, saut si infrieur ) et JNGE (jump not greater than
or equal to, saut si non plus grand ou gal). Ce sont les mmes instructions
car :
x < y = not(x y)
Les branchements non signs utilisent A pour
below
above
44
1
2
3
4
5
6
7
CHAPITRE 2.
cmp
jge
mov
jmp
thenblock:
mov
next:
eax, 5
thenblock
ebx, 2
next
ebx, 1
seule oprande.
6=
0 et ZF = 1, saute
LOOPNE, LOOPNZ
6=
et ZF = 0, saute
Les deux dernires instructions de boucle sont utiles pour les boucles de
recherche squentielle. Le pseudo-code suivant :
sum = 0;
for ( i=10; i >0; i )
sum += i;
peut tre traduit en assembleur de la faon suivante :
1
2
3
4
5
mov
mov
loop_start:
add
loop
2.3
eax, 0
ecx, 10
eax, ecx
loop_start
Traduire les Structures de Contrle Standards
2.3.1 Instructions if
Le pseudo-code suivant :
2.3.
if ( condition )
then_block;
else
else_block;
peut tre implment de cette faon :
1
2
3
4
5
6
7
; code
jxx
; code
jmp
else_block:
; code
endif:
1
2
3
4
endif.
1
2
3
4
5
6
while:
; code
jxx
; body
jmp
endwhile:
do while
do {
corps de la boucle;
} while ( condition );
46
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
CHAPITRE 2.
1
2
3
4
do:
; corps de la boucle
; code pour positionner FLAGS selon la condition
jxx
do
; choisir xx pour se brancher si la condition est vraie
2.4
3 sous une limite donne. Si aucun facteur ne peut tre trouv pour
2.4.
1
2
3
47
prime.asm
%include "asm_io.inc"
segment .data
Message
db
segment .bss
Limit
Guess
1
1
4
5
6
7
resd
resd
8
9
10
11
12
13
segment .text
global _asm_main
_asm_main:
enter 0,0
pusha
; routine d'intialisation
14
15
16
17
18
mov
call
call
mov
eax, Message
print_string
read_int
[Limit], eax
mov
call
call
mov
call
call
eax, 2
print_int
print_nl
eax, 3
print_int
print_nl
; printf("2\n");
dword [Guess], 5
; Guess = 5;
; while ( Guess <= Limit )
; scanf("%u", &limit);
19
20
21
22
23
24
25
; printf("3\n");
26
27
28
29
30
31
mov
while_limit:
mov
cmp
jnbe
eax,[Guess]
eax, [Limit]
end_while_limit
32
33
34
35
36
37
38
39
40
41
mov
while_factor:
mov
mul
jo
cmp
jnb
mov
mov
ebx, 3
eax,ebx
eax
end_while_factor
eax, [Guess]
end_while_factor
eax,[Guess]
edx,0
48
42
43
44
CHAPITRE 2.
div
cmp
je
ebx
edx, 0
end_while_factor
45
46
47
48
49
50
51
52
53
54
55
56
add
ebx,2
jmp
while_factor
end_while_factor:
je
end_if
mov
eax,[Guess]
call
print_int
call
print_nl
end_if:
add
dword [Guess], 2
jmp
while_limit
end_while_limit:
; factor += 2;
; si !(guess % factor != 0)
; printf("%u\n")
; guess += 2
57
58
59
60
61
popa
mov
leave
ret
eax, 0
; retour au C
prime.asm
Chapitre 3
Oprations de Dcalage
Le langage assembleur permet au programmeur de manipuler individuellement les bits des donnes. Une opration courante sur les bits est appele
dcalage. Une opration de dcalage dplace la position des bits d'une donne. Les dcalages peuvent tre soit vers la gauche (i.e. vers les bits les plus
signicatifs) soit vers la droite (les bits les moins signicatifs).
Dcal gauche
Dcal droite
SHR
SHL
et
CL.
1
2
3
4
mov
shl
shr
shr
ax,
ax,
ax,
ax,
0C123H
1
1
1
50
5
6
7
8
CHAPITRE 3.
mov
shl
mov
shr
ax,
ax,
cl,
ax,
0C123H
2
3
cl
10112
(ou 11 en dcimal), dcalez d'un cran vers la gauche pour obtenir 101102 (ou
22). Le quotient d'un division par une puissance de deux est le rsultat d'un
dcalage droite. Pour diviser par deux, utilisez un dcalage d'une position
3
ser par 8 (2 ), dcalez de 3 positions vers la droite,
dcalage sont trs basiques et sont
MUL
et
DIV
correspondantes !
+32, 767 !
SAL
Shift Arithmetic Left (Dcalage Arithmtique Gauche) - Cette instruction est juste un synonyme pour
SHL. Elle est assemble pour donSHL. Tant que le bit de signe
SAR
i.e.
le msb) de
son oprande. Les autres bits sont dcals normalement sauf que les
nouveaux bits qui entrent par la gauche sont des copies du bit de
signe (c'est--dire que si le bit de signe est 1, les nouveaux bits sont
galement 1). Donc, si un octet est dcal avec cette instruction, seuls
les 7 bits de poids faible sont dcals. Comme pour les autres dcalages,
le dernier bit sorti est stock dans le drapeau de retenue.
3.1.
mov
sal
sal
sar
1
2
3
4
51
OPRATIONS DE DCALAGE
ax,
ax,
ax,
ax,
0C123H
1
1
2
; ax = 8246H, CF = 1
; ax = 048CH, CF = 1
; ax = 0123H, CF = 0
ROL
et
ROR
qui eectuent
mov
rol
rol
rol
ror
ror
1
2
3
4
5
6
ax,
ax,
ax,
ax,
ax,
ax,
0C123H
1
1
1
2
1
;
;
;
;
;
ax
ax
ax
ax
ax
=
=
=
=
=
8247H,
048FH,
091EH,
8247H,
C123H,
CF
CF
CF
CF
CF
=
=
=
=
=
1
1
0
1
1
RCL
et
RCR.
Par exemple, si
elle applique
mov
clc
rcl
rcl
rcl
rcr
rcr
1
2
3
4
5
6
7
ax, 0C123H
ax,
ax,
ax,
ax,
ax,
1
1
1
2
1
;
;
;
;
;
;
1
2
mov
mov
bl, 0
ecx, 32
52
CHAPITRE 3.
ET
1
ET
3
4
5
6
7
8
count_loop:
shl
jnc
inc
skip_inc:
loop
eax, 1
skip_inc
bl
count_loop
EAX (EAX vaut zro la n de
EAX, la ligne 4 pourrait tre
3.2
rol eax, 1.
table de vrit
3.2.1 L'opration ET
Le rsultat d'un
ET
ET
au contenu de
AL
et
BL,
l'opration
ET
de
base est applique chacune des 8 paires de bits correspondantes dans les
deux registres, comme le montre la Figure 3.2. Voici un exemple de code :
1
2
mov
and
ax, 0C123H
ax, 82F6H
; ax = 8022H
3.2.
OU
53
XOR
3.2.2 L'opration OU
Le
OU
1
2
mov
or
ax, 0C123H
ax, 0E831H
; ax = E933H
OU
1
2
mov
xor
ax, 0C123H
ax, 0E831H
; ax = 2912H
NOT
d'un bit est la valeur oppose du bit comme le montre la table de vrit du
Tableau 3.4. Voici un exemple de code :
1
2
mov
not
ax, 0C123H
ax
; ax = 3EDCH
54
CHAPITRE 3.
NOT
Allumer le bit
Eectuer un
OU
2i
(qui
Eteindre le bit
d'allum)
Eectuer un
ET
XOR
masque
2i
Notez que le
NOT
FLAGS.
TEST
AND
dernier aurait t
soustraction mais
allum.
1
2
3
4
5
6
7
8
mov
or
and
xor
or
and
xor
xor
ax,
ax,
ax,
ax,
ax,
ax,
ax,
ax,
0C123H
8
0FFDFH
8000H
0F00H
0FFF0H
0F00FH
0FFFFH
;
;
;
;
;
;
;
3.3.
ET
L'opration
55
vision par une puissance de deux. Pour trouver le reste d'une division par
2i ,
eectuez un
ET
2i 1.
Ce masque
ET
1
2
3
mov
mov
and
eax, 100
; 100 = 64H
ebx, 0000000FH ; masque = 16 - 1 = 15 ou F
ebx, eax
; ebx = reste = 4
En utilisant le registre
CL
1
2
3
4
mov
mov
shl
or
cl, bh
ebx, 1
ebx, cl
eax, ebx
BH.
EAX.
Le numro du bit
1
2
3
4
5
mov
mov
shl
not
and
cl, bh
ebx, 1
ebx, cl
ebx
eax, ebx
xor
eax, eax
; eax = 0
Cette instruction est utilise car son code machine est plus petit que l'instruction
3.3
MOV
correspondante.
56
1
2
3
4
5
6
CHAPITRE 3.
mov
mov
count_loop:
shl
adc
loop
bl, 0
ecx, 32
eax, 1
bl, 0
count_loop
ADC
fois. Les branchements conditionnels posent un problme ce type de fonctionnement. Le processeur, en gnral, ne sait pas si le branchement sera
eectu ou pas. Selon qu'il est eectu ou non, un ensemble d'instructions
dirent sera excut. Les processeurs essaient de prvoir si le branchement
sera eectu. Si la prvision est mauvaise, le processeur a perdu son temps
en excutant le mauvais code.
Une faon d'viter ce problme est d'viter d'utiliser les branchements
conditionnels lorsque c'est possible. Le code d'exemple de 3.1.5 fournit un
exemple simple de la faon de le faire. Dans l'exemple prcdent, les bits
allums du registre EAX sont dcompts. Il utilise un branchement pour
viter l'instruction
SETxx
stock est un, s'il est faux, zro est stock. Par exemple :
setz
al
CMP
1
2
; file: max.asm
%include "asm_io.inc"
3.3.
57
segment .data
4
5
6
7
8
9
segment .bss
10
11
input1 resd
12
13
14
15
16
17
segment .text
global _asm_main
_asm_main:
enter 0,0
pusha
; routine d'initialisation
18
19
20
21
22
mov
call
call
mov
eax, message1
print_string
read_int
[input1], eax
mov
call
call
eax, message2
print_string
read_int
xor
cmp
setg
neg
mov
and
not
and
or
ebx,
eax,
bl
ebx
ecx,
ecx,
ebx
ebx,
ecx,
;
;
;
;
;
;
;
;
;
mov
call
mov
call
call
eax, message3
print_string
eax, ecx
print_int
print_nl
23
24
25
26
27
28
29
30
31
32
33
34
35
36
ebx
[input1]
ebx
eax
[input1]
ebx
ebx = 0
compare le premier et le second nombre
ebx = (input2 > input1) ?
1 :
ebx = (input2 > input1) ? 0xFFFFFFFF :
ecx = (input2 > input1) ? 0xFFFFFFFF :
ecx = (input2 > input1) ?
input2 :
ebx = (input2 > input1) ?
0 :
ebx = (input2 > input1) ?
0 :
ecx = (input2 > input1) ?
input2 :
37
38
39
40
41
42
43
44
popa
; affichage du rsultat
0
0
0
0
0xFFFFFFFF
input1
input1
58
CHAPITRE 3.
mov
leave
ret
45
46
47
eax, 0
; retour au C
L'astuce est de crer un masque de bits qui peut tre utilis pour slectionner la valeur correcte pour le maximum. L'instruction
SETG la ligne 30
NEG.
3.4
&1 . L'opration OU
ET
. Et l'opration
et du C. L'oprateur eectue les dcalages gauche et l'op eectue les dcalages droite. Ces oprateurs prennent deux op-
int),
1
2
3
4
short int s ;
short unsigned u;
s = 1;
u = 100;
1
Ces oprateur est dirent des oprateurs && binaire et & unaire !
3.4.
59
Macro
S_IRUSR
S_IWUSR
S_IXUSR
S_IRGRP
S_IWGRP
S_IXGRP
S_IROTH
S_IWOTH
S_IXOTH
Signication
l'utilisateur peut lire
l'utilisateur peut crire
l'utilisateur peut excuter
le groupe peut lire
le groupe peut crire
le groupe peut excuter
les autres peuvent lire
les autres peuvent crire
les autres peuvent excuter
5
6
7
8
9
u = u | 0x0100;
s = s & 0xFFF0;
s = s ^ u;
u = u << 3;
s = s >> 2;
/ u = 0x0164 /
/ s = 0xFFF0 /
/ s = 0xFE94 /
/ u = 0x0B20 (dcalage logique) /
/ s = 0xFFA5 (dcalage arithmetique) /
x *= 2.
POSIX 3 et Win32)
contiennent des fonctions qui utilisent des oprandes donc les donnes sont
codes sous forme de bits. Par exemple, les systmes POSIX conservent les
permissions sur les chiers pour trois dirents types d'utilisateurs :
teur
propritaire ), groupe
et
autres.
utilisa-
Chaque
chmod
missions sur un chier. Cette fonction prend deux paramtres, une chaine
60
CHAPITRE 3.
lums pour les permissions dsires. Par exemple, le code ci-dessous dnit
les permissions pour permettre au propritaire du chier de lire et d'crire
dedans, au groupe de lire le chier et interdire l'accs aux autres.
stat
chmod,
il est possible de modier certaines des permissions sans changer les autres.
Voici un exemple qui retire l'accs en criture aux autres et ajoute les droits
de lecture pour le propritaire. Les autres permissions ne sont pas altres.
1
2
3
4
gros (big)
bits sont stocks en premier. La mthode little endian stocke les octets dans
l'ordre inverse (moins signcatif en premier). La famille des processeurs x86
utilise la reprsentation little endian.
Par exemple, considrons le double mot reprsentant
1234567816 .
En re-
prsentation big endian, les octets seraient stocks 12 34 56 78. En reprsentation little endian, les octets seraient stocks 78 56 34 12.
Le lecteur est probablement en train de se demander pourquoi n'importe
quelle concepteur de puce sain d'esprit utiliserait la reprsentation little endian ? Les ingnieurs de chez Intel taient-ils sadiques pour iniger une
multitude de programmeurs cette reprsentation qui prte confusion ? Il
peut sembler que le processeur ait faire du travail supplmentaire pour
stocker les octets en mmoire dans l'odre inverse (et pour les rinverser lorsqu'il lit partir de la mmoire). En fait, le processeur ne fait aucun travail
supplmentaire pour lire et crire en mmoire en utilisant le format little
endian. Il faut comprendre que le processeur est constitu de beaucoup de
3.5.
61
circuits lectroniques qui ne travaillent que sur des valeurs de un bit. Les bits
(et les octets) peuvent tre dans n'importe quel ordre dans le processeur.
p traite la variable
lments. Donc, p[0] corres-
word
caractre
big
ou
little
pour
les
donnes
pour
les donnes.
reprsenter
62
1
2
3
4
5
ip [0] =
ip [1] =
ip [2] =
ip [3] =
7
8
9
10
11
13
12
CHAPITRE 3.
xp [3];
xp [2];
xp [1];
xp [0];
return invert ;
htonl ()
convertit un
double mot (ou un entier long) depuis le format hte vers le format rseau.
La fonction function
tme big endian les deux fonctions retournent leur paramtre inchang. Cela
permet d'crire des programmes rseau qui compileront et s'excuteront correctement sur n'importe quel systme sans tenir compte de la reprsentation
utilise. Pour plus d'information sur les reprsentations big et little endian
et la programmation rseau, voyez l'excellent livre de W. Richard Steven :
BSWAP
bswap
edx
L'instruction ne peut pas tre utilise sur des registres de 16 bits. Cependant,
l'instruction
XCHG
xchg
5
ah,al
En fait, passer d'une reprsentation l'autre pour entier consiste inverser l'ordre
des octets ; donc, convertir de little vers big ou de big vers little est la mme opration.
Donc, ces deux fonctions font la mme chose.
3.6.
1
2
3
4
5
6
7
8
9
10
63
while ( data != 0 ) {
data = data & (data 1);
cnt++;
}
return cnt;
3.6
Plus haut, nous avons donn une technique intuitive pour compter le
nombre de bits allums dans un double mot. Cette section dcrit d'autres
mthodes moins directes de le faire pour illustrer les oprations sur les bits
dont nous avons parl dans ce chapitre.
data.
data
data
data.
data
data - 1 ?
data, mais
partir du 1 le plus droite, les bits seront les complments des bits originaux
de
o les x sont les mmes pour les deux nombres. Lorsque l'on applique un
sur
data
avec
data - 1,
ET
data
64
1
2
3
4
5
void initialize_count_bits ()
{
int cnt , i , data;
7
8
9
10
11
12
13
14
16
17
18
19
20
22
24
21
23
15
CHAPITRE 3.
4 milliards
trs gros et que l'initialiser prendrait beaucoup de temps (en fait, moins
que l'on ait l'intention d'utiliser le tableau plus de 4 milliards de fois, cela
prendrait plus de temps de l'initialiser que cela n'en prendrait de calculer le
nombre de bits avec la mthode une !).
Une mthode plus raliste calculerait le nombre de bits pour toutes les
valeurs possibles d'octet et les stockerait dans un tableau. Le double mot
peut alors tre dcompos en quatre valeurs d'un octet. Le nombre de bits
pour chacune de ces 4 valeurs d'un octet est recherch dans le tableau et
3.6.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
65
additionn aux autres pour trouver le nombre de bits du double mot original.
La Figure 3.7 montre le code implmentant cette approche.
dword[0]
data
for
for
loop unrolling
(droulage de boucle).
66
CHAPITRE 3.
data.
La
1) & 0x55)
vers une position impaire et utilise le mme masque pour supprimer ces bits.
Maintenant, la premire oprande contient les bits impairs et la seconde les
bits pairs de
data.
101100112 ,
+
alors :
00
soit
01
00
01
01
01
00
01
01
10
00
10
011000102 ):
+
soit
0010
0010
0001
0000
0011
0010
3.6.
67
Maintenant,
data
data
gale
001100102 ):
00000010
soit
00000011
00000101
une implmentation de cette mthode qui compte les bits dans un double
mot. Elle utilise une boucle
68
CHAPITRE 3.
Chapitre 4
Sous-Programmes
Ce chapitre explique comment utiliser des sous-programmes pour crer
des programmes modulaires et s'interfacer avec des langages de haut niveau
(comme le C). Les fonctions et les procdures sont des exemples de sousprogrammes dans les langages de haut niveau.
Le code qui appelle le sous-programme et le sous-programme lui-mme
doivent se mettre d'accord sur la faon de se passer les donnes. Ces rgles
sur la faon de passer les donnes sont appeles
conventions d'appel .
Une
4.1
Adressage Indirect
1
2
3
mov
mov
mov
ax, [Data]
ebx, Data
ax, [ebx]
70
CHAPITRE 4.
SOUS-PROGRAMMES
4.2
JMP.
d'un registre pour dterminer o sauter (donc, le registre agit plus comme
un
pointeur de fonction
1
2
3
sub1.asm
; fichier : sub1.asm
; Programme d'exemple de sous-programme
%include "asm_io.inc"
4
5
6
7
8
9
10
segment
prompt1
prompt2
outmsg1
outmsg2
outmsg3
.data
db
db
db
db
db
11
12
13
14
segment .bss
input1 resd 1
input2 resd 1
15
16
17
18
19
20
21
segment .text
global _asm_main
_asm_main:
enter 0,0
pusha
; routine d'initialisation
4.2.
22
23
71
mov
call
eax, prompt1
print_string
; affiche l'invite
mov
mov
jmp
ebx, input1
ecx, ret1
short get_int
mov
call
eax, prompt2
print_string
; affiche l'invite
mov
mov
jmp
ebx, input2
ecx, $ + 7
short get_int
mov
add
mov
eax, [input1]
eax, [input2]
ebx, eax
mov
call
mov
call
mov
call
mov
call
mov
call
mov
call
call
eax, outmsg1
print_string
eax, [input1]
print_int
eax, outmsg2
print_string
eax, [input2]
print_int
eax, outmsg3
print_string
eax, ebx
print_int
print_nl
24
25
26
27
28
ret1:
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
;
;
;
;
;
;
popa
mov
eax, 0
; retour au C
leave
ret
subprogram get_int
Paramtres:
ebx - addresse du dword dans lequel stocker l'entier
ecx - addresse de l'instruction vers laquelle retourner
Notes:
la valeur de eax est perdue
72
64
65
66
67
CHAPITRE 4.
get_int:
call
mov
jmp
read_int
[ebx], eax
ecx
Le sous-programme
SOUS-PROGRAMMES
ret1
$ + 7
Ces deux calculs d'adresses de code de retour sont compliqus. La premire mthode requiert la dnition d'une tiquette pour tout appel de sousprogramme. La seconde mthode ne requiert pas d'tiquette mais ncessite
de rchir attentivement. Si un saut proche avait t utilis la place d'un
saut court, le nombre ajouter
une faon plus simple d'appeler des sous-programmes. Cette mthode utilise
la
pile.
4.3
La pile
Beaucoup de processeurs ont un support intgr pour une pile. Une pile
est une liste Last-In First-Out (
est une zone de la mmoire qui est organise de cette faon. L'instruction
PUSH
POP
sommet
que par units de double mots. C'est--dire qu'il est impossible de placer un
octet seul sur la pile.
L'instruction
PUSH
[ESP]
[ESP].
L'instruction
POP
lit le double
1000H.
1
En fait, il est galement possible d'empiler des mots, mais en mode protg 32 bits, il
est mieux de ne travailler qu'avec des doubles mots sur la pile.
4.4.
1
2
3
4
5
6
73
push
push
push
pop
pop
pop
dword 1
dword 2
dword 3
eax
ebx
ecx
;
;
;
;
;
;
1 est
2 est
3 est
EAX =
EBX =
ECX =
stock
stock
stock
3, ESP
2, ESP
1, ESP
La pile peut tre utilise comme un endroit appropri pour stocker des
donnes temporairement. Elle est galement utilise pour eectuer des appels
de sous-programmes, passer des paramtres et des variables locales.
Le 80x86 fournit galement une instruction,
des registres EAX, EBX, ECX, EDX, ESI, EDI et EBP (pas dans cet ordre).
L'instruction
4.4
POPA
Le 80x86 fournit deux instructions qui utilisent la pile pour eectuer des
appels de sous-programmes rapidement et facilement. L'instruction CALL
eectue un saut inconditionnel vers un sous-programme et
l'instruction suivante. L'instruction RET
dpile
empile l'adresse de
mov
call
ebx, input1
get_int
mov
call
ebx, input2
get_int
et en changeant le sous-programme
get_int:
call
mov
ret
get_int
en :
read_int
[ebx], eax
74
CHAPITRE 4.
SOUS-PROGRAMMES
get_int
appelle
adresse. la n du code de
l'adresse de retour et saute
get_int est
asm_main. Cela
RET de
vers
LIFO.
Souvenez vous, il est
trs
1
2
3
4
5
get_int:
call
mov
push
ret
read_int
[ebx], eax
eax
; dpile la valeur de EAX, pas l'adresse de retour !!
4.5
Conventions d'Appel
(l'
appel )
gages de haut niveau ont des manires standards de passer les donnes appeles
conventions d'appel.
p.e.
pil (
RET.
rentrants.
valeur.
adresse
4.5.
75
CONVENTIONS D'APPEL
ESP + 4
ESP
Paramtre
Adresse de retour
ESP + 8
Paramtre
ESP + 4
Adresse de retour
ESP
Donn'ee du sous-programme
CALL,
l'adresse de
de
l'utilisation
de
indirect,
le
via la pile. Lorsque le sous-programme est appel, la pile ressemble la l'adressage
processeur
80x86
accde
([ESP+4]
2 ).
dirents
l'expression
indirect.
segments
selon
ESP
le
(et
EBP)
segment
de
l'on fait rfrence des paramtres peut tre une source d'erreurs. Pour ECX
et
EDX
utilisent
rsoudre ce problme, Le 80386 fournit un autre registre utiliser : EBP. La le segment de donnes.
Cependant, ce n'est habiseule utilit de ce registre est de faire rfrence des donnes sur la pile. La
tuellement pas important
convention d'appel C stipule qu'un sous-programme doit d'abord empiler la
pour la plupart des provaleur de EBP puis dnir EBP pour qu'il soit gal ESP. Cela permet ESP
grammes en mode protg,
de changer au fur et mesure que des donnes sont empiles ou dpiles sans car pour eux, les segments
modier EBP. A la n du programme, la valeur originale de EBP doit tre de pile et de donnes sont
restaure (c'est pourquoi elle est sauvegarde au dbut du sous-programme). les mmes.
76
1
2
3
4
5
6
CHAPITRE 4.
etiquette_sousprogramme:
push ebp
mov
ebp, esp
; code du sousprogramme
pop
ebp
ret
SOUS-PROGRAMMES
ESP + 8
EBP + 8
Parametre
ESP + 4
EBP + 4
Adresse de retour
ESP
EBP
EBP sauvegard
La Figure 4.3 montre la forme gnrale d'un sous-programme qui suit ces
conventions.
[EBP + 8]
pascal
stdcall,
fonctionne galement de cette faon. Quel est son avantage ? Elle est un petit
peu plus ecace que la convention C. Pourquoi toutes les fonctions C n'utilisent pas cette convention alors ? En gnral, le C autorise une fonction a
avoir un nombre variable d'arguments (
4.5.
push
call
add
1
2
3
77
CONVENTIONS D'APPEL
dword 1
fun
esp, 4
; passe 1 en paramtre
; retire le paramtre de la pile
lement tre utilise, mais cela ncessiterait le stockage d'un paramtre inutile
dans un registre. En fait, dans ce cas particulier, beaucoup de compilateurs
%include "asm_io.inc"
sub3.asm
2
3
4
segment .data
sum
dd 0
5
6
7
segment .bss
input resd 1
8
9
10
11
12
13
14
15
;
;
;
;
;
;
;
algorithme en pseudo-code
i = 1;
sum = 0;
while( get_int(i, &input), input != 0 ) {
sum += input;
i++;
78
16
17
18
19
20
21
22
CHAPITRE 4.
; }
; print_sum(num);
segment .text
global _asm_main
_asm_main:
enter 0,0
pusha
SOUS-PROGRAMMES
; routine d'initialisation
23
24
25
26
27
28
29
mov
while_loop:
push
push
call
add
edx, 1
edx
dword input
get_int
esp, 8
; empile i
; empile l'adresse de input
; dpile i et &input
30
31
32
33
mov
cmp
je
eax, [input]
eax, 0
end_while
add
[sum], eax
inc
jmp
edx
short while_loop
34
35
; sum += input
36
37
38
39
40
41
42
43
end_while:
push
call
pop
dword [sum]
print_sum
ecx
44
45
46
47
popa
leave
ret
48
49
50
51
52
53
54
55
56
57
; sous-programme get_int
; Paramtres (dans l'ordre de l'empilement)
; nombre de saisies (en [ebp + 12])
; adresse du mot o stocker la saisie (en [ebp + 8])
; Notes:
; les valeurs de eax et ebx sont dtruites
segment .data
prompt db
") Entrez un nombre entier (0 pour quitter): ", 0
4.5.
58
59
60
61
79
CONVENTIONS D'APPEL
segment .text
get_int:
push
mov
ebp
ebp, esp
62
63
64
mov
call
mov
call
eax, prompt
print_string
call
mov
mov
read_int
ebx, [ebp + 8]
[ebx], eax
pop
ret
ebp
65
66
67
68
69
70
71
72
73
74
; retour l'appelant
75
76
77
78
79
80
81
82
83
; sous-programme print_sum
; affiche la somme
; Paramtre:
; somme afficher (en [ebp+8])
; Note: dtruit la valeur de eax
;
segment .data
result db
"La somme vaut ", 0
84
85
86
87
88
segment .text
print_sum:
push
mov
ebp
ebp, esp
89
90
91
mov
call
eax, result
print_string
mov
call
call
eax, [ebp+8]
print_int
print_nl
pop
ret
ebp
92
93
94
95
96
97
98
sub3.asm
80
1
2
3
4
5
6
7
8
CHAPITRE 4.
etiquette_sousprogramme:
push
ebp
mov
ebp, esp
sub
esp, LOCAL_BYTES
; code du sousprogramme
mov
esp, ebp
pop
ebp
ret
SOUS-PROGRAMMES
Fig. 4.6 Forme gnrale d'un sous-programme avec des variables locales
1
2
3
4
5
6
7
8
automatiques
important si l'on veut que les sous-programmes soient rentrants. Un programme rentrant fonctionnera qu'il soit appel de n'importe quel endroit,
mme partir du sous-programme lui-mme. En d'autres termes, les sousprogrammes rentrants peuvent tre appels
rcursivement.
Utiliser la pile
global
ou
4.5.
1
2
3
4
81
CONVENTIONS D'APPEL
cal_sum:
push
mov
sub
ebp
ebp, esp
esp, 4
5
6
7
8
9
10
mov
mov
for_loop:
cmp
jnle
; sum = 0
; ebx (i) = 1
ebx, [ebp+12]
end_for
; i <= n ?
[ebp-4], ebx
ebx
short for_loop
; sum += i
ebx, [ebp+8]
eax, [ebp-4]
[ebx], eax
; ebx = sump
; eax = sum
; *sump = sum;
11
12
13
14
add
inc
jmp
15
16
17
18
19
end_for:
mov
mov
mov
20
21
22
23
mov
pop
ret
esp, ebp
ebp
crit en assembleur.
La Figure 4.9 montre quoi ressemble la pile aprs le prologue du programme de la Figure 4.8. Cette section de la pile qui contient les paramtres,
les informations de retour et les variables locales est appele
cadre de pile
(stack frame). Chaque appel de fonction C cre un nouveau cadre de pile sur
la pile.
ENTER
but. L'instruction
L'instruction
n'a pas d'oprande. La Figure 4.10 montre comment ces instructions sont exemples o il ne faut pas
utilises. Notez que le squelette de programme (Figure 1.7) utilise galement supposer qu'une instruction est plus rapide qu'une
ENTER et LEAVE.
squence de plusieurs instructions.
82
CHAPITRE 4.
SOUS-PROGRAMMES
ESP + 16
EBP + 12
ESP + 12
EBP + 8
n
sump
ESP + 8
EBP + 4
Adresse de retour
ESP + 4
EBP
ESP
EBP - 4
EBP sauv
sum
1
2
3
4
5
etiquette_sousprogramme:
enter LOCAL_BYTES, 0
; code du sous-programme
leave
ret
Fig. 4.10 Forme gnrale d'un sous-programme avec des variables locales
utilisant
4.6
Un
ENTER
et
LEAVE
Programme Multi-Modules
programme multi-modules
chier objet. Tous les programmes prsents jusqu'ici sont des programmes
multi-modules. Il consistent en un chier objet C pilote et le chier objet
assembleur (plus les chiers objet de la bibliothque C). Souvenez vous que
l'diteur de liens combine les chier objets en un programme excutable
unique. L'diteur de liens doit rapprocher toutes les rfrences faites chaque
i.e.
module. An que le module A puisse utiliser une tiquette dnie dans le
module B, la directive
extern
extern
vient une liste d'tiquettes dlimites par des virgules. La directive indique
l'assembleur de traiter ces tiquettes comme
qu'il s'agit d'tiquettes qui peuvent tre utilises dans ce module mais sont
dnies dans un autre. Le chier
asm_io.inc
read_int,
globale
interne _asm_main.
4.6.
83
PROGRAMME MULTI-MODULES
%include "asm_io.inc"
print_sum)
_asm_main.
main4.asm
2
3
4
segment .data
sum
dd 0
5
6
7
segment .bss
input resd 1
8
9
10
11
12
13
14
segment .text
global _asm_main
extern get_int, print_sum
_asm_main:
enter 0,0
; routine d'initialisation
pusha
15
16
17
18
19
20
21
mov
while_loop:
push
push
call
add
edx, 1
edx
dword input
get_int
esp, 8
; empile i
; empile l'adresse de input
; dpile i et &input
22
23
24
25
mov
cmp
je
eax, [input]
eax, 0
end_while
add
[sum], eax
inc
jmp
edx
short while_loop
26
27
; sum += input
28
29
30
31
32
33
34
35
end_while:
push
call
pop
36
37
38
popa
leave
dword [sum]
print_sum
ecx
84
39
CHAPITRE 4.
ret
SOUS-PROGRAMMES
main4.asm
%include "asm_io.inc"
sub4.asm
2
3
4
segment .data
prompt db
5
6
7
8
9
segment .text
global get_int, print_sum
get_int:
enter 0,0
10
11
12
mov
call
mov
call
eax, prompt
print_string
call
mov
mov
read_int
ebx, [ebp + 8]
[ebx], eax
13
14
15
16
17
18
19
20
21
22
leave
ret
; retour l'appelant
23
24
25
segment .data
result db
segment .text
print_sum:
enter
0,0
26
27
28
29
30
31
32
mov
call
eax, result
print_string
mov
call
call
eax, [ebp+8]
print_int
print_nl
33
34
35
36
37
38
39
leave
ret
sub4.asm
4.7.
85
4.7
Aujourd'hui, trs peu de programmes sont crits compltement en assembleur. Les compilateurs sont trs performants dans la conversion de code
de haut niveau en code machine ecace. Comme il est plus facile d'crire
du code dans un langage de haut niveau, ils sont plus populaires. De plus,
le code de haut niveau est
beaucoup
GAS est l'assembleur GNU que tous les compilateurs GNU utilisent. Il utilise la syntaxe AT&T qui est trs direntes des syntaxes relativement similaires de MASM, TASM
et NASM.
86
1
2
3
CHAPITRE 4.
segment .data
x
dd
format
db
SOUS-PROGRAMMES
0
"x = %d\n", 0
4
5
6
7
8
9
10
segment .text
...
push dword [x]
push dword format
call _printf
add
esp, 8
;
;
;
;
empile la valeur de x
empile l'adresse de la chane format
notez l'underscore!
dpile les paramtres
EBP + 12
EBP + 8
EBP + 4
EBP
printf
Valeur de
printf
register
peut
tre utilis dans une dcla- registres suivants : EBX, ESI, EDI, EBP, CS, DS, SS, ES. Cela ne signie
ration de variable C pour pas que la sous-routine ne les change pas en interne. Cela signie que si elle
suggrer au compilateur
change leur valeurs, elle doit les restaurer avant de revenir. Les valeurs de
d'utiliser un registre pour
EBX, ESI et EDI doivent tre inchanges car le C utilise ces registres pour
cette variable plutot qu'un
les
. Habituellement, la pile est utilise pour sauvegarder
emplacement mmoire. On
appelle ces variables, va- les valeurs originales de ces registres.
variables de registre
riables
de
registre.
Les
compilateurs modernes le
font automatiquement sans
qu'il y ait besoin d'une suggestion.
aucun
doit
tre tiquete
simplement l'tiquette
pour la fonction
f.
_asm_main.
4.7.
87
inverse
l'appel de la fonction.
Considrons l'expression C suivante :
printf("x = %d\n",x);
la Fi-
gure 4.11 montre comment elle serait compile (dans le format NASM quivalent). La Figure 4.12 montre quoi ressemble la pile aprs le prologue de
la fonction
printf.
printf
La fonction
thque C qui peut prendre n'importe quel nombre d'arguments. Les rgles
des conventions d'appel C ont t spcialement crite pour autoriser ce type
de fonctions. Comme l'adresse de la chane format est empile en dernier, Il
son emplacement sur la pile sera
x!
data
ou
bss
foo).
Si
x)
8 sur la pile, on ne
eax, ebp - 8
MOV
lea
eax, [ebp - 8]
foo.
pas
grer
alatoire
C.
ncessaire
l'assembleur
un
des
peuvent
en
stdarg.h
L'en-tte
dnit
nombre
d'arguments
macros
tre
qui
utilises
vrai. L'instruction
LEA
ne lit
jamais
bon
livre
sur
d'une variable locale (ou d'un paramtre) sur la pile n'est pas aussi intui-
pour
quel
mov
pas
n'est
d'utiliser
simplement l'adresse qui sera lue par une autre instruction et stocke cette
le
88
CHAPITRE 4.
SOUS-PROGRAMMES
adresse dans sa premire oprande registre. Comme elle ne lit pas la mmoire,
aucune taille mmoire (
dans le registre EAX. S'ils sont plus petits que 32 bits, ils sont tendus
32 bits lors du stockage dans EAX (la faon dont ils sont tendus dpend
du fait qu'ils sont signs ou non). Les valeurs 64 bits sont retournes dans
la paire de registres EDX:EAX. Les valeurs de pointeurs sont galement
stockes dans EAX. Les valeurs en virgule ottante sont stockes dans le
registre STP du coprocesseur arithmtique (ce registre est dcrit dans le
chapitre sur les nombres en virgule ottante).
trs
n'est pas toujours le cas . Les compilateurs qui utilisent plusieurs conventions ont souvent des options de ligne de commende qui peuvent tre utiliss
pour changer la convention par dfaut. Il fournissent galement des extensions la syntaxe C pour assigner explicitement des conventions d'appel
des fonctions de manire individuelle. Cependant, ces extensions ne sont pas
standardises et peuvent varier d'un compilateur l'autre.
Le compilateur GCC autorise direntes conventions d'appel. La convention utilise par une fonction peut tre dclare explicitement ent utilisant
l'extension
__attribute__
int,
standard call
. La fonction
cdecl par stdcall. La dirence entre stdcall et cdecl est que stdcall
4
Le compiltateur C Watcom est un exemple qui n'utilise pas les conventions standards
par dfaut. Voyez les sources exemple pour Watcom pour plus de dtails
4.7.
89
i.e.
printf
et
scanf).
celles
regparm qui
indique au compilateur d'utiliser les registres pour passer jusqu' 3 arguments entiers une fonction au lieu d'utiliser la pile. C'est un type d'optimisation courant que beaucoup de compilateurs supportent.
Borland et Microsoft utilisent une syntaxe commune pour dclarer les
conventions d'appel. Ils ajoutent les mots cls
__cdecl
et
__stdcall
au
et Microsoft:
Elle peut tre utilise pour n'importe quel type de fonction C et sur n'importe
quel compilateur C. Utiliser d'autres conventions peut limiter la portabilit
de la sous-routine. Son principal inconvnient est qu'elle peut tre plus lente
que certaines autres et utilise plus de mmoire (puisque chaque appel de
fonction ncessite du code pour retirer les paramtres de la pile).
principal inconvnient est qu'elle ne peut pas tre utilise avec des fonctions
qui ont un nombre variable d'arguments.
L'avantage d'utiliser une convention qui se sert des registres pour passer
des paramtres entiers est la rapidit. Le principal inconvnient est que la
convention est plus complexe. Certains paramtres peuvent se trouver dans
des registres et d'autres sur la pile.
4.7.7 Exemples
Voici un exemple qui montre comment une routine assembleur peut tre
interface avec un programme C (notez que ce programme n'utilise pas le
programme assembleur squelette (Figure 1.7) ni le module driver.c).
main5.c
1
2
3
4
#include <stdio.h>
/ prototype de la routine assembleur /
void calc_sum( int , int ) __attribute__((cdecl));
90
5
6
7
9
10
11
12
14
SOUS-PROGRAMMES
13
CHAPITRE 4.
main5.c
1
2
3
4
5
6
7
8
9
10
11
12
13
;
;
;
;
;
;
;
;
;
;
;
;
;
sub5.asm
sous-routine _calc_sum
trouve la somme des entiers de 1 n
Paramtres:
n
- jusqu'o faire la somme (en [ebp + 8])
sump - pointeur vers un entier dans lequel stocker la somme (en [ebp + 12])
pseudo-code C:
void calc_sum( int n, int * sump )
{
int i, sum = 0;
for( i=1; i <= n; i++ )
sum += i;
*sump = sum;
}
14
15
16
17
18
19
20
21
22
segment .text
global _calc_sum
;
; variable locale :
; sum en [ebp-4]
_calc_sum:
enter 4,0
push
ebx
23
24
25
26
27
28
29
mov
dword [ebp-4],0
dump_stack 1, 2, 4
mov
ecx, 1
for_loop:
cmp
ecx, [ebp+8]
jnle
end_for
; sum = 0
; affiche la pile de ebp-8 ebp+16
; ecx est le i du pseudocode
; cmp i et n
; si non i <= n, quitter
4.7.
91
30
31
32
33
add
inc
jmp
[ebp-4], ecx
ecx
short for_loop
; sum += i
ebx, [ebp+12]
eax, [ebp-4]
[ebx], eax
; ebx = sump
; eax = sum
ebx
; restaure ebx
34
35
36
37
38
end_for:
mov
mov
mov
39
40
41
42
pop
leave
ret
Pourquoi la ligne 22 de
sub5.asm
sub5.asm est si importante ? Parce que les conven-
tions d'appel C imposent que la valeur de EBX ne soit pas modie par l'appel de fonction. Si ce n'est pas respect, il est trs probable que le programme
ne fonctionne pas correctement.
La ligne 25 montre la faon dont fonctionne la macro
dump_stack.
Sou-
venez vous que le premier paramtre est juste une tiquette numrique et
les deuxime et troisime paramtres dterminent respectivement combien
de doubles mots elle doit acher en-dessous et au-dessus de EBP. La Figure 4.13 montre une excution possible du programme. Pour cette capture,
on peut voir que l'adresse du dword o stocker la somme est BFFFFB80 (en
EBP + 12) ; le nombre jusqu'auquel additionner est 0000000A (en EBP + 8) ;
l'adresse de retour pour la routine est 08048501 (en EBP + 4) ; la valeur sauvegarde de EBP est BFFFFB88 (en EBP); la valeur de la variable locale est
92
CHAPITRE 4.
SOUS-PROGRAMMES
calc_sum
plutt que d'utiliser un pointeur. Comme la somme est une valeur entire,
elle doit tre place dans le registre EAX. La ligne 11 du chier
main5.c
deviendrait :
sum = calc_sum(n);
De plus, le prototype de
calc_sum
bleur modi :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
sub6.asm
; sous-routine _calc_sum
; trouve la somme des entiers de 1 n
; Paramtres:
; n
- jusqu'o faire la somme (en [ebp + 8])
; Valeur de retour :
; valeur de la somme
; pseudo-code C :
; int calc_sum( int n )
; {
; int i, sum = 0;
; for( i=1; i <= n; i++ )
;
sum += i;
; return sum;
; }
segment .text
global _calc_sum
;
; variable locale :
; sum en [ebp-4]
_calc_sum:
enter 4,0
; fait de la place pour la somme sur la pile
22
23
24
25
26
27
mov
mov
for_loop:
cmp
jnle
dword [ebp-4],0
ecx, 1
; sum = 0
; ecx est le i du pseudocode
ecx, [ebp+8]
end_for
; cmp i et n
; si non i <= n, quitter
[ebp-4], ecx
ecx
short for_loop
; sum += i
28
29
30
31
add
inc
jmp
4.8.
1
2
93
segment .data
format
db "%d", 0
3
4
5
6
7
8
9
10
11
segment .text
...
lea
eax, [ebp-16]
push eax
push dword format
call _scanf
add
esp, 8
...
scanf
depuis l'assembleur
32
33
34
end_for:
mov
eax, [ebp-4]
; eax = sum
35
36
37
leave
ret
sub6.asm
scanf pour
lire un entier depuis le clavier ? La Figure 4.14 montre comment le faire. Une
chose trs importante se rappeler est que
C standards la lettre. Cela signie qu'elle prserve les valeurs des registres
EBX, ESI et EDI ; cependant, les registres EAX, ECX et EDX peuvent tre
modis ! En fait, EAX sera modi car il contiendra la valeur de retour de
l'appel
4.8
94
CHAPITRE 4.
mov
add
word [cs:$+7], 5
ax, 2
SOUS-PROGRAMMES
Ce code fonctionnerait en mode rel, mais sur les systmes d'exploitation en mode protg, le segment de code est marqu en lecture seule.
Lorsque la premire ligne ci-dessus s'excute, le programme est interrompu sur ces systmes. Cette faon de programmer est mauvaise pour
beaucoup de raison. Elle porte confusion, est dicile maintenir et
ne permet pas le partage de code (voir ci-dessous).
Il ne doit pas modier de donnes globales (comme celles qui se trouvent
data
et
bss).
la pile.
Il y a plusieurs avantages crire du code rentrant.
Un sous-programme rentrant peut tre appel rcursivement.
Un programme rentrant peut tre partag par plusieurs processus.
Sur beaucoup de systmes d'exploitation multi-tches, s'il y a plusieurs instances d'un programme en cours, seule
une
copie du code se
Link Libraries,
Dynamic
principe.
Les sous-programmes rentrants fonctionnent beaucoup mieux dans
etc.) supportent
directe
soit
programme, disons
foo,
foo.
La rcur-
sivit indirecte survient lorsqu'un sous-programme ne s'appelle pas directement lui-mme mais via un autre sous-programme qu'il appelle. Par exemple,
le sous-programme
foo
pourrait appeler
bar
et
bar
pourrait appeler
son.
foo.
condition de terminai-
Lorsque cette condition est vraie, il n'y a plus d'appel rcursif. Si une
x = fact (3);
/ trouve 3! /
5
Un programme multi-thread a plusieurs threads d'excution. C'est--dire que le programme lui-mme est multi-tches.
4.8.
1
2
3
4
5
; trouve n!
segment .text
global _fact
_fact:
enter 0,0
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mov
cmp
jbe
dec
push
call
pop
mul
jmp
term_cond:
mov
end_fact:
leave
ret
eax, [ebp+8]
; eax = n
eax, 1
term_cond
; si n <= 1, termin
eax
eax
_fact
; appel fact(n-1) rcursivement
ecx
; rponse dans eax
dword [ebp+8]
; edx:eax = eax * [ebp+8]
short end_fact
eax, 1
cadre n=3
n(3)
Adresse de retour
EBP Sauv
n(2)
cadre n=2
Adresse de retour
EBP Sauv
n(1)
cadre n=1
Adresse de retour
EBP Sauv
Fig. 4.16 Cadres de pile pour la fonction factorielle
95
96
1
2
3
4
5
6
7
8
CHAPITRE 4.
SOUS-PROGRAMMES
void f ( int x )
{
int i ;
for ( i=0; i < x; i++ ) {
printf ("%d\n", i);
f( i );
}
}
Fig. 4.17 Un autre exemple (version C)
La Figure 4.16 montre quoi ressemble la pile au point le plus profond pour
l'appel de fonction ci-dessus.
Les Figures 4.17 et 4.18 montrent un exemple rcursif plus compliqu en
l'instruction
pareil.
global
bss)
data
ou
dfaut, on peut y accder de n'importe quelle fonction dans le programme ; cependant, si elles sont dclares comme
static,
i.e.
seules les
en termes as-
static
static
data
ou
bss),
automatic
C'est le type par dfaut d'une variable C dnie dans une fonc-
tion. Ces variables sont alloues sur la pile lorsque la fonction dans
laquelle elles sont dnies est appele et sont dsalloues lorsque la
fonction revient. Donc, elles n'ont pas d'emplacement mmoire xe.
register
requte.
Le compi-
4.8.
1
2
3
4
5
6
7
8
9
%define i ebp-4
%define x ebp+8
; macros utiles
segment .data
format
db "%d", 10, 0
; 10 = '\n'
segment .text
global _f
extern _printf
_f:
enter 4,0
; alloue de la place sur la pile pour i
10
11
12
lp:
13
14
15
mov
dword [i], 0
; i = 0
mov
cmp
jnl
eax, [i]
eax, [x]
quit
; i < x?
push
push
call
add
eax
format
_printf
esp, 8
; appelle printf
push
call
pop
dword [i]
_f
eax
; appelle f
inc
jmp
dword [i]
short lp
; i++
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
quit:
leave
ret
97
98
CHAPITRE 4.
lateur n'a
pas
SOUS-PROGRAMMES
volatile
volatile.
une variable qui peut tre modie par deux threads d'un programme
multi-thread. Considrons le code suivant :
1
2
3
x = 10;
y = 20;
z = x;
Si
pouvait tre altr par un autre thread, il est possible que l'autre
thread change
Cependant, si
x
x
10.
Chapitre 5
Tableaux
5.1
Introduction
Un
bss,
utilisez les
resb, resw, etc. Souvenez vous que ces directives ont une oprande
100
1
2
3
4
5
6
7
8
9
10
CHAPITRE 5.
TABLEAUX
segment .data
; dfinit un tableau de 10 doubles mots initialiss 1,2,..,10
a1
dd 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
; dfinit un tableau de 10 mots initialiss 0
a2
dw 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
; idem mais en utilisant TIMES
a3
times 10 dw 0
; dfinit un tableau d'octets avec 200 0 puis 100 1
a4
times 200 db 0
times 100 db 1
11
12
13
14
15
16
segment .bss
; dfinit un
a5
; dfinit un
a6
toutes
1 + 2 4 + 50 2 = 109
octets.
[ ]
accder un lment d'un tableau, son adresse doit tre calcule. Considrons
les deux dnitions de tableau suivantes :
array1
array2
db
dw
5, 4, 3, 2, 1
5, 4, 3, 2, 1
; tableau d'octets
; tableau de mots
5.1.
101
INTRODUCTION
EBP - 1
char
unused
EBP - 8
dword 1
EBP - 12
dword 2
word
array
word
array
EBP - 100
EBP - 104
dword 1
EBP - 108
dword 2
EBP - 109
EBP - 112
char
unused
1
2
3
4
5
6
7
mov
mov
mov
mov
mov
mov
mov
al, [array1]
al, [array1 +
[array1 + 3],
ax, [array2]
ax, [array2 +
[array2 + 6],
ax, [array2 +
1]
al
2]
ax
1]
;
;
;
;
;
;
;
al = array1[0]
al = array1[1]
array1[3] = al
ax = array2[0]
ax = array2[1] (PAS array2[2]!)
array2[3] = ax
ax = ??
array1
ADD
des octets et d'obtenir une somme qui serait trop grande pour tenir sur un
octet. En utilisant DX, la somme peut atteindre 65 535. Cependant, il est
important de raliser que AH est galement additionn. C'est pourquoi, AH
est positionn zro
1 la ligne 3.
1
Positionner AH zro quivaut supposer implicitement que AL est un nombre non
sign. S'il est sign, l'action approprie serait d'insrer une instruction CBW entre les lignes 6
102
1
2
3
4
5
lp:
6
7
8
9
CHAPITRE 5.
TABLEAUX
mov
mov
mov
mov
ebx, array1
dx, 0
ah, 0
ecx, 5
mov
add
inc
loop
al, [ebx]
dx, ax
ebx
lp
; al = *ebx
; dx += ax (pas al!)
; bx++
1
2
3
4
lp:
5
6
7
8
9
10
next:
mov
mov
mov
ebx, array1
dx, 0
ecx, 5
add
jnc
inc
dl, [ebx]
next
dh
; dl += *ebx
; si pas de retenue goto next
; incrmente dh
inc
loop
ebx
lp
; bx++
reg de base
est un des registres EAX, EBX, ECX, EDX, EBP, ESP, ESI
ou EDI.
facteur
et 7.
5.1.
1
2
3
4
5
6
7
8
lp:
103
INTRODUCTION
mov
mov
mov
ebx, array1
dx, 0
ecx, 5
add
adc
inc
loop
dl, [ebx]
dh, 0
ebx
lp
; dl += *ebx
; dh += drapeau de retenue + 0
; bx++
reg d'index
est un des registres EAX, EBX, ECX, EDX, EBP, ESI, EDI
constante
est une constante 32 bits. Cela peut tre une tiquette (ou une
expression d'tiquette).
5.1.4 Exemple
Voici un exemple qui utilise un tableau et le passe une fonction. Il
array1c.c
driver.c.
utilise le programme
programme
1
2
array1.asm
3
4
5
6
7
8
9
segment .data
FirstMsg
Prompt
SecondMsg
ThirdMsg
InputFormat
db
db
db
db
db
segment .bss
array
resd ARRAY_SIZE
10
11
12
13
14
15
16
17
18
segment .text
extern _puts, _printf, _scanf, _dump_line
global _asm_main
_asm_main:
enter 4,0
; variable locale dword en EBP - 4
104
19
20
CHAPITRE 5.
push
push
TABLEAUX
ebx
esi
21
22
23
24
25
26
27
28
29
mov
mov
init_loop:
mov
add
loop
ecx, ARRAY_SIZE
ebx, array
[ebx], ecx
ebx, 4
init_loop
30
31
32
33
push
call
pop
dword FirstMsg
_puts
ecx
push
push
call
add
dword 10
dword array
_print_array
esp, 8
; affiche FirstMsg
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
lea
push
push
call
add
cmp
je
eax, [ebp-4]
; eax = adresse du dword local
eax
dword InputFormat
_scanf
esp, 8
eax, 1
; eax = valeur de retour de scanf
InputOK
call
jmp
53
54
55
56
57
58
59
60
InputOK:
mov
push
push
esi, [ebp-4]
dword [array + 4*esi]
esi
5.1.
61
62
63
105
INTRODUCTION
push
call
add
dword SecondMsg
_printf
esp, 12
push
call
pop
dword ThirdMsg
_puts
ecx
push
push
call
add
dword 10
dword array + 20*4
_print_array
esp, 8
pop
pop
mov
leave
ret
esi
ebx
eax, 0
64
65
66
67
68
69
70
71
72
; adresse de array[20]
73
74
75
76
77
78
; retour au C
79
80
81
82
83
84
85
86
87
88
;
;
;
;
;
;
;
;
;
routine _print_array
Routine appelable depuis le C qui affiche les lments d'un tableau de doubles mots
comme des entiers signs.
Prototype C:
void print_array( const int * a, int n);
Paramtres:
a - pointeur vers le tableau afficher (en ebp+8 sur la pile)
n - nombre d'entiers afficher (en ebp+12 sur la pile)
89
90
91
segment .data
OutputFormat
db
92
93
94
95
96
97
98
segment .text
global
_print_array:
enter
push
push
_print_array
0,0
esi
ebx
99
100
101
102
xor
mov
mov
esi, esi
ecx, [ebp+12]
ebx, [ebp+8]
; esi = 0
; ecx = n
; ebx = adresse du tableau
106
103
104
CHAPITRE 5.
print_loop:
push
TABLEAUX
ecx
push
push
push
call
add
; empile tableau[esi]
inc
pop
loop
esi
ecx
print_loop
pop
pop
leave
ret
ebx
esi
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
array1.asm
array1c.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
int asm_main( void );
void dump_line( void );
int main()
{
int ret_status ;
ret_status = asm_main();
return ret_status ;
}
/
fonction dump_line
retire tous les caractres restant sur la ligne courante dans le buer d'entre
/
void dump_line()
{
int ch;
20
21
22
23
5.1.
107
INTRODUCTION
array1c.c
L'instruction LEA revisite
L'instruction
LEA peut tre utilise dans d'autres cas que le calcul d'adresse.
Elle est assez couramment utilise pour les calculs rapides. Considrons le
code suivant :
lea
Il stocke la valeur de
doit
Donc, par exemple, cette instruction ne peut pas tre utilise pour multiplier
par 6 rapidement.
int a [3][2];
Le compilateur C rserverait de la place pour un tableau d'entiers de 6
(=
2 3)
a[0][0]
a[0][1]
a[1][0]
a[1][1]
a[2][0]
a[2][1]
a[0][0]
a[0][1]
etc.
Chaque ligne du
tableau deux dimensions est stocke en mmoire de faon contige. Le dernier lment d'une ligne est suivi par le premier lment de la suivante. On
108
1
2
3
4
5
mov
sal
add
mov
mov
CHAPITRE 5.
eax,
eax,
eax,
eax,
[ebp
[ebp 1
[ebp [ebp +
- 52],
44]
;
;
48]
;
4*eax - 40] ;
eax
;
Fig. 5.6
Assembleur correspondant
TABLEAUX
au niveau ligne
x = a[ i ][ j ]
a[i][j]
j. La formule dans ce cas est 2i+j . Il n'est pas compliqu de voir comment
on obtient cette formule. Chaque ligne fait deux lments de long ; donc le
i est l'emplacement 2i. Puis on obtient l'emplaj 2i. Cette analyse montre galement
gnralise un tableau de N colonnes : N i + j .
en ajoutant
pas
du nombre de lignes.
gcc
x = a[ i ][ j ];
La Figure 5.6 montre le code assembleur correspondant. Donc, le compilateur
convertit grossirement le code en :
x = (&a[0][0] + 2 i + j );
et en fait, le programmeur pourrait l'crire de cette manire et obtenir le
mme rsultat.
Il n'y a rien de magique propos du choix de la reprsentation niveau
ligne du tableau. Une reprsentation niveau colonne fonctionnerait galement :
Indice
Elment
a[0][0]
a[1][0]
a[2][0]
a[0][1]
a[1][1]
a[2][1]
Dans la reprsentation niveau colonne, chaque colonne est stocke de manitre contige. L'lment
langages (FORTRAN, par exemple) utilisent la reprsentation niveau colonne. C'est important lorsque l'on interface du code provenant de multiples
langages.
5.1.
109
INTRODUCTION
int b [4][3][2];
Ce tableau serait stock comme s'il tait compos de trois tableaux deux
dimensions, chacun de taille
b[0][0][0]
b[0][0][1]
b[0][1][0]
b[0][1][1]
b[0][2][0]
b[0][2][1]
10
11
b[1][0][0]
b[1][0][1]
b[1][1][0]
b[1][1][1]
b[1][2][0]
b[1][2][1]
Indice
Elment
b[i][j][k]
[3][2]. En
est
6i + 2j + k .
Le
bleau de dimension
dans la formule.
Pour les dimensions plus grandes, le mme procd est gnralis. Pour un
tableau
dimensions de dimension
i1
in
D1
Dn ,
D2 D3 Dn i1 + D3 D4 Dn i2 + + Dn in1 + in
ou pour les fanas de maths, on peut l'crire de faon plus concise :
n
X
n
Y
Dk ij
j=1
La premire dimension,
D1 ,
k=j+1
C'est
prenez
que
que
vous
l'auteur
comest
j1
Y
j=1
Dk ij
k=1
n
X
la rfrence FORTRAN
Dn ,
110
CHAPITRE 5.
TABLEAUX
void f ( int a [ ][ ] );
void f ( int a [ ] );
Cela dnit un tableau une dimension de pointeurs sur des entiers (qui peut
son tour tre utilis pour crer un tableau de tableaux qui se comportent
plus comme un tableau deux dimensions).
Pour les tableaux de dimensions suprieures, toutes les dimensions sauf
la premire doivent tre donnes pour les paramtres. Par exemple, un paramtre tableau quatre dimensions peut tre pass de la faon suivante :
Instructions de Tableaux/Chanes
de chanes.
instructions
drapeau de direction
CLD
STD
On peut indiquer une taille mais elle n'est pas prise en compte par le compilateur.
5.2.
INSTRUCTIONS DE TABLEAUX/CHANES
LODSB
AL = [DS:ESI]
ESI = ESI 1
AX = [DS:ESI]
ESI = ESI 2
EAX = [DS:ESI]
ESI = ESI 4
LODSW
LODSD
STOSB
STOSW
STOSD
111
[ES:EDI] = AL
EDI = EDI 1
[ES:EDI] = AX
EDI = EDI 2
[ES:EDI] = EAX
EDI = EDI 4
1
2
segment .data
array1 dd 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
3
4
5
segment .bss
array2 resd 10
6
7
8
9
10
11
12
13
14
15
segment .text
cld
mov
esi, array1
mov
edi, array2
mov
ecx, 10
lp:
lodsd
stosd
loop lp
; ne pas oublier !
Une erreur
trs
sitionner le drapeau de direction. Cela conduit souvent un code qui fonctionne la plupart du temps (lorsque le drapeau de direction est dans la position dsire), mais ne fonctionne pas
tout
le temps.
Index
112
CHAPITRE 5.
MOVSB
MOVSW
MOVSD
TABLEAUX
1
2
segment .bss
array resd 10
3
4
5
6
7
8
9
segment .text
cld
mov
edi, array
mov
ecx, 10
xor
eax, eax
rep stosd
; ne pas oublier !
donne est xe (AL, AX ou EAX). Enn, notez que les instructions de stockage utilisent ES pour dterminer le segment dans lequel crire, pas DS. En
programmation en mode protg, ce n'est habituellement pas un problme,
puisqu'il n'y a qu'un segment de donnes et ES est automatiquement initialis pour y faire rfrence (tout comme DS). Cependant, en programmation
en mode rel, il est
trs
14 de la Figure 5.8) est trs courante. En fait, cette combinaison peut tre
eectue par une seule instruction de chane
MOVSx.
MOVSD
avec le mme
Une autre complication est qu'on ne peut pas copier la valeur du registre DS dans ES
directement en utilisant une instruction MOV. A la place, la valeur de DS doit tre copie
dans un registre universel (comme AX) puis tre copi depuis ce registre dans ES, ce qui
utilise deux instruction MOV.
5.2.
113
INSTRUCTIONS DE TABLEAUX/CHANES
CMPSB
CMPSW
CMPSD
SCASB
SCASW
SCASD
rsultat. La seule dirence serait que le registre EAX ne serait pas utilis
du tout dans la boucle.
4 appel
REP
qui
peut tre utilis avec les instructions de chane prsentes ci-dessus. Ce prxe indique au processeur de rpter l'instruction de chane qui suit un
nombre prcis de fois. Le registre ECX est utilis pour compter les itrations (exactement comme pour l'instruction
REP,
LOOP).
En utilisant le prxe
rep movsd
La Figure 5.10 montre un autre exemple qui met zro le contenu d'un
tableau.
Un prxe d'instruction n'est pas une instruction, c'est un octet spcial qui est plac
avant une instruction de chane qui modie son comportement. D'autres prxes sont
galement utiliss pour modier le segment par dfaut des accs mmoire
114
1
2
CHAPITRE 5.
TABLEAUX
segment .bss
array
resd 100
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
segment .text
cld
mov
edi, array
; pointeur vers le dbut du tableau
mov
ecx, 100
; nombre d'lments
mov
eax, 12
; nombre rechercher
lp:
scasd
je
found
loop lp
; code excuter si non trouv
jmp
onward
found:
sub
edi, 4
; edi pointe maintenant vers 12
; code excuter si trouv
onward:
CMP.
Les instructions
CMPSx
SCASx
SCASD
la ligne 10 ajoute
toujours 4 EDI, mme si la valeur recherche est trouve. Donc, si l'on veut
trouver l'adresse du 12 dans le tableau, il est ncessaire de soustraire 4 de
EDI (comme le fait la ligne 16).
REP
5.2.
115
INSTRUCTIONS DE TABLEAUX/CHANES
REPE, REPZ
REPNE, REPNZ
1
2
3
4
5
6
7
8
9
10
11
12
REPx
segment .text
cld
mov
esi, block1
; adresse du premier bloc
mov
edi, block2
; adresse du second bloc
mov
ecx, size
; taille des blocs en octets
repe cmpsb
; rpter tant que Z est allum
je
equal
; Z est allum => blocs gaux
; code excuter si les blocs ne sont pas gaux
jmp
onward
equal:
; code excuter s'ils sont gaux
onward:
REPE
et
le sont
prxe (comme
chanes rpte
zro
aprs
la
arrte cause de son rsultat ou si c'est parce que ECX a atteint zro.
JE
equal.
5.2.5 Exemple
Cette section contient un chier source assembleur avec plusieurs fonctions qui implmentent des oprations sur les tableaux en utilisant des instructions de chane. Beaucoup de ces fonctions font doublons avec des fonc-
comparaison rpte ?
116
CHAPITRE 5.
TABLEAUX
memory.asm
global _asm_copy, _asm_find, _asm_strlen, _asm_strcpy
2
3
4
5
6
7
8
9
10
11
segment .text
; fonction _asm_copy
; copie deux blocs de mmoire
; prototype C
; void asm_copy( void * dest, const void * src, unsigned sz);
; paramtres:
; dest - pointeur sur le tampon vers lequel copier
; src - pointeur sur le tampon depuis lequel copier
; sz - nombre d'octets copier
12
13
14
15
16
17
18
19
20
21
22
mov
mov
mov
23
24
25
esi, src
edi, dest
ecx, sz
movsb
26
cld
rep
27
28
29
pop
pop
leave
ret
30
31
32
33
edi
esi
34
35
36
37
38
39
40
;
;
;
;
;
fonction _asm_find
recherche un octet donn en mmoire
void * asm_find( const void * src, char target, unsigned sz);
paramtres :
src
- pointeur sur le tampon dans lequel chercher
5.2.
41
42
43
44
45
46
47
48
49
50
51
52
INSTRUCTIONS DE TABLEAUX/CHANES
117
53
54
55
56
_asm_find:
enter
push
0,0
edi
57
58
59
60
61
mov
mov
mov
cld
eax, target
edi, src
ecx, sz
; al a la valeur recherche
repne
scasb
found_it
eax, 0
short quit
62
63
64
65
66
67
68
69
70
71
72
73
74
je
mov
jmp
found_it:
mov
dec
quit:
pop
leave
ret
eax, edi
eax
edi
75
76
77
78
79
80
81
82
;
;
;
;
;
;
fonction _asm_strlen
retourne la taille d'une chane
unsigned asm_strlen( const char * );
paramtre :
src - pointeur sur la chane
valeur de retour :
118
83
CHAPITRE 5.
TABLEAUX
84
85
86
87
88
89
90
91
92
93
mov
mov
xor
cld
edi, src
; edi = pointeur sur la chane
ecx, 0FFFFFFFFh ; utilise la plus grande valeur possible de ECX
al,al
; al = 0
repnz
scasb
94
95
; recherche le 0 terminal
96
97
98
99
100
101
102
;
; repnz ira un cran trop loin, donc la longueur vaut FFFFFFFE - ECX,
; pas FFFFFFFF - ECX
;
mov
eax,0FFFFFFFEh
sub
eax, ecx
; longueur = 0FFFFFFFEh - ecx
103
104
105
106
pop
leave
ret
edi
107
108
109
110
111
112
113
114
115
116
117
118
119
120
; fonction _asm_strcpy
; copie une chane
; void asm_strcpy( char * dest, const char * src);
; paramtres :
; dest - pointeur sur la chaine vers laquelle copier
; src - pointeur sur la chane depuis laquelle copier
;
%define dest [ebp + 8]
%define src [ebp + 12]
_asm_strcpy:
enter 0,0
push
esi
push
edi
121
122
123
124
mov
mov
cld
edi, dest
esi, src
5.2.
125
126
127
128
129
INSTRUCTIONS DE TABLEAUX/CHANES
cpy_loop:
lodsb
stosb
or
jnz
al, al
cpy_loop
;
;
;
;
119
130
pop
pop
leave
ret
131
132
133
134
edi
esi
memory.asm
memex.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#dene STR_SIZE 30
/ prototypes /
void asm_copy( void , const void , unsigned ) __attribute__((cdecl));
void asm_nd( const void ,
char target , unsigned ) __attribute__((cdecl));
unsigned asm_strlen( const char ) __attribute__((cdecl));
void asm_strcpy( char , const char ) __attribute__((cdecl));
int main()
{
char st1 [STR_SIZE] = "chane test";
char st2 [STR_SIZE];
char st ;
char ch;
asm_copy(st2, st1, STR_SIZE); / copie les 30 caractres de la chane /
printf ("%s\n", st2);
printf ("Entrez un caractre : " ); / recherche un octet dans la chane /
scanf("%c%[^\n]", &ch);
st = asm_nd(st2, ch, STR_SIZE);
if ( st )
printf ("Trouv : %s\n", st );
else
printf ("Pas trouv\n");
120
31
32
33
34
35
36
37
39
TABLEAUX
st1 [0] = 0;
printf ("Entrez une chane :");
scanf("%s", st1);
printf ("longueur = %u\n", asm_strlen(st1));
30
38
CHAPITRE 5.
return 0;
memex.c
Chapitre 6
Virgule Flottante
6.1
0, 1012 = 1 21 + 0 22 + 1 23 = 0, 625
Cette ide peut tre associe avec les mthodes appliques aux entiers dans
le Chapitre 1 pour convertir un nombre quelconque :
a, b, c, . . .
0, abcdef . . .
Multipliez le nombre par deux. La reprsentation du nouveau nombre sera :
a, bcdef . . .
121
122
CHAPITRE 6.
0, 5625 2 = 1, 125
VIRGULE FLOTTANTE
premier bit
= 1
0, 125 2 = 0, 25
deuxime bit
= 0
0, 25 2 = 0, 5
troisime bit
= 0
0, 5 2 = 1, 0
quatrime bit
= 1
0, 85 2 = 1, 7
0, 7 2 = 1, 4
0, 4 2 = 0, 8
0, 8 2 = 1, 6
0, 6 2 = 1, 2
0, 2 2 = 0, 4
0, 4 2 = 0, 8
0, 8 2 = 1, 6
par
pour
obtenir :
0, bcdef . . .
et multipliez nouveau par deux, vous obtenez :
b, cdef . . .
Maintenant le deuxime bit (b) est la premire place. Cette procdure peut
tre rpte jusqu' ce qu'autant de bits que ncessaires soient trouvs. La
Figure 6.1 montre un exemple rel qui converti 0,5625 en binaire. La mthode
s'arrte lorsque la partie dcimale arrive zro.
Prenons un autre exemple, considrons la conversion de 23,85 en binaire,
il est facile de convertir la partie entire (23
= 101112 ),
6.1.
123
l'on regarde les nombres attentivement, on trouve une boucle innie ! Cela
signie que 0,85 est un binaire rptitif (par analogie un dcimal rptitif
0, 85 = 0, 1101102 .
Donc,
exactement
1
(Tout comme
3 ne peut pas tre reprsent en dcimal avec un nombre ni
de chires). Comme le montre ce chapitre, les variables
float et double en C
sont stockes en binaire. Donc, les valeurs comme 23,85 ne peuvent pas tre
stockes exactement dans ce genre de variables. Seule une approximation de
23,85 peut tre stocke.
Pour simplier le matriel, les nombres en virgule ottante sont stocks
dans un format standard. Ce format utilise la notation scientique (mais en
binaire, en utilisant des puissances de deux, pas de dix). Par exemple, 23,85
ou
10111, 11011001100110 . . .2
1, 011111011001100110 . . . 2100
(o l'exposant (100) est en binaire). Un nombre en virgule ottante
normalis
a la forme :
1, ssssssssssssssss 2eeeeeee
o
1, sssssssssssss
est la
mantisse
et
eeeeeeee
exposant.
est l'
double.
Le coprocesseur arithmtique d'Intel utilise galement une troisime prcision plus leve appele
Cela n'est pas surprenant qu'un nombre puisse tre rptitif dans une base, mais pas
dans une autre. Pensez 13 , il se rpte en dcimal, mais en ternaire (base 3) il vaudrait
0, 13 .
124
CHAPITRE 6.
31
s
30
23
VIRGULE FLOTTANTE
22
exposant dcal
est
1, sssssssss).
pas stock !
Cela
faut
toujours
l'esprit
41
BE
que
CC
les
CD
octets de signe est 0, Ensuite, l'exposant rel est 4, donc l'exposant dcal est
peuvent
le
comme
programme !
un
Vus
nombre
en
7F + 4 = 8316 .
un
mot,
ils
mais
entier
vus
double
reprsentent
vous que le un de tte est masqu). En les mettant bout bout (pour clarier
reprsentation en un masqu .
Comment serait stock 23,85 ? Tout d'abord, il est positif, donc le bit
garder
Le type long double de certains compilateurs (comme celui de Borland) utilise cette
prcision tendue. Par contre, d'autres compilateurs utilisent la double prcision la fois
pour les double et long double (C'est autoris par le C ANSI).
6.1.
125
e = 0 et f = 0
e = 0 et f 6= 0
indique un
e = FF et f = 0
e = FF et f 6= 0
NaN
(Not
et
Cela ne fait pas exactement 23,85 (puisqu'il s'agit d'un binaire rptitif ). Si
l'on convertit le chire ci-dessus en dcimal, on constate qu'il vaut approximativement 23,849998474, ce nombre est trs proche de 23,85 mais n'est pas
exactement le mme. En ralit, en C, 23,85 ne serait pas reprsent exactement comme ci-dessus. Comme le bit le plus gauche qui a t supprim de
la reprsentation exacte valait 1, le dernier bit est arrondi 1, donc 23,85 serait reprsent par 41 BE CC CD en hexa en utilisant la simple prcision. En
dcimal, ce chire vaut 23,850000381 ce qui est une meilleure approximation
de 23,85.
Comment serait reprsent -23,85 ? Il sut de changer le bit de signe :
C1 BE CC CD. Ne prenez
pas
Certaines combinaisons de
le complment deux !
et
ottants IEEE. Le Tableau 6.1 dcrit ces valeurs. Un inni est produit par
un dpassement de capacit ou une division par zro. Un rsultat indni
est produit par une opration invalide comme rechercher la racine care d'un
nombre ngatif, additionner deux innis,
etc.
Nombres dnormaliss
Les nombres dnormaliss peuvent tre utiliss pour reprsenter des nombres
avec des grandeurs trop petites normaliser (
Par exemple, considrons le nombre
Dans la forme normalise, l'exposant est trop petit. Par contre, il peut tre
reprsent sous une forme non normalise :
0, 010012 2127 .
Pour stocker
ce nombre, l'exposant dcal est positionn 0 (voir Tableau 6.1) et la partie dcimale est la mantisse complte du nombre crite comme un produit
avec
2127
dcimal). La reprsentation de
1, 001 2129
est donc :
126
CHAPITRE 6.
63
62
52
VIRGULE FLOTTANTE
51
f
Fig. 6.4 Double prcision IEEE
10308
et
10308 .
4+3FF = 403
0 100 0000 0011 0111 1101 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1010
ou 40 37 D9 99 99 99 99 9A en hexa. Si l'on reconvertit ce nombre en
dcimal, on trouve 23,8500000000000014 (il y a 12 zros !) ce qui est une
approximation de 23,85 nettement meilleure.
21023
au lieu
127 .
de 2
6.2
3
La seule dirence est que pour l'inni et les valeurs indnies, l'exposant dcal vaut
7FF et non pas FF.
6.2.
127
considrs comme exacts. Comme nous l'avons montr dans la section prcdente, sur un ordinateur beaucoup de nombres ne peuvent pas tre reprsents exactement avec un nombre ni de bits. Tous les calculs sont eectus
avec une prcision limite. Dans les exemples de cette section, des nombres
avec une mantisse de 8 bits seront utiliss pour plus de simplicit.
6.2.1 Addition
Pour additionner deux nombres en virgule ottante, les exposants doivent
tre gaux. S'ils ne le sont pas dj, alors, il faut les rendre gaux en dcalant
la mantisse du nombre ayant le plus petit exposant. Par exemple, considrons
ou en binaire :
1, 0100110 23
+ 1, 1001011 22
Ces deux nombres n'ont pas le mme exposant, donc on dcale la mantisse
pour rendre les exposants gaux, puis on eectue l'addition :
1.0100110 23
+ 0.1100110 23
10.0001100 23
1, 100101122 supprime le un de tte et arrondi plus
3
3
tard le rsultat en 0, 1100110 2 . Le rsultat de l'addition, 10, 0001100 2
4
(ou 1, 00001100 2 ), est gal 10000, 1102 ou 16,75, ce n'est pas gal
(a + b) b = a;
6.2.2 Soustraction
La soustraction fonctionne d'une faon similaire l'addition et soure
des mmes problmes. Par exemple, considrons
1, 0000110 24
1, 1111111 23
128
CHAPITRE 6.
Dcaler
1, 1111111 23
VIRGULE FLOTTANTE
1, 0000000 24
1, 0000110 24
1, 0000000 24
0, 0000110 24
0, 0000110 24 = 0, 112 = 0, 75
1, 0100110 23
1, 0100000 21
10100110
+ 10100110
1, 10011111000000 24
Bien sr, le rsultat rel serait arrondi 8 bits pour donner :
f(x)
if ( f(x) == 0.0 )
Mais, que se passe-t-il si
ment que
l'galit ne sera pas vrie. Il n'y a peut tre aucune valeur en virgule ottante IEEE de
dans
f(x).
6.3.
129
LE COPROCESSEUR ARITHMTIQUE
EPS
est une macro dnissant une trs petite valeur positive (du genre
1 1010 ).
f(x)
x)
une
autre ( ), on utilise :
6.3
Le Coprocesseur Arithmtique
6.3.1 Matriel
Les premiers processeurs Intel n'avaient pas de support matriel pour
les oprations en virgule ottante. Cela ne signie pas qu'ils ne pouvaient
pas eectuer de telles oprations. Cela signie seulement qu'elles devaient
tre ralises par des procdures composes de beaucoup d'instructions qui
n'taient pas en virgule ottante. Pour ces systmes, Intel fournissait une
puce appele
coprocesseur mathmatique.
Un coprocesseur mathmatique a
des instructions machine qui eectuent beaucoup d'oprations en virgule ottante beaucoup plus rapidement qu'en utilisant une procdure logicielle (sur
les premiers processeurs, au moins 10 fois plus vite !). Le coprocesseur pour
le 8086/8088 s'appelait le 8087, pour le 80286, il y avait un 80287 et pour
le 80386, un 80387 ; le processeur 80486DX intgrait le coprocesseur math-
toujours stocks sous forme de nombres 80 bits en prcision tendue dans ces
registres. Les registres sont appels
Les registres en
Last-In First-Out
pile.
5
Cependant, le 80486SX n'avait
80487SX pour ces machines.
pas
130
CHAPITRE 6.
VIRGULE FLOTTANTE
sont ajouts au sommet de la pile. Les nombres existants sont dcals vers
le bas pour faire de la place au nouveau.
Il y a galement un registre de statut dans le coprocesseur arithmtique.
Il a plusieurs drapeaux. Seuls les 4 drapeaux utiliss pour les comparaisons
seront traits : C0 , C1 , C2 et C3 . Leur utilisation est traite plus tard.
6.3.2 Instructions
Pour faciliter la distinction entre les instructions du processeur normal et
celles du coprocesseur, tous les mnmoniques du coprocesseur commencent
par un
F.
Chargement et stockage
Il y a plusieurs instructions qui chargent des donnes au sommet de la
pile du coprocesseur :
FLD source
FILD source
Lit un
entier
source
peut tre
FLD1
FLDZ
6.3.
131
LE COPROCESSEUR ARITHMTIQUE
FST dest
destination
FSTP dest
destination
FIST dest
destination
mot de contrle
mot spcial (pas en virgule ottante) qui contrle le fonctionnement du coprocesseur. Par dfaut, le mot de contrle est
initialis an qu'il arrondisse l'entier le plus proche lorsqu'il
convertit vers un entier. Cependant, les instructions
(Store Control Word, stocker le mot de contrle) et
FSTCW
FLDCW
FISTP dest
Identique
supprim et la
destination
mot.
Il y a deux autres instructions qui peuvent placer ou retirer des donnes
de la pile.
FXCH STn
ST0
et
STn
sur la pile (o
est un
FFREE STn
Addition et soustraction
ST0
et d'un
132
1
2
3
CHAPITRE 6.
VIRGULE FLOTTANTE
segment .bss
array
resq SIZE
sum
resq 1
4
5
6
7
8
9
10
11
12
13
segment .text
mov
ecx, SIZE
mov
esi, array
fldz
lp:
fadd qword [esi]
add
esi, 8
loop lp
fstp qword sum
; ST0 = 0
; ST0 += *(esi)
; passe au double suivant
; stocke le rsultat dans sum
FADD source
ST0 += source .
La
source
registre du coprocesseur.
FADDP dest ou
FADDP dest, STO
dest += ST0
nation
desti-
seur.
FIADD source
Ajoute un entier
ST0.
soit par
La
RP.
ajoute les lments d'un tableau de doubles. Au niveau des lignes 10 et 13, il
faut spcier la taille de l'oprande mmoire. Sinon l'assembleur ne saurait
pas si l'oprande mmoire est un oat (dword) ou un double (qword).
6.3.
133
LE COPROCESSEUR ARITHMTIQUE
FSUB source
ST0 -= source .
La
source
FSUBR source
La
source
registre du coprocesseur.
La
destination
FSUBP dest ou
FSUBP dest, STO
FSUBRP dest ou
FSUBRP dest, ST0
destination
processeur.
FISUB source
source
moire.
FISUBR source
source
Soustrait
ST0
d'un
en mmoire.
Multiplication et division
Les instructions de multiplication sont totalement analogues celles d'addition.
FMUL source
ST0 *= source .
La
source
FMULP dest ou
FMULP dest, ST0
FIMUL source
un entier avec
un double mot
en mmoire.
Ce n'est pas tonnant, les instructions de division sont analogues celles
de soustraction. La division par zro donne l'inni.
134
CHAPITRE 6.
FDIV source
ST0 /= source .
La
VIRGULE FLOTTANTE
source
FDIVR source
source
La
registre du coprocesseur.
La
destination
FDIVP dest ou
FDIVP dest, ST0
FDIVRP dest ou
FDIVRP dest, ST0
destination
processeur.
FIDIV source
FIDIVR source
Divise
ST0
par un entier.
Comparaisons
Le coprocesseur eectue galement des comparaisons de nombres en vir-
FCOM source
compare
ST0
et
un registre du
FCOMP source
compare
ST0
et
source ,
source
FCOMPP
FICOM source
compare
compare
FICOMP source
compare
La
ST0
source
et
(float)source ,
en mmoire.
FTST
compare
ST0
et 0,
6.3.
1
2
3
4
5
6
7
8
9
10
11
12
13
;
;
135
LE COPROCESSEUR ARITHMTIQUE
if ( x > y )
fld
fcomp
fstsw
sahf
jna
then_part:
; code
jmp
else_part:
; code
end_if:
qword [x]
qword [y]
ax
; ST0 = x
; compare STO et y
; place les bits C dans FLAGS
else_part
si vrai
end_if
si faux
tructions nouvelles :
FSTSW destination
SAHF
LAHF
JNA.
Les Pentium Pro (et les processeurs plus rcents (Pentium II and III))
supportent deux nouveaux oprateurs de comparaison qui modient directement le registre FLAGS du processeur.
FCOMI source
compare
ST0
et
source .
La
source
coprocesseur.
FCOMIP source
compare
ST0
et
source ,
FCOMIP.
FICOMP).
Instructions diverses
Cette section traite de diverses autres instructions fournies par le coprocesseur.
source
136
CHAPITRE 6.
FCHS
FABS
FSQRT
FSCALE
VIRGULE FLOTTANTE
6.3.3 Exemples
6.3.4 Formule quadratique
Le premier exemple montre comment la formule quadratique peut tre
code en assembleur. Souvenez vous, la formule quadratique calcule les solutions de l'quation :
ax2 + bx + c = 0
x: x1 et x2 .
b b2 4ac
x1 , x2 =
2a
4ac)
est appele le
dterminant. Sa
valeur est utile pour dterminer laquelle des trois possibilits suivantes est
vrie pour les solutions.
1. Il n'y a qu'une seule solution double.
2. Il y a deux solutions relles.
b2 4ac = 0
b2 4ac > 0
b2 4ac < 0
quadt.c
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
int quadratic ( double, double, double, double , double );
int main()
{
double a,b,c , root1 , root2;
printf ("Entrez a , b, c : ");
scanf("%lf %lf %lf", &a, &b, &c);
if ( quadratic ( a , b, c, &root1, &root2 ) )
printf (" racines : %.10g %.10g\n", root1, root2 );
6.3.
13
14
15
16
LE COPROCESSEUR ARITHMTIQUE
137
else
printf ("pas de racine relle \n");
return 0;
}
quadt.c
Voici la routine assembleur :
1
2
3
4
5
6
7
8
9
10
11
12
;
;
;
;
;
;
;
;
;
;
;
;
quad.asm
fonction quadratic
trouve les solutions l'quation quadratique :
a*x**2 + b*x + c = 0
prototype C :
int quadratic( double a, double b, double c,
double * root1, double *root2 )
Paramtres:
a, b, c - coefficients des puissances de l'quation quadratique (voir ci-dessus)
root1 - pointeur vers un double o stocker la premire racine
root2 - pointeur vers un double o stocker la deuxime racine
Valeur de retour :
retourne 1 si des racines relles sont trouves, sinon 0
13
14
15
16
17
18
19
20
%define
%define
%define
%define
%define
%define
%define
a
b
c
root1
root2
disc
one_over_2a
qword
qword
qword
dword
dword
qword
qword
[ebp+8]
[ebp+16]
[ebp+24]
[ebp+32]
[ebp+36]
[ebp-8]
[ebp-16]
21
22
23
segment .data
MinusFour
dw
-4
24
25
26
27
28
29
30
31
segment .text
global _quadratic
_quadratic:
push
ebp
mov
ebp, esp
sub
esp, 16
push
ebx
32
33
fild
138
CHAPITRE 6.
fld
fld
fmulp
fmulp
fld
fld
fmulp
faddp
ftst
fstsw
sahf
jb
fsqrt
fstp
fld1
fld
fscale
fdivp
fst
fld
fld
fsubrp
fmulp
mov
fstp
fld
fld
fchs
fsubrp
fmul
mov
fstp
mov
jmp
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
a
c
st1
st1
b
b
st1
st1
ax
;
;
;
;
pile
pile
pile
pile
;
;
;
;
pile : b, b, -4*a*c
pile : b*b, -4*a*c
pile : b*b - 4*a*c
compare avec 0
no_real_solutions
;
disc
;
;
a
;
;
st1
;
one_over_2a
;
b
;
disc
;
st1
;
st1
;
ebx, root1
qword [ebx]
;
b
;
disc
;
;
st1
;
one_over_2a
;
ebx, root2
qword [ebx]
;
eax, 1
;
short quit
:
:
:
:
VIRGULE FLOTTANTE
a, -4
c, a, -4
a*c, -4
-4*a*c
; si <
pile :
stocke
pile :
pile :
pile :
pile :
pile :
pile :
pile :
pile :
pile :
stocke
pile :
pile :
pile :
pile :
pile :
dans *root1
b
disc, b
-disc, b
-disc - b
(-b - disc)/(2*a)
68
69
70
no_real_solutions:
mov
eax, 0
71
72
73
74
75
quit:
pop
mov
pop
ebx
esp, ebp
ebp
6.3.
LE COPROCESSEUR ARITHMTIQUE
ret
76
139
quad.asm
readt.c
1
2
3
4
5
6
7
8
9
10
11
12
#include <stdio.h>
extern int read_doubles( FILE , double , int );
#dene MAX 100
int main()
{
int i ,n;
double a[MAX];
13
n = read_doubles(stdin, a , MAX);
14
15
16
17
18
19
readt.c
Voici la routine assembleur
1
2
segment .data
format db
read.asm
"%lf", 0
3
4
5
6
segment .text
global _read_doubles
extern _fscanf
7
8
9
%define SIZEOF_DOUBLE
%define FP
8
dword [ebp + 8]
140
10
11
12
CHAPITRE 6.
%define ARRAYP
%define ARRAY_SIZE
%define TEMP_DOUBLE
VIRGULE FLOTTANTE
13
14
15
16
17
18
19
20
21
22
23
24
25
;
;
;
;
;
;
;
;
;
;
;
;
fonction _read_doubles
prototype C :
int read_doubles( FILE * fp, double * arrayp, int array_size );
Cette fonction lit des doubles depuis un fichier texte dans un tableau, jusqu'
EOF ou que le tableau soit plein.
Paramtres :
fp
- FILE pointeur partir duquel lire (doit tre ouvert en lecture)
arrayp
- pointeur vers le tableau de double vers lequel lire
array_size - nombre d'lments du tableau
Valeur de retour :
nombre de doubles stocks dans le tableau (dans EAX)
26
27
28
29
30
_read_doubles:
push
ebp
mov
ebp,esp
sub
esp, SIZEOF_DOUBLE
31
32
33
34
push
mov
xor
esi
esi, ARRAYP
edx, edx
; sauve esi
; esi = ARRAYP
; edx = indice du tableau (initialement 0)
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
while_loop:
cmp
edx, ARRAY_SIZE
; edx < ARRAY_SIZE ?
jnl
short quit
; si non, quitte la boucle
;
; appelle fscanf() pour lire un double dans TEMP_DOUBLE
; fscanf() peut changer edx, donc on le sauvegarde
;
push
edx
; sauve edx
lea
eax, TEMP_DOUBLE
push
eax
; empile &TEMP_DOUBLE
push
dword format
; emplie &format
push
FP
; emplie file pointer
call
_fscanf
add
esp, 12
pop
edx
; restaure edx
cmp
eax, 1
; fscanf a retourn 1?
6.3.
LE COPROCESSEUR ARITHMTIQUE
jne
52
short quit
141
53
54
55
56
57
58
59
60
61
;
; copie TEMP_DOUBLE dans ARRAYP[edx]
; (Les 8 octets du double sont copis en deux fois 4 octets)
;
mov
eax, [ebp - 8]
mov
[esi + 8*edx], eax
; copie des 4 octets de poids faible
mov
eax, [ebp - 4]
mov
[esi + 8*edx + 4], eax ; copie des 4 octets de poids fort
62
63
64
inc
jmp
edx
while_loop
pop
esi
; restaure esi
mov
eax, edx
mov
pop
ret
esp, ebp
ebp
65
66
67
quit:
68
69
70
71
72
73
read.asm
fprime.c
142
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main()
{
int statut ;
unsigned i;
unsigned max;
int a;
printf ("Combien de nombres premiers voulez vous trouver ? ");
scanf("%u", &max);
19
20
21
22
23
if ( a ) {
24
25
nd_primes(a,max);
26
27
28
29
30
31
free (a);
statut = 0;
32
33
}
else {
fprintf ( stderr , "Impossible de crer un tableau de %u entiers\n", max);
statut = 1;
}
34
35
36
37
38
39
41
VIRGULE FLOTTANTE
#include <stdio.h>
#include <stdlib.h>
/
fonction nd_primes
recherche le nombre indiqu de nombres premiers
Paramtres:
a tableau pour contenir les nombres premiers
n nombre de nombres premiers trouver
/
extern void nd_primes( int a , unsigned n );
18
40
CHAPITRE 6.
return statut ;
6.3.
LE COPROCESSEUR ARITHMTIQUE
143
fprime.c
Voici la routine assembleur :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
prime2.asm
segment .text
global _find_primes
;
; fonction find_primes
; trouve le nombre indiqu de nombre premiers
; Paramtres:
; array - tableau pour contenir les nombres premiers
; n_find - nombre de nombres premiers trouver
; Prototype C :
;extern void find_primes( int * array, unsigned n_find )
;
%define array
ebp + 8
%define n_find
ebp + 12
%define n
ebp - 4
; Nombre de nombres premiers trouvs
%define isqrt
ebp - 8
; racine du candidat
%define orig_cntl_wd ebp - 10
; mot de contrle original
%define new_cntl_wd ebp - 12
; nouveau mot de contrle
18
19
20
_find_primes:
enter
12,0
push
push
ebx
esi
fstcw
mov
or
mov
fldcw
word [orig_cntl_wd]
ax, [orig_cntl_wd]
ax, 0C00h
[new_cntl_wd], ax
word [new_cntl_wd]
mov
mov
mov
mov
mov
esi, [array]
dword [esi], 2
dword [esi + 4], 3
ebx, 5
dword [n], 2
;
;
;
;
;
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
;
; Cette boucle externe trouve un nouveau nombre premier chaque itration qu'il
; ajoute la fin du tableau. Contrairement au programme de recherche de nombres
144
39
40
41
42
43
44
45
46
CHAPITRE 6.
VIRGULE FLOTTANTE
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
mov
push
fild
pop
fsqrt
fistp
ecx, 1
ebx
dword [esp]
ebx
;
;
;
;
;
;
dword [isqrt]
;
; Cette boucle interne divise le candidat (ebx) par les nombres premiers
; calculs prcdemment jusqu' ce qu'il trouve un de ses facteurs premiers
; (ce qui signifie que le candidat n'est pas premier) ou jusqu' ce que le
; nombre premier par lequel diviser soit plus grand que floor(sqrt(guess))
;
while_factor:
mov
eax, dword [esi + 4*ecx]
; eax = array[ecx]
cmp
eax, [isqrt]
; while ( isqrt < array[ecx]
jnbe
short quit_factor_prime
mov
eax, ebx
xor
edx, edx
div
dword [esi + 4*ecx]
or
edx, edx
; && guess % array[ecx] != 0 )
jz
short quit_factor_not_prime
inc
ecx
; essaie le nombre premier suivant
jmp
short while_factor
71
72
73
74
75
76
77
78
79
80
;
; found a new prime !
;
quit_factor_prime:
mov
eax, [n]
mov
dword [esi + 4*eax], ebx
inc
eax
mov
[n], eax
6.3.
81
82
83
LE COPROCESSEUR ARITHMTIQUE
quit_factor_not_prime:
add
ebx, 2
jmp
short while_limit
145
84
85
quit_limit:
86
87
88
89
fldcw
pop
pop
word [orig_cntl_wd]
esi
ebx
90
91
92
leave
ret
prime2.asm
146
CHAPITRE 6.
VIRGULE FLOTTANTE
global _dmax
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
segment .text
; fonction _dmax
; retourne le plus grand de ses deux arguments double
; prototype C
; double dmax( double d1, double d2 )
; Paramtres :
; d1 - premier double
; d2 - deuxime double
; Valeur de retour :
; le plus grand de d1 et d2 (dans ST0)
%define d1 ebp+8
%define d2 ebp+16
_dmax:
enter 0, 0
17
18
19
20
21
22
23
24
25
fld
qword
fld
qword
fcomip st1
jna
short
fcomp st0
fld
qword
jmp
short
d2_bigger:
26
27
28
29
exit:
[d2]
[d1]
d2_bigger
[d1]
exit
leave
ret
FCOMIP
6.3.
1
2
3
147
LE COPROCESSEUR ARITHMTIQUE
segment .data
x
dq 2,75
five
dw 5
4
5
6
7
8
segment .text
fild dword [five]
fld
qword [x]
fscale
; ST0 = 5
; ST0 = 2,75, ST1 = 5
; ST0 = 2,75 * 32, ST1 = 5
FSCALE
148
CHAPITRE 6.
VIRGULE FLOTTANTE
Chapitre 7
Structures et C++
7.1
Structures
7.1.1 Introduction
Les structures sont utilises en C pour regrouper des donnes ayant un
rapport entre elles dans une variable composite. Cette technique a plusieurs
avantages :
1. Cela clarie le code en montrant que les donnes dnies dans la structure sont intimement lies.
2. Cela simplie le passage des donnes aux fonctions. Au lieu de passer
plusieurs variables sparment, elles peuvent tre passes en une seule
entit.
3. Cela augmente la
localit 1
du code.
tableaux sont toujours de la mme taille et du mme type. C'est cette proprit qui permet de calculer l'adresse de n'importe quel lment en connaissant l'adresse de dbut du tableau, la taille des lments et l'indice de l'lment voulu.
Les lments d'une structure ne sont pas ncessairement de la mme taille
(et habituellement, ils ne le sont pas). A cause de cela, chaque lment d'une
structure doit tre explicitement spci et doit recevoir un
tag
(ou nom) au
dplacement relatif
de cet
1
Votez le chapitre sur la gestion de la mmoire virtuelle de n'importe quel livre sur les
Systmes d'Exploitation pour une explication de ce terme.
149
150
CHAPITRE 7.
Dplacement
STRUCTURES ET C++
Elment
0
2
y
6
z
Fig. 7.1 Structure S
struct S {
short int x ;
int
y;
double z ;
};
pourrait ressembler en mmoire. Le standard ANSI C indique que les lments d'une structure sont organiss en mmoire dans le mme ordre que
celui de leur dnition dans le
struct.
offsetof().
stddef.h
appele
type
Figure 7.1.
offsetof
en
vous
gcc (et beaucoup d'autre compilateurs) aligne les variables sur des
adresse est sur un multiple multiples de doubles mots par dfaut. En mode protg 32 bits, le processeur
de double mot si elle est lit la mmoire plus vite si la donne commence sur un multiple de double
divisible par 4
mot. La Figure 7.2 montre quoi ressemble la structure S en utilisant
.
gcc
y
z) sur un multiple de double mot. Cela montre pourquoi c'est une bonne
7.1.
151
STRUCTURES
Oset
0
2
Elment
inutilis
y
8
z
Fig. 7.2 Structure S
ide d'utiliser
gcc
gcc
unaligned_int
sances de deux pour spcier d'autres alignements (2 pour s'aligner sur les
mots, 4 sur les doubles mots,
en un type
unaligned_int, gcc
placerait
au dplacement 2. Cependant,
devrait aussi
152
CHAPITRE 7.
STRUCTURES ET C++
struct S {
short int x ;
/ entier sur 2 octets /
int
y;
/ entier sur 4 octets /
double z ;
/ ottant sur 8 octets /
} __attribute__((packed));
Fig. 7.3 Structure comprime sous
gcc
#pragma.
#pragma pack(1)
La directive ci-dessus indique au compilateur d'aligner les lments des struc-
tre remplac par deux, quatre, huit ou seize pour spcier un alignement
sur des multiples de mots, doubles mots, quadruples mots ou de paragraphe,
respectivement. La directive reste active jusqu' ce qu'elle soit crase par
une autre. Cela peut poser des problmes puisque ces directives sont souvent
utilises dans des chiers d'en-tte. Si le chier d'en-tte est inclus avant
d'autres chiers d'en-tte dnissant des structures, ces structures peuvent
tre organises diremment de ce qu'elles auraient t par dfaut. Cela peut
conduire des erreurs trs diciles localiser. Les dirents modules d'un
programmes devraient organiser les lments des structures
dirents
en-
droits !
Il y a une faon d'viter ce problme. Microsoft et Borland permettent la
sauvegarde de l'tat de l'alignement courant et sa restauration. La Figure 7.4
montre comment on l'utilise.
7.1.
153
STRUCTURES
struct S {
unsigned f1
unsigned f2
unsigned f3
unsigned f4
};
: 3;
: 10;
: 11;
: 8;
/ champ de 3 bits /
/ champ de 10 bits /
/ champ de 11 bits /
/ champ de 8 bits /
unsigned int
ou un
int
gure 7.5 en montre un exemple. Elle dnit une variable 32 bits dcompose
comme suit :
8 bits
11 bits
10 bits
3 bits
f4
f3
f2
f1
Le premier champ de bits est assign aux bits les moins signicatifs du double
mot .
Nanmoins, le format n'est pas si simple si l'on observe comment les bits
sont stocks en mmoire. La dicult apparat lorsque les champs de bits
sont cheval sur des multiples d'octets. Car les octets, sur un processeur
little endian seront inverss en mmoire. Par exemple, les champs de bits de
la structure
5 bits
3 bits
3 bits
5 bits
8 bits
8 bits
f2l
f1
f3l
f2m
f3m
f4
L'tiquette
f2l
f2.
L'tiquette
limites d'octets. Si l'on inverse tous les octets, les morceaux des champs
et
f3
L'organisation de la mmoire physique n'est habituellement pas importante moins que des donnes de soient transfres depuis ou vers le programme (ce qui est en fait assez courant avec les champs de bits). Il est
courant que les interfaces de priphriques matriels utilisent des nombres
impairs de bits dont les champs de bits facilitent la reprsentation.
En fait, le standard ANSI/ISO C laisse une certaine libert au compilateur sur la faon
d'organiser les bits. Cependant, les compilateurs C courants (gcc, Microsoft et Borland )
organisent les champs comme cela.
154
CHAPITRE 7.
Byte
Bit
STRUCTURES ET C++
d'Unit Logique
msb de l'ABL
Longueur du Transfert
Contrle
qui
est cheval sur trois octets dirents de la commande. D'aprs la Figure 7.6,
on constate que les donnes sont stockes au format big endian. La Figure 7.7
montre une dnition qui essaie de fonctionner avec tous les compilateurs.
Les deux premires lignes dnissent une macro qui est vraie si le code est
compil avec un compilateur Borland ou Microsoft. La partie qui peut porter
confusion va des lignes 11 14. Tout d'abord, on peut se demander pourquoi les champs
un champ unique de 16 bits. C'est parce que les donnes sont stockes au
format big endian. Un champ de 16 bits serait stock au format little endian
par le compilateur. Ensuite, les champs
lba_msb
et
logical_unit
semblent
tre inverss ; cependant, ce n'est pas le cas. Ils doivent tre placs dans cet
ordre. La Figure 7.8 montre comment les champs sont organiss sous forme
d'une entit de 48 bits (les limites d'octets sont l encore reprsentes par des
lignes doubles). Lorsqu'elle est stocke en mmoire au format little endian,
les bits sont rarrangs au format voulu (Figure 7.6).
Pour compliquer encore plus le problme, la dnition de
SCSI_read_cmd
sizeof (SCSI_read_cmd)
est value, le C Microsoft renvoie 8 et non pas 6 ! C'est parce que le compilateur Microsoft utilise le type du champ de bits pour dterminer comment
organiser les bits. Comme tous les bits sont dclars comme
unsigned,
le
n'a plus besoin d'ajouter d'octets d'alignement puisque six octets forment
3
etc.
Small Computer Systems Interface, un standard de l'industrie pour les disques durs,
7.1.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
155
STRUCTURES
un nombre entier de mots de deux octets . Les autres compilateurs fonctionnent galement correctement avec ce changement. La Figure 7.9 montre
une autre dnition qui fonctionne sur les trois compilateurs. Il ne dclare
plus que deux champs de bits en utilisant le type
unsigned char.
4
Mlanger dirents types de champs de bits conduit un comportement trs trange !
Le lecteur est invit tester.
156
CHAPITRE 7.
8 bits
8 bits
8 bits
8 bits
3 bits
5 bits
8 bits
control
transfer_length
lba_lsb
lba_mid
logical_unit
lba_msb
opcode
1
2
3
4
5
6
7
8
9
10
11
12
13
STRUCTURES ET C++
SCSI_read_cmd
struct SCSI_read_cmd {
unsigned char opcode;
unsigned char lba_msb : 5;
unsigned char logical_unit : 3;
unsigned char lba_mid; / bits du milieu /
unsigned char lba_lsb;
unsigned char transfer_length;
unsigned char control;
}
#if dened(__GNUC__)
__attribute__((packed))
#endif
;
Fig. 7.9 Structure du Format de la Commande de Lecture SCSI Alternative
soit :
1
2
3
4
5
6
7
%define
_zero_y:
enter
mov
mov
leave
ret
y_offset
0,0
eax, [ebp + 8]
; rcupre s_p depuis la pile
dword [eax + y_offset], 0
Le C permet de passer une structure par valeur une fonction ; cependant, c'est une mauvaise ide la plupart du temps. Toutes les donnes de la
7.2.
1
2
3
4
5
6
7
8
9
10
11
157
ASSEMBLEUR ET C++
#include <stdio.h>
void f ( int x )
{
printf ("%d\n", x);
}
void f ( double x )
{
printf ("%g\n", x);
}
Fig. 7.10 Deux fonctions
f()
structure doivent tre copies sur sur la pile puis rcupres par la routine. Il
est beaucoup plus ecace de passer un pointeur vers la structure la place.
Le C permet aussi qu'une fonction renvoie une structure. Evidemment,
une structure ne peut pas tre retourne dans le registre
EAX.
Des compi-
7.2
Assembleur et C++
le mme nom en C, l'diteur de liens produira une erreur car il trouvera deux
158
CHAPITRE 7.
STRUCTURES ET C++
dnitions pour le mme symbole dans les chiers objets qu'il est en train
de lier. Par exemple, prenons le code de la Figure 7.10. Le code assembleur
quivalent dnirait deux tiquettes appeles
_f
erreur.
Le C++ utilise le mme procd d'dition de liens que le C mais vite
cette erreur en eectuant une
diant le symbole utilis pour nommer une fonction. D'une certaine faon, le
C utilise dj la dcoration de nom. Il ajoute un caractre de soulignement
au nom de la fonction C lorsqu'il cre l'tiquette pour la fonction. Cependant, il dcorera le nom des deux fonctions de la Figure 7.10 de la mme
faon et produira une erreur. Le C++ utilise un procd de dcoration plus
sophistiqu qui produit deux tiquettes direntes pour les fonctions. Par
_f__Fi
_f__Fd, sous DJGPP. Cela vite toute erreur d'dition de liens.
deux fonctions de la Figure 7.10. Cependant, les rgles ne sont pas totalement
arbitraires. Le nom dcor encode la
signature
de la fonction. La signature
d'une fonction est donne par l'ordre et le type de ses paramtres. Notez
que la fonction qui ne prend qu'un argument
int
a un
la n de son nom
dcor ( la fois sous DJGPP et Borland) et que celle qui prend un argument
double a un d la
f avec le prototype
_f__Fiid
pas
et Borland en
@f$qiid.
n'est pas encod dans nom dcor. Ce fait explique une rgle de la surcharge
en C++. Seules les fonctions dont les signatures sont uniques peuvent tre
surcharges. Comme on le voit, si deux fonctions avec le mme nom et la
mme signature sont dnies en C++, elle donneront le mme nom dcor
et creront une erreur lors de l'dition de liens. Par dfaut, toutes les fonctions C++ sont dcores, mme celles qui ne sont pas surcharges. Lorsqu'il
compile un chier, le compilateur n'a aucun moyen de savoir si une fonction
particulire est surcharge ou non, il dcore donc touts les noms. En fait,
il dcore galement les noms des variables globales en encodant le type de
la variable d'une faon similaire celle utilise pour les signatures de fonctions. Donc, si l'on dnit une variable globale dans un chier avec un certain
type puis que l'on essaie de l'utiliser dans un autre chier avec le mauvais
type, l'diteur de liens produira une erreur. Cette caractristique du C++
est connue sous le nom de
typesafe linking
types) . Cela cre un autre type d'erreurs, les prototypes inconsistants. Cela
7.2.
159
ASSEMBLEUR ET C++
CALL
prototype est :
moyen pour que le C++ puisse appeler du code C. C'est trs important car
il existe une
norme
extern
fonction ou la variable globale qu'il modie utilise les conventions C normales. Dans la terminologie C++, la fonction ou la variable globale utilise
une
La correspondance n'a pas tre exacte, le compilateur prendra en compte les correspondances trouves en transtypant les arguments. Les rgles de ce procd sont en dehors
de la porte de ce livre. Consultez un livre sur le C++ pour plus de dtails.
160
1
2
3
4
5
6
7
8
9
10
CHAPITRE 7.
STRUCTURES ET C++
int main()
{
int y = 5;
f(y );
// une rfrence sur y est passe, pas de & ici !
printf ("%d\n", y); // ache 6 !
return 0;
}
Fig. 7.11 Exemple de rfrence
printf
extern "C" {
/ variables globales et prototypes des fonction ayant une dition de liens C /
}
Si l'on examine les chiers d'en-tte ANSI C fournis avec les compilateur
C/C++ actuels, on trouve ce qui suit vers le dbut de chaque chier d'entte :
#ifdef __cplusplus
extern "C" {
#endif
Et une construction similaire, vers la n, contenant une accolade fermante.
Les compilateurs C++ dnissent la macro
__cplusplus
(avec
deux
carac-
extern "C").
se par n'importe quel programmeur pour crer un chier d'en-tte pour des
routines assembleur pouvant tre utilises en C ou en C++.
7.2.
161
ASSEMBLEUR ET C++
7.2.2 Rfrences
Les
rfrences
var
adresse
de
y.
6 :
et
b.
cette expression pourrait tre rcrite avec une notation fonction sous la
forme
operator +(a,b)).
l'adresse des objets string la place de les passer par valeur. Sans rfrence,
fonctions inline
SQR(x++).
Bien sr, il faudrait dclarer la fonction avec une dition de liens en C, comme nous
en avons parl dans la Section 7.2.1
7
Les compilateurs supportent souvent cette fonctionnalit comme une extension du C
ANSI.
162
1
2
3
4
5
6
7
8
9
10
11
12
13
CHAPITRE 7.
STRUCTURES ET C++
Les macros sont utilises car elles liminent la surcharge d'un appel pour
une fonction simple. Comme le chapitre sur les sous-programmes l'a dmontr, eectuer un appel de fonction implique plusieurs tapes. Pour une fonction trs simple, le temps pass l'appeler peut tre plus grand que celui
pass dans la fonction ! Les fonctions inline sont une faon beaucoup plus
pratique d'crire du code qui ressemble une fonction mais qui n'eectue
pas
de
CALL
inline
au dbut de sa dnition.
Par exemple, considrons les fonctions dclares dans la Figure 7.12. L'appel
la fonction
f,
supposant que
1
2
3
4
push
call
pop
mov
est l'adresse
ebp-8
1
3
mov
imul
mov
en
ebp-4):
dword [ebp-8]
_f
ecx
[ebp-4], eax
et
inline_f,
ligne 11 ressemblerait :
eax, [ebp-8]
eax, eax
[ebp-4], eax
7.2.
163
ASSEMBLEUR ET C++
porte quelle
tous
n'im-
la fonction doivent tre recompils. Souvenez vous que pour les fonctions
non-inline, si le prototype ne change pas, souvent les chiers qui utilisent la
fonction n'ont pas besoin d'tre recompils. Pour toutes ces raisons, le code
des fonctions inline est gnralement plac dans les chiers d'en-tte. Cette
pratique est contraire la rgle stricte habituelle du C selon laquelle on ne
doit
jamais
7.2.4 Classes
Une classe C++ dcrit un type d'
objet.
8
membres donnes et des membres fonctions . En d'autres termes, il s'agit
d'une
struct
drons la classe simple dnie dans la Figure 7.13. Une variable de type
Simple
ressemblerait une
pas
struct
int.
les fonctions membres sont direntes des autres fonctions. On leur passe un mot cl this pour accparamtre
. Ce paramtre est un pointeur vers l'objet sur lequel agit la der au pointeur vers l'ob-
cach
fonction.
Par exemple, considrons la mthode
set_data
de la classe
Simple
de
DJGPP
gcc
-S
du compilateur
DJGPP
et
gcc
.s
164
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
CHAPITRE 7.
class Simple {
public :
Simple ();
~Simple();
int get_data() const;
void set_data( int );
private :
int data ;
};
STRUCTURES ET C++
Simple :: Simple()
{ data = 0; }
Simple::~Simple()
{ / rien / }
DJGPP
set_data
doivent
frentes. Les paramtres sont encods an que la classe puisse surcharger la
mthode
set_data
EAX.
Ce n'est
pas
le
7.2.
165
ASSEMBLEUR ET C++
1
2
3
_set_data__6Simplei:
push ebp
mov
ebp, esp
; nom dcor
mov
mov
mov
5
6
7
leave
ret
9
10
Simple
dans
EDX
et la ligne 7 stocke
EDX
sur lequel on agit, qui, tant la seule donne de la classe, est stock
au dplacement 0 de la structure
Simple.
Exemple
Cette section utilise les ides de ce chapitre pour crer une classe C++
qui reprsente un entier non sign d'une taille arbitraire. Comme l'entier
peut faire n'importe quelle taille, il sera stock dans un tableau d'entiers
non signs (doubles mots). Il peut faire n'importe quelle taille en utilisant
11
l'allocation dynamique. Les doubles mots sont stocks dans l'ordre inverse
i.e. le double mot le moins signicatif est au dplacement 0). La Figure 7.16
Big_int12 . La taille d'un Big_int est med'unsigned utilis pour stocker les donnes.
11
number_
size_
Pourquoi ? Car les oprations d'addition commenceront ainsi toujours par le dbut du
tableau et avanceront.
12
Voyez le code source d'exemple pour obtenir le code complet de cet exemple. Le texte
ne se rfrera qu' certaines parties du code.
166
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
CHAPITRE 7.
class Big_int {
public :
/
Paramtres :
size
initial_value
STRUCTURES ET C++
int normaux
int normaux
valeur .
/
Big_int( size_t
size ,
const char initial_value );
Big_int( const Big_int & big_int_to_copy);
~Big_int();
// renvoie la taille du Big_int (en termes d'unsigned int)
size_t size () const;
7.2.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
ASSEMBLEUR ET C++
167
inline Big_int operator + ( const Big_int & op1, const Big_int & op2)
{
Big_int result (op1. size ());
int res = add_big_ints(result , op1, op2);
if ( res == 1)
throw Big_int::Overow();
if ( res == 2)
throw Big_int::Size_mismatch();
return result ;
}
inline Big_int operator ( const Big_int & op1, const Big_int & op2)
{
Big_int result (op1. size ());
int res = sub_big_ints(result , op1, op2);
if ( res == 1)
throw Big_int::Overow();
if ( res == 2)
throw Big_int::Size_mismatch();
return result ;
}
Fig. 7.17 Code de l'Arithmtique sur la Classe Big_int
168
CHAPITRE 7.
STRUCTURES ET C++
Pour simplier l'exemple, seuls les objets ayant des tableaux de la mme
taille peuvent tre additionns entre eux.
La classe a trois constructeurs : le premier (ligne 9) initialise l'instance de
la classe en utilisant un entier non sign normal ; le second, (ligne 19) initalise
l'instance en utilisant une chane qui contient une valeur hexadcimale. Le
troisime constructeur (ligne 22) est le
Cette explication se concentre sur la faon dont fonctionnent les oprateurs d'addition et de soustraction car c'est l que l'on utilise de l'assembleur.
La Figure 7.17 montre les parties du chier d'en-tte relatives ces oprateurs. Elles montrent comment les oprateurs sont paramtrs pour appeler
des routines assembleur. Comme des compilateurs dirents utilisent des
rgles de dcoration radicalement direntes pour les fonctions oprateur,
des fonctions oprateur inline sont utilises pour initialiser les appels aux
routines assembleur lies au format C. Cela les rend relativement simples
porter sur des compilateurs dirents et est aussi rapide qu'un appel direct.
Cette technique limine galement le besoin de soulever une exception depuis
l'assembleur !
Pourquoi l'assembleur n'est-il utilis qu'ici ? Souvenez vous que pour effectuer de l'arithmtique en prcision multiple, la retenue doit tre ajoute
au double mot signicatif suivant. Le C++ (et le C) ne permet pas au programmeur d'accder au drapeau de retenue du processeur. On ne pourrait
eectuer l'addition qu'en recalculant indpendamment en C++ la valeur du
drapeau de retenue et en l'ajoutant de faon conditionnelle au double mot
suivant. Il est beaucoup plus ecace d'crire le code en assembleur partir
duquel on peut accder au drapeau de retenue et utiliser l'instruction
ADC
add_big_ints sera
big_math.asm) :
1
2
3
4
big_math.asm
segment .text
global add_big_ints, sub_big_ints
%define size_offset 0
%define number_offset 4
5
6
7
8
%define EXIT_OK 0
%define EXIT_OVERFLOW 1
%define EXIT_SIZE_MISMATCH 2
9
10
11
12
13
explique
7.2.
ASSEMBLEUR ET C++
169
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
add_big_ints:
push
ebp
mov
ebp, esp
push
ebx
push
esi
push
edi
;
; initialise esi pour pointer vers op1
;
edi pour pointer vers op2
;
ebx pour pointer vers res
mov
esi, [op1]
mov
edi, [op2]
mov
ebx, [res]
;
; s'assure que les 3 Big_int ont la mme taille
;
mov
eax, [esi + size_offset]
cmp
eax, [edi + size_offset]
jne
sizes_not_equal
; op1.size_ != op2.size_
cmp
eax, [ebx + size_offset]
jne
sizes_not_equal
; op1.size_ != res.size_
36
37
38
39
40
41
42
43
44
45
46
mov
ecx,
;
; initialise
;
esi =
;
edi =
;
ebx =
;
mov
ebx,
mov
esi,
mov
edi,
eax
47
48
49
50
51
52
53
54
55
clc
xor
edx, edx
;
; boucle d'addition
add_loop:
mov
eax, [edi+4*edx]
adc
eax, [esi+4*edx]
mov
[ebx + 4*edx], eax
170
56
57
CHAPITRE 7.
inc
loop
edx
add_loop
STRUCTURES ET C++
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
jc
overflow
ok_done:
xor
eax, eax
jmp
done
overflow:
mov
eax, EXIT_OVERFLOW
jmp
done
sizes_not_equal:
mov
eax, EXIT_SIZE_MISMATCH
done:
pop
edi
pop
esi
pop
ebx
leave
ret
big_math.asm
Big_int
passs la fonction via des registres. Souvenez vous que les rf-
rences sont des pointeurs. Les lignes 31 35 vrient que les tailles des trois
objets sont les mmes (Notez que le dplacement de
size_
est ajout au
number_
La boucle des lignes 52 57 additionne les entiers stocks dans les tableaux en additionnant le double mot le moins signicatif en premier, puis
les doubles mots suivants,
Big_int
Big_int. Notez
la ligne 16. C'est ncessaire pour deux raisons. Tout d'abord, il n'y a
pas de constructeur de conversion qui convertisse un entier non sign en
Big_int.
Big_int
7.2.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
171
ASSEMBLEUR ET C++
#include "big_int.hpp"
#include <iostream>
using namespace std;
int main()
{
try {
Big_int b(5,"8000000000000a00b");
Big_int a(5,"80000000000010230");
Big_int c = a + b;
cout << a << " + " << b << " = " << c << endl;
for ( int i=0; i < 2; i++ ) {
c = c + a;
cout << "c = " << c << endl;
}
cout << "c1 = " << c Big_int(5,1) << endl;
Big_int d(5, "12345678");
cout << "d = " << d << endl;
cout << "c == d " << (c == d) << endl;
cout << "c > d " << (c > d) << endl;
}
catch( const char str ) {
cerr << "Caught : " << str << endl;
}
catch( Big_int::Overow ) {
cerr << "Dpassement de capacit " << endl;
}
catch( Big_int::Size_mismatch ) {
cerr << "Non concordance de taille" << endl;
}
return 0;
}
Fig. 7.18
Utilisation Simple de
Big_int
172
CHAPITRE 7.
STRUCTURES ET C++
L'
et
B,
o la classe
hrite de
A.
Taille de a : 4 Dplacement de ad : 0
Taille de b : 8 Dplacement de ad: 0 Dplacement de bd: 4
A::m()
A::m()
Notez que les membres
ad
A)
sont au mme
soit
est cod en dur dans la fonction. Dans le cadre d'une vraie programmation
oriente objet, la mthode appele devrait dpendre du type d'objet pass
gure 7.21 montre comment les deux classes seraient modies. Rien dans le
restet du code n'a besoin d'tre chang. Le polymorphisme peut tre implment de beaucoup de manires. Malheureusement, l'implmentation de
gcc
Size of a: 8 Dplacement de ad : 4
Size of b: 12 Dplacement de ad : 4 Dplacement de bd : 8
A::m()
B::m()
Maintenant, le second appel
passe un objet
B.
appelle la mthode
B::m()
car on lui
7.2.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
173
ASSEMBLEUR ET C++
#include <cstddef>
#include <iostream>
using namespace std;
class A {
public :
void __cdecl m() { cout << "A::m()" << endl; }
int ad;
};
class B : public A {
public :
void __cdecl m() { cout << "B::m()" << endl; }
int bd;
};
void f ( A p )
{
p>ad = 5;
p>m();
}
int main()
{
A a;
B b;
cout << "Taille de a : " << sizeof(a)
<< " Dplacement de ad : " << osetof(A,ad) << endl;
cout << "Taille de b : " << sizeof(b)
<< " Dplacement de ad : " << osetof(B,ad)
<< " Dplacement de bd : " << osetof(B,bd) << endl;
f(&a);
f(&b);
return 0;
}
Fig. 7.19
Hritage Simple
174
1
2
3
4
5
6
7
8
9
10
11
_f__FP1A:
push
mov
mov
mov
mov
push
call
add
leave
ret
CHAPITRE 7.
ebp
ebp, esp
eax, [ebp+8]
dword [eax], 5
eax, [ebp+8]
eax
_m__1A
esp, 4
STRUCTURES ET C++
1
2
3
4
5
6
7
8
9
10
11
class A {
public :
virtual void __cdecl m() { cout << "A::m()" << endl; }
int ad;
};
class B : public A {
public :
virtual void __cdecl m() { cout << "B::m()" << endl; }
int bd;
};
Fig. 7.21
Hritage Polymorphique
7.2.
1
2
3
175
ASSEMBLEUR ET C++
?f@@YAXPAVA@@@Z:
push ebp
mov
ebp, esp
4
5
6
mov
mov
eax, [ebp+8]
dword [eax+4], 5 ; p->ad = 5;
mov
mov
mov
push
call
add
ecx, [ebp + 8]
edx, [ecx]
eax, [ebp + 8]
eax
dword [edx]
esp, 4
pop
ret
ebp
7
8
9
10
11
12
13
;
;
;
;
;
;
ecx = p
edx = pointeur sur la vtable
eax = p
empile le pointeur "this"
appelle la premire fonction de la vtable
nettoie la pile
14
15
16
B).
f()
De plus, le dplacement de
ad
vaut
vtable.
et
(de
EDX.
13
Pour les classes sans mthode virtuelle, les compilateurs C++ rendent toujours la
classe compatible avec une structure C normale qui aurait les mmes donnes membres.
14
Bien sr, la valeur est dj dans le registre ECX. Elle y a t place la ligne 8 et
le ligne 10 pourrait tre supprime et la ligne suivante change de faon empiler ECX.
Le code n'est pas trs ecace car il a t gnr en dsactivant les optimisations du
compilateur.
176
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
24
25
26
27
28
29
30
32
33
34
35
36
37
38
39
40
41
STRUCTURES ET C++
class A {
public :
virtual void __cdecl m1() { cout << "A::m1()" << endl; }
virtual void __cdecl m2() { cout << "A::m2()" << endl; }
int ad;
};
23
31
CHAPITRE 7.
int main()
{
A a; B b1; B b2;
cout << "a: " << endl; print_vtable(&a);
cout << "b1: " << endl; print_vtable(&b1);
cout << "b2: " << endl; print_vtable(&b2);
return 0;
}
Fig. 7.23
7.2.
177
ASSEMBLEUR ET C++
0 vtablep s
0- &B::m1()
ad
4 &A::m2()
bd
vtable
b1
Fig. 7.24 Reprsentation interne de
b1
liaison prcoce
ECX au
lieu d'utiliser la pile. La pile est toujours utilise pour les autres paramtres
explicites de la mthode. Le modicateur
__cdecl
demande l'utilisation de
et
m1
et
A.
vtable appartient une classe pas un objet (comme une donne membre
static).
et
car la classe
m2
m2
m1
est au dplacement 4
hrite de la mthode
m2
de la classe
A.
15
gcc
15 . L'adresse de la
178
CHAPITRE 7.
STRUCTURES ET C++
a:
Adresse de la vtable = 004120E8
dword 0: 00401320
dword 1: 00401350
A::m1()
A::m2()
b1:
Adresse de la vtable = 004120F0
dword 0: 004013A0
dword 1: 00401350
B::m1()
A::m2()
b2:
Adresse de la vtable = 004120F0
dword 0: 004013A0
dword 1: 00401350
B::m1()
A::m2()
Fig. 7.25 Sortie du programme de la Figure 7.23
this
pas
de code de ce genre ! Il
n'est utilis que pour illustrer le fait que les mthodes virtuelles utilisent la
vtable.
Il y a plusieurs leons pratiques tirer de cela. Un fait important est
qu'il faut tre trs attentif lorsque l'on crit ou que l'on lit des variables de
type classe depuis un chier binaire. On ne peut pas utiliser simplement une
lecture ou une criture binaire car cela lirait ou crirait le pointeur vtable
depuis le chier ! C'est un pointeur sur l'endroit o la vtable rside dans
la mmoire du programme et il varie d'un programme l'autre. La mme
chose peut arriver avec les structures en C, mais en C, les structures n'ont
des pointeurs que si le programmeur en dnit explicitement. Il n'y a pas de
pointeurs explicites dclars dans les classes
ou
B.
Une fois encore, il est ncessaire de raliser que des compilateurs dirents
implmentent les mthodes virtuelles diremment. Sous Windows, les objets
de la classe COM (Component Object Model) utilisent des vtables pour
implmenter les interfaces COM
16
Les classes COM utilisent galement la convention d'appel __stdcall, pas la convention C standard.
7.2.
179
ASSEMBLEUR ET C++
les vtables des mthodes virtuelles comme le fait Microsoft peuvent crer des
classes COM. C'est pourquoi Borland utilise la mme implmentation que
Microsoft et une des raisons pour lesquelles
p.e., le RunTime
180
CHAPITRE 7.
STRUCTURES ET C++
Annexe A
Instructions 80x86
A.1
Cette section liste et dcrit les actions et les formats des instructions hors
virgule ottante de la famille de processeurs Intel 80x86.
Les formats utilisent les abbrviations suivantes :
R
registre gnral
R8
registre 8 bits
R16
registre 16 bits
R32
registre 32 bits
SR
registre de segment
mmoire
M8
octet
M16
mot
M32
double mot
valeur immdiate
Elles peuvent tre combines pour les instructions plusieurs oprandes. Par
exemple, le format
R,R
oprandes. L'abbrviation
C
un ?
gure
gure
CLD
et
STD,
182
ANNEXE A.
Nom
ADC
ADD
AND
BSWAP
CALL
CBW
CDQ
Description
INSTRUCTIONS 80X86
Formats
Flags
O S Z A P C
O2
Addition entire
O2
ET niveau bit
O2
Echange d'Octets
R32
Appel de Routine
R M I
Conversion Octet-Mot
Conversion
DMot-
QMot
CLC
CLD
CMC
CMP
CMPSB
CMPSW
CMPSD
Eteindre la retenue
Eteindre la direction
Inverser la retenue
C
C
Comparaison d'Octets
Comparaison Entire
O2
Comparaison de Mots
Comparaison
R M
R M
Cration de cadre de
I,0
de
DMots
CWD
Conversion Mot-DMot
CWDE
Conversion Mot-DMot
DEC
Decrmentation d'En-
dans DX:AX
dans EAX
tier
DIV
ENTER
pile
IDIV
IMUL
Division Signe
R M
Multiplication Signe
R16,R/M16
R32,R/M32
R16,I R32,I
R16,R/M16,I
R32,R/M32,I
INC
Incrmentation
d'En-
R M
tier
INT
tion
JA
JAE
Saut si Au-dessus
Saut si Au-Dessus ou
Egal
JB
Saut si En-Dessous
A.1.
JBE
Nom
Description
Formats
Saut si En-Dessous ou
Egal
JC
JCXZ
JE
JG
JGE
Saut si Retenue
Saut si CX = 0
Saut si Egal
Saut si Suprieur
Saut
si
Suprieur
ou
Egal
JL
JLE
Saut si Infrieur
Saut
si
Infrieur
ou
Egal
JMP
JNA
JNAE
Saut Inconditionnel
R M I
ou Egal
JNB
Saut
si
Non
En-
Non
En-
Dessous
JNBE
Saut
si
Dessous ou Egal
JNC
JNE
JNG
JNGE
ou Egal
JNL
JNLE
ou Egal
JNO
JNS
JNZ
JO
JPE
JPO
JS
JZ
LAHF
Saut si Overow
Saut si Pair
Saut si Impair
Saut si Signe
Saut si Zro
Charge
FLAGS
I
dans
AH
LEA
R32,M
183
Flags
O S Z A P C
184
ANNEXE A.
Nom
LEAVE
Description
Quitter
le
INSTRUCTIONS 80X86
Formats
Cadre
Flags
O S Z A P C
de
Pile
LODSB
LODSW
LODSD
Charger un Octet
Charger un Mot
Charger
un
Double
Mot
LOOP
LOOPE/LOOPZ
LOOPNE/LOOPNZ
MOV
Boucler
Boucler si Egal
Dplacement
O2
de
don-
nes
SR,R/M16
R/M16,SR
MOVSB
MOVSW
MOVSD
Dplacement d'Octet
Dplacement de Mot
Dplacement
de
Double Mot
MOVSX
Dplacement Sign
R16,R/M8
R32,R/M8
R32,R/M16
MOVZX
Dplacement
Non
Si-
gn
R16,R/M8
R32,R/M8
R32,R/M16
MUL
R M
R M
gne
NEG
NOP
NOT
OR
POP
Inverser
Pas d'Opration
Complment 1
R M
OU niveau bit
O2
Retirer de la pile
R/M16
R/M32
POPA
POPF
Retirer FLAGS de la
PUSH
Empiler
PUSHA
PUSHF
RCL
Tout Empiler
Empiler FLAGS
Rotation
avec Retenue
Gauche
R/M,I
R/M,CL
A.1.
185
RCR
Nom
REP
REPE/REPZ
REPNE/REPNZ
RET
ROL
Description
Formats
R/M,I
Retenue
R/M,CL
Flags
O S Z A P C
C
Rpter
Rpter Si Egal
Rpter Si Non Egal
Retour
Rotation Gauche
R/M,I
R/M,CL
ROR
Rotation Droite
SAHF
Copie
SAL
Dcalage Arithmtique
R/M,I
Gauche
R/M, CL
O2
R/M,I
R/M,CL
AH
dans
FLAGS
SBB
C
C
Recherche d'Octet
Recherche de Mot
Recherche de Double-
nue
SCASB
SCASW
SCASD
Mot
SETA
SETAE
Allumer Si Au-Dessus
R/M8
Allumer Si Au-Dessus
R/M8
ou Egal
SETB
SETBE
Allumer Si En-Dessous
R/M8
Allumer Si En-Dessous
R/M8
ou Egal
SETC
SETE
SETG
SETGE
Allumer Si Retenue
R/M8
Allumer Si Egal
R/M8
R/M8
R/M8
ou Egal
SETL
SETLE
R/M8
R/M8
ou Egal
SETNA
Allumer
Si
Non
Au
R/M8
Au-
R/M8
En-
R/M8
Dessus
SETNAE
Allumer
Si
Non
Dessus ou Egal
SETNB
Allumer
Dessous
Si
Non
186
ANNEXE A.
Nom
SETNBE
Description
Allumer
Si
Non
INSTRUCTIONS 80X86
Formats
En-
R/M8
R/M8
Flags
O S Z A P C
Dessous ou Egal
SETNC
tenue
SETNE
SETNG
R/M8
R/M8
Grand
SETNGE
R/M8
Grand ou Egal
SETNL
R/M8
rieur
SETNLE
R/M8
rieur ou Egal
SETNO
R/M8
ow
SETNS
SETNZ
SETO
SETPE
SETPO
R/M8
R/M8
Allumer Si Overow
R/M8
R/M8
R/M8
paire
SETS
SETZ
SAR
SHR
Allumer Si Signe
R/M8
Allumer Si Zro
R/M8
Dcalage Arithmtique
R/M,I
Droite
R/M, CL
Dcalage
Logique
Droite
SHL
Dcalage
R/M,I
R/M, CL
Logique
Gauche
STC
STD
R/M,I
R/M, CL
Allumer la Retenue
Allumer le Drapeau de
Direction
STOSB
STOSW
STOSD
SUB
TEST
Soustraction
O2
Comparaison Logique
R/M,R
XCHG
Echange
R/M,R
Stocker l'Octet
Stocker le Mot
Stocker le Double-Mot
R/M,I
R,R/M
A.1.
XOR
187
Nom
Description
Ou Exclusif Niveau Bit
Formats
O2
Flags
O S Z A P C
0
188
ANNEXE A.
A.2
INSTRUCTIONS 80X86
Dans cette section, la plupart des instructions du coprocesseur mathmatique du 80x86 son dcrites. La section description dcrit brivement l'opration eectue par l'instruction. Pour conomiser de la place, il n'est pas
prcis si l'instruction dcale la pile ou non.
La colonne format indique le type d'oprande pouvant tre utilis avec
chaque instruction. Les abbrviations suivantes sont utilises :
ST
Registre du coprocesseur
I16
I32
I64
Instruction
FABS
FADD src
FADD dest, ST0
FADDP dest [,ST0]
FCHS
FCOM src
FCOMP src
FCOMPP src
FCOMI src
FCOMIP src
FDIV src
FDIV dest, ST0
FDIVP dest [,ST0]
FDIVR src
FDIVR dest, ST0
FDIVRP dest [,ST0]
FFREE dest
FIADD src
FICOM src
FICOMP src
FIDIV src
FIDIVR src
Description
ST0 = |ST0|
ST0 += src
dest += STO
dest += ST0
ST0 = ST0
Compare ST0 et src
Compare ST0 et src
Compare ST0 et ST1
Compare dans FLAGS
Compare dans FLAGS
ST0 /= src
dest /= STO
dest /= ST0
ST0 = src /ST0
dest = ST0/dest
dest = ST0/dest
Marquer comme vide
ST0 += src
Compare ST0 et src
Compare ST0 et src
STO /= src
STO = src /ST0
Format
n
STn
STn
ST
n
n
F D
ST
F D
ST
F D
n
STn
STn
STn
STn
STn
STn
STn
STn
ST
F D
F D
I16 I32
I16 I32
I16 I32
I16 I32
I16 I32
A.2.
189
Instruction
FILD src
FIMUL src
FINIT
FIST dest
FISTP dest
FISUB src
FISUBR src
FLD src
FLD1
FLDCW src
FLDPI
FLDZ
FMUL src
FMUL dest, ST0
FMULP dest [,ST0]
FRNDINT
FSCALE
FSQRT
FST dest
FSTP dest
FSTCW dest
FSTSW dest
FSUB src
FSUB dest, ST0
FSUBP dest [,ST0]
FSUBR src
FSUBR dest, ST0
FSUBP dest [,ST0]
FTST
FXCH dest
Place
src
Description
sur la Pile
ST0 *= src
Format
I16 I32 I64
I16 I32
Initialise le Coprocesseur
ST0
Stocke ST0
ST0 -= src
ST0 = src - ST0
Stocke
Place
src
sur la Pile
I16 I32
I16 I32 I64
I16 I32
I16 I32
ST
F D E
I16
sur la Pile
ST0 *= src
dest *= STO
dest *= ST0
Arrondir ST0
bST1c
ST0 = ST0
2
ST0 = STO
Stocke ST0
Stocke ST0
n
n
STn
ST
F D
ST
n
n
ST
F D
ST
F D E
I16
I16 AX
ST0 -= src
dest -= STO
dest -= ST0
ST0 = src -ST0
dest = ST0-dest
dest = ST0-dest
Compare ST0 avec 0.0
Echange ST0 et dest
n
n
STn
STn
STn
STn
ST
F D
ST
ST
F D
Index
$, 72
rfrences, 161
dition de liens, 24
virtual, 172
vtable, 175179
ADC, 39, 56
CALL, 7374
ADD, 13, 39
CBW, 33
CDQ, 33
tableaux, 102107
CLC, 39
AND, 52
CLD, 110
array1.asm, 103107
CMP, 40
assembleur, 12
invert_endian, 62
COM, 178
binaire, 12
commentaire, 13
addition, 2
compilateur, 6, 12
Borland, 23, 24
boucle while, 45
DJGPP, 23, 24
gcc, 23
BSWAP, 62
BYTE, 17
155, 156
Microsoft, 23
C++, 157179
Watcom, 88
types, 158
classes, 163179
arithmtique, 3539
mthode une, 63
convention d'appel, 69, 7481, 88
hritage, 172179
89
__cdecl, 89
__stdcall, 89
polymorphisme, 172179
191
INDEX
tiquettes, 86
dump_stack, 19
paramtres, 87
print_char, 18
registres, 86
print_int, 18
valeurs de retour, 88
print_nl, 18
Pascal, 76
print_string, 18
registre, 89
read_char, 18
standard call, 89
read_int, 18
endian, 26
entiers, 2940
bit de signe, 29, 32
comparaisons, 40
division, 3637
extension de signe, 3235
multiplication, 3536
comparaisons, 134135
matriel, 129130
prcision tendue, 39
reprsentation, 2935
134
CWD, 33
CWDE, 33
representation
dbogage, 1819
signs, 2932, 40
one's complement, 30
dcimal, 1
excution spculative, 55
DEC, 14
directive, 1416
FABS, 136
%dene, 14
FADD, 132
FADDP, 132
X, 15, 99
DD, 16
FCHS, 136
donnes, 1516
FCOM, 134
DQ, 16
FCOMI, 135
equ, 14
extern, 82
FCOMP, 134
FCOMPP, 134
RES
FDIV, 134
TIMES, 16, 99
FDIVP, 134
X, 15, 99
DIV, 36, 50
FDIVR, 134
DWORD, 17
FDIVRP, 134
FFREE, 131
E/S, 1719
bibliothque asm_io, 1719
FIADD, 132
chier listing, 25
dump_math, 19
chier squelette, 26
dump_mem, 18
FICOM, 134
dump_regs, 18
FICOMP, 134
192
INDEX
FIDIV, 134
JLE, 43
FIDIVR, 134
JMP, 41
FILD, 130
JNC, 42
FIST, 131
JNE, 43
FISUB, 133
JNG, 43
FISUBR, 133
JNGE, 43
FLD, 130
JNL, 43
FLD1, 130
JNLE, 43
FLDCW, 131
JNO, 42
FLDZ, 130
JNP, 42
FMUL, 133
JNS, 42
FMULP, 133
JNZ, 42
JO, 42
FSQRT, 136
JP, 42
FST, 131
JS, 42
FSTCW, 131
JZ, 42
FSTP, 131
FSTSW, 135
label, 1517
FSUB, 133
LAHF, 135
FSUBP, 133
lanceur C, 20
FSUBR, 133
FSUBRP, 133
langage machine, 5, 11
FTST, 134
FXCH, 131
localit, 149
LODSB, 111
gas, 163
hexadcimal, 34
horloge, 6
LODSD, 111
LODSW, 111
LOOP, 44
LOOPE, 44
IDIV, 37
LOOPNE, 44
immdiat, 13
LOOPNZ, 44
IMUL, 3536
LOOPZ, 44
INC, 14
instruction if, 4445
instructions de chanes, 110120
interfaage avec le C, 8593
interruptions, 11
mmoire, 45
pages, 10
segments, 9, 10
virtuelle, 10
mthodes, 163
JC, 42
math.asm, 3739
JE, 43
memory.asm, 116120
JG, 43
mnmonique, 12
JGE, 43
mode protg
JL, 43
16 bits, 910
193
INDEX
32 bits, 10
rcursivit, 9396
mode rel, 9
RCL, 51
mot, 8
RCR, 51
MOV, 13
read.asm, 139141
MOVSB, 112
register
MOVSD, 112
MOVSW, 112
MOVSX, 33
FLAGS
SF, 40
registre, 5, 78
MOVZX, 33
32 bits, 8
EDI, 111
NASM, 12
NEG, 37, 58
NOT, 54
octet, 4
oprations sur les bits
assembleur, 5455
C, 5860
dcalages, 4952
dcalages arithmtiques, 50
51
dcalages logiques, 4950
rotations, 51
ET, 52
NOT, 53
OU, 53
XOR, 53
opcode, 12
OR, 53
pile, 7281
CF, 40
DF, 110
OF, 40
PF, 42
ZF, 40
index, 7
IP, 8
pointeur de base, 7, 8
pointeur de pile, 7, 8
segment, 7, 8, 112
REP, 113
REPE, 115
REPNE, 115
REPNZ, voir REPNE
REPZ, voir REPE
RET, 7374, 76
paramtres, 7477
ROL, 51
ROR, 51
SAHF, 135
SAL, 50
SAR, 50
SBB, 39
SCASB, 113, 114
SCASD, 113, 114
quad.asm, 136139
quadruplet, 4
SCSI, 153155
QWORD, 17
segment bss, 22
194
INDEX
segment de code, 22
reprsentation, 121126
segment de donnes, 22
dnormaliss, 125126
SET
xx, 56
SETG, 58
IEEE, 123126
SHL, 49
SHR, 49
un masqu, 124
sous-programme, 7098
appel, 7381
rentrant, 9394
STD, 110
storage types
volatile, 98
STOSB, 111
STOSD, 111
structures, 149157
alignement, 150152
champs de bits, 153155
osetof(), 150
SUB, 14, 39
subroutine, voir subprogram
tableaux, 99120
accs, 100107
dnition, 99100
statique, 99
variable locale, 100
multidimensionnels, 107110
deux dimensions, 107108
paramtres, 110
TCP/IP, 61
TEST, 54
text segment, voir code segment
TWORD, 17
types de stockage
automatic, 96
global, 96
register, 96
static, 96
UNICODE, 61
virgule ottante, 121145
arithmtique, 126129
WORD, 17
XCHG, 62
XOR, 53