Bon fini avec les affichages, mais cela nous a permis de
créer 2 outils indispensables à la recherche des erreurs : le vidage de
tous les registres et le vidage mémoire. Toutes ces routines très utiles sont
mises dans un fichier externe et compiler avec as. (Remarque : en plus du
code il faut déclarer les zones de la section .data utilisées et mettre le nom
de la routine accessible à tous les programmes par la directive .global
nomroutine).
Ensuite il suffit de modifier l’étape du link pour insérer
le nom de l’objet crée comme ceci :
ld –o $1 $1 ».o » ~/asm/routinesARM.o –e main
Mon fichier s’appelle routinesARM.o et il est situé au
dessus des répertoires de mes sources ce qui permet de ne l’avoir qu’une fois.
Bon maintenant voyons comment saisir une chaine de caractère
au clavier. Dans la partie .data, nous préparons un message d’invite que nous
afficherons en début du programme. Nous préparons aussi deux messages pour
afficher les erreurs rencontrées. Un message affichera le contenu en hexa et en
décimal signé du registre r0. Pour varier les instructions, nous remplissons la
2ième zone en utilisant la directive .fill longueur, taille en
octet, caractère de remplissage.
Lien vers le source du programme
Lien vers le source du programme
Puis nous trouvons une nouvelle section .bss que j’ai
appelée Données non initialisées. En fait, cela veut dire données non
initialisées par le programmeur car les données de cette section vont être
remplies de zéros au démarrage du programme. Ici nous déclarons et un buffer de
100 octets par la directive .skip. La longueur est donnée par une constante
placée en début de programme ce qui facilite les évolutions futures.
Dans la partie code, nous commençons par sauvegarder les 2
registres fp et lr et a positionner le pointeur de contexte à l’adresse de la
pile + 8 octets cad à la valeur de la pile à l’entrée du programme exactement comme si notre programme principal
était une routine. Bien que nous n’ayons pas d’utilité ici, c’est une bonne
habitude de commence les programmes avec ces instructions.
Ensuite nous avons un appel à la routine vidtousregistres,
c’est ma routine de vidage de tous les registres et qui se trouve maintenant
dans mon fichier routinesARM.o et dont j’ai parlé plus haut. J’ai mis ce vidage
là pour voir quelles étaient les valeurs des registres au démarrage d’un
programme sous Linux.
Puis nous trouvons l’appel pour afficher le message d’invite
de saisie dans la console puis les instructions pour lire les données saisies.
Pour cela nous utilisons l’appel système READ (valeur 3) et nous regardons dans
la documentation pour connaitre son fonctionnement. J’ai trouvé le site http://syscalls.kernelgrok.com/ qui
donne tous les appels système LINUX. Hélas je n’ai pas trouvé la doc pour ceux
de Raspbian pour le Raspberry mais je suppose que les principaux seront les
mêmes. Et re hélas, les registres indiqués pour le passage des paramètres sont
ceux des processeurs intel 32 bits (comme eax). Mais c’est pas grave car il
suffit de mettre le code fonction dans le registre r7 et les autres paramètres
dans l’ordre r0, r1,r2 ,r3. Et évidement la nature des paramètres est
prévu pour le C et donc il faudra un peu de réflexion. Si nous cliquons sur le
libellé sys-read, nous trouvons tout le détail de cet appel : les
paramètres, la description, la valeur retournée et les erreurs possibles.
Avec cela nous pouvons programmer l’appel en assembleur en
mettant le code 3 dans le registre r7, le code de la console standard d’entrée
soit 0 dans r0, l’adresse du buffer qui va recevoir les données dans r1 et la
taille de ce buffer dans r2 (avec la même constante que la déclaration du
buffer). La documentation indique que
des erreurs peuvent être retournées dans r0 sous forme d’un nombre négatif. Il
est vivement souhaitable donc de tester le registre r0 et s’il est
négatif ; d’afficher un message d’erreur et d’arrêter le programme. C’est
que nous faisons ici, dans la routine afficheErreur qui prépare un message dont
l’adresse est reçu dans le registre r1 puis affiche la valeur du registre r0 en
hexa et en décimal.
Pour voir le contenu des registres après l’appel, nous
refaisons un appel à la routine vidtousregistres et à la routine de vidage
mémoire pour afficher le buffer et voir son contenu.
Lançons le programme puis après le message d’invite,
saisissons des caractères. Ceux-ci s’affichent dans la console et il nous faut
terminer la saisie en appuyant sur la touche <enter> . Notre programme
affiche les registres et le contenu du buffer comme ceci :
Nous voyons que le registre r0 contient la longueur du texte
saisi, que les autres registres r1 à R10 sont inchangés.
Et nous voyons que le buffer contient la chaine saisie
suivie par le caractère 0A (retour ligne). Nous remarquons que la longueur dans
r0 tient compte de ce caractère. Ces informations vont nous permettre de
traiter la chaine comme nous le voulons.
Accessoirement nous remarquons que devant notre buffer, il y
a d’autres caractères (des blancs suivis du retour ligne 0A) et je me demande
bien d’où ils viennent (question à creuser ?).
Maintenant, nous allons effectuer quelques tests :
d’abord générer une erreur volontaire en mettant 5 dans le registre r0 avant
l’appel.
Et la signification du code -9 est EBADF 9 /* Bad file number */ ce qui correspond bien à notre erreur.
Nous remarquons qu’en plus de la valeur 0 (STDIN) les
valeurs 1 (STDOUT) et 2 (STDERR) fonctionnent.
Maintenant limitons la longueur du buffer à 10 caractères et
effectuons une saisie de plus de 10 caractères :
Nous voyons que la saisie est tronquée à 10 caractères et se
termine par le caractère 00 binaire. Et nous constatons que les caractères
résiduels restent dans le buffer du clavier et sont envoyés à notre console en
fin de programme. C’est pas beau et donc nous devrons penser à mettre une
taille du buffer convenable pour chaque saisie.
Remarque : peut être vous demandez-vous pourquoi dans
mon vidage des registres, le registre lr est marqué inconnu ? Et bien, si
j’avais affiché le contenu du registre tel qu’il se trouve dans la sous routine
d’affichage ce n’est pas la valeur qu’il avait avant l’appel de cette procédure.
En effet il contient maintenant l’adresse de retour vers le programme
principal. Et pour éviter toute mauvaise interprétation, je préfère mettre
inconnu. Si j'ai besoin de vérifier la valeur de lr, il me suffit de le copier dans un autre registre inutilisé (par ex r11) avant d'appeler la routine de vidage.
Exercices :
modifier le programme pour enchainer 2 saisies : vérifier le
contenu du buffer avec des saisies de différentes longueurs.
Afficher la chaine par votre sous
routine d’affichage (à quoi faut-il penser ?).
Aucun commentaire:
Enregistrer un commentaire