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