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 :
nullest 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