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 :
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.
Vous devez rendre
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.
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é.
int;long int.
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.
| Code | Nom | Action |
| 0 | ACC0 | On met dans l'accumulateur l'élément au sommet de la pile (sans le dépiler). |
| 1 | ACC1 | On met dans l'accumulateur l'élément en dessous du sommet de la pile (sans le dépiler). |
| 2 | ACC2 | On met dans l'accumulateur l'élément à la profondeur 2 dans la pile (sans le dépiler). |
| 3 | ACC3 | On met dans l'accumulateur l'élément à la profondeur 3 dans la pile (sans le dépiler). |
| 4 | ACC4 | On met dans l'accumulateur l'élément à la profondeur 4 dans la pile (sans le dépiler). |
| 5 | ACC5 | On met dans l'accumulateur l'élément à la profondeur 5 dans la pile (sans le dépiler). |
| 6 | ACC6 | On met dans l'accumulateur l'élément à la profondeur 6 dans la pile (sans le dépiler). |
| 7 | ACC7 | On met dans l'accumulateur l'élément à la profondeur 7 dans la pile (sans le dépiler). |
| 8 | ACC | 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. |
| 9 | PUSH | On empile le contenu de l'accumulateur. |
| 10 | PUSHACC0 | On 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). |
| 11 | PUSHACC1 | On 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). |
| 12 | PUSHACC2 | On 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). |
| 13 | PUSHACC3 | On 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). |
| 14 | PUSHACC4 | On 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). |
| 15 | PUSHACC5 | On 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). |
| 16 | PUSHACC6 | On 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). |
| 17 | PUSHACC7 | On 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). |
| 18 | PUSHACC | On 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. |
| 19 | POP | On 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. |
| 20 | ASSIGN | On 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. |
| 143 | STOP | L'exécution de la machine se termine. Elle retourne la valeur contenue dans l'accumulateur. |
| 92 | CHECK_SIGNALS | Traite les signaux s'il y en a. Nous nous contenterons de ne rien faire. |
| Code | Nom | Action |
| 84 | BRANCH | On 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. |
| 85 | BRANCHIF | On 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. |
| 86 | BRANCHIFNOT | On 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. |
| 87 | SWITCH | On 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. |
| 88 | BOOLNOT | L'accumulateur contient l'encodage d'un booléen. On le remplace par l'encodage de sa négation. |
| 121 | EQ |
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. |
| 122 | NEQ |
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. |
| Code | Nom | Action |
| 99 | CONST0 | On met dans l'accumulateur l'encodage de 0. |
| 100 | CONST1 | On met dans l'accumulateur l'encodage de 1. |
| 101 | CONST2 | On met dans l'accumulateur l'encodage de 2. |
| 102 | CONST3 | On met dans l'accumulateur l'encodage de 3. |
| 103 | CONSTINT | 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. |
| 104 | PUSHCONST0 | On empile le contenu de l'accumulateur. On met dans l'accumulateur l'encodage de 0. |
| 105 | PUSHCONST1 | On empile le contenu de l'accumulateur. On met dans l'accumulateur l'encodage de 1. |
| 106 | PUSHCONST2 | On empile le contenu de l'accumulateur. On met dans l'accumulateur l'encodage de 2. |
| 107 | PUSHCONST3 | On empile le contenu de l'accumulateur. On met dans l'accumulateur l'encodage de 3. |
| 108 | PUSHCONSTINT | 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. |
| 109 | NEGINT | L'accumulateur contient l'encodage d'un entier n. On le remplace par l'encodage de -n. |
| 110 | ADDINT | 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. |
| 111 | SUBINT | 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. |
| 112 | MULINT | 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. |
| 113 | DIVINT |
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.
|
| 114 | MODINT |
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.
|
| 115 | ANDINT | 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. |
| 116 | ORINT | 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. |
| 117 | XORINT | 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. |
| 118 | LSLINT | 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. |
| 119 | LSRINT | 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. |
| 120 | ASRINT | 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. |
| 123 | LTINT | 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. |
| 124 | LEINT | 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. |
| 125 | GTINT | 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. |
| 126 | GEINT | 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. |
| 137 | ULTINT | 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. |
| 138 | UGEINT | 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. |
| 127 | OFFSETINT | 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. |
| 129 | ISINT |
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.
|
| 131 | BEQ | 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. |
| 132 | BNEQ | 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. |
| 133 | BLTINT | 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. |
| 134 | BLEINT | 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. |
| 135 | BGTINT | 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. |
| 136 | BGEINT | 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. |
| 139 | BULTINT | 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. |
| 140 | BUGEINT | 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. |
(t) e.
| Code | Nom | Action |
| 93 | C_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. |
| 94 | C_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. |
| 95 | C_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. |
| 96 | C_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. |
| 97 | C_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. |
| 98 | C_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 primitive | Nombre de paramètres attendus | Valeur retournée |
| 15 | 2 | Le 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.) |
| 288 | 1 | Le premier paramètre est en fait
un FILE * que l'on purge (fflush). On
renverra 1. |
| 293 | 1 | Le 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. |
| 302 | 1 | Si le premier paramètre est 1, on
renverra stdin (en utilisant une conversion explicite
pour obtenir une valeur).
|
| 304 | 1 | Si 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).
|
| 310 | 2 | 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. |
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.
| Code | Nom | Action |
| 53 | GETGLOBAL | On 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. |
| 54 | PUSHGETGLOBAL | On 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. |
| 57 | SETGLOBAL | On 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. |
| Code | Nom | Action |
| 62 | MAKEBLOCK | On 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.) |
| 63 | MAKEBLOCK1 | On 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.) |
| 64 | MAKEBLOCK2 | On 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.) |
| 65 | MAKEBLOCK3 | On 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.) |
| Code | Nom | Action |
| 67 | GETFIELD0 | 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. |
| 68 | GETFIELD1 | 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. |
| 69 | GETFIELD2 | 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. |
| 70 | GETFIELD3 | 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. |
| 71 | GETFIELD | 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 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. |
| 73 | SETFIELD0 | 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. |
| 74 | SETFIELD1 | 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. |
| 75 | SETFIELD2 | 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. |
| 76 | SETFIELD3 | 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. |
| 77 | SETFIELD | 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. |
| 80 | GETVECTITEM | 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. |
| 81 | SETVECTITEM | 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. |
| 55 | GETGLOBALFIELD | 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. |
| 56 | PUSHGETGLOBALFIELD | 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. |
| 128 | OFFSETREF | 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. |
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).
| Code | Nom | Action |
| 58 | ATOM0 | On met dans l'accumulateur l'atome d'indice 0. |
| 59 | ATOM | 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. |
| 60 | PUSHATOM0 | On empile le contenu de l'accumulateur. On met dans l'accumulateur l'atome d'indice 0. |
| 61 | PUSHATOM | 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. |
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 :
SOBF ;
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 :
Index: suivi de la valeur de l'indice ;
Accumulator: suivi de la
valeur contenue dans l'accumulateur ;
Stack:, puis, à la ligne, les
valeurs contenues dans la pile, une par ligne, en commençant par
celle au sommet ;
Global:, puis, à la ligne,
les valeurs contenues dans le tableau de valeurs globales, une par
ligne, en les précédant du numéro d'indice dans le tableau.
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
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.
Les bonus ne sont à réaliser que si le reste fonctionne parfaitement.
Vous devez avoir lu jusqu'ici avant de commencer.