0% ont trouvé ce document utile (0 vote)
203 vues5 pages

TP Assembleur SPARC : Addition et Factorielle

Ce document décrit un programme assembleur SPARC qui additionne deux entiers passés en paramètres. Il présente ensuite un exemple de code assembleur généré par le compilateur gcc à partir d'un programme C utilisant une fonction à 8 paramètres.

Transféré par

Amina Ben
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
203 vues5 pages

TP Assembleur SPARC : Addition et Factorielle

Ce document décrit un programme assembleur SPARC qui additionne deux entiers passés en paramètres. Il présente ensuite un exemple de code assembleur généré par le compilateur gcc à partir d'un programme C utilisant une fonction à 8 paramètres.

Transféré par

Amina Ben
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Architecture des ordinateurs

Corrigé du TP 1 : Assembleur S PARC


Arnaud Giersch, Benoît Meister et Frédéric Vivien

Remarques préliminaires :
– le nom d’un fichier contenant un programme assembleur doit obligatoirement avoir le suffixe .s ;
– on passe d’un programme assembleur à un fichier exécutable en utilisant gcc de la même manière qu’avec un
programme C.
1. Étude d’un programme en assembleur S PARC
(a) Récupérez le programme addition.s qui réalise la somme de 2 entiers. Compilez en utilisant la com-
mande gcc -o addition addition.s et exécutez-le.
URL : [Link]
Étudiez ce programme et rajoutez des commentaires explicatifs dans ce fichier addition.s.
Correction :

save %sp,-104,%sp ! réserve de l’espace sur la pile:


! Fonction addition ! 96 + 8 octets pour deux variables
! -> retourne la somme de ses deux arguments ! locale aux adresses %fp-4 et %fp-8
!------------------------------------------------------------
! printf
.section ".text" ! -> code sethi %hi(.PRINTF1),%o0 ! %o0 <- .PRINTF1
.align 4 ! aligné sur 4 octets or %o0,%lo(.PRINTF1),%o0
.global add ! exporte le symbole « add » call printf ! appel de « printf (.PRINTF1) »
nop ! « nop » après un « call »
add:
save %sp,-64,%sp ! réserve de l’espace sur la pile ! scanf
add %i0,%i1,%i0 ! calcule la somme des paramètres, add %fp,-4,%o1 ! %o1 <- %fp-4
! le résultat est la valeur de retour add %fp,-8,%o2 ! %o2 <- %fp-8
! de la fonction sethi %hi(.SCANF),%o0 ! %o0 <- .SCANF
ret ! retour de la fonction add or %o0,%lo(.SCANF),%o0
restore call scanf ! appel de
! « scanf (.SCANF1, %fp-4, %fp-8) »
nop ! « nop » après un « call »
! Programme principal
!------------------------------------------------------------ ! addition
ld [%fp-4],%o0 ! %o0 <- [%fp-4]
.section ".data" ! -> données ld [%fp-8],%o1 ! %o1 <- [%fp-8]
.align 8 ! alignées sur 8 octets call add ! appel de « add ([%fp-4], [%fp-8]) »
.PRINTF1: ! résultat dans %o0
.asciz "Entrez a et b : " ! chaîne de caractères avec zéro nop ! « nop » après un « call »
! terminal
.SCANF: ! printf
.asciz "%d %d" ! idem mov %o0,%o1 ! %o1 <- %o0
.PRINTF2: sethi %hi(.PRINTF2),%o0 ! %o0 <- .PRINTF2
.asciz "c : %d\n" ! idem or %o0,%lo(.PRINTF2),%o0
call printf ! appel de « printf (.PRINTF2, %o0) »
.section ".text" ! -> code nop ! « nop » après un « call »
.align 4 ! aligné sur 4 octets
.global main ! exporte le symbole « main » ret ! retour de la fonction main
restore
main:

