Programmation impérative, projet 2025–26

Dates et principe

Cette page peut être mise à jour, avec informations complémentaires, précisions, questions bonus, etc. Pensez à y revenir souvent.

Correction du 11 décembre : les codes associés à EQ et NEQ étaient erronés.

Ajout du 15 décembre : ajout de l'exemple wumpus.sobf dans l'archive de fichiers d'entrées.

Correction du 16 décembre : les codes associés à POP et ASSIGN étaient erronés, et il manquait l'instruction PUSHACC. Précision dans la description de OFFSETREF. Corrections dans le fichier d'exemple wumpus.sobf. Ajout d'explications pour les exemples.

Correction du 17 décembre : le nombre de paramètres attendus par la primitive numéro 310 est deux. Ajout du contenu du tableau de codes dans la description de certains exemples.

Projet à rendre pour le 11/1/2026 à 23h59, aucun retard ne sera toléré.
Des soutenances pourront potentiellement être organisées ensuite.



Lire tout le sujet.

Un rendu de projet comprend :

Avez-vous lu tout le sujet ?

Protocole de dépôt

Vous devez rendre

rassemblés dans une archive tar gzippée identifiée comme votre_prénom_votre_nom.tgz.
La commande devrait ressembler à :
tar zcvf randolph_carter.tgz rapport.pdf fichiers.c autres_trucs_éventuels
N'OUBLIEZ surtout PAS de mettre le nom identifiant l'archive (donc nouveau) en PREMIER.
Lisez le man ! et testez le contenu de votre archive (une commande comme par exemple :
tar tvf randolph_carter.tgz doit lister les fichiers et donner leur taille). Votre code utilisera exclusivement les notions abordées en cours. Dans le cas contraire, il ne sera pas corrigé. En particulier, on n'utilisera pas d'arithmétique de pointeur ni de fonctions comme valeur. L'utilisation de la syntaxe x++ sera tolérée à condition qu'elle n'apparaisse pas à l'intérieur d'une expression mais uniquement comme une instruction à part entière. En cas de doute, demandez à votre chargé de TP ou au responsable du cours.

Toute tentative de fraude (plagiat, etc.) sera sanctionnée. Si plusieurs projets ont des sources trop similaires (y compris sur une partie du code uniquement), tous leurs auteurs se verront attribuer la note 0/20. En particulier, il faudra prendre soin de ne pas publier son travail sur un dépôt public (en tout cas pas avant la date de fin de rendu). On évitera également de demander (ou de donner) des conseils trop précis à ses camarades (y compris des promotions précédentes), ces conseils ayant pu être donnés à plusieurs personnes. On se méfiera également des IA génératives qui risquent de donner le même code à tout le monde. Les rendus seront comparés deux à deux.

Procédure de dépôt
Vous devez enregistrer votre archive tgz dans le dépôt dédié au cours PRIM11 (prim11-2025) en vous connectant à http://exam.ensiie.fr. Ce dépôt sera ouvert jusqu'au 11 janvier inclus.


Contexte

Le but de ce projet est d'implémenter (une partie de) la machine virtuelle pour le bytecode d'OCaml.

Le langage de programmation OCaml, que vous verrez au deuxième semestre, est compilé vers du bytecode, un langage bas niveau qui est ensuite interprété dans une machine virtuelle pour exécuter le programme. L'avantage principal est qu'il suffit d'avoir une machine virtuelle pour exécuter le code, quelque soit l'architecture sur laquelle il a été compilé.

Description de la machine virtuelle

On considère deux types de données :

Pour représenter des entiers comme des valeurs et pouvoir les distinguer, ils seront encodés par des entiers impairs : l'entier n sera représenté par la valeur 2n+1. De même, les booléens true et false seront représentés comme les entiers 1 et 0, et ils seront donc encodés respectivement par 3 et 1.

La machine virtuelle OCaml est constituée des parties suivantes :

