# 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 manipuler la mémoire depuis une extension.

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

# Mise en place

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

    PLUGIN ram

    DEFINE

# Lecture et écriture

## Lecture

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.read INT:address -> VALUE
    %{
    	auto a = ARGV_VALUE(0,integer);
    	return ::svm_memory_read_address(svm,CURRENT(kernel),a);
    %}

    INSTRUCTION ram.read_internal INT:address INT:type -> VALUE
    %{
    	auto a = ARGV_VALUE(0,integer);
    	auto t = ARGV_VALUE(1,integer);
    	if((t<0) or (t>=PLUGIN_TYPE))
    	{
    		ERROR_INTERNAL(FAILURE,"Wrong type");
    	}
    	return ::svm_memory_read_address_type_internal(svm,CURRENT(kernel),a,static_cast<SVM_Type>(t));
    %}

    INSTRUCTION ram.read_external INT:address PEP:type -> VALUE
    %{
    	auto a = ARGV_VALUE(0,integer);
    	SVM_Value_PluginEntryPoint t = ::svm_parameter_value_get(svm,argv[1]);
    	return ::svm_memory_read_address_type_external(svm,CURRENT(kernel),a,t);
    %}

Générez et compilez l'extension. Puis écrivez une petite application de test qui utilise ces trois instructions.

Vous pouvez constater que la première instruction permet de réaliser une lecture sans test de type. Les deux autres instructions vérifient le type en même temps.

## Ecriture

### Par copie

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.write INT:address VALUE
    %{
    	auto a = ARGV_VALUE(0,integer);
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[1]);
    	::svm_memory_write_address(svm,CURRENT(kernel),a,v);
    %}

Générez et compilez l'extension. Puis écrivez une petite application de test qui utilise cette instruction pour écrire une valeur en mémoire.

Ici, la valeur réellement écrite en mémoire n'est pas celle donnée en paramètre de la fonction `svm_memory_write_address`, mais une copie de cette valeur.

### Par déplacement

La copie peut ne pas être voulue lors d'une écriture en mémoire. Soit parce que la valeur ne peut pas être copiée, soit parce que cette copie est inutile.

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.write INT:address VALUE
    %{
    	auto a = ARGV_VALUE(0,integer);
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_String s = ::svm_value_print(svm,v);
    	SVM_Value_String vs = ::svm_value_string_new(svm,s);
    	::svm_value_state_set_movable(svm,vs);
    	::svm_memory_write_address(svm,CURRENT(kernel),a,vs);
    %}

Générez et compilez l'extension. Puis écrivez une petite application de test qui utilise cette instruction. Ici, la représentation textuelle de la valeur est écrite en mémoire.

Cette représentation textuelle, créée dans le code de l'instruction, peut être déplacée en mémoire pour éviter une copie. La valeur est marquée comme déplaçable juste avant d'être écrite en mémoire.

Pour vous en convaincre, modifiez le code de l'extension :

    PLUGIN ram

    code:
    %{
    	SVM_Value_String _s;
    %}

    DEFINE

    INSTRUCTION ram.write INT:address VALUE
    %{
    	auto a = ARGV_VALUE(0,integer);
    	SVM_Value v = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_String s = ::svm_value_print(svm,v);
    	_s = ::svm_value_string_new(svm,s);
    	::svm_variable_scope_set_global(svm,_s);
    	::svm_value_state_set_movable(svm,_s);
    	::svm_memory_write_address(svm,CURRENT(kernel),a,_s);
    %}

    INSTRUCTION ram.test
    %{
    	::svm_value_string_set__raw(svm,_s,"");
    	::svm_variable_scope_set_local(svm,_s);
    %}

Générez et compilez l'extension. Puis écrivez une application contenant ce code :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmpluginram/libsvmram.so"
    PLUGIN "svmcom.so"
    PROCESS "ram"
    	CODE "main" INLINE
    		:memory STR/s
    		:ram.write 0 17
    		:com.message "[" @&s "]"
    		:ram.test
    		:com.message "[" @&s "]"
    	END
    END

