mercredi 17 avril 2019

Les registres : le premier registre


Un registre :
Au tout début, l’Ingénieur créa le registre. Il s’agit d’un minuscule composant électrique composé de 8,ou 16 ou 32 ou 64 contacts. Si un contact est ouvert, le courant électrique ne passe pas et cela est représenté par un 0. Si le contact est fermé, le courant passe et cela est représenté par un 1. Chaque position est appelé bit, 8 bits forment un octet(ou byte), et la taille d’un registre représente un mot (word). Ici, comme nous avons un processeur  32bits, le registre et donc un mot ont une taille de 32 bits.Il est aussi possible de nommer un demi-mot (half-word) d’une taille de 16 bits.
L’Ingénieur a appelé ce premier registre r0 mais il aurait pu l’appeler A ou un,  car pour le processeur il s’agit du registre 0.
Que pouvons-nous faire avec ce registre ? Nous pouvons manipuler ses bits avec les instructions disponibles et en premier mettre tous les bits à zero avec l’instruction :
            mov r0,#0
mov représente le code opération de l’instruction et est un raccourci de move (déplacer en anglais). r0 est le nom du registre et #0 représente la valeur immédiate zéro. Donc cette instruction signifie mettre zéro dans le registre destinataire r0. Et oui, dans l’instruction il faut mettre le registre destinataire avant la valeur : cela date des débuts de l’informatique et cela perdure aujourd’hui dans beaucoup de langage d’assemblage. Si vous faites l’inverse, le compilateur signalera une erreur.
Pour vérifier cette instruction, nous allons utiliser le petit programme indiqué dans le chapitre précédent et qui va afficher les 32 bits du registre r0 sous la forme de 0 ou de 1. Comme le programme précèdent, je vous demande de saisir ou de copier ce programme puis de le compiler sans encore avoir les moyens de comprendre tout son fonctionnement. Ce qui est important c’est de voir l’instruction ligne 18  mov r0,#0  suivi de l’instruction bl affichageReg2 qui effectuera l’affichage du contenu.
Par la suite, vous n’aurez qu’à modifier et/ou ajouter de nouvelles instructions à la suite de ces 2 premières.
Voici le résultat :
Vous remarquerez que ce programme affiche un message de début et de fin de programme, ce qui est une bonne façon de s’assurer que les programmes fonctionnent correctement. Vous voyez aussi que l’affichage du contenu du registre s’effectue en découpant les 32 bits en octets ce qui facilite la lecture. Et tous les bits sont bien à zéro.
Mais votre programme n’affiche rien ou alors un message d’erreur comme « erreur de segmentation ». Avez-vous effectué la démarche de mise en place des outils vue au paragraphe précédent ? Effectué le premier test ? Si oui, relisez le programme et vérifier une à une les instructions et sinon, me contacter pour trouver l’erreur.
Maintenant, mettons le premier bit à 1 en utilisant l’instruction :
            mov r0,#1
Recompilons le programme et lançons l’exécution. Voici le résultat :

