Aller au contenu

Fonctionnement d'un ordinateur/Les circuits de masquage

Un livre de Wikilivres.

Dans ce chapitre, nous allons voir les opérations bit à bit, un ensemble d'opérations qui appliquent une opération binaire sur un ou deux nombres. Elles sont très utilisées en programmation, et tout ordinateur digne de ce nom contient un circuit capable d'effectuer ces opérations. Dans ce chapitre, nous allons voir divers circuits capables d'effectuer des opérations bit à bit, et voir comment les combiner.

La plus simple d'entre elles est l'opération NON, aussi appelée opération de complémentation. Elle inverse tous les bits d'un nombre. Il s'agit de l'opération la plus simple et nous en avions déjà parlé dans les chapitres précédents. Elle est accompagnée par des opérations bit à bit qui font un ET/OU/XOR entre deux nombres. Pour être plus précis, elles font un ET/OU/XOR entre les deux bits de même poids. L'exemple du OU bit à bit est illustré ci-dessous, les exemples du ET et du XOR sont similaires.

Opération OU bit à bit.

De telles opérations sont appelées bit à bit car elles combinent les bits de même poids de deux opérandes. Par contre, il n'y a pas de calculs entre bits de poids différents, l'opération est appliquée colonne par colonne. Les opérations bit à bit classiques peuvent prendre une ou deux opérandes. La plupart en prenant deux comme les opérations ET/OU/XOR, l'opération NON en prend une seule. Les opérations bit à bit sur deux opérandes sont au nombre de 16, ce qui correspond au nombre de portes logiques à deux entrées possibles. Mais ce chiffre de 16 inclut les opérations bit à bit sur un opérande unique, qui sont au nombre de 4. Les opérations bit à bit sur un seul opérande sont plus simples à voir, nous verrons les opérations bit à bit à deux opérandes plus tard.

Les opérations bit à bit à un opérande

[modifier | modifier le wikicode]

Les opérations bit à bit n'ayant qu'un seul opérande sont au nombre de quatre :

  • Mettre à zéro l'opérande (porte FALSE).
  • Mettre à 11111...11111 l'opérande (porte TRUE).
  • Inverser les bits de l'opérande (porte NON).
  • Recopier l'opérande (porte OUI).

Dans ce qui va suivre, nous allons créer plusieurs circuits : un circuit qui inverse le bit d'entrée à la demande, un autre qui le met à 1, un autre qui le met à 0. Ces trois circuits ont une entrée de commande qui détermine s'il faut faire l'opération, ou si le circuit doit se comporter comme une simple porte OUI qui ne fait donc aucune opération. Le circuit recopie le bit d'entrée si cette entrée est à 0, mais il inverse/set/reset le bit d'entrée si elle est à 1.

Pour comprendre comment concevoir ces circuits, il faut rappeler les relations suivantes, qui donnent le résultat d'un ET/OU/XOR entre un bit d'opérande noté a et un bit qui vaut 0 ou 1.

Opération Interprétation du résultat
Porte ET Mise à zéro du bit d'entrée
Recopie du bit d'entrée
Porte OU Mise à 1 du bit d'entrée
Recopie du bit d'entrée
Porte XOR Recopie du bit d'entrée
Inversion du bit d'entrée

Pour résumer ce qui va suivre :

  • Le circuit de mise à 1 commandable est une porte simple OU.
  • Le circuit d’inversion commandable est une simple porte XOR.
  • Le circuit de Reset, qui permet de mettre à zéro un bit si besoin, est une porte ET un peu modifiée.

Le circuit de mise à la valeur maximale

[modifier | modifier le wikicode]

Dans cette section, nous allons voir un circuit : soit recopie l'entrée sur sa sortie, soit la met à 11111...111. Le choix entre les deux situations est réalisé par une entrée Set de 1 bit : un 1 sur cette entrée met la sortie à la valeur maximale, un 0 signifie que l'entrée est recopiée en sortie. La porte OU est toute indiquée pour cela. La mise à 1 d'un bit d'entrée demande de faire un OU de celui-ci avec un 1, alors que recopier un bit d'entrée demande de faire un OU de celui-ci avec un 0.

