Fonctionnement d'un ordinateur/Langage machine et assembleur

Un livre de Wikilivres.
Aller à la navigation Aller à la recherche

Ce chapitre va aborder le langage machine, à savoir un standard qui définit les instructions du processeur, le nombre de registres, etc. Dans ce chapitre, on considérera que le processeur est une boite noire au fonctionnement interne inconnu. Nous verrons le fonctionnement interne d'un processeur dans quelques chapitres. Les concepts que nous allons aborder ne sont rien d'autre que les bases nécessaires pour apprendre l'assembleur. Nous allons surtout parler des instructions du processeur. Pour simplifier, on peut classer les instructions en quatre grands types :

  • les échanges de données entre mémoires ;
  • les calculs et autres opérations arithmétiques ;
  • les instructions de comparaison ;
  • les instructions de branchement.

À côté de ceux-ci, on peut trouver d'autres types d'instructions plus exotiques, pour gérer du texte, pour modifier la consommation en électricité de l'ordinateur, pour chiffrer ou de déchiffrer des données de taille fixe, générer des nombres aléatoires, etc.

Les instructions d'accès mémoire[modifier | modifier le wikicode]

Les instructions d’accès mémoire permettent de copier ou d'échanger des données entre le processeur et la RAM. On peut ainsi copier le contenu d'un registre en mémoire, charger une donnée de la RAM dans un registre, initialiser un registre à une valeur bien précise, etc. Il en existe plusieurs, les plus connues étant les suivantes : LOAD, STORE et MOV.

L'instruction MOV copie le contenu d'un registre dans un autre sans passer par la mémoire. C'est donc un échange de données entre registres, qui n'implique en rien la mémoire RAM, mais MOV est quand même considérée comme une instruction d'accès mémoire. Les données sont copiées d'un registre source vers un registre de destination. Le contenu du registre source est conservé, alors que le contenu du registre de destination est écrasé (il est remplacé par la valeur copiée). Cette instruction est utile pour gérer les registres, notamment sur les architectures avec peu de registres et/ou sur les architectures avec des registres spécialisés. Mais quelques rares architectures ne disposent pas d'instruction MOV, qui n'est formellement pas nécessaire, même si bien utile.

Instruction MOV.

Les instructions LOAD et STORE sont deux instructions qui permettent d'échanger des données entre la mémoire RAM et les registres. Elles copient le contenu d'un registre dans la mémoire, ou au contraire une portion de mémoire RAM dans un registre.

  • L'instruction LOAD est une instruction de lecture : elle copie le contenu d'un ou plusieurs mots mémoire consécutifs dans un registre. Le contenu du registre est remplacé par le contenu des mots mémoire de la mémoire RAM.
  • L'instruction STORE fait l'inverse : elle copie le contenu d'un registre dans un ou plusieurs mots mémoire consécutifs en mémoire RAM.
Instruction LOAD. Instruction STORE.

Les instructions de calcul[modifier | modifier le wikicode]

Tout ordinateur gère des instructions qui font des calculs arithmétiques simples. Elles dépendent de la représentation utilisée pour coder ces nombres : on ne manipule pas de la même façon des nombres signés, des nombres codés en complément à 1, des flottants simple précision, des flottants double précision, etc. Tout dépend du type de la donnée, à savoir est-ce un flottant, un entier codé en BCD, etc.

Dans le cas le plus simple, le processeur dispose d'une instruction par type à manipuler : une instruction de multiplication pour les flottants, une autre pour les entiers codés en complément à deux, etc. Sur d'autres machines assez anciennes, on stockait le type de la donnée dans la mémoire. Chaque nombre manipulé par le processeur incorporait un tag, une petite suite de bits qui permettait de préciser son type. Le processeur ne possédait pas d'instructions en plusieurs exemplaires pour faire la même chose, et utilisait le tag pour déduire comment faire ses calculs. Par exemple, ces processeurs n'avaient qu'une seule instruction d'addition, qui pouvait traiter indifféremment flottants, nombres entiers codés en BCD, en complément à deux, etc. Le traitement effectué par cette instruction dépendait du tag incorporé dans la donnée. Des processeurs de ce type s'appellent des architectures à tags, ou tagged architectures.

