# Introduction

Ce didacticiel est destiné aux utilisateurs maîtrisant la programmation en C ou C++, ainsi que l'architecture de la Simple Virtual Machine.

Dans ce didacticiel, vous allez interagir avec la machine virtuelle depuis une extension.

Le temps de lecture de ce didacticiel est estimé à 20 minutes.

# Mise en place

Pour commencer, créez le canevas de l'extension dans le fichier interactions.svm_plugin en utilisant ce code :

    PLUGIN interact

    DEFINE

    INSTRUCTION interact.jump INT
    %{
    %}

    INSTRUCTION interact.write INT VALUE ?
    %{
    %}

    INSTRUCTION interact.read INT -> VALUE ?
    %{
    %}

# Interface programmatique

## Première instruction

Modifiez le code de l'extension pour ajouter le code de la première instruction :

    PLUGIN interact

    DEFINE

    INSTRUCTION interact.jump INT
    %{
    	auto address = ARGV_VALUE(0,integer);
    	if(address<0)
    	{
    		ERROR_INTERNAL(PROCESSOR,"Invalid address");
    	}
    	::svm_processor_jump_local(svm,CURRENT(kernel),address);
    %}

    INSTRUCTION interact.write INT VALUE ?
    %{
    %}

    INSTRUCTION interact.read INT -> VALUE ?
    %{
    %}

Générez et compilez l'extension. Ensuite, créez cette application de test dans un fichier exécutable interactions.svm :

    #!/usr/bin/env svm
    LOG
    PLUGIN "svmcom.so"
    LOCAL PLUGIN "svmplugininteract/libsvminteract.so"
    PROCESS "first"
    	CODE "main" INLINE
    		:debug BREAK
    		:memory INT/i, INT/s
    		0 -> &i
    		0 -> &s
    		:shift @&i &s
    		:goto end :unless @&s IN &0*10000
    		:shift &i
    		:interact.jump 4
    	:label end
    		:com.message @&i " => " @&s
    	END
    END

Enfin, lancez l'application (en mode débugueur si vous le souhaitez) :

    ./interactions.svm
    141 => 10011

En fait, cette extension introduit un nouveau type de saut : un saut local sans utiliser d'étiquette, où il suffit de préciser directement l'adresse locale de l'instruction cible en argument de l'instruction de saut !

Pour bien comprendre comment cela est possible, il faut étudier le code de l'instruction :

- la première ligne lit l'argument de l'instruction grâce à la macro `ARGV_VALUE` définie par le générateur d'extension,
- les quatre lignes suivantes permettent simplement de vérifier la valeur d'entrée,
- la dernière ligne est le cœur de l'instruction : elle modifie un processeur de la machine (ici, le processeur courant) pour réaliser un saut à l'adresse indiquée.

Cette fonction `svm_processor_jump_local` fait en réalité partie d'un grand nombre de fonctions C et de types C, qui sont l'interface programmatique de la machine virtuelle : cela permet aux extensions d'interagir avec la machine virtuelle afin d'en modifier l'état.

Pour trouver le fichier d'interface de la machine, vous pouvez vous aider de l'extension générée :

    cat svmplugininteract/src/plugin.h | tail -n 4

    #include "/usr/local/include/svm/svm_compatibility.h"
    #include "/usr/local/include/svm/svm.h"

La dernière ligne indique le fichier d'interface programmatique. Vous pouvez ouvrir ce fichier pour regarder le prototype de la fonction C `svm_processor_jump_local`.

Si vous observez le code généré de l'extension, vous noterez que les macros définies par le générateur d'extension utilisent aussi des fonctions de l'interface programmatique.

## Autres instructions

Modifiez le code de l'extension pour ajouter le code des deux autres instructions :

    PLUGIN interact

    DEFINE

    INSTRUCTION interact.jump INT
    %{
    	auto address = ARGV_VALUE(0,integer);
    	if(address<0)
    	{
    		ERROR_INTERNAL(PROCESSOR,"Invalid address");
    	}
    	::svm_processor_jump_local(svm,CURRENT(kernel),address);
    %}

    INSTRUCTION interact.write INT VALUE ?
    %{
    	auto address = ARGV_VALUE(0,integer);
    	SVM_Value value = NEW_NULL_VALUE(automatic);
    	if(argc>1)
    	{
    		value = ::svm_parameter_value_get(svm,argv[1]);
    	}
    	if(::svm_memory_address_is_writable(svm,CURRENT(kernel),address,value))
    	{
    		::svm_memory_write_address(svm,CURRENT(kernel),address,value);
    	}
    %}

    INSTRUCTION interact.read INT -> VALUE ?
    %{
    	auto address = ARGV_VALUE(0,integer);
    	if(::svm_memory_address_is_initialised(svm,CURRENT(kernel),address))
    	{
    		return ::svm_memory_read_address(svm,CURRENT(kernel),address);
    	}
    	return NEW_NULL_VALUE(automatic);
    %}

Modifiez le code de l'application de test pour obtenir :

    #!/usr/bin/env svm
    LOG
    PLUGIN "svmrun.so"
    LOCAL PLUGIN "svmplugininteract/libsvminteract.so"
    PROCESS "others"
    	CODE "main" INLINE
    		:debug BREAK
    		:memory INT/i, INT/j, STR/s, INT*2/r
    		:interact.write 1 17
    		:interact.write 2 17
    		:interact.read 1 -> (r/0)
    		:interact.read 2 -> (r/1)
    		:run.coredump STDOUT
    	END
    END