Pour exécuter le programme, on regarde le code situé à l'indice courant. On effectue l'action associée à ce code (cf. infra), qui peut lire ou modifier l'indice, l'accumulateur, la pile et/ou les valeurs globales. Puis on passe à l'indice suivant (sauf indication contraire).

On peut regrouper les instructions en plusieurs ensembles. On n'implémentera qu'un sous-ensemble des instructions de la machine virtuelle, uniquement celles présentées ici. On pourra également se référer à ce document qui date un peu mais reste pertinent pour décrire les instructions.

Instructions de base

CodeNomAction
0ACC0On met dans l'accumulateur l'élément au sommet de la pile (sans le dépiler).
1ACC1On met dans l'accumulateur l'élément en dessous du sommet de la pile (sans le dépiler).
2ACC2On met dans l'accumulateur l'élément à la profondeur 2 dans la pile (sans le dépiler).
3ACC3On met dans l'accumulateur l'élément à la profondeur 3 dans la pile (sans le dépiler).
4ACC4On met dans l'accumulateur l'élément à la profondeur 4 dans la pile (sans le dépiler).
5ACC5On met dans l'accumulateur l'élément à la profondeur 5 dans la pile (sans le dépiler).
6ACC6On met dans l'accumulateur l'élément à la profondeur 6 dans la pile (sans le dépiler).
7ACC7On met dans l'accumulateur l'élément à la profondeur 7 dans la pile (sans le dépiler).
8ACC On regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On met dans l'accumulateur l'élément à la profondeur n dans la pile (sans le dépiler). On avancera l'indice de 2 pour aller à la prochaine instruction.
9PUSHOn empile le contenu de l'accumulateur.
10PUSHACC0On empile le contenu de l'accumulateur. On met dans l'accumulateur l'élément de pile situé maintenant à la profondeur 0 (sans le dépiler).
11PUSHACC1On empile le contenu de l'accumulateur. On met dans l'accumulateur l'élément de pile situé maintenant à la profondeur 1 (sans le dépiler).
12PUSHACC2On empile le contenu de l'accumulateur. On met dans l'accumulateur l'élément de pile situé maintenant à la profondeur 2 (sans le dépiler).
13PUSHACC3On empile le contenu de l'accumulateur. On met dans l'accumulateur l'élément de pile situé maintenant à la profondeur 3 (sans le dépiler).
14PUSHACC4On empile le contenu de l'accumulateur. On met dans l'accumulateur l'élément de pile situé maintenant à la profondeur 4 (sans le dépiler).
15PUSHACC5On empile le contenu de l'accumulateur. On met dans l'accumulateur l'élément de pile situé maintenant à la profondeur 5 (sans le dépiler).
16PUSHACC6On empile le contenu de l'accumulateur. On met dans l'accumulateur l'élément de pile situé maintenant à la profondeur 6 (sans le dépiler).
17PUSHACC7On empile le contenu de l'accumulateur. On met dans l'accumulateur l'élément de pile situé maintenant à la profondeur 7 (sans le dépiler).
18PUSHACCOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On empile le contenu de l'accumulateur. On met dans l'accumulateur l'élément de pile situé maintenant à la profondeur n (sans le dépiler). On avancera l'indice de 2 pour aller à la prochaine instruction.
19POPOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On dépile n éléments de la pile (sans garder leur valeur). On avancera l'indice de 2 pour aller à la prochaine instruction.
20ASSIGNOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On remplace la valeur située dans la pile à la profondeur n par celle dans l'accumulateur. On met 1 dans l'accumulateur. On avancera l'indice de 2 pour aller à la prochaine instruction.
143STOPL'exécution de la machine se termine. Elle retourne la valeur contenue dans l'accumulateur.
92CHECK_SIGNALS Traite les signaux s'il y en a. Nous nous contenterons de ne rien faire.

Branchements

CodeNomAction
84BRANCHOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On avancera l'indice de n + 1 pour aller à la prochaine instruction.
85BRANCHIFOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. Si la valeur dans l'accumulateur n'est pas l'encodage de false, on avancera l'indice de n+1 pour aller à la prochaine instruction, sinon on l'avancera de 2.
86BRANCHIFNOTOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. Si la valeur dans l'accumulateur est l'encodage de false, on avancera l'indice de n+1 pour aller à la prochaine instruction, sinon on l'avancera de 2.
87SWITCHOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. Le tableau de code contient alors les valeurs suivantes :

