Vous êtes sur la page 1sur 16

TP 4

MI01

Lefebvre Philippe
Boucher Yann

Introduction .............. 1

Exercice 2
Partie A
. . . . . . . . . . .
. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. . . . . 2
2
Question 1 2

Partie B . ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
Question 2

Question 1
2
8
8
Question 2 ............... 9

Exercice 1
Partie A
. . . . . . . . . . .
. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. . . . . 9
9
Question 1 9
Question 2
Partie B
. . . . . . . . . . .
. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. . . . . 10
12
Question 1 12
Partie C . .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
Question 1
13
13
Question 2
Question 3
.. .. .. .. .. .. .. .. .. .. .. .. .. .. .. 15
15

Conclusion ............... 16

Introduction
Durant ce premier TP de programmation assembleur pour l’architecture x86_64, nous
découvrirons l’environnement de développement et de déboguage Eclipse couplé avec
GNU Assembler. Nous réaliserons quelques programmes simples pour nous famil-
iariser avec le jeu d’instructions et les outils fournis.

1
Exercice 2
Partie A
Question 1
Dans le fichier « hello1.S », définissez une variable msg, de type « chaîne de caractères
» qui contient « Bonjour tout le monde! » et une variable longueur qui contient la
longueur de la chaîne. Quelle doit être la taille de la zone mémoire à réserver pour
longueur, sachant qu’elle sera comparée au registre r13 dans le programme ?

Réponse : Pour déterminer la taille de longueur, nous étudions le fonctionnement du


code et comment longueur est utilisé.
cmp r13, longueur[rip]
r13 est un registre 64-bit. Pour que l’instruction soit valide, il faut qu’une valeur de
64 bits soit récupérée depuis l’adresse longueur[rip]. Par conséquent, longueur doit
correspondre à un entier 64-bits.
Nous définissons les variables msg et longueur à l’aide des directives .ascii pour insérer
une chaîne de caractères et .quad pour définir un entier 64-bits.

1 msg: .ascii "Bonjour tout le monde!"


2 longueur: .quad 22

Le code complet de la réponse se situe dans le fichier ex1_1.asm.

Question 2
Décrivez le fonctionnement de ce programme en donnant l’algorithme utilisé. Que
représente le registre r13 ?

Réponse : L’algorithme utilisé dans le code est le suivant :


1: 𝑖 ← 0
2: repeat
3: 𝑝𝑡𝑟 ← &𝑚𝑠𝑔[𝑖]
4: 𝑖←𝑖+1
5: 𝑤𝑟𝑖𝑡𝑒(1, 𝑝𝑡𝑟, 1)
6: until 𝑖 < 𝑙𝑜𝑛𝑔𝑢𝑒𝑢𝑟
7: 𝑒𝑥𝑖𝑡(0)

r13 est un index permettant de parcourir la chaîne msg caractère par caractère pour
l’afficher.

2
Observez le déroulement de ce programme au moyen du débogueur.

Réponse :

Affichage de la mémoire à l’adresse msg : contenu du message en représentation


ASCII

3
Contenu de la mémoire à l’adresse longueur : la valeur 22 est encodée sur 8 octets en
little-endian

4
Affichage de l’état des registres après l’exécution des instructions jusqu’au write

5
État du processeur après l’exécution d’une itération : l’index r13 vaut 1, le carry flag
est set, le saut sera effectué

6
État du processeur après l’exécution de la dernière itération : l’index r13 vaut la
longueur, le carry flag n’est pas set, le saut ne sera pas effectué

7
État du processeur avant exit

Partie B
Question 1
On veut maintenant se passer de la longueur de la chaîne à afficher. À cet effet, on
marque la fin de la chaîne de caractères par un caractère NUL (code ASCII 0). Modifiez
la valeur initiale de la chaîne msg en conséquence et supprimez la variable longueur.

Quel algorithme réaliser?

