Nachdem ich nun noch "etwas" an meinem ByteCode-Interpreter gearbeitet habe, möchte ich dazu ein paar Details erzählen.
Vorab: Ich bin kein guter Coder, aber dass ich sowas überhaupt hinbekommen habe, ist schon ein echter Erfolg für mich. Die Möglichkeiten sind inzwischen so umfangreich, dass meine Dokumentation dafür derzeit 12 A4 Seiten in Anspruch nimmt. Ohne diese Dokumentation steige ich schon selbst nicht mehr durch 
Abgesehen davon würde ich ihn mit dem dadurch erworbenen Wissen nun ganz anders Programmieren. So wie er derzeit ist, ist er in bester Spaghetti-Code Struktur erstellt und leider nicht auf Speicherplatzverbrauch (was den eigentlichen ByteCode angeht) optimiert. Das lässt sich nun im Nachgang (für das bestehende Projekt) nicht mehr so ohne weiteres ändern, da der ByteCode selbst inzwischen so umfangreich ist (bestimmt insgesamt so 5-10kb händisch erstellt), dass wohl etliche Stunden draufgehen würden, den wieder anzupassen. Aber vielleicht dann für das nächste Projekt...
Aber zurück zum ByteCode:
Wofür ist der ByteCode Interpreter:
Damit werden Eingaben, die ein Spieler in einem Spiel getätigt hat ausgewertet und entsprechende Aktionen eingeleitet.
Vorher wurden die Eingaben aber bereits in Token umgewandelt.
Die Aktionen können entweder bestimmte Textausgaben sein oder es werden Trigger gesetzt, die irgendwelche Veränderungen im Spielablauf bewirken.
Es gibt "normalen" ByteCode, BlockByteCode, MultiByteCode und KontextByteCode. Wobei die verschiedenen ByteCodearten beliebig miteinander kombiniert werden können. Die Begrifflichkeiten habe ich so für mich gewählt, ob die so allgemeingültig wären, weiss ich nicht, aber ist vom Grundsatz auch egal.
1. Normaler ByteCode:
!byte 10,$02,$00,$73,$00,$F0,42,1,$F0,%00000000
Erläuterung:
10 Länge des ByteCodes
$02-$F0 Eingaben des Spielers in Token sowie Endkennung.
42,1,$F0 Textausgaben (Text 42 aus aktuellem Raum) und Endkennung.
%00000000 BinärFlag, damit werden weitere Aktionen gesteuert, z.B. ob Voraussetzungen oder Inventargegenstände notwendig sind, ob man in der Nähe von etwas sein muss, ob Textausgaben unterdrückt werden sollen und Trigger gesetzt werden sollen.
Wenn das entsprechende Bit gesetzt ist, werden weitere Bytes angehängt und ausgewertet.
2. BlockByteCode:
!byte $BC
!byte 9,$02,$00,$A7,$00,$99,$99,$F0
!byte 8,$02,$00,$00,$00,$A7,$00,$F0
!byte 10,$02,$00,$00,$00,$A7,$00,$99,$99,$F0
!byte $BC
!byte 10
!byte 62,1,$F0,%00001000,$FF,$FF,<Nähe_zu_irgendwas,>Nähe_zu_irgendwas
$BC Kennzeichnet den Beginn und das Ende eines Blocks und enthält die verschiedenen Eingaben des Spielers.
Dann kommt eine neue Längenkennung und der Rest ist wie bei normalem ByteCode.
3. MultiByteCode
!byte 27,$90,$00,$A7,$00,$99,$99,$F0,$E9,%01100111,7,129,0,176,0,$F0,%00000000
!byte $E9,%01010101,14,0,129,0,130,0,$F0,%00000000
Der Anfang ist wie normaler ByteCode, dann kommt ein Steuerzeichen ($E9) mit dem noch Bedingungen abgefragt werden können. Das darauf folgende BinärFlag gibt diese Bedingungen vor (hier: keine Negierung, Inventar, Flag für Sprung bei Nichterfüllung, 5 Bits für Nummer des Gegenstandes (hier Gegenstand 7)). Das nächste Byte gibt vor, um wieviel Bytes weiter gesprungen werden soll bei Nichterfüllung. Bei Erfüllung werden 2 Texte (hier Texte 129 und 176 aus den globalen Texten) ausgegeben und der Code bis zum BinärFlag ausgeführt. Ansonsten gehts beim nächsten $E9 weiter.
4. KontextByteCode
!byte %10000000+15,%01000000,$FF,$FF,<Nähe_zu_irgendwas,>Nähe_zu_irgendwas
!byte $02,$00,$09,$00,$F0,4,1,$F0,%00000000
Wenn im ersten Byte des ByteCodes Bit 7 gesetzt ist, dann wird der ByteCode nur ausgeführt, wenn ein bestimmter Kontext gesetzt ist.
Der Kontext ist hier %01000000:
01 für Spieler muss in der Nähe zu etwas bestimmten sein.
Die weiteren Bits würden ausgewertet, wenn entweder ein Gegenstand oder eine andere Voraussetzung vorliegen müssen.
5. ByteCodearten Kombiniert
!byte $BC
!byte %10000000+8,%00000000,$11,$00,$9E,$00,$F0
!byte 6,$11,$00,$6D,$00,$F0
!byte 8,$11,$00,$6D,$00,$9E,$00,$F0
!byte 8,$93,$00,$30,$00,$22,$00,$F0
!byte 12,$93,$00,$30,$00,$09,$00,$11,$00,$9E,$00,$F0
!byte 6,$93,$00,$9E,$00,$F0
!byte 10,$93,$00,$30,$00,$09,$00,$9E,$00,$F0
!byte $BC
!byte 63
!byte $E9,%11010010,52,0
!byte $E9,%10001010,23,1, $EA,$FF,$FF,<Nähe_zu_irgendwas,>Nähe_zu_irgendwas,0,1
!byte $E9,%10100100,6,15,1,$F0,%10000000,12
!byte $E9,%10100101,6,15,1,$F0,%10000000,12
!byte $E9,%10100110,6,16,1,$F0,%10000000,9
!byte $E9,%10001010,23,1
!byte $E9,%10100111,7,20,1,8,1,$F0,%00000000
!byte $E9,%00000111,18,1,18,1,$F0,%10000000,5
Hier werden ByteCode, BlockByteCode, MultiByteCode und KontextByteCode kombiniert verwendet.
Das ist schon ein ByteCode mit umfangreichen Abfragen und Verzweigungen.
Wofür sind die ByteCodeArten:
1. normaler ByteCode ist dafür, wenn aus einer bestimmten Eingabe nur eine bestimmte Ausgabe resultiert.
2. BlockByteCode ist dafür, dass mehrere Eingaben des Spielers eine bestimmte Ausgabe/Aktion erzeugen. Das spart Speicher, da nicht für jede einzelne Eingabe eine eigene ByteCodeZeile mit den zugehörigen Ausgaben erzeugt werden muss.
3. MultiByteCode heißt, dass "wenn-dann-sonst" Szenarien abgebildet werden können.
4. KontextByteCode bedeutet, dass zuerst auf einen Kontext geprüft wird, bevor Auswertung von Eingaben und die daraus resultierenden Ausgaben bearbeitet werden. Z.B. Spieler steht auf einer Leiter, oder hat einen bestimmten Gegenstand oder eine andere Voraussetzung liegt vor.
Weiteres dazu beim nächsten Mal 