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é à 0x10000
S
: pointeur de pile initialisé à 0x0ffff
Le 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.