Réponse : On supprime longueur, et on remplace msg par une chaîne ASCII délimitée
par un NUL en utilisant .asciz.
msg: .asciz "Bonjour tout le monde!"
L’algorithme de la partie A peut être modifié afin que la condition de sortie de la boucle
soit sur un test de la valeur du caractère au lieu d’une comparaison entre l’index et la
longueur. On change aussi la boucle en un while afin de pouvoir gérer gracieusement
le cas d’une chaîne vide.
1: 𝑖 ← 0
2: 𝑝𝑡𝑟 ← &𝑚𝑠𝑔[0]
3: while ∗𝑝𝑡𝑟 ≠ 0 do

8
4: 𝑝𝑡𝑟 ← &𝑚𝑠𝑔[𝑖]
5: 𝑖←𝑖+1
6: 𝑤𝑟𝑖𝑡𝑒(1, 𝑝𝑡𝑟, 1)
7: 𝑒𝑥𝑖𝑡(0)

Question 2
Modifiez le programme « hello2.S » pour implémenter cet algorithme.

Réponse : La boucle d’affichage est désormais la suivante :

1 mov rdi, 1 /* fd = 1 (stdout) */


2 mov rdx, 1 /* 1 seul caractère */
3 cmp byte ptr [r12], 0
4 je out
5 suivant:
6 lea rsi, [r12+r13] /* Adresse du caractère */
7 mov rax, 1 /* Appel no 1 (write) */
8 syscall /* Appel système */
9
10 add r13, 1 /* Passer au caractère suivant */
11 cmp byte ptr [r12+r13], 0 /* Fin de la chaîne */
12 jnz suivant /* Si non, passer au suivant */

Le code complet de la réponse se situe dans le fichier ex1_2.asm.

Exercice 1
Partie A
Question 1
On stocke les chiffres du nombre converti en base 10 dans une chaîne de n caractères,
n à définir. Chaque caractère occupe exactement un octet en mémoire. Sachant que
nombre est exprimé sur 64 bits au plus en base 2, donnez l’expression de la valeur de
n en fonction du plus grande valeur possible de nombre. Quelle valeur utiliser dans
le programme? Remplacez les deux «? », ligne 13 dans le programme, par la valeur
appropriée.

Réponse : Le nombre maximal représentable par un entier 64-bit est 264 − 1. En base
10, le nombre de caractères nécéssaires pour représenter une telle valeur est donc :

⌊log10 (264 − 1) + 1⌋ = 20

Il est donc nécéssaire de réserver 20 octets pour la chaîne.

9
Question 2
Proposez un programme en assembleur x86-64 qui réalise la conversion de nombre et
stocke chaque caractère successif obtenu par le reste de la division à partir de l’adresse
chaine. Vous prendrez garde à la condition d’arrêt des itérations ainsi qu’à la cohérence
de taille des opérandes, en particulier lors de la division et de la conversion du reste
en chiffre affichable : nombre est stocké sur 64 bits, chaque caractère de la chaîne est
un octet et le code ASCII du caractère « 0 » est 0x30. Vérifiez le fonctionnement de
votre programme au moyen du débogueur en visualisant le contenu de la mémoire à
l’adresse chaine.

Réponse : Le code de la boucle de conversion du nombre en chaîne est le suivant

1 mov rbx, 10 /* base */


2 lea rsi, chaine[rip] /* ptr vers le début de chaine */
3
4 mov rax, nombre[rip]
5 loop: xor rdx, rdx /* div effectue une division de edx:eax, on clear edx */
6 div rbx /* quotient => rax, reste => rdx */
7 add dl, '0'
8 mov byte ptr [rsi], dl
9 inc rsi
10 test rax, rax /* si le nombre après divison vaut zéro, on arrête les itérations */
11 jnz loop

Le code complet de la réponse se situe dans le fichier ex2_1.asm.