Lancez l'application :

    ./memoire.svm
    [17]
    []

La deuxième ligne montre une chaine vide ! Cela est dû au fait que la fonction `svm_value_string_set__raw` a agit sur la valeur qui avait été déplacée en mémoire et conservée comme variable Simple Virtual Machine globale.

Enlevez l'appel de la fonction `svm_value_state_set_movable` puis regénérez et compilez l'extension. Relancez l'application :

    ./memoire.svm
    [17]
    [17]

Cette fois, la modification de la chaine n'a pas altéré la mémoire, car la valeur y a été copiée durant l'écriture en mémoire.

- La fonction de l'interface programmatique `svm_memory_read_address` et ses variantes permettent d'écrire des valeurs en mémoire.
- La fonction de l'interface programmatique `svm_memory_write_address` permet d'écrire une valeur en mémoire.
- La valeur est par défaut copiée dans la mémoire lors d'une écriture, mais peut y être déplacée. Dans ce cas, l'écriture est plus rapide mais peut engendrer des effets de bord.

# Allocation et libération

## Allocation

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.new INT:size INT:address ? -> PTR ?
    %{
    	SVM_Value_Integer s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal(svm,z,AUTOMATIC,s);
    	if(argc==1)
    	{
    		return ::svm_memory_allocate(svm,CURRENT(kernel),z);
    	}
    	else
    	{
    		auto a = ARGV_VALUE(1,integer);
    		return ::svm_memory_allocate_address(svm,CURRENT(kernel),z,a);
    	}
    %}

Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction pour créer un tableau.

Vous pouvez noter que :

- L'allocation mémoire se fait en deux étapes :
  1. d'abord, une variable de type `SVM_Memory_Zone` est créée. Cette variable contient la description du bloc mémoire à allouer,
  2. ensuite, l'allocation à proprement parler est faite en utilisant la description de bloc.
- Il y a deux fonctions d'interface programmatique pour allouer de la mémoire :
  1. `svm_memory_allocate` alloue la mémoire comme l'instruction `:memory` le fait, en choisissant une adresse convenable pour loger la mémoire allouée,
  2. `svm_memory_allocate_address` alloue la mémoire à une adresse donnée. Si l'allocation échoue, un pointeur nul est retourné par la fonction. Cette allocation est utile lorsqu'une contrainte forte d'adresse existe
- Dans les deux allocations, la mémoire allouée a une portée locale : elle sera automatiquement libérée au prochain retour de fonction.

## Libération

En plus des retours de fonctions, l'interface programmatique permet de libérer la mémoire explicitement.

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.delete PTR
    %{
    	SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_memory_free(svm,CURRENT(kernel),p);
    %}

Générez et compilez l'extension. Puis écrivez une application avec ce code :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmpluginram/libsvmram.so"
    PLUGIN "svmcom.so"
    PROCESS "ram"
    	CODE "main" INLINE
    		:debug BREAK
    		:memory INT*10
    		:ram.delete &2*5
    	END
    END

Lancez cette application dans le débugueur. Après l'exécution de l'instruction `:ram.delete`, vous pouvez constater que :

- Dans la mémoire, les adresses correspodant au pointeur `&2*5` ne sont plus définies.
- Dans le processeur, ces adresses sont toutefois toujours marquées comme allouées : elles sont considérées comme de la mémoire locale, ce qui n'a ici aucune incidence sur la suite du code.

- L'interface programmatique permet d'allouer de la mémoire avec la fonction `svm_memory_allocate`, ou `svm_memory_allocate_address` quand la mémoire doit être allouée à une adresse précise.
- Le bloc mémoire est décrit par une variable Simple Virtual Machine de type `SVM_Memory_Zone` qui se construit avec la fonction `svm_memory_zone_new`, puis en ajoutant des types internes avec la fonction `svm_memory_zone_append_internal` et des types extensions avec la fonction `svm_memory_zone_append_external`.
- La mémoire allouée est locale : elle sera supprimée au retour de la fonction courante.
- La mémoire peut être libérée par anticipation avec la fonction `svm_memory_free`. Cette fonction n'altère pas la portée de la mémoire supprimée.