(b) Réalisez un programme C faisant appel à une fonction prenant 8 paramètres, produisez le programme
assembleur correspondant (option -S de gcc : gcc -S fichiersource).
Déterminez ensuite quels paramètres sont placés dans les registres, lesquels ne le sont pas et trouvez
l’emplacement mémoire de ces derniers.
Correction :
– le programme C :

1
#include <stdio.h> int main (void)
{
int add8 (int a, int b, int c, int d, int e, int f, int g, int h) printf ("somme 1..8 = %d\n",
{ add8 (1, 2, 3, 4, 5, 6, 7, 8));
return a + b + c + d + e + f + g + h;
} return 0;
}

– le code assembleur correspondant :

.file "8arg.c" .section ".rodata"


gcc2_compiled.: .align 8
.section ".text" .LLC0:
.align 4 .asciz "somme 1..8 = %d\n"
.global add8 .section ".text"
.type add8,#function .align 4
.proc 04 .global main
add8: .type main,#function
!#PROLOGUE# 0 .proc 04
save %sp, -112, %sp main:
!#PROLOGUE# 1 !#PROLOGUE# 0
st %i0, [%fp+68] save %sp, -120, %sp
st %i1, [%fp+72] !#PROLOGUE# 1
st %i2, [%fp+76] mov 7, %o0
st %i3, [%fp+80] st %o0, [%sp+92]
st %i4, [%fp+84] mov 8, %o0
st %i5, [%fp+88] st %o0, [%sp+96]
ld [%fp+68], %o0 mov 1, %o0
ld [%fp+72], %o1 mov 2, %o1
add %o0, %o1, %o0 mov 3, %o2
ld [%fp+76], %o1 mov 4, %o3
add %o0, %o1, %o0 mov 5, %o4
ld [%fp+80], %o1 mov 6, %o5
add %o0, %o1, %o0 call add8, 0
ld [%fp+84], %o1 nop
add %o0, %o1, %o0 mov %o0, %o1
ld [%fp+88], %o1 sethi %hi(.LLC0), %o2
add %o0, %o1, %o0 or %o2, %lo(.LLC0), %o0
ld [%fp+92], %o1 call printf, 0
add %o0, %o1, %o0 nop
ld [%fp+96], %o1 mov 0, %i0
add %o0, %o1, %o0 b .LL3
mov %o0, %i0 nop
b .LL2 .LL3:
nop ret
.LL2: restore
ret .LLfe2:
restore .size main,.LLfe2-main
.LLfe1: .ident "GCC: (GNU) 2.95.3 20010315 (release)"
.size add8,.LLfe1-add8

On remarque que les 6 premiers paramètres sont passés par les registres %o0 à %o5 (%i0 à %i5 dans la
fonction), les deux derniers sont passés sur la pile aux adresses %sp+92 et %sp+96 (%fp+92 et %fp+96
dans la fonction).
2. Exercices de programmation
(a) Écrivez un programme assembleur calculant la factorielle d’un entier de manière itérative (une seule fonc-
tion principale contenant une boucle).
Correction :

!!! sethi %hi(.SCANF), %o0


!!! calcul de la factorielle d’un entier, version itérative or %o0, %lo(.SCANF), %o0
!!! add %fp, -4, %o1
call scanf ! scanf (.SCANF, %fp-4)
! Programme principal nop
!------------------------------------------------------------
! -> calcul de [%fp-4]! dans %l1,
.section ".data" ! -> données ! utilisation de %l0 comme compteur
.align 8
.PRINTF1: ld [%fp-4], %l0 ! %l0 <- [%fp-4]
.asciz "n? " mov 1, %l1 ! %l1 <- 1
.SCANF: loop:
.asciz "%u" cmp %l0, 1 ! while (%l0 > 1) {
.PRINTF2: ble end_loop !
.asciz "n! = %u\n" nop !
umul %l0, %l1, %l1 ! %l1 <- %l1 * %l0
dec %l0 ! %l0 --
.section ".text" ! -> code b loop ! }
.align 4 nop
.global main
end_loop:
main: sethi %hi(.PRINTF2), %o0
save %sp, -96, %sp ! réserve de la place sur la pile or %o0, %lo(.PRINTF2), %o0
! pour un entier [%fp-4] (mais on mov %l1, %o1
! arrondit à un multiple de 8) call printf ! printf (.PRINTF2, %l1)
nop
sethi %hi(.PRINTF1), %o0
or %o0, %lo(.PRINTF1), %o0 clr %i0 ! return 0
call printf ! printf (.PRINTF1) ret
nop restore