Les tous premiers ordinateurs pouvaient manipuler des données de taille arbitraire. Alors certes, ces processeurs n'utilisaient pas vraiment les encodages de nombres qu'on a vus au premier chapitre. À la place, ils stockaient leurs nombres dans des chaines de caractères ou des tableaux encodés en BCD. De nos jours, les ordinateurs utilisent des entiers de taille fixe. La taille des données à manipuler peut dépendre de l'instruction. Ainsi, un processeur peut avoir des instructions pour traiter des nombres entiers de 8 bits, et d'autres instructions pour traiter des nombres entiers de 32 bits, par exemple. On peut aussi citer le cas des flottants : il faut bien faire la différence entre flottants simple précision et double précision !

Les instructions arithmétiques[modifier | modifier le wikicode]

Les instructions arithmétiques sont les plus courantes et comprennent au minimum l'addition, la soustraction, la multiplication, éventuellement la division, parfois les opérations plus complexes comme la racine carrée. La division est une opération très complexe et particulièrement lente, bien plus qu'une addition ou une multiplication. Pour information, sur les processeurs actuels, la division est 20 à 80 fois plus lente qu'une addition/soustraction, et presque 7 à 26 fois plus lente qu'une multiplication. Mais on a de la chance : c'est aussi une opération assez rare.

L'instruction de division[modifier | modifier le wikicode]

Un programme effectue rarement des divisions, les plus rares étant les divisions entières tandis que les plus fréquentes sont les divisons entre deux nombres flottants. Les plus couramment utilisées dans un programme sont des divisions par une constante : un programme devant manipuler des nombres décimaux aura tendance à effectuer des divisions par 10, un programme manipulant des durées pourra faire des divisions par 60 (gestion des minutes/secondes) ou 24 (gestion des heures). Diverses astuces permettent de remplacer les divisions par une constante par des suites d'instructions plus simples mais qui donnent le même résultat. J'ai parlé auparavant des décalages, qui permettent de remplacer de divisions par . Mais il existe d'autres méthodes, qui fonctionnent pour un grand nombre de constantes. Par exemple, on peut les remplacer par une multiplication un peu bizarre : la multiplication par un entier réciproque.

Sachant cela, certains processeurs ne possèdent pas d'instruction de division. Inclure une instruction de division n'accélérerait qu'un faible nombre d'instructions, et ne donnerait pas lieu à des gains assez importants en termes de performance : accélérer 1% des instructions d'un programme (ici, les divisions) en implémentant un circuit complexe et gourmand en transistors alors qu'on pourrait utiliser ces circuits pour câbler des instructions plus utiles serait du gâchis. Certains processeurs implémentent toutefois la division dans une instruction machine, disposant souvent d'un circuit dédié. Les gains ne sont pas forcément faramineux, mais ne sont pas forcément négligeables non plus.

La ou les instructions de multiplication[modifier | modifier le wikicode]

L'opération de multiplication entière est très courante, mais il en existe parfois plusieurs version sur un même processeur. Ces différentes version servent à résoudre un problème quant à la taille du résultat d'une multiplication. En effet, la multiplication de deux opérandes de bits donne un résultat de , ce qui ne tient donc pas dans un registre. Pour résoudre ce problème, il existe plusieurs solutions :

  • Certains processeurs se contentent de garder les bits de poids faible, ce qui peut entrainer l'apparition de débordements d'entiers.
  • D'autres ont une instruction qui mémorisent leur résultat dans deux registres : un pour les bits de poids faible et un autre pour les bits de poids fort.
  • D'autres processeurs disposent de deux instructions de multiplication : une instruction pour calculer les bits de poids fort du résultat et une autre pour les bits de poids faible.
  • Enfin, certains processeurs disposent d'instructions de multiplication configurables, qui permettent de choisir quels bits conserver : poids fort ou poids faible.