# Alias

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.new STR PTR
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[1]);
    	::svm_memory_add_alias(svm,CURRENT(kernel),n,p);
    %}

    INSTRUCTION ram.delete STR
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_memory_remove_alias(svm,CURRENT(kernel),n);
    %}

    INSTRUCTION ram.get STR -> PTR ?
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	if(::svm_memory_has_alias(svm,CURRENT(kernel),n))
    	{
    		return ::svm_memory_alias_get_pointer(svm,CURRENT(kernel),n);
    	}
    	return NEW_NULL_VALUE(pointer);
    %}

Générez et compilez l'extension. Puis écrivez une application manipulant les alias. Notez que l'instruction permettant de retourner le pointeur associé à un alias ne lève pas d'interruption quand l'alias n'existe pas.

- La fonction `svm_memory_add_alias` de l'interface programmatique permet de créer un alias.
- La fonction `svm_memory_remove_alias` de l'interface programmatique permet de supprimer un alias.
- La fonction `svm_memory_has_alias` de l'interface programmatique permet de détecter si un alias est défini.
- La fonction `svm_memory_alias_get_pointer` de l'interface programmatique permet de récupérer le pointeur associé à un alias.

# Portée

## Mémoire

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.local PTR
    %{
    	SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_memory_scope_set_local(svm,CURRENT(kernel),p);
    %}

    INSTRUCTION ram.global PTR
    %{
    	SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_memory_scope_set_global(svm,CURRENT(kernel),p);
    %}

Générez et compilez l'extension. Puis écrivez une application manipulant la portée de la mémoire, et lancez la dans le débugueur.

Notez que ces fonctions, même si elles sont placées dans le module de mémoire de l'interface programmatique, modifient le processeur et non la mémoire elle-même. En effet, elles indiquent au processeur comment gérer la mémoire lors d'un retour de fonction.

## Alias

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.local STR
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_memory_scope_set_local_alias(svm,CURRENT(kernel),n);
    %}

    INSTRUCTION ram.global STR
    %{
    	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_memory_scope_set_global_alias(svm,CURRENT(kernel),n);
    %}

Générez et compilez l'extension. Puis écrivez une application manipulant la portée de la mémoire, et lancez la dans le débugueur. Cette fois encore, ces fonctions modifient le processeur.

- Les fonctions de modification de portée mémoire altèrent le processeur, pour piloter son comportement lors d'un retour de fonction.
- Les fonctions `svm_memory_scope_set_local` et `svm_memory_scope_set_global` modifient la portée de la mémoire elle-même, sans toucher aux alias.
- Les fonctions `svm_memory_scope_set_local_alias` et `svm_memory_scope_set_global_alias` modifient la portée des alias, sans toucher à la portée de la mémoire.
- Il convient de modifier en même temps

# Etats

## Définie et initialisée

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.defined INT -> BLN
    %{
    	auto a = ARGV_VALUE(0,integer);
    	return NEW_VALUE(boolean,::svm_memory_address_is_defined(svm,CURRENT(kernel),a));
    %}

    INSTRUCTION ram.initialised INT -> BLN
    %{
    	auto a = ARGV_VALUE(0,integer);
    	return NEW_VALUE(boolean,::svm_memory_address_is_initialised(svm,CURRENT(kernel),a));
    %}

Générez et compilez l'extension. Puis écrivez une application utilisant ces deux instructions.

## Types

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.type INT -> STR
    %{
    	auto a = ARGV_VALUE(0,integer);
    	SVM_Type t = ::svm_memory_address_get_type(svm,CURRENT(kernel),a);
    	return NEW_VALUE(string,::svm_type_print(svm,t));
    %}

Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.

## Un raccourci

