# Introduction

Ce didacticiel est destiné aux utilisateurs sachant écrire un peu de code de la Simple Virtual Machine.

Dans ce didacticiel, vous allez explorer la mémoire de la machine virtuelle.

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

# Mise en place

Pour commencer, créez le canevas de l'application dans le fichier exécutable memoire.svm en utilisant ce code :

    #!/usr/bin/env svm
    DESCRIPTION
    Memory exploration
    END
    LOG
    DEBUG "Memory"
    PROCESS "application"
    	CODE "main" INLINE
    		:debug BREAK
    	END
    END

# Eléments mémoire

Modifiez le code pour ajouter de la mémoire :

    #!/usr/bin/env svm
    DESCRIPTION
    Memory exploration
    END
    LOG
    DEBUG "Memory"
    PROCESS "application"
    	CODE "main" INLINE
    		:memory INT/i, STR*3/s, PTR/p
    		:debug BREAK
    	END
    END

Puis lancez l'application en mode débugueur. Sur le point d'arrêt, la mémoire ressemble à :

Memory
Memory - K main - P application

Address | Type | Value
--- | --- | ---
&0 | INT |
&1 | STR |
&2 | STR |
&3 | STR |
&4 | PTR |

Alias | Pointer
--- | ---
i | &0*1
p | &4*1
s | &1*3

Address:
Display
Aliases
P
Focus
Back
Clear

Le premier tableau donne une idée de la structure générale de la mémoire : elle est principalement composée d'éléments indépendants placés l'un après l'autre dans une séquence.

## Position

La première colonne indique la position d'un élément dans la séquence : cette position numérique est une adresse mémoire, qui identifie de manière univoque un seul élément mémoire.

Les adresses mémoire présentes ici ont un lien étroit avec celles écrites dans le code machine : celles du code permettent de calculer les adresses utilisées dans les opérations mémoire, elles mêmes mise en correspondance avec celles existant dans la mémoire au moment de l'opération mémoire.

Modifiez le code pour ajouter deux écritures en mémoire :

    #!/usr/bin/env svm
    DESCRIPTION
    Memory exploration
    END
    LOG
    DEBUG "Memory"
    PROCESS "application"
    	CODE "main" INLINE
    		:memory INT/i, STR*3/s, PTR/p
    		"123" -> &1
    		"abc" -> (s/1)
    		:debug BREAK
    	END
    END

Lancez l'application dans le débugueur, et observez où les valeurs ont été écrites en mémoire :

1. la première est écrite explicitement dans l'adresse `&1`,
2. la seconde est écrite dans une adresse qui se trouve être le résultat d'un calcul d'adresse. Ici, il s'agit de l'adresse `&2`.

- La mémoire est constituée d'une séquence d'éléments mémoire.
- Chaque élément est identifié par sa position dans la séquence : son adresse mémoire.
- Les adresses mémoire sont utilisées dans les opérations mémoire pour indiquer où elles agissent.
- Le code machine comporte un système de calcul intégré des adresses mémoire.

## Type

Modifiez le code à nouveau :

    #!/usr/bin/env svm
    DESCRIPTION
    Memory exploration
    END
    LOG
    DEBUG "Memory"
    PROCESS "application"
    	CODE "main" INLINE
    		:memory INT/i, STR*3/s, PTR/p
    		"123" -> &1
    		"abc" -> (s/1)
    		"xyz" -> &0
    		:debug BREAK
    	END
    END

Puis lancez l'application sans le débugueur :

    ./memoire.svm
    ### Simple Virtual Machine 12345 : PROCESS application | 2023-01-01 00:00:00 GMT #####################################################################
    Kernel interrupted: @(main, line 4) Interruption MEMORY not handled: Write to defined address &0 with a value of type STR instead of INT.

    Core dump:
    Kernel:
    State: I, transmit_interruption, interrupted @(main, line 4) Interruption MEMORY not handled: Write to defined address &0 with a value of type STR instead of INT.

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

    Code at <main:1/3>:
         :memory INT/i , STR*3/s , PTR/p    # <0> @(main, line 1)
         "123" -> &1        # <1> @(main, line 2)
         "abc" -> (s/1)     # <2> @(main, line 3)
    ====== HERE ======
         "xyz" -> &0        # <3> @(main, line 4)
    ==================
         :debug BREAK       # <4> @(main, line 5)
     Labels:
     Symbols:

    Memory:
      &0: INT
      &1: STR "123"
      &2: STR "abc"
      &3: STR
      &4: PTR
     Aliases:
      i -> &0*1
      p -> &4*1
      s -> &1*3
     Free space:

    ### Simple Virtual Machine 12345 : SYSTEM | 2023-01-01 00:00:00 GMT ##################################################################################
    Process application interrupted: @(main, line 4) Interruption MEMORY not handled: Write to defined address &0 with a value of type STR instead of INT.