Si la valeur dans l'accumulateur est l'encodage de l'entier i on avancera l'indice de ci + 2 pour aller à la prochaine instruction.
88BOOLNOTL'accumulateur contient l'encodage d'un booléen. On le remplace par l'encodage de sa négation.
121EQ On dépile une valeur et on la compare à la valeur contenue dans l'accumulateur. Si c'est la même, on remplace l'accumulateur par l'encodage de true, sinon on remplace l'accumulateur par l'encodage de false.
NB: les valeurs comparées peuvent ne pas être des encodages d'entiers.
122NEQ On dépile une valeur et on la compare à la valeur contenue dans l'accumulateur. Si c'est la même, on remplace l'accumulateur par l'encodage de false, sinon on remplace l'accumulateur par l'encodage de true.
NB: les valeurs comparées peuvent ne pas être des encodages d'entiers.

Entiers

CodeNomAction
99CONST0On met dans l'accumulateur l'encodage de 0.
100CONST1On met dans l'accumulateur l'encodage de 1.
101CONST2On met dans l'accumulateur l'encodage de 2.
102CONST3On met dans l'accumulateur l'encodage de 3.
103CONSTINT On regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On met dans l'accumulateur l'encodage de n. On avancera l'indice de 2 pour aller à la prochaine instruction.
104PUSHCONST0 On empile le contenu de l'accumulateur. On met dans l'accumulateur l'encodage de 0.
105PUSHCONST1 On empile le contenu de l'accumulateur. On met dans l'accumulateur l'encodage de 1.
106PUSHCONST2 On empile le contenu de l'accumulateur. On met dans l'accumulateur l'encodage de 2.
107PUSHCONST3 On empile le contenu de l'accumulateur. On met dans l'accumulateur l'encodage de 3.
108PUSHCONSTINT On regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On empile l'accumulateur. On met dans l'accumulateur l'encodage de n. On avancera l'indice de 2 pour aller à la prochaine instruction.
109NEGINT L'accumulateur contient l'encodage d'un entier n. On le remplace par l'encodage de -n.
110ADDINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage de n + m.
111SUBINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage de n - m.
112MULINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage de n * m.
113DIVINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. Si m vaut 0, on affichera Fatal error: exception Division_by_zero sur la sortie d'erreur et on quittera avec le code d'erreur 2. Sinon, on remplace l'accumulateur par l'encodage de n / m.
114MODINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. Si m vaut 0, on affichera Fatal error: exception Division_by_zero sur la sortie d'erreur et on quittera avec le code d'erreur 2. Sinon, on remplace l'accumulateur par l'encodage de n % m.
115ANDINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage du et bit à bit entre n et m.
116ORINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage du ou bit à bit entre n et m.
117XORINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage du ou exclusif bit à bit entre n et m.
118LSLINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage du décalage de n de m bits vers la gauche.
119LSRINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage du décalage de n de m bits vers la droite. On ne préserve pas forcément le signe de n.
120ASRINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage du décalage de n de m bits vers la droite. On préserve le signe de n.
123LTINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage du booléen qui dit si n est strictement plus petit que m.
124LEINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage du booléen qui dit si n est plus petit que ou égal à m.
125GTINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage du booléen qui dit si n est strictement plus grand que m.
126GEINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage du booléen qui dit si n est plus grand que ou égal à m.
137ULTINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage du booléen qui dit si n est strictement plus petit que m, pour la comparaison non signée.
138UGEINT L'accumulateur contient l'encodage d'un entier n. On dépile une valeur qui est l'encodage d'un entier m. On remplace l'accumulateur par l'encodage du booléen qui dit si n est plus grand que ou égal à m, pour la comparaison non signée.
127OFFSETINT L'accumulateur contient l'encodage d'un entier n. On regarde dans la tableau de code l'entier m situé à la case suivant celle de l'indice. On remplace l'accumulateur par l'encodage de n + m. On avancera l'indice de 2 pour aller à la prochaine instruction.
129ISINT On remplace l'accumulateur par l'encodage de true si l'accumulateur actuel contient l'encodage d'un entier, sinon par l'encodage de false. Il s'agira donc de tester si la valeur contenue dans l'accumulateur est impaire ou non.
131BEQ L'accumulateur contient l'encodage d'un entier n. On regarde dans la tableau de code l'entier m situé à la case suivant celle de l'indice, et l'entier c situé à la case encore après. Si les entiers n et m sont égaux, on avancera l'indice de c+2 pour aller à la prochaine instruction, sinon on l'avancera de 3.
132BNEQ L'accumulateur contient l'encodage d'un entier n. On regarde dans la tableau de code l'entier m situé à la case suivant celle de l'indice, et l'entier c situé à la case encore après. Si les entiers n et m sont différents, on avancera l'indice de c+2 pour aller à la prochaine instruction, sinon on l'avancera de 3.
133BLTINT L'accumulateur contient l'encodage d'un entier n. On regarde dans la tableau de code l'entier m situé à la case suivant celle de l'indice, et l'entier c situé à la case encore après. Si m est strictement plus petit que n, on avancera l'indice de c+2 pour aller à la prochaine instruction, sinon on l'avancera de 3.
134BLEINT L'accumulateur contient l'encodage d'un entier n. On regarde dans la tableau de code l'entier m situé à la case suivant celle de l'indice, et l'entier c situé à la case encore après. Si m est plus petit que ou égal à n, on avancera l'indice de c+2 pour aller à la prochaine instruction, sinon on l'avancera de 3.
135BGTINT L'accumulateur contient l'encodage d'un entier n. On regarde dans la tableau de code l'entier m situé à la case suivant celle de l'indice, et l'entier c situé à la case encore après. Si m est strictement plus grand que n, on avancera l'indice de c+2 pour aller à la prochaine instruction, sinon on l'avancera de 3.
136BGEINT L'accumulateur contient l'encodage d'un entier n. On regarde dans la tableau de code l'entier m situé à la case suivant celle de l'indice, et l'entier c situé à la case encore après. Si m est plus grand que ou égal à n, on avancera l'indice de c+2 pour aller à la prochaine instruction, sinon on l'avancera de 3.
139BULTINT L'accumulateur contient l'encodage d'un entier n. On regarde dans la tableau de code l'entier m situé à la case suivant celle de l'indice, et l'entier c situé à la case encore après. Si m est strictement plus petit que n pour la comparaison non signée, on avancera l'indice de c+2 pour aller à la prochaine instruction, sinon on l'avancera de 3.
140BUGEINT L'accumulateur contient l'encodage d'un entier n. On regarde dans la tableau de code l'entier m situé à la case suivant celle de l'indice, et l'entier c situé à la case encore après. Si m est plus grand que ou égal à n pour la comparaison non signée, on avancera l'indice de c+2 pour aller à la prochaine instruction, sinon on l'avancera de 3.