Lorsqu'une écriture doit être faite en mémoire, il peut être utile de vérifier que cette écriture ne va pas échouer. Avec les fonctions de l'interface programmatique que nous venons de voir, il faut s'assurer que :

1. l'adresse mémoire est bien définie avec `svm_memory_address_is_defined`,
2. le type de l'adresse est compatible avec celui de la valeur avec `svm_memory_address_get_type`.

Ce test peut devenir rapidement fastidieux à écrire. Pour éviter ce code fastidieux, il est possible d'utiliser la fonction `svm_memory_address_is_writable` avec les mêmes paramètres que la fonction d'écriture. Cette fonction réalise tous les tests nécessaires, et indique si l'écriture est réalisable.

- Avec l'interface programmatique, il est possible de tester l'état d'une adresse mémoire : `svm_memory_address_is_defined` pour savoir si l'adresse est définie, `svm_memory_address_is_initialised` pour savoir si elle est initialisée, `svm_memory_address_get_type` pour récupérer son type lorsqu'elle est définie.
- L'ensemble de ces tests sont rassemblés en une seule fonction pour l'écriture en mémoire avec la fonction `svm_memory_address_is_writable`.

# Tableaux

## Lecture

Les instructions, par exemple, peuvent admettre un nombre quelconque d'arguments, en fonction d'expression rationnelle de ses arguments. Parfois, ces arguments peuvent être considérés comme un tableau d'argument. Cependant, il est parfois plus pratique d'utiliser un tableau en mémoire, et de le lire depuis un pointeur passé en argument.

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.read_array PTR -> STR
    %{
    	SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Address a = ::svm_value_pointer_get_address(svm,p);
    	SVM_Size s = ::svm_value_pointer_get_size(svm,p);
    	std::ostringstream oss;
    	oss << "[";
    	for(SVM_Address i=a; i<(a+s) ; ++i)
    	{
    		SVM_Value v = ::svm_memory_read_address(svm,CURRENT(kernel),i);
    		SVM_String vs = ::svm_value_print(svm,v);
    		oss << " " << RAW_STRING(vs);
    	}
    	oss << " ]";
    	return NEW_VALUE(string,NEW_STRING(oss.str()));
    %}

Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.

La lecture du tableau se fait en deux étapes :

1. Décomposer le pointeur en adresse et taille,
2. Boucler sur les adresses entre l'adresse de début et de fin du pointeur. A chaque itération de la boucle, la mémoire est lue sur l'adresse indice de boucle.

Notez qu'il est également possible de lire un tableau d'un seul coup avec la fonction `svm_memory_read_pointer` et ses variantes. Dans ce cas, la valeur retournée est un tableau de valeurs terminé par un pointeur nul.

## Ecriture

### Depuis une structure de données C ou C++

Il est aussi parfois très pratique pour une instruction de retourner un tableau. Dans ce cas, les valeurs sont écrites en mémoire, puis le pointeur vers les données écrites en mémoire est retourné par l'instruction.

Modifiez le code de l'extension :

    PLUGIN ram

    includes:
    %{
    #include <vector>
    %}

    DEFINE

    INSTRUCTION ram.write_array INT -> PTR
    %{
    	auto s = ARGV_VALUE(0,integer);
    	std::vector<long long int> v;
    	for(size_t i=0 ; i<s ; ++i)
    	{
    		v.push_back(i);
    	}
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,INTEGER,v.size());
    	SVM_Value_Pointer p = ::svm_memory_allocate(svm,CURRENT(kernel),z);
    	SVM_Address pa = ::svm_value_pointer_get_address(svm,p);
    	SVM_Size ps = ::svm_value_pointer_get_size(svm,p);
    	auto it = v.begin();
    	for(SVM_Address i=pa ; i<(pa+ps) ; ++i)
    	{
    		SVM_Value_Integer vi = ::svm_value_integer_new(svm,*it);
    		::svm_value_state_set_movable(svm,vi);
    		::svm_memory_write_address(svm,CURRENT(kernel),i,vi);
    		++it;
    	}
    	return p;
    %}

Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.

