La machine M99 est un ordinateur en papier dotée de 100 cases mémoire et d’un processeur. On vous offre cet ordinateur à chacun pour ce TD.
La mémoire est composée de 100 mots mémoire de 3 chiffres décimaux (valeur de 000 à 999) et d’un signe. Ces 100 mots mémoire sont adressables par des nombres sur 2 chiffres (de 0 à 99).
Le processeur dispose de deux registres généraux (nommés A et B) et d’un registre accumulateur/résultat nommé R. Comme les mots mémoires, ces registres peuvent contenir des nombres signés sur 3 chiffres. Le processeur dispose aussi d’un quatrième registre nommé PC (Program Counter). C’est le pointeur d’instruction, contenant l’adresse mémoire de la prochaine instruction à exécuter. Ce registre ne peut contenir que des nombres décimaux sur 2 chiffres. Enfin il y a un registre nommé IR qui contient le code de l'instruction en cours d'exécution. Il ne peut contenir que des nombres décimaux sur 3 chiffres sans le signe.
L’unité arithmétique et logique UAL est en charge d’effectuer les calculs. Les opérandes et résultats sont dans les registres :
A et B pour les opérandes,
R pour le résultat.
L’unité de commande pilote l’ordinateur. Son cycle de fonctionnement, nommé fetch / decode / execute, comporte 3 étapes :
charger l’instruction depuis la case mémoire pointée par PC vers la zone dédiée dans l’UAL (le registre IR) et incrémenter ensuite le PC ;
décoder l’instruction : à partir des 3 chiffres codant l’instruction, identifier quelle est l’opération à réaliser (vous avez un pense-bête à droite de la machine) ;
exécuter l’instruction.
La machine démarre avec la valeur nulle comme pointeur d’instruction (PC=0). Elle s’arrête si le pointeur d’instruction vaut 99. On peut donc utiliser le mnémonique HLT
comme
synonyme de JMP 99
.
Les entrées/sorties sont mappées en mémoire :
écrire une valeur dans le mot mémoire à l’adresse 99 écrit cette valeur sur le terminal,
les valeurs saisies sur le terminal seront lues dans le mot mémoire 99.
Dans ce TD c’est vous qui jouerez le rôle du processeur (unité de commande et UAL).
code | mnémonique | instruction à réaliser |
---|---|---|
x y | STR xy |
copie le contenu du registre R dans le mot mémoire d’adresse xy |
x y | LDA xy |
copie le mot mémoire d’adresse xy dans le registre A |
x y | LDB xy |
copie le mot mémoire d’adresse xy dans le registre B |
x y | MOV x y |
copie registre x dans y (0 → R ; 1 → A ; 2 → B) |
- - | ||
0 0 | ADD |
ajoute les valeurs des registres A et B, produit le résultat dans R |
0 1 | SUB |
soustrait la valeur du registre B à celle du registre A, produit le résultat dans R |
x y | JMP x y |
branche en xy (PC reçoit la valeur xy). JMP 99 provoque l’arrêt de la machine |
x y | JPP x y |
branche en xy si la valeur du registre R est positive |
x y | JEQ x y |
saute une case (PC += 2) si la valeur du registre R est égale à xy |
x y | JNE x y |
saute une case (PC += 2) si la valeur du registre R est différent de xy |
Décodez (directement sur les cases mémoire de votre ordinateur) le programme chargé de l’adresse 0 à l’adresse 11.
En appliquant le cycle de commande jusqu’à un arrêt, expliquez ce que fait ce programme.
Décodez le programme débutant à l’adresse 13 puis essayer d’expliquez ce qu’il fait.
Écrivez un programme (en utilisant les mnémoniques) affichant le maximum de deux nombres saisis au clavier.
Codez votre programme. À quelle adresse mémoire pouvez-le stocker ?
Que fait le programme débutant à l’adresse 40 (pour les entrées 5 et 2) ?
Peut-on le raccourcir ?
Corrigez ce programme quand la seconde entrée vaut 0.
Le M99 est un ordinateur en papier, assez simple à utiliser avec seulement un crayon, mais il a été pensé pour être relativement réaliste des vrais ordinateurs.
La mémoire d’un vrai ordinateur est également découpée en mots mémoires, chacun étant doté d’une adresse unique. En général, les vrais ordinateurs utilisent des mots de 1 octet (8 bits). Les ordinateurs 32bits peuvent avoir jusqu’à 2^{32} mots (soit un peu plus de 4Go de mémoire) tandis que les ordinateurs 64bits peuvent en avoir jusqu’à 2^{64} en théorie (18 Exaoctets, 18.10^{18} octets).
Les vrais processeurs ont également des registres afin de gérer au mieux le problème de la barrière mémoire. Ils ont également des caches pour optimiser les échanges entre la mémoire et le CPU. Là où lire en mémoire peut demander une centaine de cycles CPU, lire en cache prend entre 10 et 30 cycles. Le M99 n’a pas de caches pour simplifier.
Les vrais programmes sont également écrits sous forme d’opcodes en mémoire des vrais ordinateurs, avec le préfixe indiquant l’opération tandis que le sufixe indique les opérandes. Le jeu d’opérations élémentaires disponibles varie beaucoup d’un processeur à l’autre.
Pour le M99, nous avons choisi d’utiliser des mots mémoires de trois positions décimales, ce qui contraint fortement le nombre d’instructions disponibles. Ces contraintes sont parfaitement réalistes de celles que doivent résoudre les fabriquants de CPU. Ajouter des instructions simplifie l’écriture de programmes efficaces, mais complique grandement le processeur, qui devient plus cher et plus énergivore.
Les processeurs de la famille RISC (reduced instruction set CPU) visent la simplicité et n’offrent que peu d’instructions tandis que ceux de la famille CISC (complex instruction set CPU) offrent des opérations optimisées plus rares, comme des opérations vectorielles.
Il serait faux de dire que l’une des familles est vraiment préférable à l’autre. Il s’agit plutôt de deux compromis différents entre complexité du processeur et complexité des programmes. Les processeurs des téléphones portables sont souvent des RISC (par exemple du constructeur ARM) tandis que ceux des ordinateurs sont souvent des CISC (par exemple des constructeurs Intel ou AMD).
Gérer les entrées/sorties au travers d’adresses particulières de l’espace d’adressage du bus mémoire est parfaitement réaliste. En revanche, il est rare d’avoir plusieurs périphériques à la même adresse et on aurait pu séparer les lectures du clavier et les écritures à l’écran dans des zones mémoire différentes. De plus, nous avons ignoré toute la synchronisation qu’un vrai processeur doit faire pour échanger avec les périphériques, souvent bien plus lent.