Primitives

La machine virtuelle est capable d'appeler un certaine nombre de fonctions primitives en leur passant certains paramètres, à l'aide des instructions ci-dessous. Certains paramètres et valeurs de retour devront être explicitement convertis vers d'autre type. On rappelle qu'en C, pour convertir une expression e en un type t on peut écrire (t) e.
CodeNomAction
93C_CALL1 On regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On appelle la primitive numéro n en lui passant comme paramètre la valeur contenue dans l'accumulateur. On met dans l'accumulateur la valeur retournée par la primitive. On avancera l'indice de 2 pour aller à la prochaine instruction.
94C_CALL2 On regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On dépile une valeur v2. On appelle la primitive numéro n en lui passant comme paramètres la valeur contenue dans l'accumulateur et v2. On met dans l'accumulateur la valeur retournée par la primitive. On avancera l'indice de 2 pour aller à la prochaine instruction.
95C_CALL3 On regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On dépile deux valeurs v2 et v3. On appelle la primitive numéro n en lui passant comme paramètres la valeur contenue dans l'accumulateur, v2 et v3. On met dans l'accumulateur la valeur retournée par la primitive. On avancera l'indice de 2 pour aller à la prochaine instruction.
96C_CALL4 On regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On dépile trois valeurs v2, v3 et v4. On appelle la primitive numéro n en lui passant comme paramètres la valeur contenue dans l'accumulateur, v2, v3 et v4. On met dans l'accumulateur la valeur retournée par la primitive. On avancera l'indice de 2 pour aller à la prochaine instruction.
97C_CALL5 On regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On dépile quatre valeurs v2, v3, v4 et v5. On appelle la primitive numéro n en lui passant comme paramètres la valeur contenue dans l'accumulateur, v2, v3, v4 et v5. On met dans l'accumulateur la valeur retournée par la primitive. On avancera l'indice de 2 pour aller à la prochaine instruction.
98C_CALLN On regarde dans la tableau de code l'entier p situé à la case suivant celle de l'indice et l'entier n situé à la case encore après. On crée un tableau de taille p contenant l'accumulateur dans la case d'indice 0 puis p - 1 valeurs obtenues en dépilant. On appelle la primitive numéro n en lui passant comme paramètres ce tableau et p. On met dans l'accumulateur la valeur retournée par la primitive. On avancera l'indice de 3 pour aller à la prochaine instruction. On n'oubliera pas de libérer le tableau le cas échéant.