Puis lancez l'application :

    ./interactions.svm
    Kernel:
    State: R, transmit_interruption

    Processor:
     State:
       Next instruction: <main:1/6> (Current one: <main:1/5>)
       Current memory  : &0*0
       Allocated memory: &0*5
       Aliases         : i j r s
       Flags           :
       Cascaded flags  :
       Local interruption handlers:
       Cascaded local interruption handlers:
     Saved states:
     Global interruption handlers:

    Code at <main:1/5>:
         :memory INT/i , INT/j , STR/s , INT*2/r    # <0> @(main, line 1)
         :interact.write 1 17       # <1> @(main, line 2)
         :interact.write 2 17       # <2> @(main, line 3)
         :interact.read 1 -> (r/0)  # <3> @(main, line 4)
         :interact.read 2 -> (r/1)  # <4> @(main, line 5)
    ====== HERE ======
         :run.coredump STDOUT       # <5> @(main, line 6) SYSTEM
    ==================
     Labels:
     Symbols:

    Memory:
      &0: INT
      &1: INT 17
      &2: STR
      &3: INT 17
      &4: INT
     Aliases:
      i -> &0*1
      j -> &1*1
      r -> &3*2
      s -> &2*1
     Free space:

Les deux instructions ici définissent des opérations mémoire paresseuses, qui ne réalisent l'action de lecture ou d'écriture en mémoire que lorsque cela est possible, mais sans lever d'interruption !

Ici, il convient de remarquer que :

- Les fonctions de l'interface programmatique ne sont pas nommées au hasard : elles commencent toutes par "svm", suivi du nom du concept concerné par la fonction, pour se terminer par l'action faite par la fonction.
- Toutes les fonctions prennent un premier paramètre nommé "svm".
- Un type C apparaît dans une fonction, qui commence par le préfixe "SVM".
- Les arguments de l'instruction semblent provenir des variables "argc" et "argv" comme dans une fonction "main" du C ou du C++. Nous reviendrons plus tard dans ce didacticiel sur ces variables.

- Les éléments d'une extension peuvent interagir avec la machine virtuelle au travers d'appels aux fonctions de l'interface programmatique de la machine virtuelle.
- Les fonctions et les types de cette interface suivent des règles de nommage pour retrouver rapidement leur usage.
- Toutes les fonctions de l'interface programmatique (sauf mention contraire) ont un premier argument commun souvent nommé "svm".

# Doxygen

L'interface programmatique, sous sa forme originelle de fichier d'en-tête C, est relativement peu pratique pour retrouver des fonctions en dehors des recherches de texte brut.

Pour pallier à cette limitation, ce fichier d'en-tête C est écrit de manière à pouvoir produire une documentation en HTML via l'utilitaire "Doxygen". (Reportez vous à la documentation de Doxygen pour la génération de cette documentation.)

Une fois généré par Doxygen, le site permet de naviguer dans l'interface de manière thématique grâce aux modules.

Chaque fonction possède une aide rapide qui permet de comprendre ses paramètres, son éventuelle valeur de retour, son fonctionnement et les interruptions qu'elle peut lever. Parfois, lorsque la fonction est complexe à utiliser, un exemple peut être fourni.

# Variables C/C++ magiques

## Notion de variable magique

Lors de l'écriture des instructions, certaines variables C/C++ apparaissent dans le code sans pour autant avoir été définies : ces variables sont définies par le générateur d'extension et peuvent être considérées comme des variables magiques.

## Trouver les variables magiques

Les variables magiques peuvent varier d'un bloc de code à l'autre dans le fichier source de l'extension.

Les seules méthodes efficaces pour trouver les variables magiques d'un bloc de code sont :

- Lire la documentation de la commande "svm_plugin" ! Ces variables sont décrites dans la documentation, avec ce qu'elles représentent.
- Générer le code C/C++ de l'extension et observer le prototype de la fonction C associée au bloc de code.

Ouvrez le code C++ de l'extension et lisez les prototypes des fonctions liées aux instructions : les variables magiques sont "svm", "argc" et "argv".

Vous pouvez noter que la variable magique "svm" existe dans toutes les fonctions exceptée celle générée pour la configuration de l'extension. Pour éviter une instabilité de la machine virtuelle en cours de fonctionnement, il est impératif de respecter l'emploi de la variable magique "svm" en tant que premier paramètre des fonctions de l'interface programmatique.

- Les variables magiques sont des variables C/C++ qui proviennent de la génération de l'extension.
- Elles peuvent être utilisées dans les blocs de code où elles sont présentes sans définition préalable.
- Sauf exception, la variable magique "svm" correspond au premier paramètre des fonctions de l'interface programmatique.

# Conclusion

Vous venez de voir le mécanisme utilisé par une extension pour modifier la machine virtuelle.

En prime, l'accès à l'interface programmatique est simplifiée par l'utilisation de Doxygen permettant une documentation d'accès plus confortable.

Enfin, les variables magiques créées par le génération d'extension ont été exposées. Leur utilisation sera largement pratiquée dans les didacticiels suivants.