jeudi 7 novembre 2019

Chapitre 69 : Assembleur Arm 64 : macro affichage de plusieurs registres et affichage mémoire


Après réflexion et un premier test, je décide d’afficher le contenu de 6 registres sur 2 lignes et d’indiquer dans la macro le N° du premier registre à afficher. Ainsi en indiquant affregtit Libellé 0, la macro affichera le texte Libellé puis le contenu des registres x0 à x5. Si nous voulons d’autres registres, nous indiquerons affregtit toto1 10 et la macro affichera les contenus des registres x10 à x15. Il est inutile de prévoir une macro pour les registres w0 à w30 puisqu’ils sont inclus dans les registres 64 bits.
Un petit problème se pose : il faut afficher les registres x0 et x1 et il faut passer à la routine d’affichage l’adresse du libellé et le N° du premier registre à afficher : Je décide de les passer dans x0 et x1 et de passer les valeurs de xo x1 précédentes sur la pile (à la réflexion c’était peut être mieux de faire le contraire).
Tout d’abord, j’écris dans le fichier routinesARM64.s, les routines pour afficher le libellé, le nom des registres et leur contenu : affRegistres16, prepRegistre16 et prepNumRegistre. Pour calculer la position d’affichage il faut utiliser les instructions de multiplication et de division. Elles sont identiques à celles en 32 bits et donc ne posent aucun problème. Pour convertir le N° de registre en caractères ascii, je fais simple : une seule division par 10 et calcul du reste grâce à l’instruction msub x4,x3,x5,x0 qui soustrait de x0 le résultat de la multiplication de x3 * x5 et qui met le résultat dans x4.
Et tout d’un coup, je m’aperçois que je commence les libellés des adresses par iAdr i pour integer alors que maintenant elles sont décrites en .quad et donc doivent commencer par qAdr. Et reste une question : faut-il bien les déclarer avec le mot clé .quad comme c’était le cas en 32 bits ? : il faudra le vérifier.
Quand ces routines sont prêtes, j’écris la macro pour appeler la routine principale : il faut manipuler la pile pour passer correctement les valeurs (et sauvegarder aussi le registre d’état). Après quelques tests, la macro fonctionne bien.
Il reste 2 petits problèmes : la macro n’affiche pas le contenu de la pile et le contenu du registre 30 (lr) affiche le contenu qu’il a dans la routine d’affichage et pas la valeur qu’il a avant l’appel de la macro. Si on veut avoir les contenus de ces 2 registres, il faut les copier avant l’appel dans 2 registres inutilisés mais l’expérience du 32 bits m’a montré que les contenus de ces 2 registres sont rarement utiles !!
Tant que je suis sur l’écriture des macros, j’écris aussi celle qui affiche les drapeaux du registre d’état en utilisant les instructions mrs x5,nzcv et msr nzcv,x5. Vous noterez que le nom du registre n’est pas le même qu’en 32 bits.
Pour l’utilisation de ces nouvelles macros, je crée le programme testaffreg.s qui verifie le contenu des registres et pour vérifier aussi que l’adresse de la pile en fin de routine est égale à celle du début.
Je crée aussi un autre programme verifinst.s pour vérifier un certain nombre d’instructions en 64 bits : les mov, cbz cbnz tbz et tbnz. Je remarque que l’instruction teq n’existe plus en 64 bits.
Je teste aussi l’écriture de registres sur la pile en diminuant l’adresse de la pile à chaque instruction. Cela me parait plus satisfaisant car on peut ajouter des écritures de paires de registres plus facilement.

Je poursuis l’écriture des macros en recopiant et modifiant la macro de vidage des zones de la mémoire. Comme il faut aussi afficher en premier un libellé, je réorganise les autres macros pour appeler la même sous-routine d’affichage.
Je décide aussi de dupliquer la routine d’affichage d’une chaine pour permettre le passage de la longueur de la chaine dans le registre x1. En effet, je me suis aperçu que la longueur des chaines à afficher étaient très souvent connue et donc qu’il est inutile de passer par une boucle de calcul de cette longueur.
Je teste ces améliorations et l’affichage des zones mémoires dans le programme debutaffmem1.

AFFICHAGE D'UN REGISTRE EN DECIMAL SIGNE ET NON SIGNE


J’ai réussi à trouver une synthèse des instructions arm64  au format pdf :
https://courses.cs.washington.edu/courses/cse469/18wi/Materials/arm64.pdf
Pour terminer cette première partie, j’écris les 2 routines qui permettent de convertir la valeur d’un registre en un nombre décimal signé et non signé. Le résultat sera une chaine de caractères ascii cadrée à gauche et se terminera donc par un zéro final. Cette solution est un peu différente de celle que j’avais proposée pour le 32 bits. Suivant le besoin, c’est donc le programme appelant qui utilisera la chaine comme il conviendra.
La chaine contiendra donc par exemple « 12345 » en non signé et « +12345 » ou « -12345 » en signé.
Remarque : c’est le programme appelant qui doit fournir la place de la zone réceptrice soit 21 caractères. Quand j’aurais testé les allocations de place sur le tas, c’est la routine qui allouera la place nécessaire (ce qui me semple mieux). La routine retourne dans le registre x0, la longueur de la chaine (hors zéro final)
La conversion s’effectue en utilisant les instructions udiv et msub dans une boucle.
Le programme de test fait appel à ces routines et affiche les résultats en appelant la nouvelle version de l’affichage de chaine donc en passant la longueur.
Quelques remarques : contrairement à ce que je pensais au début, les instructions de l’assembleur 64 bits ne font pas 64 bits de long mais seulement 32. La manipulation des zones en mémoire peut se faire en 8bits,16 bits 32 bits et 64 bits et il est conseillé que ces zones soient alignées sur leur frontière(si ce n’est pas le cas, il n’y a pas d’erreur mais une augmentation du nombre de cycles internes). La pile doit être toujours alignée sur une frontière de double mot (donc se terminer par 4 bits à zéro).
 
Tous les sources se trouvent ici : https://github.com/vincentARM/ARMassembly64 
 dans le projet64_001.

Aucun commentaire:

Enregistrer un commentaire