L'instruction ici commence par construire un vecteur d'entiers, puis :

1. Alloue une zone mémoire permettant de contenir toutes les valeurs du vecteurs,
2. Récupère l'adresse et la taille du pointeur de la zone allouée,
3. Ecrit chaque valeur à une adresse consécutive dans le tableau en mémoire,
4. Retourne le pointeur où se situe les données en mémoire.

### Depuis un tableau interface programmatique

Une alternative est possible lorsque le tableau à écrire en mémoire provient d'un tableau généré par l'interface programmatique.

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.write_array -> PTR
    %{
    	SVM_Value_Interruption *irq = ::svm_machine_list_interruption(svm);
    	SVM_Size s = 0;
    	for(SVM_Value_Interruption *i=irq ; *i ; ++i)
    	{
    		::svm_value_state_set_movable(svm,*i);
    		++s;
    	}
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,INTERRUPTION,s);
    	SVM_Value_Pointer p = ::svm_memory_allocate(svm,CURRENT(kernel),z);
    	::svm_memory_write_pointer(svm,CURRENT(kernel),p,irq);
    	return p;
    %}

Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.

Ici, l'instruction alloue toujours la mémoire et écrit les valeurs en mémoire, mais différemment :

1. Elle doit calculer la taille du tableau pour allouer la mémoire,
2. Elle écrit directement le tableau entier dans le pointeur grâce à la fonction `svm_memory_write_pointer`. De plus, cette fonction écrit l'ensemble des valeurs de manière atomique : soit l'écriture réussit et toutes les valeurs sont écrites, soit l'écriture échoue, et aucune valeur n'est écrite en mémoire.

- La lecture d'un tableau se fait en décomposant un pointeur, puis en parcourant en C ou C++ les adresses du pointeur. Pour chaque adresse du pointeur, la valeur est lue en mémoire.
- L'écriture d'un tableau se fait en allouant de la mémoire (ou en utilisant de la mémoire pré-allouée), puis en écrivant chaque valeur à une adresse du tableau.
- Les fonctions `svm_memory_read_pointer` et `svm_memory_write_pointer` permettent d'accéder à un tableau en mémoire de manière atomique.

# Opérations

## Types

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.type_auto INT
    %{
    	auto a = ARGV_VALUE(0,integer);
    	::svm_memory_address_set_type(svm,CURRENT(kernel),a,::svm_type_new_internal(svm,AUTOMATIC));
    %}

    INSTRUCTION ram.type_plugin INT PEP
    %{
    	auto a = ARGV_VALUE(0,integer);
    	SVM_Value_PluginEntryPoint p = ::svm_parameter_value_get(svm,argv[1]);
    	::svm_memory_address_set_type(svm,CURRENT(kernel),a,::svm_type_new_external(svm,p));
    %}

Générez et compilez l'extension. Puis écrivez une application utilisant ces instructions.

Notez qu'en plus de changer le type associé à une adresse, ces deux fonctions retirent la valeur éventuelle associée à l'adresse modifiée.

## Modification

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.shift INT INT
    %{
    	auto a = ARGV_VALUE(0,integer);
    	SVM_Value_Integer i = ::svm_parameter_value_get(svm,argv[1]);
    	::svm_memory_address_shift(svm,CURRENT(kernel),a,i);
    %}

Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.

La fonction `svm_memory_address_shift` ne fonctionne que sur des adresses ayant le type entier, et modifie la valeur associée à une adresse.

## Accessibilité

### Mémoire

