francebalade.fr       Cours de Mathématiques       Table des matières       Votre avis sur ce site
Logique

Des circuits au langage C

Cinq marches séparent un interrupteur d'un programme qui tourne : l'unité de calcul, la mémoire, le langage machine, l'assembleur, et un compilateur miniature — construit et exécuté sous vos yeux.

L'intuition de Boole (1854) : Le mathématicien George Boole formalise une algèbre où les variables n'ont que deux valeurs (vrai ou faux, 1 ou 0) combinées par des opérateurs logiques fondamentaux (ET, OU, NON).
L'application physique (1938) : Dans sa thèse pionnière, Claude Shannon démontre que ces équations logiques peuvent être directement modélisées par des circuits électriques grâce à des interrupteurs ouverts ou fermés.

La page précédente s'arrêtait sur un fait : une seule porte, le NON-ET, suffit à tout construire. Celui-ci part de là et va jusqu'au bout — comment des portes assemblées en unité de calcul, cadencées par une horloge, lisant et écrivant en mémoire, exécutant des instructions codées en binaire, finissent par exécuter un programme écrit en langage C. Chaque étape est simulée réellement : les nombres affichés sont calculés par le circuit simulé, jamais par une explication toute faite.

Naissance des processeurs et de l'architecture de Von Neumann
  - Les portes logiques et l'ALU : L'invention du transistor permet de miniaturiser ces interrupteurs pour créer des portes logiques. Celles-ci sont combinées en unités arithmétiques et logiques (ALU) capables d'effectuer des calculs binaires complexes.
  - Le modèle de Von Neumann : Cette architecture unifie en une seule mémoire les données et les instructions du programme, permettant au processeur de les exécuter séquentiellement de manière autonome.

1

L'unité de calcul

Le cahier sur l'algèbre de Boole a montré comment câbler un additionneur avec des portes. Une unité arithmétique et logique (UAL) va plus loin : elle calcule plusieurs opérations en parallèle et un multiplexeur choisit laquelle transmettre, selon un petit code de sélection — exactement ce qu'un processeur fait à chaque instruction.

9
5

Les quatre résultats sont calculés en même temps ; le multiplexeur ne laisse passer que celui désigné par le code de sélection.

A (binaire)1001
B (binaire)0101
Code de sélection10
Résultat (UAL)14
Vérifié sur 1024 cas1024 / 1024
Le résultat du multiplexeur (logique bit à bit, indépendante) et le résultat de l'opération arithmétique choisie sont comparés sur les 16 × 16 × 4 = 1024 combinaisons possibles de A, B et du code : ce sont deux façons de calculer la même chose, et elles s'accordent toujours. Changer les deux bits de sélection, c'est déjà « programmer » l'UAL.
2

Registres et mémoire

Un circuit qui calcule ne suffit pas : il faut aussi garder des valeurs entre deux calculs. Un registre mémorise un résultat au rythme d'une horloge ; la mémoire vive (RAM) range des milliers de ces valeurs, chacune à sa propre adresse.

Le registre : une valeur figée jusqu'au prochain front d'horloge

42

Faites varier D librement : Q ne change qu'au clic sur le front d'horloge.

D (entrée, change librement)42
Q (mémorisé au dernier front)—
Fronts d'horloge reçus0

La mémoire vive : lire et écrire à une adresse

0
100
Dernière valeur lue—
Test aléatoire : cases correctes16 / 16
Le registre et la case mémoire ne diffèrent que par l'échelle : quelques bits pour l'un, des milliers d'octets adressables pour l'autre. Le test aléatoire écrit 50 fois de suite à des adresses tirées au hasard, puis relit les 16 cases : chacune doit contenir très exactement sa dernière valeur écrite, jamais une valeur intermédiaire.

L'assembleur : donner des noms aux instructions
  - Le saut sémantique : Programmer en binaire pur (suite de 0 et de 1) s'avérant extrêmement fastidieux et sujet aux erreurs, l'assembleur introduit des mnémoniques textuels (comme MOV, ADD, JMP) faciles à retenir.
  - Traduction directe : Chaque ligne d'assembleur correspond de façon univoque à une instruction machine élémentaire, mais le code reste strictement dépendant de l'architecture matérielle du processeur cible.

3

Le langage machine

Une instruction n'est qu'un nombre : ce cahier utilise un mot de 16 bits, découpé en quatre champs — le code opération (4 bits, 16 instructions possibles), deux numéros de registre (2 bits chacun, R0 à R3) et un opérande (8 bits, une constante ou une adresse). Composez une instruction : son encodage binaire se construit sous vos yeux, puis se redécode pour vérifier qu'on retombe bien sur la même instruction.

