Introduction

Ce didacticiel est destiné aux nouveaux utilisateurs de la Simple Virtual Machine.

Dans ce didacticiel, vous allez utiliser les types et les interruptions provenant d'extensions.

Le temps de lecture de ce didacticiel est estimé à 15 minutes si l'utilisation des instructions extensions a été abordée.

Mise en place

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

#!/usr/bin/env svm
DESCRIPTION
Plugin types and interruptions example
END
LOG
DEBUG "Plugin types and interruptions" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmreal.so"
PLUGIN "svmstr.so"
PROCESS "application"
	CODE "main" INLINE
		:debug BREAK
	END
END

Types extension

Les instructions permettent d'étendre les capacités du processeur. Ici, nous allons étendre celles de la mémoire.

Allocation mémoire

Modifiez le code pour ajouter une allocation mémoire :

#!/usr/bin/env svm
DESCRIPTION
Plugin types and interruptions example
END
LOG
DEBUG "Plugin types and interruptions" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmreal.so"
PLUGIN "svmstr.so"
PROCESS "application"
	CODE "main" INLINE
		:debug BREAK
		:memory real.number/r, str.pattern/p
	END
END

Puis lancez l'application en mode débugueur. La fenêtre mémoire finit par ressembler à :

Plugin types and interruptions
Memory - K main - P application
AddressTypeValue
&0real.number
&1str.pattern
AliasPointer
p&1*1
r&0*1
Address:
Display
Aliases
P
Focus
Back
Clear

En somme, la même construction qui servait à nommer des instructions extension sert ici à dénommer des types de valeurs pouvant être écrites en mémoire.

Utilisation

Dans le débugueur, affichez la fenêtre des extensions, et filtrez sur le texte "real." :

