mardi 24 octobre 2017

Chapitre 4 : saisie dans la console



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
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