mercredi 8 janvier 2020

Chapitre 77 : Assembleur ARM 64 bits : calculs en virgule flottante (instructions Float)


Je commence l’année par découvrir les instructions de calcul en virgule flottante. En 64 bits, ces instructions sont très différentes de l’assembleur 32 bits et à première vue sont plus simples d’utilisation. Pour effectuer des calculs en double précision, nous disposons de 32 registres de 64 bits dont le nom commence par d.

Comme nous n’avons pas de routines assembleur pour afficher des nombres en virgule flottante, il nous faut utiliser l’instruction printf du C en utilisant le formatage « %+09.25f\n ». Comme il y a peu d’exemple sur internet de programmation 64 bits arm en float, il nous faut découvrir comment passer les arguments à la fonction.
Après quelques essais, je trouve que la chaine de formatage doit être passée dans le registre x0 et les valeurs à afficher dans les registres d0 d1 etc. En effet en virgule flottante les registres à utiliser en double précisions sont des registres 64 bits qui commencent par d et nous pouvons utiliser xx registres. Attention : l’appel de la fonction printf détruit le contenu des registres d0 d1 et peut être d’autres (à vérifier). L’utilisation des instructions du langage C necessite aussi l’utilisation de gcc pour effectuer le link. 
Source du programme calculFloat1.s
Donc je commence à alimenter le registre d0 avec une valeur immédiate avec l’instruction fmov d0,2 et tout se passe bien avec plusieurs valeurs lorsqu’une erreur de compilation arrive pour la valeur 50 !!
La recherche dans les 5000 pages de la documentation ARM montre que les valeurs immediates possibles doivent pouvoir être codées sur 8 bits dont un exposant de 3 bits et une mantisse de 4 bits. La figure C2-2 de la page C2-136 donne les valeurs possibles : par exemple 1 2 4 8 16 mais aussi 0,25 ou 0,1796875 mais pas 50.
Bien ! maintenant passons une valeur dans le registre x0 puis dans le registre d0 et comme en 32 bit, il faut effectuer une opération de conversion en float avec l’instruction scvtf d0,d0.
Et enfin nous chargeons une valeur double stockée en mémoire par la simple instruction ldr.
Après affichage, nous la rechargeons dans le registre d0 puis la convertissons en entier non signé avec l’instruction fcvtzu et la transferrons dans le registre x0 en passant par le registre d1 pour varier un peu. L’affichage du registre x0 montre bien la valeur 3. Pour les valeurs signées il faut utiliser fcvtzs.
Nous effectuons quelques opérations de calcul, addition, multiplication, division, minimum, racine carrée et quelques comparaisons qui sont plus simples qu’en 32 bits.
Enfin nous vérifions le stockage d’une valeur en mémoire. Il existe encore plein d’instructions pour manipuler ces nombres (et pour des conversions en simple précision).
J’effectue aussi quelques tests pour voir les conversions 32 bits 64 bits.
Comme exemple je vais reprendre le programme écrit en 32 bits pour analyser la génération de nombres aléatoires. Voir le programme verifAlea.s. Celui çi ne pose pas de problème particulier sauf que la valeur immédiate zéro ne peut pas être mis dans un registre dn avec l’instruction fmov. Mais en utilisant le registre particulier xzr, cela fonctionne.
Vous remarquerez que j’utilise le registre d8 pour les calculs pour éviter la modification d’un registre comme d4 par le premier appel de printf.

Ensuite le programme saisieFloat1.s donne une routine de conversion d’une chaine de caractères (abc,def ou abc.def) en nombre float. La chaine est passés d’abord dans la ligne de commande puis est ensuite saisie en utilisant l’appel système read.
Je voulais utiliser l’instruction de conversion     scvtf d0,x0, n bits   mais le position de la virgule par n bits ne peut pas être la valeur d’un registre. Donc j’ai utilisé une autre technique, je recopie la chaine de caractère en supprimant la virgule ou le point et en conservant leur position. Ensuite je convertit en décimal la chaine résultante et convertit en float le résultat.
Avec la position de la virgule, je calcule la puissance de 10 necessaire pour effectuer la division en float pour trouver le résultat correct. Exemple :
Soit la saisie de 12345,67 qui donne la chaine 1234567 convertit en float pour donner le dividende.
La virgule est en position 5 à partir de la gauche et 2 à partir de la droite . Il faut calculer la puissance 2 de 10 soit 100 qui convertit en float donnera le diviseur.
Il ne reste plus qu’à effectuer la division en float de 1234567 par 100 pour avoir un nombre float résultant correct dans le registre d0.

Aucun commentaire:

Enregistrer un commentaire