Assembleur SPARC v8

publicité
Assembleur SPARC v8
01/12/2009
Cours d'introduction
Ing1 – Promotion 2012
Matthieu Bucchianeri (2005-2007)
Laurent Lec (2009)
v 2.2
Assembleur & microprocesseurs RISC
●
●
●
L'assembleur est le langage le plus proche du
microprocesseur.
Pas de variables ni de fonctions, mais des registres, des
adresses mémoires...
Pas de structures de contrôles avancées comme les
conditions ou les boucles, mais des instructions de saut
et de branchement.
Types de données
●
●
SPARC v8 est une spécification de microprocesseur 32
bits
–
mot (word)
= 32 bits
–
demi-mot (half-word)
= 16 bits
–
octet (byte)
= 8 bits
L'ordre des octets en mémoire est big endian.
Registres
●
●
Seules des opérandes dans des registres peuvent être
utilisées pour les calculs
Disposition des registres sur SPARC :
–
●
%g0 à %g7
●
%g0 vaut toujours 0
●
%g5 à %g7 sont réservés
–
%i0 à %i7 : recevoir des arguments
–
%l0 à %l7 : registres locaux
–
%o0 à %o7 : passer des arguments
En principe, on ne touche pas trop aux registres
globaux...
Instructions de manipulation des
registres
●
Copier une valeur d'un registre à un autre
MOV
●
Mettre une valeur (< 13 bits = 8192) dans un registre
MOV
●
regs, regd
imm, regd
Mettre une valeur (> 13 bits) dans un registre
@SET imm, regd
●
Mettre à zéro un registre
CLR
regd
Instructions d'accès mémoire
●
Lire depuis la mémoire vers un registre
LD
●
Ecrire le contenu d'un registre en mémoire
ST
●
●
[address], regd
regs, [address]
Adresse : « registre + registre » ou « registre +
constante »
On peut préciser le type de la donnée
Utiliser des variables
●
●
Une variable est :
–
un registre ;
–
une zone mémoire.
En assembleur, créer une variable revient à réserver un
bloc de mémoire
ma_variable: .reserve 4 ; int ma_variable
@SET ma_variable, %l0
LD
[%l0], %l1
ADD
%l1, 2, %l1
ST
%l1, [%l0] ; ma_variable = ma_variable + 2
Piège !
●
Sur SPARC les accès mémoires sont alignés :
–
mot de 32 bits = une adresse multiple de 4 octets
–
demi-mot de 16 bits = une adresse multiple de 2
.align boundary 4
; exemple : 4000
var1:
.reserve 2
; 4000
var2:
.reserve 4
; 4002
@SET var2, %l0
; (4002 % 4) != 0
LD
; Bus Error
[%l0], %l1
Piège !
●
La solution
.align boundary 4
var1:
.reserve 2
.align boundary 4
var2:
; exemple : 4000
; 4002 aligné à 4 = 4004
.reserve 4
@SET var2, %l0
; (4004 % 4) == 0
LD
; ok
[%l0], %l1
Utiliser des variables
●
Les variables n'ont pas de type.
●
On met ce qu'on veut dans les blocs mémoires
table: .reserve 8
; short[4]
@SET ma_variable, %l0
LDSH [%l0], %l1
; table[0]
LDSH [%l0 + 4], %l2
; table[2]
Quelques opérations de base
●
Un microprocesseur sait principalement faire de
l'arithmétique et de la logique ;
●
Prend 1 ou 2 registres en opérande ;
●
Un registre en destination ;
●
Quelques opération pour exemple :
ADD
SUB
regs1, (regs2 | imm), regd
regs1, (regs2 | imm), regd
Un exemple
●
Le code suivant place 2 entiers dans %i0 et %l1 et
retourne leur somme dans %o0 :
@SET 12345, %i0
; car > 8192
MOV
; car < 8192
ADD
●
●
1234, %l1
%i0, %l1, %o0
On mélange sans problème des registres de catégorie
différente.
Un registre peut être à la fois source et destination.
Encore d'autres instructions
AND
regs1, (regs2 | imm), regd
OR
NEG
INC
DEC
regs1, (regs2 | imm), regd
regs, regd
(imm ,) regd
(imm ,) regd
Multiplications, divisions, décalages
●
Multiplication et division (préciser si signés ou non) :
UMUL
SMUL
UDIV
SDIV
●
(regs2 |
(regs2 |
(regs2 |
(regs2 |
imm),
imm),
imm),
imm),
regd
regd
regd
regd
Décalages :
SLL
SRL
SRA
regs1,
regs1,
regs1,
regs1,
regs1, (regs2 | imm), regd
regs1, (regs2 | imm), regd
regs1, (regs2 | imm), regd
Astuces
●
SRA effectue un décalage arithmétique
–
●
Il propage le bit de signe à droite
Multiplication par un nombre 2n
–
Décalage vers la gauche de n bits
UMUL %l0, 4, %l0
SLL
%l0, 2, %l0
●
Une division par 2n
–
Décalage à droite de n bits
SDIV
SRA
–
%l0, 8, %l0
%l0, 3, %l0
Notez l'usage de SRA et pas SRL
Contrôler le chemin d'exécution
●
On veut des if et des boucles
–
●
●
Un microprocesseur sait juste faire des « GOTO »
4 codes de conditions
–
Z – le résultat vaut zéro
–
N – le résultat est négatif
–
C – il y a une retenue
–
V – il y a un débordement
GOTO à une adresse si Z = 1, si Z = 0, si N = 1...
Les codes de condition
●
La mise à jour des codes de condition se fait par les
instructions directement, en ajoutant le suffixe CC
ADDCC
●
Ou bien explicitement
TST
regs1, (regs2 | imm), regd
regs
Les branchements dans tout ça ?
●
●
●
Une condition :
–
Tester qu'une valeur est égale ou non à une autre
–
Tester la relation entre deux valeurs (plus petit, plus
grand...)
Solution : soustraire les deux valeurs à tester (B - A)
–
Si Z, alors A == B
–
Si N, alors A > B
–
Si N ou Z, alors A ≥ B
–
Etc...
Utiliser CMP
Les instructions de branchement
BA
BE
BNE
BL(U)
BLE(U)
BG(U)
BGE(U)
BNEG
BPOS
BZ
BNZ
label
label
label
label
label
label
label
label
label
label
label
« branche toujours »
==
!=
<
≤
>
≥
<0
>0
== 0
!=
0
Les structures de contrôle
●
Le If
●
La boucle
test condition
Loop:
si faux goto CAS_faux
test condition
[ Execution cas vrai ]
si faux goto Fin
goto Fin
[ Execution boucle ]
CAS_faux:
goto Loop
[ Execution cas faux ]
Fin:
Fin:
●
Il y a d'autres formes !
Un if...then...else, naïvement
y = abs(x) : if (x < 0) y = -x
else y = x
TST
%l0
BNEG
neg
MOV
%l0, %l1
BA
end
neg:
NEG
; CMP %l0, 0 revient au même
;y=x
Si %l0 < 0, je saute !
%l0, %l1
; y = -x
end:
MAIS DELAY-SLOT ! NE MARCHE PAS
Avec des delay-slot, toujours
naïvement
●
L'instruction qui suit un branchement est toujours
exécutée
TST
%l0
BNEG
neg
NOP
MOV
%l0, %l1
BA
end
NOP
neg:
NEG
%l0, %l1
end:
Les NOP ne servent à rien !
Avec des delay-slot et mieux pensé
TST
%l0
BNEG
end
NEG
MOV
%l0, %l1
%l0, %l1
end:
Somme des entiers d'un tableau,
version (très) naïve
; %i0 = int tab[]
; %i1 = int size;
CLR
%l0
; int compteur = 0;
loop:
TST
%i1
BZ
end
; if (size == 0) goto end;
DEC
%i1
; size--;
LD
[%i0], %l2
; int tmp = *tab;
ADD
%l0, %l2, %l0
; compteur = compteur + tmp;
INC
4, %i0
; tab++;
BA
loop
; goto loop;
NOP
NOP
end:
MOV
%l0, %i0
; return (compteur);
Version moins naïve...
TST
%i1
BZ
end
; if (size == 0) goto end;
%l0
; int compteur = 0
SLL
%i1, 2, %l1
; size = size * 4;
ADD
%i0, %l1, %l1
; int *end_tab = (char*)tab + size;
LD
[%i0], %l2
; int tmp = *tab;
INC
4, %i0
; tab++;
CMP
%i0, %l1
BNE
loop
; if (tab != end_tab) goto loop
%l0, %l2, %l0
; compteur = compteur + tmp;
%l0, %i0
; return (compteur);
CLR
loop:
ADD
end:
MOV
Les fonctions
●
Une fonction = un label (= une adresse)
●
Une fonction a une structure particulière :
ma_fonction:
SAVE
%sp, -96, %sp
; instructions ...
RET
RESTORE
Les fonctions (explications)
●
Les instructions SAVE et RESTORE font coulisser la
fenêtre de registres :
–
–
SAVE :
●
%o0-%o7 deviennent %i0-%i7
●
%l0-%l7 sont sauvés
●
%i0-%i7 sont sauvés
RESTORE :
●
●
●
%i0-%i7 redeviennent %o0-%o7
Les valeurs dans %l0-%l7 sont restaurées comme
avant l'appel de fonction
Idem pour %i0-%i7
SAVE/RESTORE : un exemple
MOV
-32, %l0
MOV
1, %o0
MOV
64, %o1
SAVE
ADD
%i0, %i1, %l0
ADD
%l0, 4, %i0
RESTORE
ADD
%o0, %l0, %l1
Que vaut %l1 ?
Les fonctions (explications)
●
RET = retour de la fonction.
●
CALL = appel de fonction.
.proc add
SAVE
%sp, -96, %sp
ADD
%i0, %i1, %i0 ; valeur de retour dans %i0
RET
RESTORE
...
MOV
123, %o0
MOV
456, %o1
CALL
add
NOP
; add(123, 456);
; %o0 vaut la valeur de retour de la fonction
Piège !
Ne pas utiliser %i6, %i7, %o6 et %o7 !
Ils sont utilisés implicitement par le microprocesseur.
Advanced Assembler (AASM)
http://savannah.nongnu.org/projects/aasm/
●
●
●
Logiciel d'assemblage écrit par Alexandre Becoulet et
Cédric Bail, qui gère le x86 et le SPARC
Première étape dans la phase de construction de
binaires à partir de code assembleur
Pour indiquer qu'on code pour SPARC :
.mod_load asm-sparc
.mod_out-elf32
.include sparc/v8.def
Sections...
●
●
Sections
–
.text : le code
–
.data : les données initialisées
–
.rodata : les données initialisées et lisibles seulement
–
.bss : les données non initialisées
Avec AASM, une section a la forme :
.section code .text
.section data .data
...
...
.ends
.ends
Section de code
.section code .text
.section_align 4
.mod_asm opcodes v8
.extern printf
; on va utiliser printf de la libc
.export main
; notre main est exporté (pas static)
.proc main
SAVE %sp, -96, %sp
...
RET
RESTORE
.endp
.ends
Section de données
.section data (.data | .rodata | .bss)
.section_align 4
var1:
.dump word
0x4242
str:
.string "Un grand verre d'huile d'arachide\0"
.align boundary 4
array:
.fill 0 8
; 8 octets mis à 0
.ends
Piège !
.dump dword
0x42424242
.dump word
0x4242
.dump byte
0x42
●
Un dword est 32 bits et non pas 64 !
●
Un word est 16 bits au lieu de 32
●
AASM était conçu pour x86 à l'origine
Un exemple simple
.mod_load asm-sparc
.mod_load elf-out32
.include sparc/v8.def
.section data .rodata
.section_align 4
str1:
.string "Luc ?\0"
str2:
.string "Ouiiiiiiiiiiiiiii\0"
.ends
(suite)
.section code .text
.section_align 4
.mod_asm opcodes v8
.extern puts
.extern sleep
.define LUC_LATENCY
1
.export main
.proc main
SAVE
%sp, -96, %sp
@SET
.rodata:str1, %o0
CALL
puts
NOP
MOV
LUC_LATENCY, %o0
CALL
sleep
NOP
@SET
.rodata:str2, %o0
CALL
puts
NOP
RET
RESTORE
.endp
.ends
Compilation en deux étapes !!!
●
Sur une machine FreeBSD x86 :
$ aasm luc.aasm
› génère luc.o
●
Sur une machine SPARC :
$ gcc -o luc luc.o
$ ./luc
Luc ?
Ouiiiiiiiiiiiiiii
Autres exemples
Vous devez pour cet exercice ecrire un programme assembleur qui
appelle la fonction strcmp() avec les deux premiers arguments
passes en ligne de commande a votre programme. Votre programme
affichera la valeur renvoyee par strcmp(). -- Sujet Exo 6 Promo 2008
.mod_load asm-sparc
.mod_load out-elf32
.include sparc/v8.def
.include my_aff_reg.inc
.section code .text
.section_align 4
.mod_asm opcodes v8
.extern strcmp
.export main
.proc main
save %sp, -96, %sp
cmp
%i0, 3
bne
exit
nop
ld
[%i1 + 4], %o0
ld
[%i1 + 8], %o1
call
strcmp
nop
@my_aff_reg %o0
exit:
clr
%i0
ret
restore
.endp
.ends
; if (argc != 3) goto exit;
; argv[1]
; argv[2]
; strcmp(argv[1], argv[2]);
Vous devez pour cet exercice ecrire un programme assembleur qui
affiche le contenu d'un fichier texte dont le nom est passe en
argument sur la ligne de commande. -- Sujet Exo 7 Promo 2008
[...]
.define BUFF_SIZE 512
.section data .data
.section_align 4
mode:
.string "rb"
.ends
.section code .text
[...]
.proc main
save
%sp, -96, %sp
cmp
%i0, 2
bne
exit
nop
ld
[%i1 + 4], %o0
@set
.data:mode, %o1
call
fopen
nop
tst
%o0
bz
exit
nop
call
do_cat
nop
call
fclose
nop
exit:
ret
restore
.endp
; if (argc != 2) goto exit;
; argv[1]
; "rb"
; FILE* f = fopen(argv[1], "rb");
; if (f == NULL) goto exit;
; do_cat();
; fclose(f);
(suite)
.extern fgets
.extern puts
.proc do_cat
; %i0 = FILE* f;
save %sp, -96, %sp
loop:
@set .bss:buff, %o0
mov
BUFF_SIZE, %o1
mov
%i0, %o2
call
fgets
; fgets(buff, BUFF_SIZE, f);
nop
tst
%o0
bz
exit
; if (fgets(...) == NULL) goto exit;
nop
@set .bss:buff, %o0
call
puts
; puts(buff);
nop
ba
loop
nop
exit:
ret
restore
.endp
.ends
.section data .bss
.section_align 4
buff:
.reserve BUFF_SIZE
.ends
Bibliographie
●
SPARC v8 Architecture Manual
http://www.sparc.com/standards/V8.pdf
●
Cours de Nicolas Pouillon
http://asm.ssji.net/
Téléchargement