TD2 : Assembleur
TD2 de Programmation 1
EX 1
Programme 1
La factorielle.
cmpl src dest
: compare la src et la destination, puis place le résultat dans un autre registre (flag
).je .end
: aller au label.end
(label = adresse symbolique)
Point important : call
VS ret
call
: fait 2 choses : il empile sur la pile l’adresse de l’instruction qui vient juste après (adresse de retour).ret
: dépile la dernière adresse, et y va.
Programme 2
Calcule la somme des carrés.
L’OS impose des conventions sur le rôle des registres :
- les callee save (fonction appelée) ⟶ Si la fonction appelée veut modifier ce registre, elle doit mémoriser sa valeur avant, et la restaurer après.
- les caller save (fonction appelante) ⟶ Le contraire : il faut que le “caller” (l’appelant) le sauvegarde.
- les temps ⟶ là pour être modifiés par tout le monde : pas faire d’hypothèse ni sur l’appelant, ni sur l’appelé.
%edx
: un “callee save”.
MIPS : ont créé des processeurs avec des instructions “totalement orthogonales” (i.e toutes de même taille) ⟶ BUT : aller plus vite, moins de confusion
L’adresse de retour : pas mise sur la pile, dans $ra
Que fait vraiment cmp
?
eflags
: contient CF
(Carry Flag), ZF
(Zero Flag), …
cmpl %eax, %ebx
⟶ calcule %eax-%ebx
, et met la retenue dans le CF
À quoi sert le Carry Flag ?
jae
(jump if above or equal) saute au label ssi
- En vrai: saute au label si le `CF` vaut 0
- Intuition : `%eax >= %ebx` en NON signé.
jb
(if below) saute au label ssi CF=1
- En vrai: saute au label si le `CF` vaut 1
- Intuition : `%eax < %ebx` en NON signé.
jge
(jump if greater than or equal) saute au label ssi
- En vrai: saute au label si le `SF` vaut 0
- Intuition : `%eax >= %ebx` en NON signé.
jl
(jump if less) saute au label ssi
- En vrai: saute au label si le `SF` vaut 1
- Intuition : `%eax < %ebx` en NON signé.
Idem pour je
(equal), jne
(not equal) avec ZF
3)
popl src
=
movl %esp, dest
addl $4, %esp
MAIS : marche pas toujours :
- Attention à
popl %esp
4)
NB: movl
: pour une adresse : va dans le contenu de l’adresse contenue dans cette adresse.
VS
leal
: pour une adresse : va dans le contenu de l’adresse directement.
a) leal (%eax, %esi, 4) %ebx
: met l’adresse de %eax + 4$%esi
dans %ebx
b) car les modes d’adressage immédiat et registre n’ont pas d’adresse.
c) leal
5)
a)
movl $0x8aa0e022, %eax
pushl %eax
jmp 0x7fe976a0
Pas bon : ça décale tout le code après
NB :
call
: 1 octet- adresse du call : 4 octets
- jmp … : 5 octets
b)
ret
:
popl %eax
jmp *%eax
NB: *%eax
: au lieu de (eax)
, car c’est un jmp
c)
- Utilise beaucoup de mémoire (un registre dans le vide)
6)
int main(){
int i;
i = 2*4+3;
printf("%d\n", i*i);
return 0;
}
a)
int main(){
int i; int j;
i = 2;
j = 4;
i = i*j;
j = 3;
i = i+j;
i = i*i;
printf("%d\n", i);
return 0;
}
b)
li "$"a, 2
li "$"b, 4
li "$"c, 3
mul a, $a, $b
add a, $a, $c
mul a, $a, $a
c)
movl $2 %eax
movl $4 %ebx
mull %ebx %eax
movl $3 %ebx
addl %ebx %eax
mull %eax %eax
movl format .asciz "%d\n" %edx
Pourquoi du code à deux valeurs en x86
?
Parce que les instructions sont de la forme “faire opération sur i, avec j puis mettre le contenu dans i”.
8) But du MIPS : aller très vite, en utilisant aussi peu de transistors que possible.
TD 2
1)
a) int *
b) int (*) (char *)
c) void (*)(intk, ___siginfo *, void *);
d) void (*(int, void (*)(int)))(int);
NB :
null
est de typevoid *
(n’a rien à voir avec void : pointeur sur n’importe quoi) ⟶void
: pas un vrai type.
void *
: peut-être associé à n’importe quel pointeur.
2)
3)
a) a[i]
et *(a+i)
: pareil
b)
e) p[i]
et i[p]
: c’est un addition de pointeurs, et l’addition est commutative ⟶ égalité.
f) (&p[i])[j]
et p[i+j]
: égalité car, addition associative (dans arithmétique des pointeurs).
4)
a)
int p[30][40];
p[0]
: 40 intp[1]
: 40 int etc…
int *p[30];
int i;
for (i=0; i<30; i++) p[i] = malloc(sizeof(int [40]));
Les zones de “40 int” sont éparpillées dans la mémoire.
⟶ moins rapide car deux accès à la mémoire successifs pour accéder à p[i][j]
Pas la même classe de stockage, mais même typage.
b)
int **p;
int i;
p = malloc(sizeof(int *[30]));
if (p==NULL) abort();
for (i=0; i<30; i++) p[i] = malloc(sizeof(int [40]));
P : double pointeur,
p[0], p[1], ⋯, p[29]
dans la même zone ⟶ pointent chacun vers des zones de “40 bit”
c) accès : p[40*i+j]
d)
Leave a comment