Plugin types and interruptions
Plugins
Definitions
TYPE com.device
TYPE real.number
TYPE str.pattern
STRUCT com.file
STRUCT com.tcp
STRUCT com.terminal
SYSTEM WAITING INSTRUCTION com.command com.device . * -> VALUE ?
INSTRUCTION com.equal com.device 2 -> BLN
WAITING INSTRUCTION com.idle ( ( > | < ( PEP | { PEP [ VALUE KEYWORD ] * } ) ? : protocol ) : mode PTR + : device_arrays ) + -> PTR : active_device
INSTRUCTION com.message [ 'STDOUT' 'STDERR' ] ? VALUE +
SYSTEM WAITING INSTRUCTION com.open PEP : device_type . * : parameters -> com.device
WAITING INSTRUCTION com.prompt ( [ 'STDOUT' 'STDERR' ] ? STR ) : prompt ? [ 'STR' 'INT' ] : output_type -> [ STR INT ]
WAITING INSTRUCTION com.read com.device PEP : protocol [ VALUE KEYWORD ] * : parameters -> STR ?
WAITING INSTRUCTION com.write com.device VALUE *
INSTRUCTION real.add ( [ INT real.number ] [ INT real.number ] + | PTR ) -> real.number
INSTRUCTION real.cmp [ INT real.number ] [ < > = <> <= => ] [ INT real.number ] -> BLN
INSTRUCTION real.div [ INT real.number ] [ INT real.number ] -> real.number
INSTRUCTION real.mul ( [ INT real.number ] [ INT real.number ] + | PTR ) -> real.number
INSTRUCTION real.parse STR -> real.number
INSTRUCTION real.print real.number ( INT INT ) ? -> STR
INSTRUCTION real.sub [ INT real.number ] [ INT real.number ] -> real.number
INSTRUCTION str.char INT : ascii_code -> STR
INSTRUCTION str.cmp STR [ < > = <> <= => ] STR -> BLN
INSTRUCTION str.find STR [ STR str.pattern ] : pattern MUTABLE INT : position MUTABLE INT : size ? -> BLN
INSTRUCTION str.index STR INT : index -> INT : ascii_code
INSTRUCTION str.join ( PTR : array_of_strings | STR + | STR : separator { ( PTR : array_of_strings | STR + ) } ) -> STR
INSTRUCTION str.pattern STR : pattern -> str.pattern
INSTRUCTION str.regex STR [ STR str.pattern ] : pattern -> PTR
INSTRUCTION str.replace MUTABLE STR [ INT 'ALL' ] : occurence [ STR str.pattern ] : pattern_old => STR : new
INSTRUCTION str.size STR -> INT
INSTRUCTION str.split STR [ STR str.pattern ] : separator_pattern -> PTR
INSTRUCTION str.sub STR INT : offset INT : size 'END' : origin_at_end ? -> STR
FUNCTION com.device STRUCT -> com.device ?
FUNCTION com.device_file_close $com.file -> BLN
FUNCTION com.device_file_command $com.file . * -> VALUE ?
FUNCTION com.device_file_open [ < > <> >> ] STR [ 'EXEC' 'PRIV' ] * -> $com.file
FUNCTION com.device_file_print $com.file -> STR
FUNCTION com.device_file_read $com.file -> STR ?
FUNCTION com.device_file_write $com.file STR
FUNCTION com.device_tcp_close $com.tcp -> BLN
FUNCTION com.device_tcp_command $com.tcp . * -> VALUE ?
FUNCTION com.device_tcp_idle $com.tcp MUTABLE INT MUTABLE INT MUTABLE INT
FUNCTION com.device_tcp_open [ < > ] STR [ STR INT ] -> $com.tcp
FUNCTION com.device_tcp_print $com.tcp -> STR
FUNCTION com.device_tcp_read $com.tcp -> STR ?
FUNCTION com.device_tcp_write $com.tcp STR
FUNCTION com.device_terminal_close $com.terminal -> BLN
FUNCTION com.device_terminal_command $com.terminal . * -> VALUE ?
FUNCTION com.device_terminal_idle $com.terminal MUTABLE INT MUTABLE INT MUTABLE INT
FUNCTION com.device_terminal_open [ 'STDIN' 'STDOUT' 'STDERR' ] -> $com.terminal
FUNCTION com.device_terminal_print $com.terminal -> STR
FUNCTION com.device_terminal_read $com.terminal -> STR ?
FUNCTION com.device_terminal_write $com.terminal STR
FUNCTION com.protocol_all STR BLN : eof -> INT ?
FUNCTION com.protocol_available STR BLN : eof -> INT ?
FUNCTION com.protocol_line STR BLN : eof STR ? -> INT ?
FUNCTION com.protocol_size STR BLN : eof INT -> INT ?
FUNCTION real.parse STR -> real.number
FUNCTION str.pattern STR : pattern -> str.pattern
INTERRUPTION com.interrupted
in
Filter

Déjà, "real.number" est bien déclaré comme type. De plus, plusieurs instructions déclarent utiliser ce nouveau type en tant qu'argument ou type de retour.

Modifiez le code pour obtenir :

#!/usr/bin/env svm
DESCRIPTION
Plugin types and interruptions example
END
LOG
DEBUG "Plugin types and interruptions" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmreal.so"
PLUGIN "svmstr.so"
PROCESS "application"
	CODE "main" INLINE
		:debug BREAK
		:memory real.number/r, str.pattern/p, STR/s
		CONST real.number "3.14159" -> &r
		:real.mul @&r 2 -> &r
		:com.message @&r
		:real.print @&r -> &s
		:str.replace @&s 1 CONST str.pattern "^[^.]*" => "X"
		:com.message @&s
	END
END

Lancez l'exécution avec le débugueur. La fenêtre de la mémoire doit ressembler à :

Plugin types and interruptions
Memory - K main - P application
AddressTypeValue
&0real.number6.28318
&1str.pattern
&2STR"X.28318"
AliasPointer
p&1*1
r&0*1
s&2*1
Address:
Display
Aliases
P
Focus
Back
Clear

Et le terminal contient bien les deux valeurs calculées :

./types_et_interruptions.svm -d 8080
### Simple Virtual Machine 1234 : debug | 2023-01-01 00:00:00 GMT ####################################################################################
Connection from 127.0.0.1:54321. Login: ************************************************************