Le jeu d'instructions complet de ce cahier (16 opcodes).

10

Encodage (16 bits) :

Redécodage automatique :

Instruction choisieLOADI R0, #10
RedécodéeLOADI R0, #10
Aller-retour vérifié (2000 essais)2000 / 2000
Encoder puis décoder 2 000 instructions choisies au hasard (mnémonique, registres, opérande) retombe à chaque fois exactement sur l'instruction de départ : l'opération est réversible, bit à bit. C'est cette réversibilité qui permet à un désassembleur de relire un programme compilé.
4

Assembleur et exécution pas à pas

L'assembleur traduit un texte lisible (des mnémoniques, des étiquettes) en mots binaires — une traduction mécanique, ligne par ligne. Le processeur simulé exécute ensuite ce binaire une instruction à la fois : chargez un programme, avancez pas à pas, et regardez les registres et la mémoire changer sous vos yeux.

Code source assembleur :

Code machine (assemblé) :

PC0
Drapeau zéronon
Instructions exécutées0
Étatprêt
Sortie(s) obtenue(s)—
Sortie(s) attendue(s) (calcul indépendant)—
Accord—
La sortie attendue de chaque programme est recalculée indépendamment en JavaScript natif (une somme, un produit, une boucle) — pas recopiée du programme assembleur — et comparée au résultat réellement produit par le processeur simulé, instruction par instruction.
5

Le compilateur miniature

Un compilateur n'est qu'un programme comme un autre : il lit un texte, en construit un arbre syntaxique qui reflète les priorités des opérations, puis parcourt cet arbre pour émettre des instructions assembleur — celles-là mêmes du cahier précédent. Modifiez l'expression ou les valeurs des variables : tout se recalcule, de l'arbre jusqu'à la sortie du processeur simulé.

3
4
5
2

Arbre syntaxique : les feuilles (carrées) sont les nombres et les variables, les nœuds (ronds) sont les opérations — construit par le même analyseur récursif qu'un vrai compilateur.

Assembleur généré :

Code machine (assemblé) :

Expressionx = (a + b) * c - d;
Résultat — processeur simulé33
Résultat — calcul JS direct33
Accordoui — identique
Résultat : —
Chaque expression aléatoire (jusqu'à trois niveaux d'imbrication, valeurs de a, b, c, d tirées au hasard) est compilée en assembleur, exécutée par le processeur simulé, et évaluée directement en JavaScript à partir du même arbre. Les deux résultats — l'un obtenu par un circuit qui manipule des bits, l'autre par un simple calcul — coïncident à chaque fois.

Les langages évolués : l'abstraction avec le Langage C
  - Portabilité et universalité : Développé par Dennis Ritchie au début des années 1970, le langage C permet d'écrire des algorithmes structurés en s'affranchissant des spécificités d'un processeur particulier.
  - Le rôle du compilateur : Un programme spécialisé, le compilateur, traduit ce code source textuel de haut niveau en un fichier binaire exécutable optimisé pour la machine cible.


Un peu d'histoire

Synthèse

Cinq couches, un seul mécanisme

Portes logiques, unité arithmétique, registres, mémoire, jeu d'instructions : chaque couche n'est qu'un arrangement de la précédente. Rien de magique n'apparaît entre le transistor et le programme — seulement de l'abstraction empilée.

Le binaire encode tout

Une instruction, une adresse, un caractère : tout devient un nombre. Le même mot de 16 bits est tour à tour un ordre pour le processeur et une donnée qu'on peut lire, copier, calculer.

Compiler, c'est traduire

Un compilateur ne fait rien qu'un assembleur ne puisse faire à la main : il découpe le texte en jetons, construit un arbre, et parcourt cet arbre pour émettre, ligne à ligne, les mêmes instructions qu'un programmeur écrirait lui-même.

Pour aller plus loin

Le jeu d'instructions de ce cahier est une invention pédagogique à 16 opcodes et registres 8 bits, dans l'esprit des processeurs réels (x86, ARM, RISC-V) mais très simplifiée. Un vrai compilateur C ajoute des passes que ce cahier ignore : typage, optimisation, gestion de la pile d'appels de fonctions, édition de liens entre fichiers objets. Le principe reste le même : lexer → parser → arbre syntaxique → génération de code.