2
(b) Écrivez un programme assembleur calculant la factorielle d’un entier de manière récursive.
Correction :

!!! .SCANF:
!!! calcul de la factorielle d’un entier, version récursive .asciz "%u"
!!! .PRINTF2:
.asciz "n! = %u\n"
! fonction fact
! -> retourne la factorielle de son argument .section ".text" ! -> code
!------------------------------------------------------------ .align 4
.global main
.section ".text" ! -> code
.align 4 main:
.global fact save %sp, -96, %sp ! réserve de la place sur la pile
! pour un entier [%fp-4] (mais on
fact: ! arrondit à un multiple de 8)
save %sp, -96, %sp
sethi %hi(.PRINTF1), %o0
cmp %i0, 1 ! if (%i0 > 1) { or %o0, %lo(.PRINTF1), %o0
ble end_fact1 ! call printf ! printf (.PRINTF1)
nop ! nop
sub %i0, 1, %o0 ! %o0 <- %i0 - 1
call fact ! %o0 <- fact (%o0) sethi %hi(.SCANF), %o0
nop ! or %o0, %lo(.SCANF), %o0
umul %i0, %o0, %i0 ! %i0 <- %o0 * %i0 add %fp, -4, %o1
b end_fact ! } else { call scanf ! scanf (.SCANF, %fp-4)
nop ! nop
end_fact1: !
mov 1, %i0 ! %i0 <- 1 ld [%fp-4], %o0
! } call fact ! %o0 <- fact ([%fp-4])
end_fact: nop
ret ! return %i0
restore mov %o0, %o1
sethi %hi(.PRINTF2), %o0
or %o0, %lo(.PRINTF2), %o0
! Programme principal call printf ! printf (.PRINTF2, %o0)
!------------------------------------------------------------ nop

.section ".data" ! -> données clr %i0 ! return 0


.align 8 ret
.PRINTF1: restore
.asciz "n? "

(c) Modifiez-le programme précédent pour qu’il affiche à chaque étape de la récursion les valeurs des pointeurs
de pile (%sp et %fp).
Correction :

!!!
!!! calcul de la factorielle d’un entier, version récursive, ! Programme principal
!!! affichage de %fp et %sp !------------------------------------------------------------
!!!
.section ".data" ! -> données
! fonction fact .align 8
! -> retourne la factorielle de son argument .PRINTF1:
!------------------------------------------------------------ .asciz "n? "
.SCANF:
.section ".data" ! -> données .asciz "%u"
.align 8 .PRINTF2:
.PRINTF_DEBUG: .asciz "n! = %u\n"
.asciz "# fact (%u): %%fp = %p, %%sp = %p\n"
.section ".text" ! -> code
.section ".text" ! -> code .align 4
.align 4 .global main
.global fact
main:
fact: save %sp, -96, %sp ! réserve de la place sur la pile
save %sp, -96, %sp ! pour un entier [%fp-4] (mais on
! arrondit à un multiple de 8)
sethi %hi(.PRINTF_DEBUG), %o0
or %o0, %lo(.PRINTF_DEBUG), %o0 sethi %hi(.PRINTF1), %o0
mov %i0, %o1 or %o0, %lo(.PRINTF1), %o0
mov %fp, %o2 call printf ! printf (.PRINTF1)
mov %sp, %o3 nop
call printf ! printf (.PRINTF_DEBUG,
! %i0, %fp, %sp) sethi %hi(.SCANF), %o0
nop or %o0, %lo(.SCANF), %o0
add %fp, -4, %o1
cmp %i0, 1 ! if (%i0 > 1) { call scanf ! scanf (.SCANF, %fp-4)
ble end_fact1 ! nop
nop !
sub %i0, 1, %o0 ! %o0 <- %i0 - 1 ld [%fp-4], %o0
call fact ! %o0 <- fact (%o0) call fact ! %o0 <- fact ([%fp-4])
nop ! nop
umul %i0, %o0, %i0 ! %i0 <- %o0 * %i0
b end_fact ! } else { mov %o0, %o1
nop ! sethi %hi(.PRINTF2), %o0
end_fact1: ! or %o0, %lo(.PRINTF2), %o0
mov 1, %i0 ! %i0 <- 1 call printf ! printf (.PRINTF2, %o0)
! } nop
end_fact:
ret ! return %i0 clr %i0 ! return 0
restore ret
restore