Il est parfois utile de réaliser un traitement sur toute la mémoire qui peut être accédée depuis un pointeur, en suivant récursivement tous les pointeurs.

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.expand PTR -> PTR
    %{
    	SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_Pointer *t = ::svm_memory_pointer_list_accessible(svm,CURRENT(kernel),p);
    	SVM_Size s = 0;
    	for(SVM_Value_Pointer *it=t ; *it ; ++it)
    	{
    		::svm_value_state_set_movable(svm,*it);
    		++s;
    	}
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,POINTER,s);
    	SVM_Value_Pointer r = ::svm_memory_allocate(svm,CURRENT(kernel),z);
    	::svm_memory_write_pointer(svm,CURRENT(kernel),r,t);
    	return r;
    %}

Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.

La fonction `svm_memory_pointer_list_accessible` renverra toujours en premier le pointeur passé en paramètre. Ensuite, elle renvoie une liste de pointeurs qui ne tient pas compte de la topologie réelle de la mémoire, mais une représentation à plat où chaque adresse accessible n'est présente qu'une fois.

### Alias

De la même manière, il est parfois important de récupérer la liste des alias définis sur une zone mémoire.

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.names PTR -> PTR
    %{
    	SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_String *t = ::svm_memory_pointer_list_alias(svm,CURRENT(kernel),p);
    	SVM_Size s = 0;
    	for(SVM_Value_Pointer *it=t ; *it ; ++it)
    	{
    		::svm_value_state_set_movable(svm,*it);
    		++s;
    	}
    	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
    	::svm_memory_zone_append_internal__raw(svm,z,STRING,s);
    	SVM_Value_Pointer r = ::svm_memory_allocate(svm,CURRENT(kernel),z);
    	::svm_memory_write_pointer(svm,CURRENT(kernel),r,t);
    	return r;
    %}

Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.

Si vous tentez de fournir un pointeur à cheval sur un alias, cet alias ne sera pas retourné : la fonction `svm_memory_pointer_list_alias` ne retourne que les alias inclus entièrement dans le pointeur fourni en paramètre.

## Copie

Copier un tableau entier depuis une zone mémoire vers une autre peut être une tâche fastidieuse. Heureusement, l'interface programmatique propose une fonction pour réaliser cette opération.

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.copy PTR PTR
    %{
    	SVM_Value_Pointer s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_Pointer d = ::svm_parameter_value_get(svm,argv[1]);
    	::svm_memory_copy(svm,CURRENT(kernel),s,CURRENT(kernel),d);
    %}

Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.

Vous pouvez remarquer que :

- La copie fonctionne même lorsque la source de la copie chevauche en partie la destination.
- Dans l'appel à la fonction `svm_memory_copy`, l'expression `CURRENT(kernel)` apparaît deux fois. En réalité, cette fonction est capable de copier des valeurs d'une mémoire à une autre mémoire : il suffit d'indiquer un noyau différent à la place d'une de ces deux expressions ! D'une manière plus générale, nous avons dans ce didacticiel manipulé que la mémoire du noyau courant, mais il est possible de modifier n'importe quelle mémoire avec ces fonctions.

## Déplacement

De la même manière, il est possible de déplacer des valeurs.

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.move PTR PTR
    %{
    	SVM_Value_Pointer s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_Pointer d = ::svm_parameter_value_get(svm,argv[1]);
    	::svm_memory_move(svm,CURRENT(kernel),s,CURRENT(kernel),d);
    %}

Générez et compilez l'extension. Puis écrivez une application utilisant cette instruction.

Les mêmes remarques vues pour la copie s'appliquent au déplacement de valeurs.

## Partage

Enfin, il est également possible de partager les valeurs entre plusieurs adresses. Cela est intéressant dans certains cas, notamment lorsqu'une partie des paramètres d'une fonction peut provenir d'une structure de données, et que la fonction peut intervenir dessus : cela donne une vue supplémentaire sur les valeurs.

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.share PTR PTR
    %{
    	SVM_Value_Pointer s = ::svm_parameter_value_get(svm,argv[0]);
    	SVM_Value_Pointer d = ::svm_parameter_value_get(svm,argv[1]);
    	::svm_memory_share(svm,CURRENT(kernel),s,CURRENT(kernel),d);
    %}