Les instructions flottantes[modifier | modifier le wikicode]

Les processeurs peuvent aussi gérer les calculs sur des nombres flottants. IEEE 754 standardise aussi quelques instructions sur les flottants qui doivent impérativement être supportées : les quatre opérations arithmétiques de base, les comparaisons et la racine carrée. Certains processeurs vont même plus loin et implémentent non seulement les instructions de la norme, mais aussi d'autres instructions sur les flottants qui ne sont pas supportées par la norme IEEE 754. Par exemple, certaines fonctions mathématiques telles que sinus, cosinus, tangente, arctangente et d'autres, sont supportées par certains processeurs. Le seul problème, c'est que ces instructions peuvent mener à des erreurs de calcul incompatibles avec la norme IEEE 754. Heureusement, les compilateurs peuvent mitiger ces désagréments.

Le support matériel et logiciel des flottants[modifier | modifier le wikicode]

Autrefois, à savoir il y a une quarantaine d'années, les processeurs n'étaient capables d'utiliser que des nombres entiers et aucune instruction machine ne pouvait manipuler de nombres flottants. On devait alors émuler les calculs flottants par une suite d'instructions machine effectuées sur des entiers. Cette émulation était effectuée par une bibliothèque logicielle, fournie par un programmeur, voire par le système d'exploitation. Quand le système d'exploitation fournissait de quoi émuler les flottants, les instructions flottantes étaient alors émulées par le biais d'exceptions matérielles (nous verrons ce concept dans quelques chapitres). Pour simplifier, le processeur interrompt temporairement l'exécution du programme en cours et exécute un sous-programme capable de traiter l'exception. Dans tous les cas, les calculs sur des nombres flottants étaient alors vraiment lents et la manière de stocker des flottants en mémoire dépendait de l'application ou du système d'exploitation.

Coprocesseurs X87.

Pour améliorer les performances, les concepteurs de processeurs ont conçus des processeurs spécialisés dans les calculs flottants. À une époque, un ordinateur de type PC pouvait contenir un processeur normal, secondé par un processeur annexe dédié aux flottants. le processeur secondaire dédié aux flottants était appelé le coprocesseur arithmétique, ou encore le coprocesseur. Ils étaient très chers et relativement peu utilisés. Seules certaines applications assez rares étaient capables d'en tirer profit : des logiciels de conception assistée par ordinateur, par exemple. Un emplacement dans la carte mère était réservé au coprocesseur.

Par la suite, les concepteurs de processeurs ont incorporé des instructions de calculs sur des nombres flottants dans les jeux d'instruction du processeur principal, rendant le coprocesseur inutile. Les premiers processeurs de ce type n'incorporaient néanmoins pas le moindre circuit capable d'effectuer des opérations flottantes. Sur ces processeurs, chaque instruction machine capable de gérer des nombres flottants était convertie en interne (c'est-à-dire dans le processeur) en une suite d'instructions entières qui émulaient l'instruction voulue. Nous verrons dans quelques chapitres comment cette conversion était faite - pour ceux qui savent, ces instructions étaient microcodées. Nous allons juste dire que le processeur incorporait une petite mémoire ROM qui stockait, pour chaque instruction à émuler, une suite de calculs qui l'émulait. Lorsqu'une instruction à émuler doit être exécutée, notre processeur lisait la suite de calculs dans la mémoire ROM et l'exécutait.

De nos jours, les processeurs contiennent des circuits de calcul flottant, ce qui fait que les instructions ne sont plus émulées sauf pour quelques-unes. Le choix du mode d'arrondi ou la gestion des exceptions sont implémentés directement dans le matériel et sont souvent configurables grâce à un registre du processeur. Suivant la valeur du registre, le processeur arrondira les résultats des calculs d'une certaine façon et pas d'une autre, ou réagira d'une certaine manière aux exceptions vues au-dessus.