En pratique on n'implémentera pas toutes les primitives et on se contentera des suivantes:

Numéro de la primitiveNombre de paramètres attendusValeur retournée
152Le premier paramètre est l'encodage d'un entier n. On alloue en mémoire un tableau de n valeurs qu'on remplit dans chaque case avec le deuxième paramètre. On retourne ce tableau. (On sera amené à faire une conversion explicite.)
2881Le premier paramètre est en fait un FILE * que l'on purge (fflush). On renverra 1.
2931Le premier paramètre est en fait un FILE *. On lira un caractère sur ce flux et on renverra l'encodage du code ASCII de ce caractère.
3021Si le premier paramètre est 1, on renverra stdin (en utilisant une conversion explicite pour obtenir une valeur).
3041Si le premier paramètre est 3, on renverra stdout (en utilisant une conversion explicite pour obtenir une valeur).
Si le premier paramètre est 5, on renverra stderr (en utilisant une conversion explicite pour obtenir une valeur).
3102 Le premier paramètre est en fait un FILE * f. Le deuxième est l'encodage d'un entier n. On affiche sur le flux f le caractère de code ASCII n.

Accès et écriture en mémoire

On va considérer une version très réduite de la gestion de la mémoire en OCaml. En effet, celle-ci repose sur l'utilisation d'un garbage collector dont le rôle est de libérer automatiquement les emplacements mémoires qui ne sont plus utiles pour le programme, et l'implémentation d'un tel outil nous entraînerait bien plus loin que ce simple projet. En pratique, les blocs mémoire OCaml contiennent donc non seulement des valeurs mais également des données supplémentaires qui seront utilisées par le garbage collector, notamment la taille de ces blocs. Ici, on se contentera de simple tableaux de valeurs alloués avec malloc. Ces blocs ne seront donc jamais libérés avant la fin de la machine virtuelle. On sera parfois amené à faire des conversions explicites, pour considérer des tableaux de valeurs comme des valeurs.
Mémoire globale
CodeNomAction
53GETGLOBALOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On met dans l'accumulateur la valeur à la case n du tableau de valeurs globales. On avancera l'indice de 2 pour aller à la prochaine instruction.
54PUSHGETGLOBALOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On empile le contenu de l'accumulateur. On met dans l'accumulateur la valeur à la case n du tableau de valeurs globales. On avancera l'indice de 2 pour aller à la prochaine instruction.
57SETGLOBALOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On met à la case n du tableau de valeurs globales la valeur contenue dans l'accumulateur. On met 1 dans l'accumulateur. On avancera l'indice de 2 pour aller à la prochaine instruction.
Création de blocs
CodeNomAction
62MAKEBLOCKOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On alloue un tableau de valeurs de taille n. On met dans ce tableau l'accumulateur dans la case d'indice 0 puis n - 1 valeurs obtenues en dépilant. On met dans l'accumulateur ce tableau (en faisant un conversion explicite). On avancera l'indice de 3 pour aller à la prochaine instruction. (Une des cases du tableau de code sera donc ignorée.)
63MAKEBLOCK1On alloue un tableau de valeurs de taille 1. On met dans ce tableau l'accumulateur dans la case d'indice 0. On met dans l'accumulateur ce tableau (en faisant un conversion explicite). On avancera l'indice de 2 pour aller à la prochaine instruction. (Une des cases du tableau de code sera donc ignorée.)
64MAKEBLOCK2On alloue un tableau de valeurs de taille 2. On met dans ce tableau l'accumulateur dans la case d'indice 0, et une valeur dépilée à l'indice 1. On met dans l'accumulateur ce tableau (en faisant un conversion explicite). On avancera l'indice de 2 pour aller à la prochaine instruction. (Une des cases du tableau de code sera donc ignorée.)
65MAKEBLOCK3On alloue un tableau de valeurs de taille 3. On met dans ce tableau l'accumulateur dans la case d'indice 0, et deux valeurs dépilées aux indices 1 et 2. On met dans l'accumulateur ce tableau (en faisant un conversion explicite). On avancera l'indice de 2 pour aller à la prochaine instruction. (Une des cases du tableau de code sera donc ignorée.)
Accès/écriture dans les blocs
CodeNomAction
67GETFIELD0 La valeur contenue dans l'accumulateur est en fait un tableau de valeurs. On la remplace par le contenu de la case d'indice 0 de ce tableau.
68GETFIELD1 La valeur contenue dans l'accumulateur est en fait un tableau de valeurs. On la remplace par le contenu de la case d'indice 1 de ce tableau.
69GETFIELD2 La valeur contenue dans l'accumulateur est en fait un tableau de valeurs. On la remplace par le contenu de la case d'indice 2 de ce tableau.
70GETFIELD3 La valeur contenue dans l'accumulateur est en fait un tableau de valeurs. On la remplace par le contenu de la case d'indice 3 de ce tableau.
71GETFIELDOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. La valeur contenue dans l'accumulateur est en fait un tableau de valeurs. On la remplace par le contenu de la case d'indice n de ce tableau. On avancera l'indice de 2 pour aller à la prochaine instruction.
73SETFIELD0 La valeur contenue dans l'accumulateur est en fait un tableau de valeurs. On remplace le contenu de la case d'indice 0 de ce tableau par une valeur qu'on dépile. On met 1 dans l'accumulateur.
74SETFIELD1 La valeur contenue dans l'accumulateur est en fait un tableau de valeurs. On remplace le contenu de la case d'indice 1 de ce tableau par une valeur qu'on dépile. On met 1 dans l'accumulateur.
75SETFIELD2 La valeur contenue dans l'accumulateur est en fait un tableau de valeurs. On remplace le contenu de la case d'indice 2 de ce tableau par une valeur qu'on dépile. On met 1 dans l'accumulateur.
76SETFIELD3 La valeur contenue dans l'accumulateur est en fait un tableau de valeurs. On remplace le contenu de la case d'indice 3 de ce tableau par une valeur qu'on dépile. On met 1 dans l'accumulateur.
77SETFIELD On regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. La valeur contenue dans l'accumulateur est en fait un tableau de valeurs. On remplace le contenu de la case d'indice n de ce tableau par une valeur qu'on dépile. On met 1 dans l'accumulateur. On avancera l'indice de 2 pour aller à la prochaine instruction.
80GETVECTITEM On dépile une valeur qui est l'encodage d'un entier n. La valeur contenue dans l'accumulateur est en fait un tableau de valeurs. On la remplace par le contenu de la case d'indice n de ce tableau.
81SETVECTITEM On dépile une valeur qui est l'encodage d'un entier n. Puis on dépile une valeur v. La valeur contenue dans l'accumulateur est en fait un tableau de valeurs. On remplace le contenu de la case d'indice n de ce tableau par v. On met 1 dans l'accumulateur.
55GETGLOBALFIELDOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice, et l'entier p situé dans la case encore après. La case n du tableau de valeurs globales est en fait un tableau de valeurs. On met dans l'accumulateur la valeur à la case p de ce tableau. On avancera l'indice de 3 pour aller à la prochaine instruction.
56PUSHGETGLOBALFIELD On empile le contenu de l'accumulateur. On regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice, et l'entier p situé dans la case encore après. La case n du tableau de valeurs globales est en fait un tableau de valeurs. On met dans l'accumulateur la valeur à la case p de ce tableau. On avancera l'indice de 3 pour aller à la prochaine instruction.
128OFFSETREF On regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. La valeur contenue dans l'accumulateur est en fait un tableau de valeur. La case d'indice 0 de ce tableau contient l'encodage d'un entier m. On le remplace par l'encodage de m + n. On met 1 dans l'accumulateur. On avancera l'indice de 2 pour aller à la prochaine instruction.
Atomes