Générez et compilez l'extension. Puis écrivez une application avec ce code :

    #!/usr/bin/env svm
    LOG
    LOCAL PLUGIN "svmpluginram/libsvmram.so"
    PLUGIN "svmcom.so"
    PROCESS "ram"
    	CODE "main" INLINE
    		:debug BREAK
    		:memory INT*10/t
    		[ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ] -> t
    		:memory (PTR, INT*3)/p
    		:ram.share (&t+3)*3 (&p+1)*3
    		:call decrease p
    		:memory INT/i
    		0 -> &i
    	:label display
    		:com.message @(t/@&i)
    		:shift &i
    		:goto display :when @&i IN t
    		:shutdown
    	:label decrease
    		:memory INT -> &P
    		1 -> &@&P
    	:label decrease_loop
    		:shift -1 (P/@&@&P)
    		:shift &@&P
    		:goto decrease_loop :when @&@&P IN P
    		:return
    	END
    END

Lancez cette application dans le débugueur. Ouvrez les fenêtres de la mémoire, du processeur et du code, puis exécutez l'application instruction par instruction.

Notez que lorsque l'instruction `:ram.share` s'exécute, rien de visible ne se produit. En revanche, lorsque la fonction "decrease" modifie un de ses paramètres entiers, le tableau d'entiers est également modifié : et pour cause, les valeurs sont physiquement les mêmes grâce à l'instruction `:ram.share` !

- Les opérations sur la mémoire sont très variées.
- La fonction `svm_memory_address_set_type` permet de modifier le type associé à une adresse.
- La fonction `svm_memory_address_shift` permet de modifier un entier en mémoire.
- Les fonctions `svm_memory_pointer_list_accessible` et `svm_memory_pointer_list_alias` permettent de récupérer des informations sur une zone mémoire, comme toute la mémoire accessible depuis une zone mémoire, ou tous les alias contenus dans une zone mémoire.
- Enfin, les fonctions `svm_memory_copy`, `svm_memory_move` et `svm_memory_share` permettent respectivement de copier, déplacer et de partager des zones mémoire.

# Synchronisation

Les adresses mémoires, lorsqu'elles sont partagées entre différentes mémoires, peuvent être accédées de manière concurente. Pour protéger la mémoire de tels accès concurents, chaque adresse peut être synchronisée : dans ce cas, les accès en lecture sont toujours possibles en parallèle, mais sont exclusifs avec les écritures, qui sont mutuellement exclusives entre elles.

Notez que seul les accès à la mémoire sont synchronisés. Les valeurs doivent à leur tour être synchronisées si elles sont modifiées.

Les adresses sont naturellement synchronisées lorsqu'elles sont partagées entre deux mémoires par la fonction `svm_memory_share`, mais cette synchronisation peut être activée ou désactivée à volonté.

Modifiez le code de l'extension :

    PLUGIN ram

    DEFINE

    INSTRUCTION ram.sync PTR
    %{
    	SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_memory_synchronisation_enable(svm,CURRENT(kernel),p);
    %}

    INSTRUCTION ram.free PTR
    %{
    	SVM_Value_Pointer p = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_memory_synchronisation_disable(svm,CURRENT(kernel),p);
    %}

La synchronisation des adresses n'étant pas particulièrement visible, il n'est pas nécessaire de tester ces instructions. Ce code est donné pour illustrer comment la synchronisation se manipule.

- Les adresses peuvent être synchronisées pour éviter les effets néfastes des accès concurents sur la mémoire.
- Cette synchronisation est activée automatiquement lorsque cela est nécessaire.
- Mais elle peut être contrôlée par les fonctions `svm_memory_synchronisation_enable` et `svm_memory_synchronisation_disable`.

# Conclusion

Vous venez de voir comment manipuler les mémoires de la machine virtuelle depuis les extensions.

La mémoire de la machine virtuelle est riche en opérations possibles. Cependant, il convient de maîtriser en particulier la lecture, l'écriture, l'allocation et la portée de la mémoire pour un usage courant dans les instructions.