Les Sous-programmes MIPS
1 Introduction
Les programmes mis en œuvre dans les chapitres précédents sont très longs et nécessitent beaucoup de code. Par exemple, chaque programme doit utiliser le service syscall 10 pour quitter un programme. Il serait bien d’abstraire la méthode de sortie du programme une fois, puis d’utiliser cette abstraction dans chaque programme. Ceci est similaire aux sous-programmes qui existent dans tous les langages de programmation et nommés comme : fonctions, procédures ou méthodes.
Le concept de sous-programme permet de créer un groupe d'instructions en langage assembleur MIPS pouvant être appelées pour effectuer une tâche.
2 Sous-programme de Sortie (Quitter)
Le premier sous-programme à introduire s'appelle Quitter et appelle le service syscall 10 pour quitter le programme. Comme il ne sera pas nécessaire de revenir au programme appelant, ce sous-programme peut être créé en créant une étiquette Quitter et en insérant du code qui configure l'appel à syscallet l'exécute.
# Fichier : Programme- 5-2
###########################
. .text
main:
# Ecrire ("Bonjour les Sous-programmes …")
li $v0, 4
la $a0, Msg
syscall
# Appel du Sous-programme (Quitter) pour sortir
jal Quitter
.data
Msg: .asciiz "\n Bonjour les Sous-programmes …"
# Sous-programme: Quitter
# Entrées: Aucune
# Sorties : Aucune
.text
Quitter:
li $v0, 10
syscall
Ce programme a deux sections .text. En effet, le sous-programme Quitterne fait pas partie du programme principal et existe donc dans une autre partie du fichier.
3 Les Sous-programmes : EcrireMsg, EcrireNum, LingneVide et LireNum
Le sous-programme EcrireMsg va afficher un message (une chaine de caractères) à l’écran. Ce sous-programme nécessitera qu'un paramètre d'entrée soit transmis au programme, l'adresse de la chaîne à imprimer. Par conventions les registres $a0 .. $a4 sont utilisés pour transmettre des paramètres d'entrée à un programme. Le registre $a0 sera utilisé pour le paramètre d'entrée de ce sous-programme. Le sous-programme chargera alors un 4 dans $v0, invoquera syscall et retournera au programme principal.
Le sous-programme EcrireNum va imprimer d'abord un message (une chaine de caractères) pour indiquer à l'utilisateur ce qui est en train d'être écrit, puis l'entier est affiché. Dans ce sous-programme, il y a deux paramètres d'entrée: l'adresse de la chaîne à imprimer, qui est stockée dans $a0; et la valeur entière à imprimer, qui est stockée dans $a1 par conventions.
Le sous-programme LingneVide va imprimer le caractère de retour à ligne (« \n ») sur la console de l'utilisateur.
Le sous-programme suivant LireNum pour la lecture d’une valeur entière, montre comment renvoyer une valeur à partir d'un sous-programme. Par conventions les registres $a0 .. $a3 sont utilisés pour transmettre des valeurs à un sous-programme et les registres $v0 .. $v1 sont utilisés pour retourner des valeurs.
# Fichier : Programme-5-3.asm
#############################
.text
main:
# Lire une valeur entière
la $a0, MsgLire
jal LireNum
move $s0, $v0
# Ecrire la valeur entière lue
la $a0, MsgRes
move $a1, $s0
jal EcrireNum
# Retour à la ligne
jal LingneVide
# Ecrire la chaine de caractère MsgFin
la $a0, MsgFin
jal EcrireMsg
# Appel du Sous-programme (Quitter) pour sortir
jal Quitter
.data
MsgLire: .asciiz "Entrer un entier ? "
MsgRes: .asciiz "Vous avez entré: "
MsgFin : .asciiz "Fin du programme. "
# Sous-programme: EcrireNum
# Entrées: $a0 - Adresse de la chaine à écrire.
# $a1 – Valeur de l’entier à ecrire.
# Sorties : Aucune
.text
EcrireNum:
# Imprime la chaine. L’adresse de la chaine est dans $a0
li $v0, 4
syscall
# Imprime l’entier. La valeur entière est dans $a1, on doit
# d’abord la mettre dans $a0.
move $a0, $a1
li $v0, 1
syscall
# Retour au programme principal
jr $ra
# Sous-programme: LireNum
# Entrées: $a0 - L’adresse de la chaine à écrire.
# Sorties : $v0 – La valeur entière saisie.
.text
LireNum:
# Imprime la chaine (MsgLire), qui est dans $a0
li $v0, 4
syscall
# lit la valeur entière dans $v0.
li $v0, 5
syscall
# Retour au programme principal
jr $ra
# Sous-programme: EcrireMsg
# Entrées: $a0 - Adresse de la chaine à écrire.
# Sorties : Aucune
.text
EcrireMsg:
addi $v0, $zero, 4
syscall
jr $ra
# Sous-programme: LingneVide
# Entrées: Aucune
# Sorties : Aucune
.text
LingneVide:
li $v0, 4
la $a0, __RL
syscall
jr $ra
.data
__RL: .asciiz "\n"
# Sous-programme: Quitter
# Entrées: Aucune
# Sorties : Aucune
.text
Quitter:
li $v0, 10
syscall
4 Créer un fichier « outils.asm »
La dernière étape de l'ajout des sous-programmes développés dans ce chapitre consiste à les mettre dans un fichier séparé afin que tout programme que vous écrivez puisse les utiliser. Pour ce faire, créez un fichier appelé « outils.asm » et copiez tous les sous-programmes dans ce fichier.
Le fichier entier « outils.asm » est décrit ci-dessous pour montrer à quoi il devrait ressembler une fois terminé.
# Fichier: outils.asm
# Objectif: Définir les utilitaires qui seront utilisés dans les programmes MIPS.
#
# Liste des sous-programmes:
# EcrireNum - Imprime une chaîne avec un entier sur la console
# LireNum - Invite l'utilisateur à entrer une nombre entier
# EcrireMsg - Imprime une chaîne sur la console
# LingneVide - Imprime un nouvelle ligne vide sur la console
# Quitter - Appelez syscall avec le service 10 pour quitter le programme
#
# Historique des modifications
# 15/12/2018 - Version initiale
# Sous-programme: EcrireNum
# Entrées: $a0 - Adresse de la chaine à écrire.
# $a1 – Valeur de l’entier à ecrire.
# Sorties : Aucune
.text
EcrireNum:
# Imprime la chaine. L’adresse de la chaine est dans $a0
li $v0, 4
syscall
# Imprime l’entier. La valeur entière est dans $a1, on doit
# d’abord la mettre dans $a0.
move $a0, $a1
li $v0, 1
syscall
# Retour au programme principal
jr $ra
# Sous-programme: LireNum
# Entrées: $a0 - L’adresse de la chaine à écrire.
# Sorties : $v0 – La valeur entière saisie.
.text
LireNum:
# Imprime la chaine (MsgLire), qui est dans $a0
li $v0, 4
syscall
# lit la valeur entière dans $v0.
li $v0, 5
syscall
# Retour au programme principal
jr $ra
# Sous-programme: EcrireMsg
# Entrées: $a0 - Adresse de la chaine à écrire.
# Sorties : Aucune
.text
EcrireMsg:
addi $v0, $zero, 4
syscall
jr $ra
# Sous-programme: LingneVide
# Entrées: Aucune
# Sorties : Aucune
.text
LingneVide:
li $v0, 4
la $a0, __RL
syscall
jr $ra
.data
__RL: .asciiz "\n"
# Sous-programme: Quitter
# Entrées: Aucune
# Sorties : Aucune
.text
Quitter:
li $v0, 10
syscall
Finalement, les sous-programmes seront placés dans un autre fichier et inclus si nécessaire par la directive: .include "fichier_des_sous_programmes.asm"
Le programme MIPS suivant est le résultat final de ce chapitre. Assurez-vous que le fichier contenant les sous-programmes « outils.asm » se trouve dans le même répertoire que le programme principal « Programme-5-3.asm ». Ouvrez « Programme-5-3.asm » dans la fenêtre d'édition de MARS, assemblez et exécutez ce dernier.
# Fichier : Programme-5-4.asm
# Objectif : Illustrer la mise en œuvre et l’appel de sous-programmes
###########################################################
.text
.globl main
main:
# Lire une valeur entière
la $a0, MsgLire
jal LireNum
move $s0, $v0
# Ecrire la valeur entière lue
la $a0, MsgRes
move $a1, $s0
jal EcrireNum
# Retour à la ligne
jal LingneVide
# Ecrire la chaine de caractère MsgFin
la $a0, MsgFin
jal EcrireMsg
# Appel du Sous-programme (Quitter) pour sortir
jal Quitter
.data
MsgLire: .asciiz "Entrer un entier ? "
MsgRes: .asciiz "Vous avez entré: "
MsgFin : .asciiz "Fin du programme. "
.include "outils.asm"
5 Appels de sous-programmes imbriqués (Utilisation d’une Pile)
Une pile (LIFO : Last In First Out) est une mémoire qui se manipule via deux opérations :
- PUSH : empiler un élément (le contenu d’un registre) au sommet de la pile.
- POP : dépiler un élément (et le récupérer dans un registre).
Ces deux instructions n’existent pas en assembleur MIPS, mais elles peuvent être “simulées”. En utilisant les instructions sw et lw. En stockant l’adresse du sommet de pile dans le registre $sp (le pointeur de pile).
Deux politiques de sauvegarde des registres :
- Sauvegarde par l’appelant : le sous-programme appelant sauvegarde tous les registres sur la pile avant l’appel (pour le passage de paramètre et la sauvegarde de l'adresse de retour).
- Sauvegarde par l’appelé : le sous-programme appelant suppose que tous les registres seront préservés par le sous-programme appelé.
Remarque: un sous-programme doit rendre la pile intacte.
Exemple : Le Sous-programme B qui appel le sous-programme C
. . .
B: . . . # début Sous-programme B
. . .
# Empiler les paramètres dans la pile
sw $s0, 0 ($sp) # Sauvegarder $s0 dans la pile
sw $s1, -4 ($sp) # Sauvegarder $s1 dans la pile
sw $ra , -8 ($sp) # Sauvegarder $ra (adresse de retour) dans la pile
add $sp, $sp , -12 # Ajuster le sommet de la pile
# appel du Sous-programme C
jal C
# Dépiler les paramètres de la pile
lw $ra , 4 ($sp) # restaurer $ra (adresse de retour au programme principal) de la pile
lw $s1, 8 ($sp) # restaurer $s1 de la pile
lw $s0, 12($sp) # restaurer $s0 de la pile
add $sp, $sp , 12 # Ajuster le sommet de la pile
. . .
. . .
jr $ra # fin du Sous-programme B
. . .
C: . . . # début Sous-programme C
. . .
Subscribe by Email
Follow Updates Articles from This Blog via Email
No Comments