10
La chaîne a été effectivement écrite en mémoire à l’adresse souhaitée. On remarque
cependant qu’elle est inversée, caractéristique qu’il faudrait prendre en compte pour
l’affichage.

11
Partie B
Question 1
Complétez le programme de conversion pour qu’il affiche la chaîne obtenue à l’écran
dans le sens de lecture naturel.

Réponse :
Pour répondre à cette question, il est nécessaire de corriger le sens d’affichage de la
chaîne généré. Afin de réduire le nombre total d’appels systèmes, nous avons fait le
choix d’inverser la chaîne directement en mémoire, avant de l’afficher d’un coup avec
un unique appel système write.

1 /* A présent, on inverse le contenu de chaîne */


2 /* A ce moment, rsi pointe vers la fin de la chaîne, rsi pointe vers le début */
3 lea rdi, chaine[rip]
4 mov rcx, rsi
5 sub rcx, rdi /* rcx = longueur de la chaîne */
6 /* Algorithme : on inverse *rdi et *rsi jusqu'à ce que rdi >= rsi */
7 dec rsi /* Ajuste rsi pour qu'il pointe vers le dernier caracètre et non pas après la fin */
8 reverse:
9 mov al, [rdi]
10 xchg al, [rsi]
11 mov [rdi], al /* On échange les valeurs à *rsi et *rdi */
12 inc rdi
13 dec rsi
14 cmp rdi, rsi
15 jb reverse
16

17 mov rax, 1 /* Appel no 1 (write) */


18 mov rdi, 1 /* fd = 1 (stdout) */
19 lea rsi, chaine[rip] /* Adresse du caractère */
20 mov rdx, rcx /* nb de caractères */
21 syscall /* Appel système */

Le code complet de la réponse se situe dans le fichier ex2_2.asm.

12
Partie C
Question 1
Comment convertir le reste de chaque division entière en caractère affichable au moyen
de ce tableau? Modifiez votre programme en conséquence.

Réponse : Pour convertir le reste de chaque division entière en caractère affichage, il


suffit d’utiliser le reste comme un index dans le tableau de conversion, et de récupérer
le caractère qui s’y trouve.

1 mov rbx, 33 /* base */


2 lea rsi, chaine[rip] /* ptr vers le début de chaine */
3 lea rdi, chiffres[rip] /* ptr vers la lookup table des caractères selon la base */
4
5 mov rax, nombre[rip]
6 loop: xor rdx, rdx /* div effectue une division de edx:eax, on clear edx */
7 div rbx /* quotient => rax, reste => rdx */
8 mov dl, [rdi + rdx]
9 mov byte ptr [rsi], dl
10 inc rsi
11 test rax, rax /* si le nombre après divison vaut zéro, on arrête les itérations */
12 jnz loop

Le code complet de la réponse se situe dans le fichier ex2_3.asm.

13
Affichage de la valeur donnée en hexadécimal.

14
Affichage de la valeur donnée en binaire.

Question 2
Quelle est maintenant la taille maximale de la chaîne nécessaire pour pouvoir stocker
tous les chiffres quelle que soit la base choisie?

Réponse : La base la plus petite possible est à présent la base 2. Le nombre de taille
maximale étant 264 − 1, il est trivial de conclure que la quantité maximale de caractères
nécéssaires est 64.

Question 3
Donnez au moyen de votre programme l’expression de nombre en base 33.

Réponse : Le résultat de l’exécution du programme avec ce nombre en base 33 est


ILOVEASM.
Au moins l’un des auteurs du rapport approuve ce message.

15
Conclusion
Au cours de ce TP, nous avons pu utiliser les outils de déboguage pour observer l’évolution
de l’état du processeur et de la mémoire à chaque instruction. Nous avons mis en place
des algorithmes simples en assembleur pour réaliser l’affichage de chaînes de carac-
tères et la conversion de nombres en chaînes.

16

Vous aimerez peut-être aussi