Le bit le plus à droite est bien à un. Ce bit occupe la position 0 du registre et ces bits à droite sont souvent nommés les bits de poids faible. Ceux à gauche sont les bits de poids fort. Car les bits représentent des nombres en notation binaire (voir  https://fr.wikipedia.org/wiki/Syst%C3%A8me_binaire ) et chaque bit exprime une puissance de 2. Donc de 2 puissance 0  à 2 puissance 31 . Pour mettre le bit de la position 1 à 1, il faut mettre la valeur binaire 10 dans le registre avec l’instruction :
            mov r0,#0b10
La partie 0b indique au compilateur que la valeur qui suit est une valeur binaire. Si l’instruction avait été  mov r0,#10, c’est la valeur décimale 10 qui aurait été stockée dans le registre et 10 en binaire est représenté par le nombre 1010  (2 puissance 1+ 2 puissance 3 = 2 + 8 = 10).
Nous pouvons le vérifier :

Début programme.
Valeur du registre : 00000000 00000000 00000000 00001010
Fin programme OK.

Attention, avec le Raspberry pi 1, il n’est pas possible de stocker toutes les valeurs binaires directement dans le registre. Nous verrons pourquoi dans la partie instructions. Il est possible de stocker toutes les valeurs de 0 à 255 (décimal) et les multiples de 4, 8 ,16 et 32 de ces valeurs. Le compilateur vous signalera une erreur si une valeur n’est pas possible directement.
Pour le raspberry pi 3 il est possible de stocker les valeurs de la plage 0-4095 avec l'instruction mov.

Pour faciliter la lecture des nombres en binaire, il est aussi possible de les manipuler en hexadécimal (base 16) : voir https://fr.wikipedia.org/wiki/Syst%C3%A8me_hexad%C3%A9cimal En base 16, la valeur 10 précédente devient A et l’instruction devient mov r0,#0xA. Voui ! il doit y avoir une raison historique pour mettre un x et pas un h. Maintenant c’est plus simple de manipuler les valeurs. Par exemple 255 en décimal est représenté en binaire par 1111 1111 et en hexa  par FF. Vous remarquerez qu’il est facile passer du binaire à l’hexa car il suffit de découper le nombre par 4 bits et de mettre leur valeur en hexa, 1111 devenant F.

Opérations logiques :
Après avoir mis une valeur dans le registre r0, nous pouvons manipuler les bits en utilisant les opérations logiques ET, OR, XOR.
Voyons les instructions assembleur et leur comportement :
Mov r0,#0b1100
And r0,#0b1010
La première instruction met les bits 3 et 2 à 1 et les bits 1 et 0 à 0
La deuxième effectue un ET logique bit à bit soit pour le 3 ième bit 1 ET 1 pour le 2ième 1 ET 0 pour le 1er 0 ET 1 et pour le bit 0, 0 ET 0
Et le résultat est le suivant :

Maintenant remplaçons l’instruction and par eor pour effectuer un OU LOGIQUE puis par xor pour un OU Exclusif. L’assembleur autorise aussi l’instruction BIC  qui effectue une combinaison du ET et du NON, et l’instruction mvn qui effectue une combinaison de la valeur 0xFFFFFFFF et du OU EXCLUSIF.

Voici les résultats de ces 5 instructions :
Vous allez me demander à quoi cela peut bien servir !!!  Et bien cela permet de stocker des valeurs logiques dans le registre et de pouvoir les extraire afin de les utiliser. Nous verrons plus tard que de nombreux périphériques utilisent souvent les bits d’un registre pour indiquer leur état, et qu’ils sont prêts à recevoir ou envoyer des données.

Comparaisons :
Il serait intéressant de pouvoir aussi comparer la valeur du registre avec une valeur donnée ou avec zéro. Pour cela, l’ingénieur a créé un registre spécial dont certains bits vont évoluer avec le résultat des comparaisons : c’est le registre d’état. Dans ce registre un bit (ou drapeau, ou fanion ou flag en anglais) permet de savoir si un résultat est une égalité ou s’il égal à zéro,  c’est le bit z (comme zéro) en position 30. Et pour conditionner une instruction à partir de ce drapeau, il suffit de rajouter eq (de equal en anglais)au code instruction si nous voulons savoir s’il y a égalité ou zéro ou ajouter ne ( de not equal en anglais) pour l’inégalité ou diffèrent de zéro. Voyons sur des exemples
Mettons zéro dans le registre r0 puis effectuons une comparaison avec l’instruction cmp et s’il y a égalité mettons 1 dans r0 et si inégalité mettons 2.
Mov r0,#0
Cmp r0,#0
Moveq r0,#1
Movne r0,#2
Et voici le résultat :

Remplaçons le mov r0,#0 par mov r0,#50 et voici le résultat :
Nous avons bien la valeur 10 soit 2 en binaire.

Mais l’Ingénieur ne s’est pas arrêté là !! il a encore simplifié en permettant d’ajouter à certaines instructions un s final qui va mettre à jour le registre d’état sans avoir à effectuer une comparaison. Donc le programme se réduit à ceci :
Movs r0,#0
Moveq r0,#1
Movne r0,#2
Et les résultats sont identiques !!
Puis pour permettre de tester un bit particulier ou un groupe de bits, l’Ingénieur a ajouté 2 instructions de comparaison : la première TST effectue une comparaison du registre après avoir fait un ET logique avec la valeur et la deuxième TSQ effectue la comparaison après un OU EXCLUSIF logique.
Voyons tout cela :
Mettons la valeur binaire 1100 dans le registre r0 puis modifions uniquement le bit 1 pour le mettre à 1 sans modifier tous les autres. Il nous faut faire un OU logique avec la valeur 0b10 puis nous testons la valeur de ce bit avec l’instruction tst r0,#0b10. Ce qui donne :
            mov r0,#0b1100  @ bit à 1 pour les positions 2 et 3
            eor r0,#0b10     @ maj du bit 1 sans toucher les autres
            bl affichageReg2  @ affichage registre pour vérification
            tst r0,#0b10        @ test du bit 1
            moveq r0,#1         @ si égal à 0, on met 1 dans r0
            movne r0,#2         @ sinon on met 2
            bl affichageReg2  @ et affichage pour vérification
Et le résultat :
Maintenant, nous recommençons le début de l’opération pour mettre les bits 1 2 et 3 à 1 puis nous continuons en mettant le bit 1 à 0 avec l’instruction bic r0,#b10 soit :
mov r0,#0b1100  @ bit à 1 pour les positions 2 et 3
            eor r0,#0b10     @ maj du bit 1 sans toucher les autres
            bl affichageReg2  @ affichage registre pour vérification
            bic r0,#0b10     @ pour remettre à zero le bit 1
            bl affichageReg2  @ affichage registre pour vérification
            tst r0,#0b10        @ test du bit 1
            moveq r0,#1         @ si egal à 0, on met 1 dans r0
            movne r0,#2         @ sinon on met 2
            bl affichageReg2  @ et affichage pour vérification
Et le résultat :

Déplacement de bits :
Les instructions assembleur lsl, lsr et ror permettent de déplacer les bits dans le registre : lsl déplace les bits sur la gauche (comme Left en anglais) lsr les déplace sur la droite et ror effectue une rotation. Il n’y a pas de rotation à gauche car  32 – n rotations à droite correspondent à n rotations à gauche
Exemple :
            mov r0,#0b11100  @ bit à 1 pour les positions 2,3 et 4
            lsl r0,#2     @ déplacement à gauche de 2 positions
            bl affichageReg2  @ affichage registre pour vérification
            lsr r0,#5    @ déplacement à droite de 5 positions
            bl affichageReg2  @ affichage registre pour vérification
            ror r0,#8    @ rotation à droite de 8 positions
            bl affichageReg2  @ affichage registre pour vérification
            ror r0,#32 - 1     @ rotation à droite de 31 positions
                                                           @ ce qui correspond à une rotation à gauche de une position
            bl affichageReg2  @ affichage registre pour vérification

Ce qui donne comme résultat :
Vous remarquerez qu’il est possible d’effectuer un calcul pour une valeur immédiate #32 – 1 mais attention, ce n’est pas le processeur qui effectue ce calcul mais le compilateur !! Ensuite lors de l’exécution de l’instruction lsr r0,#5, le bit le plus à droite est perdu, sur les 3 de départ il n’en reste plus que 2. Mais heureusement l’Ingénieur a prévu le cas et si c’est nécessaire nous pouvons connaitre la valeur du bit perdu. Il nous suffit d’ajouter le code s à la fin de l’instruction pour mettre à jour un bit du registre d’état avec la valeur de ce bit perdu. Ce bit d’état s’appelle le drapeau de retenue ( flag carry  en anglais  ou C). Ce bit d’état peut être testé avec les codes cc (Carry Clear) pour savoir s’il est égal à 0 ou cs (Carry Set) s’il est égal à 1 :
Exemple :
            @recup du carry
            mov r0,#0b101    @ mettre bits 0 et 2 à 1
            lsrs r0,#1       @ decalage de 1 à droit et maj du carry
            bl affichageReg2  @ affichage registre pour vérification
            movcc r0,#1        @ on met 1 si le carry est a zero
            movcs r0,#2        @ on met 2 si le carry est à un
            bl affichageReg2  @ affichage registre pour vérification
Résultat :
Puis ajoutons ceci :
            lsrs r0,#1       @ decalage de 1 à droit et maj du carry
            bl affichageReg2  @ affichage registre pour vérification
            movcc r0,#1        @ on met 1 si le carry est a zero
            movcs r0,#2        @ on met 2 si le carry est à un
            bl affichageReg2  @ affichage registre pour vérification
Pour déplacer d’une position à droite le résultat (qui doit être 10 en binaire) et donc mettre le premier bit (soit 0) dans le carry, tester le carry et vérifier le résultat. Bravo c’est un !!!


Nous pouvons aussi inverser l’ordre des bits par l’instruction rbit
Mov r0,#12
Rbit r0,r0
Nous avons aussi les instructions rev,rev16 et revsh qui inversent les octets, les demi mots. Vous pouvez tester ces instructions sur différentes valeurs.
Enfin nous pouvons compter le nombre de zéros contenus dans le registre en partant de la gauche avec l’instruction clz.

Nous avons bien 26 zéros à gauche dans le registre.



Aucun commentaire:

Enregistrer un commentaire