Le support des formats de flottants[modifier | modifier le wikicode]

Il faut néanmoins préciser que le support de la norme IEEE 754 par la FPU/le jeu d'instruction n'est pas une obligation : certains processeurs s'en moquent royalement. Dans certaines applications, les programmeurs ont souvent besoin d'avoir des calculs qui s'effectuent rapidement et se contentent très bien d'un résultat approché. Dans ces situations, on peut utiliser des formats de flottants différents de la norme IEEE 754 et les circuits de la FPU sont simplifiés pour être plus rapides. Par exemple, certains circuits ne gèrent pas les underflow, overflow, les NaN ou les infinis, voir utilisent des formats de flottants exotiques. Sur d'autres processeurs, le processeur peut être configuré de façon à soit ignorer ces exceptions en fournissant un résultat, soit déclencher une exception matérielle (à ne pas confondre avec les exceptions de la norme). Il suffit pour cela de modifier la valeur d'un registre de configuration intégré dans le processeur. Le choix du mode d'arrondi ou la gestion des exceptions flottantes sont implémentés directement dans le matériel et sont souvent configurables grâce à un registre du processeur : suivant la valeur mise dans celui-ci, le processeur arrondira les résultats des calculs d'une certaine façon et pas d'une autre, ou réagira d'une certaine manière aux exceptions vues au-dessus.