Exemple d’exécution :

3
$ ./fact_r_print
n? 6
# fact (6): %fp = ffbef800, %sp = ffbef7a0
# fact (5): %fp = ffbef7a0, %sp = ffbef740
# fact (4): %fp = ffbef740, %sp = ffbef6e0
# fact (3): %fp = ffbef6e0, %sp = ffbef680
# fact (2): %fp = ffbef680, %sp = ffbef620
# fact (1): %fp = ffbef620, %sp = ffbef5c0
n! = 720
(d) Dans un processeur où la multiplication n’est pas implémentée par un circuit, celle-ci peut être réalisée
efficacement en se basant sur l’algorithme suivant :
a × b := si b = 0 alors 0
sinon si b = 2 × b0 alors (a × 2) × b0
sinon ((a × 2) × b0 ) + a
En vous appuyant sur cette méthode, proposez une fonction assembleur réalisant la multiplication entière
en utilisant uniquement des additions et des décalages.
Correction :
Deux versions,
– une version récursive :

!!! call mult ! %o0 <- mult (%o0, %o1)


!!! fonction multiplication: version récursive nop !
!!! andcc %i1, 1, %g0 ! if (%i1 & 1 != 0) {
bz even ! // -> équivalent à
.section ".text" nop ! // if (%i1 % 2 == 1) {
.align 4 add %o0, %i0, %o0 ! %o0 <- %o0 + %i0
.global mult even: ! }
mov %o0, %i0 ! %i0 <- %o0
mult: b return !
save %sp, -96, %sp nop !
zero: ! } else {
cmp 0, %i1 ! if (%i1 != 0) { mov 0, %i0 ! %i0 <- 0
be zero ! ! }
nop ! return:
sll %i0, 1, %o0 ! %o0 <- %i0 << 1 ret ! return %i0
srl %i1, 1, %o1 ! %o1 <- %i1 >> 1 restore

– une version itérative :

!!! nop !
!!! fonction multiplication: version itérative andcc %i1, 1, %g0 ! if (%i1 & 1 != 0) {
!!! bz even ! // -> équivalent à
nop ! // if (%i1 % 2 == 1) {
.section ".text" add %l0, %i0, %l0 ! %l0 <- %l0 + %i0
.align 4 even: !
.global mult sll %i0, 1, %i0 ! %i0 <- %i0 << 1
srl %i1, 1, %i1 ! %i1 <- %i1 >> 1
mult: b loop ! }
save %sp, -96, %sp nop
end_loop:
clr %l0 ! %l0 <- 0 mov %l0, %i0 ! return %l0
loop: ret
cmp 0, %i1 ! while (%i1 != 0) { restore
be end_loop !

(e) Utilisez la fonction précédente dans un programme C. L’exécutable s’obtient en donnant simplement le
fichier C et le fichier assembleur au programme gcc, ce dernier s’occupe de faire les liens.
Correction :

#include <stdio.h>
printf ("a, b? ");
extern unsigned mult (unsigned, unsigned); scanf ("%u %u", &a, &b);
printf (" %u x %u = %u\n", a, b, mult (a, b));
int main (void)
{ return 0;
unsigned a, b; }

(f) Écrivez un programme assembleur de recherche de l’élément minimum d’un tableau. Le tableau est une
variable locale à la routine principale. Cette dernière fait appel à une routine pour la recherche de l’élément
minimum d’un tableau.
Correction :

4
!!! .asciz "%d"
!!! recherche du minimum dans un tableau .PRINTF2:
!!! .asciz "minimum: [%d] = %d\n"

! Fonction min: recherche du minimum dans un tableau d’entiers


! 2 arguments: adresse du tableau et nombre d’éléments .section ".text" ! -> code
! retourne l’indice du minimum dans le tableau .align 4
! précondition: la tableau contient au moins 1 élément .global main
!------------------------------------------------------------
main:
.section ".text" ! -> code save %sp, -136, %sp ! réserve de la place pour un
.align 4 ! tableau de 10 entiers à
.global min ! l’adresse %fp-40
set .PRINTF1, %o0
min: call printf ! printf (.PRINTF1)
save %sp, -64, %sp nop
! registres locaux utilisés:
! %l0: index du min. courant ! lecture des entiers
! %l1: minimum courant mov 10, %l0 ! %l0 <- 10 (nombre d’entiers
! %l2: index courant ! à lire)
! %l3: valeur courante sub %fp, 40, %l1 ! %l1 <- %fp-40 (adresse du
! tableau)
clr %l0 ! %l0 <- 0 read_loop: ! do {
ld [%i0], %l1 ! %l1 <- tab[0] set .SCANF, %o0 !
clr %l2 ! %l2 <- 0 mov %l1, %o1 !
orcc %i1, %g0, %g0 call scanf ! scanf (.SCANF, %l1)
min_loop: ! while (%i1 != 0) { nop !
bz end_min ! add %l1, 4, %l1 ! %l1 <- %l1
nop ! ! + sizeof (int)
inc %l2 ! %l2 ++ deccc %l0 ! %l0 --
add %i0, 4, %i0 ! %i0 <- %i0 bnz read_loop ! } while (%l0 != 0)
! + sizeof(int) nop
ld [%i0], %l3 ! %l3 <- tab[%l2]
cmp %l1, %l3 ! if (%l1 > %l3) { ! calcul du minimum
ble min_ok ! sub %fp, 40, %o0 ! %o0 <- %fp-40 (adresse du
nop ! ! tableau)
mov %l2, %l0 ! %l0 <- %l2 mov 10, %o1 ! %o1 <- 10 (taille du
mov %l3, %l1 ! %l1 <- %l3 ! tableau)
min_ok: ! } call min ! %o0 <- min (%o0, %o1)
deccc %i1 ! %i1 -- nop
b min_loop ! }
nop ! affichage du résultat
mov %o0, %o1 ! %o1 <- %o0 (indice du min.)
end_min: sll %o0, 2, %o0 ! %o0 <- %o0 * 4
mov %l0, %i0 ! return %l0 ! (4 == sizeof (int))
ret sub %o0, 40, %o0 ! %o0 <- %o0 - 40
restore ld [%fp+%o0], %o2 ! %o2 <- [%fp+%o0] (tab[%o1],
! valeur du min.)
set .PRINTF2, %o0
! Programme principal call printf ! printf (.PRINTF2,
! -> lit 10 entiers et trouve le minimum ! indice_du_min,
!------------------------------------------------------------ ! valeur_du_min)
nop
.section ".data" ! -> données
.align 8 clr %i0 ! return 0
.PRINTF1: ret
.asciz "Entrez 10 entiers:\n" restore
.SCANF:

Vous aimerez peut-être aussi