Un élément mémoire ne peut pas accueillir n'importe quelle valeur. En effet, chaque élément mémoire contient un type servant à limiter le stockage de valeurs ayant le même type. Cela permet de structurer le stockage des valeurs en mémoire et de rendre explicites les erreurs de lecture ou d'écriture en mémoire, comme dans l'exemple ci-dessus où une chaine de caractère tente d'être écrite à une adresse n'acceptant que des entiers.

## Valeur

Un élément mémoire peut contenir une valeur dont le type correspond à celui de l'élément. Dans l'exemple précédent, les adresses impliquées dans une écriture mémoire réussie contiennent une valeur, là où les autres n'en contiennent pas.

La valeur ne peut être lue depuis un élément mémoire (et donc depuis son adresse) que si une valeur y a été écrite au préalable : un élément mémoire supporte le fait de ne pas avoir de valeur.

Dans ce cas, l'élément contient une valeur dite nulle (ce qui est particulièrement vrai pour les pointeurs : un pointeur nul est un pointeur sans valeur).

## Synchronisation

Dans certaines circonstances, les accès mémoire en lecture ou en écriture ne peuvent pas être faits au même moment sur une adresse donnée. Dans tels cas, un verrou peut être activé dans un élément mémoire pour s'assurer que des lectures et écritures sur l'adresse correspondante seront faites en séquence.

- Un élément mémoire contient un type : il n'acceptera des opérations qu'avec des valeurs ayant ce type précis.
- Un élément mémoire peut contenir une valeur, lorsqu'une au moins une écriture a été réussie sur cet élément mémoire.
- Lorsqu'un élément mémoire ne contient pas de valeur, cette valeur fantôme est parfois nommée valeur nulle.

# Etats d'une adresse mémoire

Dans l'exemple précédent, observez les adresses :

1. `&5` : cette adresse n'est pas dans la liste des adresses connues de la mémoire, et pour cause, aucun élément mémoire lui est associé. Dans ce cas, les opérations de lecture et d'écriture vers cette adresse vont échouer. L'adresse est dite non définie.
2. `&0` : cette adresse est dans la liste des adresses connues de la mémoire, mais aucune valeur n'a été écrite à cette adresse. Une opération de lecture a cette adresse va échouer, mais une écriture reste possible. L'adresse est dite définie, et parfois non initialisée.
3. `&1` : cette adresse est dans la liste des adresses connues de la mémoire et contient une valeur. Les opérations de lecture et d'écriture vont dès lors réussir. L'adresse est dite initialisée.

Après une allocation mémoire, les adresses nouvellement créées sont toujours définies mais non initialisées. Cela permet d'éviter l'écueil des valeurs lues avant toute écriture, généralement catastrophique sur les machines réelles.

- Une adresse sans élément mémoire associé est dite non définie.
- Une adresse avec un élément mémoire sans valeur est dite définie.
- Une adresse avec un élément mémoire avec valeur est dite initialisée.

# Alias

D'une exécution de l'application à l'autre, les données en mémoire peuvent ne pas être à la même adresse car l'allocation mémoire choisit automatiquement où les adresses vont être définies. Cependant, certaines données doivent rester facilement accessible sans un calcul d'adresse complexe à maintenir : la mémoire peut attribuer un nom à une zone mémoire, et ce nom est appellé alias.

La mémoire contient l'association entre chaque alias et la zone mémoire désignée par un pointeur mémoire de façon à ce qu'un alias ne peut référencer qu'un seul pointeur à la fois.

Au tout début de ce didacticiel, la fenêtre mémoire du débugueur montre cette table d'association entre les alias et les pointeurs correspondants.

- Certaines zones mémoire peuvent porter un nom : ce nom est nommé alias.
- La table d'association entre alias et pointeurs est dans la mémoire : la portée d'un alias est globale et ne peut référencer qu'un seul pointeur à la fois.

# Conclusion

Vous venez de voir comment fonctionne la mémoire de la machine virtuelle.

Il est important de bien maîtriser comment les valeurs utilisées intensivement par les instructions sont stockées, pour anticiper les interruptions qui peuvent être émises lors d'opérations mémoire invalides.

Cette maîtrise permet également d'organiser correctement les valeurs utilisées par l'application en ensembles cohérents, en utilisant toutes les vérifications faites par la mémoire pour détecter au plus tôt une erreur de code.