Sur certains processeurs, tous les formats de flottants IEEE754 ne sont pas supportés. Il arrive que certains ne supportent que les flottants simple précision, mais pas les double précision ou d'autres formats assez courants. Si un processeur ne supporte pas un format de flottant, il doit émuler son support, généralement par logiciel. Exemple : les premiers processeurs Intel ne géraient que les flottants double précision étendue. Un autre exemple est la gestion des flottants dénormalisés, qui n'est pas forcément supportée par les processeurs. Sur quelques architectures, les dénormaux sont émulés en logiciel. Mais même lorsque la gestion des dénormaux est implémentée en hardware (comme c'est le cas sur certains processeurs AMD), celle-ci reste malgré tout très lente.

Plus rarement, il arrive que certains processeurs gèrent des formats de flottants spéciaux qui ne font pas partie de la norme IEEE 754.

Les instructions logiques[modifier | modifier le wikicode]

À côté des instructions de calcul, on trouve des instructions logiques qui travaillent sur des bits ou des groupes de bits. Les opérations bit à bit ont déjà été vues dans les premiers chapitres, ceux sur l'électronique. Pour rappel, les plus courantes sont :

  • La négation, ou encore le NON bit à bit, inverse tous les bits d'un nombre : les 1 deviennent des 0 et les 0 deviennent des 1.
  • Le ET bit à bit, qui agit sur deux nombres : il va prendre les bits qui sont à la même place et va effectuer un ET (l’opération effectuée par la porte logique ET). Exemple : 1100·1010=1000.
  • Les instructions similaires avec le OU ou le XOR.

Les instructions de décalage décalent tous les bits d'un nombre vers la gauche ou la droite. Pour rappel, il existe plusieurs types de décalages : les rotations, les décalages logiques, et les décalages arithmétiques. Nous avions déjà vu ces trois opérations dans le chapitre sur les circuits de décalage et de rotation, ce qui fait que nous allons simplement faire un rappel dans le tableau suivant. Pour résumer, voici la différence entre les trois opérations :

Schéma Gestion des bits sortants Remplissage des vides laissés par le décalage
Rotation Rotations de bits. Réinjectés dans les vides laissés par le décalage Remplis par les bits sortants
Décalage logique Décalage logique. Les bits sortants sont oubliés Remplis par des zéros
Décalage arithmétique Décalage arithmétique. Remplis par :
  • le bit de signe pour les bits de poids fort ;
  • des zéros pour les bits de poids faible .

Pour rappel, les décalages de n rangs sont équivalents à une multiplication/division par 2^n. Un décalage à gauche est une multiplication par 2^n, alors qu'un décalage à droite est une division par 2^n. Les décalages logiques effectuent la multiplication/division pour un nombre non-signé (positif), alors que les décalages arithmétiques sont utilisés pour les nombres signés. Précisons cependant que pour ce qui est des décalages à droite, les décalages logiques et arithmétiques n'arrondissent pas le résultat de la même manière. Les décalages logiques à droite arrondissent vers zéro, alors que les décalages arithmétiques arrondissent vers la valeur inférieure (vers moins l'infini). De plus, les décalages à gauche entraînent des débordements d'entiers qui ne se gèrent pas de la même manière entre décalage logique et décalage arithmétique.

La plupart des processeurs stockent le bit sortant dans un bit du registre d'indicateurs (le bit de retenue le plus souvent). Ils possèdent aussi une variante des instructions de décalage où le bit de cet indicateur est utilisé pour remplir le bit libéré au lieu de mettre un zéro. Cela permet d'enchaîner les décalages sur un nombre de bits plus grand que celui supporté par les instructions en procédant par morceaux. Par exemple, pour un processeur supportant les décalages sur 8 et 16 bits, il peut enchaîner deux décalages de 16 bits pour décaler un nombre de 32 bits ; ou bien un décalage de 8 bits et un autre de 16 bits pour décaler un nombre de 24 bits.

Les instructions de test[modifier | modifier le wikicode]

Les instructions de test comparent deux valeurs (des adresses, ou des nombres entiers ou à virgule flottante). Sur la majorité des processeurs, ces instructions ne font qu'une comparaison à la fois. D'autres processeurs ont des instructions de test qui effectuent plusieurs comparaisons en même temps et fournissent plusieurs résultats. Par exemple, un processeur x86 possède une instruction CMP qui vérifie simultanément si deux valeurs A et B sont égales, différentes, si A est inférieur à B, si A est supérieur à B, etc. Les comparaison permettent souvent d'effectuer les comparaisons suivantes :

  • A > B (est-ce que A est supérieur à B ?) ;
  • A < B (est-ce que A est inférieur à B ?) ;
  • A == B (est-ce que A est égal à B ?) ;
  • A != B (est-ce que A est différent de B ?).

Il arrive que certaines de ces instructions de test effectuent plusieurs comparaisons en même temps et fournissent plusieurs résultats. Par exemple, un processeur peut très bien posséder une instruction cmp capable de vérifier si deux valeurs A et B sont égales, différentes, si A est inférieure à B, et si A est supérieur à B, en même temps.

Le stockage du résultat d'une comparaison[modifier | modifier le wikicode]

Le résultat d'une comparaison est un bit, qui dit si la condition testée est vraie ou fausse. Dans la majorité des cas, ce bit vaut 1 si la comparaison est vérifiée, et 0 sinon. Une fois que l'instruction a fait son travail, il reste à stocker son résultat quelque part. Et pour ce faire, il existe plusieurs techniques :

  • soit on utilise des registres d'état ;
  • soit on utilise des registres à prédicats ;
  • soit on ne mémorise pas le résultat.

Certains processeurs incorporent un registre d'état, qui stocke des bits qui ont chacun une signification prédéterminée lors de la conception du processeur. Le ou les bits du registre d'état modifiés par une instruction de test dépendent de l'instruction utilisée : par exemple, on peut utiliser un bit qui indiquera si l'entier testé est égal à un autre, un autre bit qui indiquera si le premier entier testé est supérieur à l'autre, etc. Ce registre peut aussi contenir d'autres bits suivant le processeur, comme le bit de débordement qui prévient quand le résultat d'une instruction est trop grand pour tenir dans un registre, le bit null qui précise que le résultat d'une instruction est nul (vaut zéro), le bit de retenue qui est utile pour les additions, le bit de signe qui permet de dire si le résultat d'une instruction est un nombre négatif ou positif. Le registre d'état a un avantage : certaines instructions arithmétiques peuvent modifier les bits du registre d'état, ce qui leur permet parfois de remplacer une instruction de test. Par exemple, prenons le cas d'un processeur où la soustraction modifie le bit null du registre d'état : on peut tester si deux nombres sont égaux en soustrayant leur contenu, le bit null étant mis à zéro si c'est le cas.

D'autres processeurs utilisent des registres à prédicats, des registres de 1 bit qui peuvent stocker n'importe quel résultat de comparaison. Une comparaison peut enregistrer son résultat dans n'importe quel registre à prédicats : elle a juste à préciser lequel avec son nom de registre. Cette méthode est plus souple que l'utilisation d'un registre d'état dont les bits ont une utilité fixée une fois pour toutes. Les registres à prédicats sont utiles pour accumuler les résultats de plusieurs comparaisons et les utiliser par la suite. Par exemple, une instruction de branchement pourra vérifier la valeur de plusieurs de ces registres pour prendre une décision. Chose impossible avec les autres approches, dont les branchements ne peuvent utiliser qu'un seul résultat de comparaison pour prendre leur décision.

Enfin, sur d'autres processeurs, il n'y a pas de registres pour stocker les résultats de comparaisons : les comparaisons sont fusionnées avec les branchements en une seule instruction, rendant le stockage du résultat inutile.

Les instructions à prédicat[modifier | modifier le wikicode]

Les instructions à prédicat sont des instructions qui ne font quelque chose que si une condition est respectée, et se comportent comme un NOP (une instruction qui ne fait rien) sinon. Elles lisent le résultat d'une comparaison, dans le registre d'état ou un registre à prédicat, et s’exécutent ou non suivant sa valeur. L'instruction à prédicat la plus représentative, présente sur de nombreux processeurs, est l'instruction CMOV, qui copie un registre dans un autre si une condition est remplie. Elle permet de faire certaines opérations assez simples, comme calculer la valeur absolue d'un nombre, le maximum de deux nombres, etc.

Certains processeurs fournissent des instructions à prédicats en plus d'instructions normales : on dit qu'ils implémentent la prédication partielle. Sur d'autres processeurs, toutes les instructions sont des instructions à prédicat : on parle de prédication totale.

Sur quelques processeurs, plus rares, le test est fusionné avec l'opération en une seule instruction à prédicat qui fait tout. Implémenter ces instructions peut être fait de plusieurs manières, la première n’exécutant pas l'instruction si la condition est invalide, l'autre l’exécutant en avance quitte à l'annuler si la condition se révèle ultérieurement invalide. La première implémentation est équivalente à enchaîner une instruction de test avec une autre instruction. Cette méthode est peu pratique : le processeur doit enchaîner deux instructions à la suite sans que le programmeur n'y voit que du feu, sans compter que cela prend plus de temps qu’exécuter une seule instruction. La seconde méthode empêche l'enregistrement du résultat dans les registres ou la mémoire. La comparaison est faite en parallèle de l'autre opération, mais l'autre opération est annulée en cas de problèmes. L'annulation se fait simplement en n'enregistrant pas le résultat de l’opération dans les registres.

Implémentation des instructions à prédicats

Les instructions de branchements[modifier | modifier le wikicode]

Un processeur serait sacrément inflexible s'il ne faisait qu'exécuter des instructions dans l'ordre. Certains processeurs ne savent pas faire autre chose, comme le Harvard Mark I, et il est difficile, voire impossible, de coder certains programmes sur de tels ordinateurs. Mais rassurez-vous : il existe de quoi permettre au processeur de faire des choses plus évoluées. Pour rendre notre ordinateur "plus intelligent", on peut par exemple souhaiter que celui-ci n'exécute une suite d'instructions que si une certaine condition est remplie. Ou faire mieux : on peut demander à notre ordinateur de répéter une suite d'instructions tant qu'une condition bien définie est respectée. Diverses structures de contrôle de ce type ont donc étés inventées.

Voici les plus utilisées et les plus courantes : ce sont celles qui reviennent de façon récurrente dans un grand nombre de langages de programmation actuels. Concevoir un programme (dans certains langages de programmation), c'est simplement créer une suite d'instructions, et utiliser ces fameuses structures de contrôle pour l'organiser. D'ailleurs, ceux qui savent déjà programmer auront reconnu ces fameuses structures de contrôle. On peut bien sur en inventer d’autres, en spécialisant certaines structures de contrôle à des cas un peu plus particuliers ou en combinant plusieurs de ces structures de contrôles de base, mais cela dépasse le cadre de ce tutoriel : ce tutoriel ne va pas vous apprendre à programmer.

Nom de la structure de contrôle Description
SI...ALORS Exécute une suite d'instructions si une condition est respectée
SI...ALORS...SINON Exécute une suite d'instructions si une condition est respectée ou exécute une autre suite d'instructions si elle ne l'est pas.
Boucle WHILE...DO Répète une suite d'instructions tant qu'une condition est respectée.
Boucle DO...WHILE aussi appelée REPEAT UNTIL Répète une suite d'instructions tant qu'une condition est respectée. La différence, c'est que la boucle DO...WHILE exécute au moins une fois cette suite d'instructions.
Boucle FOR Répète un nombre fixé de fois une suite d'instructions.

Pour implémenter ces structures de contrôle, les instructions de test sont combinées avec des instructions de branchement. Ces dernières modifient la valeur stockée dans le registre d'adresse d'instruction, ce qui permet de sauter directement à une instruction et de poursuivre l'exécution à partir de celle-ci. Il existe deux types de branchements :

  • les branchements inconditionnels, avec lesquels le processeur passe toujours à l'instruction vers laquelle le branchement va renvoyer ;
  • les branchements conditionnels, où le branchement n'est exécuté que si une condition est respectée.

Il existe de nombreuses manières de mettre en œuvre les branchements conditionnels et tous les processeurs ne font pas de la même manière. Sur la plupart des processeurs, les branchements conditionnels sont précédés d'une instruction de test ou de comparaison, qui vérifie si la condition voulue est respectée. Le résultat de la comparaison est stocké dans certains bits du registre d'état, ou dans un registre de prédicat, et le branchement lit ce résultat pour décider s'il s’exécute. Mais d'autres processeurs, assez fréquents, effectuent le test et le branchement en une seule instruction machine, pour se passer de registre d'état ou de registres à prédicats. Les deux types de branchements précédents peuvent être vus comme une forme particulière d'instructions à prédicats, bien que les deux types d'instructions soient souvent opposés. Ce sont les deux types principaux de branchements conditionnels, les autres étant plus rares. Parmi ces cas rares, sur de rares processeurs, les instructions de test permettent de zapper l'instruction suivante si la condition testée est fausse : cela permet de simuler un branchement conditionnel à partir d'un branchement inconditionnel. Sur quelques rares processeurs, le program counter est un registre général qui peut être modifié par n'importe quelle opération arithmétique : cela permet de remplacer les branchements par une simple écriture dans le program counter.

Les structures de contrôle conditionnelles[modifier | modifier le wikicode]

Le IF permet d’exécuter une suite d'instructions si et seulement si une certaine condition est remplie.

Codage d'un SI...ALORS en assembleur.

Le IF...ELSE sert à effectuer une suite d'instructions différente selon que la condition est respectée ou non : c'est un SI…ALORS contenant un second cas. Une boucle consiste à répéter une suite d'instructions machine tant qu'une condition est valide (ou fausse).

Codage d'un SI...ALORS..SINON en assembleur.

Les structures de contrôle itératives[modifier | modifier le wikicode]

Les boucles sont une variante du IF dont le branchement renvoie le processeur sur une instruction précédente.

Commençons par la boucle DO…WHILE : la suite d'instructions est exécutée au moins une fois, et est répétée tant qu'une certaine condition est vérifiée. Pour cela, la suite d'instructions à exécuter est placée avant les instructions de test et de branchement, le branchement permettant de répéter la suite d'instructions si la condition est remplie. Si jamais la condition testée est fausse, on passe tout simplement à la suite du programme.

DO...WHILE.

Une boucle WHILE…DO est identique à une boucle DO…WHILE à un détail près : la suite d'instructions de la boucle n'a pas forcément besoin d'être exécutée au moins une fois. On peut donc adapter une boucle DO…WHILE pour en faire une boucle WHILE…DO : il suffit de tester si la boucle doit être exécutée au moins une fois avec un IF, et exécuter une boucle DO…WHILE équivalente si c'est le cas.

WHILE...DO.

Résumé[modifier | modifier le wikicode]

Pour résumer, les instructions les plus courantes sont les suivantes :

Instruction Utilité
Instructions arithmétiques Ces instructions font simplement des calculs sur des nombres. On peut citer par exemple :
  • L'addition ;
  • la multiplication ;
  • la division ;
  • le modulo ;
  • la soustraction ;
  • la racine carrée ;
  • le cosinus ;
  • et parfois d'autres.
Instructions logiques Elles travaillent sur des bits ou des groupes de bits. On peut citer :
  • Le ET logique.
  • Le OU logique.
  • Le OU exclusif (XOR).
  • Le NON , qui inverse tous les bits d'un nombre : les 1 deviennent des 0 et les 0 deviennent des 1.
  • Les instructions de décalage à droite et à gauche, qui vont décaler tous les bits d'un nombre d'un cran vers la gauche ou la droite. Les bits qui sortent du nombre sont considérés comme perdus.
  • Les instructions de rotation, qui font la même chose que les instructions de décalage, à la différence près que les bits qui "sortent d'un côté du nombre" après le décalage rentrent de l'autre.
Instructions de test Elles peuvent comparer deux nombres entre eux pour savoir si une condition est remplie ou pas.

Pour citer quelques exemples, il existe certaines instructions qui peuvent vérifier si :

  • deux nombres sont égaux ;
  • si deux nombres sont différents ;
  • si un nombre est supérieur à un autre ;
  • si un nombre est inférieur à un autre.
Instructions de contrôle (branchements) Elles permettent de contrôler la façon dont notre programme s’exécute sur notre ordinateur. Elles permettent notamment de choisir la prochaine instruction à exécuter, histoire de répéter des suites d'instructions, de ne pas exécuter des blocs d'instructions dans certains cas, et bien d'autres choses.
Instructions d’accès mémoire Elles permettent d'échanger des données entre le processeur et la mémoire, ou encore permettent de gérer la mémoire et son adressage.

En plus de ces instructions, beaucoup de processeurs ajoutent d'autres instructions. Par exemple, certains processeurs sont capables d'effectuer des instructions sur du texte directement. Pour stocker du texte, la technique la plus simple utilise une suite de lettres, stockées les unes à la suite des autres dans la mémoire, dans l'ordre dans lesquelles elles sont placées dans le texte. Quelques ordinateurs disposent d'instructions pour traiter ces suites de lettres. D'ailleurs, n'importe quel PC x86 actuel dispose de telles instructions, bien qu'elles ne soient pas utilisées car paradoxalement trop lente comparé aux solutions logicielles ! Cela peut paraître surprenant, mais il y a une explication assez simple qui sera compréhensible dans quelques chapitres (les instructions en question sont microcodées).

Il existe aussi une grande quantité d'autres instructions, qui sont fournies par certains processeurs pour des besoins spécifiques. Par exemple, certains processeurs ont des instructions spécialement adaptées aux besoins des OS modernes. D'autres permettent de modifier la consommation en électricité de l'ordinateur (instructions de mise en veille du PC, par exemple). On peut aussi trouver des instructions spécialisées dans les calculs cryptographiques : certaines instructions permettent de chiffrer ou de déchiffrer des données de taille fixe. De même, certains processeurs ont une instruction permettant de générer des nombres aléatoires. Et on peut trouver bien d'autres exemples...