Une macro permet d’inclure dans un programme assembleur, une
série d’instructions qui seront compilées en même temps que le programme (donc
différent d’un objet qui est inclus au moment du link).
Les macros évitent de retaper plusieurs fois les mêmes
instructions et il est possible de leur passer des paramètres.
Par exemple dans les premiers chapitres nous avons écrit une
routine pour afficher un message dans la console, routine que nous avons inclus
dans un autre programme contenant toutes les routines. Mais nous aurions pu
écrire cette fonction sous forme de macro :
mov r1,r0 @ save adresse.macro afficheMessage @ définition de la macro@ r0 contient l’adresse du message et il faut d’abord calculer sa longueu
mov r2,#0 @ compteur de caractère1\@: @ boucle de comptage de caractèresldrb r3,[r1,r2] @ lecture un octet du messagecmp r3,#0 @ c'est la fin ?addne r2,#1 @ non ajout de 1 au compteurbne 1\@b @ et boucle@ appel systeme fonction affichagemov r0, #1 @ r0 ← 1mov r7, #4 @ write code 4swi #0.endm @ fin de la macro
L’appel de la macro dans le programme s’effectue simplement
par le nom : afficheMessage
Les instructions utilisées sont les mêmes que l’assembleur
sauf le label 1\@ qui est particulier. En effet une macro peut être insérée
plusieurs fois et si nous avions mis un label comme 1 : ou Boucle1 :
, la compilation aurait produit une erreur car elle aurait trouvé plusieurs
labels identiques. Vous remarquerez que nous utilisons des registres sans les
sauvegarder donc il faudra faire attention et les sauvegarder si nécessaire
Nous pouvons passer un ou plusieurs paramètres à une macro :
dans la macro comparVal nous passons 2 adresses de message et une valeur à tester
dont la valeur par défaut sera 0 (en respectant la syntaxe #0). L’utilisation
de ces données dans le corps de la macro s’effectue en mettant un \ devant le
nom. Nous pouvons appeler une autre macro dans la macro, ici en fonction du
résultat de la comparaison le message affiché sera différent.
L’appel de la macro s’effectue simplement par comparVal r1,
r2, #4 , Les registres r1 et r2 contiendront les 2 adresses nécessaires et la
valeur de test sera passée comme une valeur immédiate.
Remarque : les macros ne sont pas à utiliser pour des
actions trop compliquées !! Mais si vous aimez mes macros, vous pouvez
vous constituer une collection et les grouper dans un fichier externe que vous appellerez
avec l’instruction .include « nomdufichier ».
Voici un autre exemple : pour l’affichage des registres
que j’utilisais depuis le début, j’affichais comme libellé l’adresse de l’instruction
appelante car il n’est pas possible avec cet assembleur de récupérer le N° de
ligne du source ce qui aurait été bien pratique. Avec une macro je peux donc récupérer
un libellé comme paramètre, le stocker sur la pile pour l’afficher sans
modifier les valeurs des registres. Bon il faut un peu bidouiller avec un
registre puisque le push exige l’utilisation d’un registre. Voici le corps de
la macro :
/* macro d'enrobage du vidage des registres avec étiquette */
.macro vidregtit str
push {r12} @ save r12
mrs
r12,cpsr /* copie du registre
d'état dans r12 */
push
{r12} @ save du registre d’état sur la
pile
ldr
r12,=lib1\@ @ recup libellé passé dans
str
push
{r12} @ passage argument sur la pile
ldr
r12,[sp,#8] @ on remet en état r12 pour
l'afficher correctement
bl
affregistres @ affichage des registres (cette routine restaure l’état de la pile)
pop
{r12} @ on récupére le save du registre d’état
msr
cpsr,r12 /*restaur registre d'état */
pop
{r12} @ on restaure R12 pour
avoir une pile réalignée
b
smacro1vidregtit\@ @ pour sauter le
stockage de la chaine.
lib1\@: .asciz
"\str" @ stockage
dans le corps du programme du libellé passé en paramètre
.align 4 @ il faut aligner l’étiquette
suivante car le libellé précedent n’est pas un multiple de 4
smacro1vidregtit\@:
@ pour continuer le reste du programme
.endm @ fin de la
macro
J’en ai aussi profité pour sauvegarder le registre d’état
avant l’appel de la routine de vidage et le restauré à la fin pour que cette
routine reste neutre.
L’appel se fait simplement par vidregtit debut
ou vidregtit Après_appel_compar_3
Et le résultat est le suivant :
Vous remarquerez que le registre r2 contient 0xD alors que l’on
s’attendrait à trouver une adresse comme dans r1.
Et d’ailleurs r0 contient aussi 0xD !! Donc il faut remonter les appels pour s’apercevoir
que ComparVal utilise en dernier afficheMessage qui utilise r2 pour calculer la
longueur du dernier message donc ici 12
caractères (ne pas oublier de compter le \n final !!!). Et l’appel système
Write (code 4) retourne dans le registre r0 la longueur du message affiché
(revoir la doc des call system Linux). Donc c’est logique !!!
Remarque avril 2018: en utilisant cette macro sur un gros programme, le compilateur signale une erreur sur l'instruction ldr r12,=lib1\@ @ recup libellé passé dans str . En effet dans un gros programme l'instruction de chargement d'une adresse avec le signe égal ne fonctionne pas. J'ai eu du mal à comprendre pourquoi puisque l'adresse du libellé ne se trouve que quelques instructions plus loin dans la même section. En fait l'instruction ldr r12,=lib1 est une pseudo instruction qui crée en fin du code programme, une variable supplémentaire contenant l'adresse de lib1. Et cette variable pour un gros programme peut se trouver au delà des 4K mots fatidiques. Donc j'ai remplacé cette instruction par l'autre pseudo instruction : adr r12,lib1 (et sans le =) qui fonctionne dans tous les cas. Cette instruction signifie de charger dans r12 l'adresse du libellé lib1.
Remarque avril 2018: en utilisant cette macro sur un gros programme, le compilateur signale une erreur sur l'instruction ldr r12,=lib1\@ @ recup libellé passé dans str . En effet dans un gros programme l'instruction de chargement d'une adresse avec le signe égal ne fonctionne pas. J'ai eu du mal à comprendre pourquoi puisque l'adresse du libellé ne se trouve que quelques instructions plus loin dans la même section. En fait l'instruction ldr r12,=lib1 est une pseudo instruction qui crée en fin du code programme, une variable supplémentaire contenant l'adresse de lib1. Et cette variable pour un gros programme peut se trouver au delà des 4K mots fatidiques. Donc j'ai remplacé cette instruction par l'autre pseudo instruction : adr r12,lib1 (et sans le =) qui fonctionne dans tous les cas. Cette instruction signifie de charger dans r12 l'adresse du libellé lib1.
Aucun commentaire:
Enregistrer un commentaire