6.28318
X.28318

Comme vous pouvez le constater :

Interruptions extension

Remplacez le code par :

#!/usr/bin/env svm
DESCRIPTION
Plugin types and interruptions example
END
LOG
DEBUG "Plugin types and interruptions" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmreal.so"
PLUGIN "svmstr.so"
PROCESS "application"
	CODE "main" INLINE
		:debug BREAK
		:interruption GLOBAL TERMINATE end
		:memory com.device/d, STR/s
		:com.open com.terminal STDIN -> &d
		:com.read @&d com.all -> &s
		:com.message "<" @&s ">"
	:label end
		:shutdown
	END
END

Puis lancez une première fois l'application. Tapez plusieurs lignes de texte, puis sur une nouvelle ligne, tapez la combinaison Control-d :

./types_et_interruptions.svm
test
test
<test
test
>

Tout s'est bien passé. Recommencez, mais cette fois, tapez la combinaison Control-c au lieu de Control-d :

./types_et_interruptions.svm
test
test
^C### Simple Virtual Machine 1234 : PROCESS application | 2023-01-01 00:00:00 GMT ######################################################################
Kernel interrupted: @(main, line 5) Interruption !com.interrupted not handled: Read interrupted.

Core dump:
Kernel:
State: I, transmit_interruption, interrupted @(main, line 5) Interruption !com.interrupted not handled: Read interrupted.

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

Code at <main:1/4>:
     :debug BREAK       # <0> @(main, line 1)
     :interruption GLOBAL TERMINATE end # <1> @(main, line 2)
     :memory com.device/d , STR/s       # <2> @(main, line 3)
     :com.open com.terminal STDIN -> &d # <3> @(main, line 4) WAITING SYSTEM
====== HERE ======
     :com.read @&d com.all -> &s        # <4> @(main, line 5) WAITING
==================
     :com.message "<" @&s ">"   # <5> @(main, line 6)
  :label end
     :shutdown  # <6> @(main, line 8)
 Labels:
  end -> <6>
 Symbols:


Memory:
  &0: com.device Terminal STDIN
  &1: STR
 Aliases:
  d -> &0*1
  s -> &1*1
 Free space:



### Simple Virtual Machine 1234 : SYSTEM | 2023-01-01 00:00:00 GMT ###################################################################################
Process application interrupted: @(main, line 5) Interruption !com.interrupted not handled: Read interrupted.

Cette fois, l'application a été interrompue par une interruption provenant de l'extension "com" : !com.interrupted.

Modifiez une nouvelle fois le code, pour ajouter un gestionnaire d'interruption :

#!/usr/bin/env svm
DESCRIPTION
Plugin types and interruptions example
END
LOG
DEBUG "Plugin types and interruptions" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmreal.so"
PLUGIN "svmstr.so"
PROCESS "application"
	CODE "main" INLINE
		:debug BREAK
		:interruption GLOBAL TERMINATE ignore
		:interruption !com.interrupted abort
		:memory com.device/d, STR/s
		:com.open com.terminal STDIN -> &d
		:com.read @&d com.all -> &s
		:com.message "<" @&s ">"
		:shutdown
	:label ignore
		:return
	:label abort
		:com.message "Aborted."
	END
END

Relancez l'application et utilisez la combinaison Control-c :

./types_et_interruptions.svm
test
test
^CAborted.

Cette fois, l'interruption a été rattrapée comme n'importe quelle interruption interne.

Conclusion

Vous venez de voir comment utiliser des valeurs dont le type est défini dans une extension et comment utiliser des interruptions définies dans une extension.

Tout comme les instructions extension, les types extension permettent d'augmenter considérablement les capacités de la machine virtuelle en plaçant en mémoire des valeurs potentiellement complexes.

Les interruptions extension permettent de distinguer des conditions d'erreur spécifiques à des extensions et y apporter le traitement spécial nécessaire grâce aux gestionnaires d'interruption.