La machine alien virtuelle (VAM pour Virtual Alien Machine) s'apparente à un pseudo assembleur x86 simplifié.
Le système semble disposer d'un espace mémoire composé de 2 segments contigus, chacun de taille 0x10000 et initialisés avec des octets 0x00 :
0x00000 à 0x0ffff : segment de pile, de type LIFO0x10000 à 0x1ffff : segment de code, contenant les instructions assembleurIl ne semble y avoir aucun mécanisme de protection de l'espace mémoire.
Les adresses sont sur 32 bits (4 octets).
Les entiers manipulés sont soit sur 8 bits (1 octet) soit sur 32 bits (4 octets en little endian).
Il y a 8 variables fixes ou registres 32 bits (4 octets) :
0x00, appelés M, U, L, D, E, R. (Est-ce un pied de nez ou Fox serait-il l'un des leurs ?)P : pointeur d'instruction initialisé à 0x10000S : pointeur de pile initialisé à 0x0ffffLe jeu d'instruction de la VAM est à taille variable. Sauf cas particulier décrit dans cette section (appel ou retour de fonction, saut conditionnel...),
à chaque cycle le pointeur d'instruction P pointe au début d'une instruction i. L'instruction i est lue entièrement avant d'être
exécutée ; ceci implique que le pointeur d'instruction P est incrémenté (a minima de 1 octet, mais le plus souvent d'autant d'octets que d'opérandes) pour
pointer sur l'instruction suivante avant que l'instruction i ne soit exécutée.
Attention également, dans certains cas (explicités également) les opérandes peuvent être de taille
variable
Nous avons identifié les instructions / opérations suivantes :
| Assembleur | Instruction | Description |
|---|---|---|
# | NOP | opération nulle |
? | gets() | réserve U octets sur la pile (S = S - U) ; lit les caractères sur l'entrée standard et les copie sur la pile à l'adresse stockée dans S. |
@ | puts() | affiche la chaîne de caractères (jusqu'au caractère terminal \0) présente à l'adresse stockée dans M. |
+ Rx Ry | ADD Rx,Ry | registre Rx = valeur du registre Rx + valeur du registre Ry. |
- Rx Ry | SUB Rx,Ry | registre Rx = valeur du registre Rx - valeur du registre Ry. |
* Rx Ry | MUL Rx,Ry | registre Rx = valeur du registre Rx * valeur du registre Ry. |
& Rx Ry | AND Rx,Ry | registre Rx = ET LOGIQUE entre la valeur du registre Rx et la valeur du registre Ry. |
| Rx Ry | OR Rx,Ry | registre Rx = OU LOGIQUE entre la valeur du registre Rx et la valeur du registre Ry. |
^ Rx Ry | XOR Rx,Ry | registre Rx = OU EXCLUSIF entre la valeur du registre Rx et la valeur du registre Ry. |
: Rx Ry | MOV Rx,Ry | registre Rx = valeur du registre Ry. |
= Rx <value> | MOV Rx,value | copie la valeur 32 bits value dans le registre Rx. |
< Rx | POP Rx | copie la valeur sur la pile pointée par S dans le registre Rx ; le pointeur de pile est mis à jour (S = S + 4). |
> Rx | PUSH Rx | le pointeur de pile est mis à jour (S = S - 4) ; copie la valeur du registre Rx sur la pile pointée par S. |
( <n> <fname> | CALL <fname> | appelle la fonction ayant pour nom fname ; n (entier sur 8 bits) correspondant au nombre de caractères du nom de la fonction (cf. section suivante). |
) | RET | retour de fonction (cf. section suivante). |
! <shift> | JNZ <shift> | si M != 0, le programme continue en P = P + 1 + shift (entier 32 bits signé en little endian), sinon le programme continue en P = P + 5. |
$ <size> <data> | PUSH <data> | S = S - size ; copie les size (entier 32 bits en little endian) octets suivants sur la pile ; le programme continue en P = P + 5 + size. |
. | END | fin du programme |
a de la fonction ayant pour nom fname.(S = S - 4 ; [S] = P + 2 + n) (pour rappel, n correspond au nombre de caractères du nom de la fonction).P = a.P = adresse présente sur la pile pointée par S ; S = S + 4.P.Une fonction est définie par : { <n> <fname> } <code> où n (entier sur 8 bits) donne le nombre de caractères du nom fname de la fonction. n et fname sont encadrés par des accolades.
Lors de son chargement, un programme est entièrement copié en mémoire à partir de l'adresse 0x10000.
Ensuite, le système recherche une fonction main dans la mémoire, et démarre son execution à celle-ci.