Circuit de mise à 1111111...11

Ce circuit est utilisé pour gérer les débordements d'entier avec l'arithmétique saturée (revoir le chapitre sur le codage des entiers pour plus d'explications). Le circuit de mise à 111111...111 gère le cas où le calcul déborde, ce qui demande de mettre la sortie à la valeur maximale. Évidemment, le circuit de calcul doit non seulement faire le calcul, mais aussi détecter les débordements d'entiers, afin de fournir le bit pour l'entrée Set. Mais nous verrons cela dans le chapitre sur les circuits de calcul entier.

Le circuit de mise à zéro

[modifier | modifier le wikicode]

Le circuit de Reset fait la même chose que le circuit précédent, sauf que sa sortie n'est pas mise à 1111...1111, mais à 0. Le circuit de Reset prend en entrée un bit de Reset qui indique s'il faut mettre à zéro l'entrée ou non. Si le signal Reset est à 1, alors on met à zéro le bit d'entrée, mais on le laisse intact sinon. Le tableau ci-dessus nous dit que la porte ET est adaptée : elle recopie le bit d'entrée si le bit de commande vaut 1, et elle le met à 0 si le bit de commande vaut 0. Cependant, rappelons que l'on souhaite que le le circuit fasse un Reset si le bit de commande est à 1, pas 0, et la porte ET fait l'inverse. Pour corriger cela, on doit ajouter une porte NON. Le tout donne le circuit ci-dessous.

Circuit de mise à zéro d'un bit

Un circuit qui met à zéro un nombre est composé de plusieurs circuits ci-dessus, à la différence que la porte NON est potentiellement partagée. Par contre, chaque bit est bien relié à une porte ET.

Circuit de mise à zéro

L'inverseur commandable

[modifier | modifier le wikicode]

L'inverseur commandable est un circuit qui, comme son nom l'indique, inverse les bits d'entrée si un bit de commande nommé Invert vaut 1. La porte XOR est toute indiquée pour, ce qui fait que le circuit d'inversion commandable est composé d'une couche de portes XOR, chaque porte ayant une entrée connectée au bit de commande.

Inverseur commandable par un bit.

Le circuit qui combine les trois précédents

[modifier | modifier le wikicode]

Voyons maintenant un circuit qui combine les trois circuits précédents, appelé la porte logique universelle 1 bit. Elle s'appelle ainsi car elle regroupe les quatre portes logiques de 1 bit en une seule. Suivant comment on la configure, elle peut servir de porte OUI, de porte NON, de porte TRUE ou de porte FALSE. Et elle est bien plus utile que vous ne pouvez le penser, on l'utilisera beaucoup dans les prochains chapitres.

L'implémentation naïve met les trois circuits précédents les uns à la suite des autres, ce qui demande d'utiliser trois bits de commandes. Il est possible d'ajouter un circuit combinatoire pour se débrouiller avec seulement deux bits de commande.

Porte logique universelle de 1 bit, faite avec trois portes

Mais il y a moyen de se passer d'une porte logique ! En effet, mettre à 0 et mettre à 1 sont deux opérations inverses l'une de l'autre. Mettre à 1 revient à mettre à 0, puis à inverser le résultat. Et inversement, mettre à 0 revient à mettre à 1 avant d'inverser le tout. Il suffit donc de mettre le circuit d'inversion commandable à la fin du circuit, juste après un circuit de mise à 0 ou de mise à 1, au choix. En faisant comme cela, il ne reste que deux portes logiques, donc deux entrées. En choisissant bien les valeurs sur l'entrée de commande, on peut connecter les entrées de commande directement sur les opérandes des deux portes, sans passer par un circuit combinatoire.

Porte logique universelle de 1 bit, faite avec deux portes

Les opérations bit à bit à deux opérandes

[modifier | modifier le wikicode]

Les opérations bit à bit à deux opérandes effectuent un ET, un OU, ou un XOR entre deux opérandes. Ici, le ET/OU/XOR se fait entre deux bits de même poids dans une opérande. Les circuits qui effectuent ces opérations sont assez simples, ils sont composés de portes logiques placées les unes à côté des autres. Il n'y a pas de possibilité de combiner des portes comme c'était le cas dans la section précédente.

Les opérations de masquage

[modifier | modifier le wikicode]

Il est intéressant de donner quelques exemples d'utilisation des opérations bit à bit ET/OU/XOR. L'utilité des opérations bit est bit est en effet loin d'être évidente. L'exemple que nous allons prendre est celui des opérations de masquage, très connue des programmeurs bas niveau. Leur but est de modifier certains bits d'un opérande, mais de laisser certains intouchés. Les bits modifiés peuvent être forcés à 1, forcés à 0, ou inversés.

Pour cela, on combine l'opérande avec un second opérande, qui est appelée le masque. Les bits à modifier sont indiqués par le masque : chaque bit du masque indique s'il faut modifier ou laisser intact le bit correspondant dans l'opérande. Le résultat dépend de l'opération entre masque et opérande, les trois opérations utilisées étant un ET, un OU ou un XOR.

Faire un ET entre l'opérande et le masque va mettre certains bits de l’opérande à 0. Les bits mis à 0 sont ceux où le bit du masque correspondant est à 0, tandis que les autres sont recopiés tels quels.

La même chose a lieu avec l'opération OU, sauf que cette fois-ci, certains bits de l'opérande sont mis à 1. Les bits mis à 1 sont ceux pour lesquels le bit du masque correspondant est un 1.

Masques 1

Dans le cas d'un XOR, les bits sont inversés. Les bits inversés sont ceux pour lesquels le bit du masque correspondant est un 1.

Masquage des n bits de poids faible

Un exemple d'utilisation des opérations de masquage

[modifier | modifier le wikicode]

Pour donner un exemple d'utilisation, parlons des droits d'accès à un fichier. Ceux-ci sont regroupés dans une suite de bits : un des bits indique s'il est accessible en écriture, un autre pour les accès en lecture, un autre s'il est exécutable, etc. Bref, modifier les droits en écriture de ce fichier demande de modifier le bit associé à 1 ou à 0, sans toucher aux autres. Cela peut se faire facilement en utilisant une instruction bit à bit avec un masque bien choisie.

Un autre cas typique est celui où un développeur compacte plusieurs données dans un seul entier. Par exemple, prenons le cas d'une date, exprimée sous la forme jour/mois/année. Un développeur normal stockera cette date dans trois entiers : un pour le jour, un pour le mois, et un pour la date. Mais un programmeur plus pointilleux sera capable d'utiliser un seul entier pour stocker le jour, le mois et l'année. Pour cela, il raisonnera comme suit :

  • un mois comporte maximum 31 jours : on doit donc encoder tous les nombres compris entre 1 et 31, ce qui peut se faire en 5 bits ;
  • une année comporte 12 mois, ce qui tient dans 4 bits ;
  • et enfin, en supposant que l'on doive gérer les années depuis la naissance de Jésus jusqu'à l'année 2047, 11 bits peuvent suffire.

Dans ces conditions, notre développeur décidera d'utiliser un entier de 32 bits pour le stockage des dates :

  • les 5 bits de poids forts serviront à stocker le jour ;
  • les 4 bits suivants stockeront le mois ;
  • et les bits qui restent stockeront l'année.

Le développeur qui souhaite modifier le jour ou le mois d'une date devra modifier une partie des bits, tout en laissant les autres intacts. Encore une fois, cela peut se faire facilement en utilisant une instruction bit à bit avec un masque bien choisi.