Les atomes permettent en quelque sorte de représenter des blocs de taille 0. On suppose qu'on dispose d'un tableau global de taille 256 de tels blocs. Chacun de ces blocs pourra être créé sous GNU/Linux à l'aide de malloc(0).

CodeNomAction
58ATOM0 On met dans l'accumulateur l'atome d'indice 0.
59ATOMOn regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On met dans l'accumulateur l'atome d'indice n. On avancera l'indice de 2 pour aller à la prochaine instruction.
60PUSHATOM0 On empile le contenu de l'accumulateur. On met dans l'accumulateur l'atome d'indice 0.
61PUSHATOM On empile le contenu de l'accumulateur. On regarde dans la tableau de code l'entier n situé à la case suivant celle de l'indice. On met dans l'accumulateur l'atome d'indice n. On avancera l'indice de 2 pour aller à la prochaine instruction.

Format de fichier d'entrée

Les exécutables au format bytecode produits par le compilateur d'OCaml sont assez complexes. Ils contiennent le tableau de codes, des informations pour créer les valeurs globales, et d'autres données.

Pour simplifier, on utilisera un format de fichier spécialement créé pour ce projet, qui contiendra uniquement le tableau de codes et le tableau des valeurs globales déjà initialisé. Un fichier dans ce format sera structuré de la manières suivante :

Exécutable demandé

L'exécutable final prendra en premier argument le nom d'un fichier au format décrit dans la section précédente. Il exécutera la machine virtuelle sur ce fichier d'entrée.

Si un deuxième argument est fourni, et qu'il est égal à --print-end-machine, on affichera à la fin de l'exécution l'état final de la machine virtuelle, de la façon suivante :

Exemple de sortie dans ce cas :

Index: 54
Accumulator: 1
Stack:
15
3
1
25
Global:
0 139752620924288
1 139752620924336
2 139752620924384
3 139752620924424
4 139752620924480
5 139752620924528
6 139752620924584
7 139752620924632
8 139752620924680
9 139752620924728
10 139752620924776
11 139752620924824
12 15

Exemples

On trouvera dans le fichier bytecode.tgz un certain nombre de fichiers d'entrées.

D'autres exemples seront possiblement proposés d'ici la date de rendu.

Lors de la correction, votre programme sera testé avec des exemples qui n'auront pas été fournis à l'avance ; faire passer les exemples ne suffira donc pas, il faudra bien respecter la spécification.

Bonus

Les bonus ne sont à réaliser que si le reste fonctionne parfaitement.


Vous devez avoir lu jusqu'ici avant de commencer.