# vm-ausfuehrung Specification ## Purpose Die TBVM führt das Bytecode-Kompilat mit der exakten Semantik des Dialekts aus: Ausdrücke mit der Zahlenkonvertierungs-Matrix des Vorbilds, Kontrollfluss, Prozeduraufrufe mit BYREF, `GOSUB`, unterbrechbare `step()`-Ausführung als Grundlage für Debugger und Ereignisschleife. ## Requirements ### Requirement: Zahlenkonvertierungs-Matrix Die VM SHALL implizite und explizite Zahlenkonvertierungen nach der in docs/tbvm-design.md dokumentierten Matrix ausführen, insbesondere: Gleitkomma→Ganzzahl rundet kaufmännisch zur nächsten geraden Zahl (Banker's Rounding); Bereichsüberschreitung löst Fehler 6 „Overflow" aus; `/` liefert stets Gleitkomma, `\` und `MOD` konvertieren ihre Operanden vorab zu Ganzzahlen; logische Operatoren (`AND`, `OR`, `XOR`, `EQV`, `IMP`) arbeiten bitweise auf zu Ganzzahlen konvertierten Operanden; CURRENCY rechnet als Festkomma ×10 000. Die Matrix SHALL durch ein Korpusprogramm mit dokumentierter Sollausgabe abgedeckt sein. #### Scenario: Banker's Rounding - **WHEN** `PRINT CINT(0.5); CINT(1.5); CINT(2.5)` ausgeführt wird - **THEN** lautet die Ausgabe ` 0 2 2 ` #### Scenario: Überlauf bei Konvertierung - **WHEN** einer INTEGER-Variablen der Wert 40000 zugewiesen wird - **THEN** tritt Laufzeitfehler 6 „Overflow" auf #### Scenario: Ganzzahldivision rundet Operanden vor - **WHEN** `PRINT 7.5 \ 2` ausgeführt wird - **THEN** wird 7.5 zuerst zu 8 gerundet und die Ausgabe ist ` 4 ` ### Requirement: Kontrollfluss Die VM SHALL `IF`/`ELSEIF`/`ELSE`, `SELECT CASE` (Werte, Bereiche, `IS`-Vergleiche, `CASE ELSE`), `FOR`/`NEXT` (inkl. `STEP`, null Durchläufen bei unpassender Grenze), `DO`/`LOOP` (alle vier Bedingungsformen), `WHILE`/`WEND`, `GOTO`, `ON expr GOTO/GOSUB` und `EXIT FOR/DO` mit der Semantik des Vorbilds ausführen. #### Scenario: FOR ohne Durchlauf - **WHEN** `FOR i% = 3 TO 1: PRINT i%: NEXT` ausgeführt wird - **THEN** wird der Schleifenkörper nicht betreten #### Scenario: Korpus Kontrollfluss - **WHEN** `tests/compat/kontrollfluss.bas` ausgeführt wird - **THEN** entspricht die Ausgabe byte-genau `kontrollfluss.out` ### Requirement: GOSUB/RETURN neben Prozeduraufrufen `GOSUB` SHALL die Rücksprungadresse auf einem GOSUB-Stack im aktuellen Frame ablegen (kein eigener Frame); `RETURN` kehrt dorthin zurück, `RETURN label` an das benannte Ziel. Ein `RETURN` ohne anhängigen `GOSUB` MUST Fehler 3 „RETURN without GOSUB" auslösen. #### Scenario: RETURN ohne GOSUB - **WHEN** ein Programm `RETURN` ohne vorangehendes `GOSUB` ausführt - **THEN** tritt Laufzeitfehler 3 auf ### Requirement: Prozeduraufrufe mit BYREF-Semantik SUB-/FUNCTION-Aufrufe SHALL eigene Frames mit Locals-Slots verwenden. Variablen, Arrayelemente und UDT-Felder werden BYREF übergeben (Änderungen im Rumpf wirken auf das Argument zurück); ein geklammertes Argument oder ein Ausdruck wird als temporärer Wert übergeben (BYVAL- Wirkung). `STATIC`-Prozeduren behalten ihre Locals zwischen Aufrufen; rekursive Aufrufe nicht-statischer Prozeduren erhalten frische Locals. `DEF FN`-Funktionen werten im Modulkontext aus (Zugriff auf Modulvariablen), ihre Parameter wirken lokal. #### Scenario: BYREF wirkt zurück - **WHEN** `SUB Inc(x%) : x% = x% + 1 : END SUB` mit `Inc n%` aufgerufen wird - **THEN** ist `n%` danach um 1 erhöht #### Scenario: Klammern erzwingen Wertübergabe - **WHEN** dieselbe SUB mit `Inc (n%)` aufgerufen wird - **THEN** bleibt `n%` unverändert ### Requirement: DATA/READ/RESTORE `READ` SHALL die `DATA`-Konstanten des Moduls in Deklarationsreihenfolge konsumieren und in den Zieltyp konvertieren; `RESTORE [label]` setzt den Lesezeiger auf den Anfang bzw. auf die erste `DATA`-Anweisung ab dem Label. Lesen hinter dem letzten Element MUST Fehler 4 „Out of DATA" auslösen; eine nicht konvertierbare Konstante MUST Fehler 13 „Type mismatch" auslösen. #### Scenario: Out of DATA - **WHEN** ein Programm mehr Werte liest als `DATA` bereitstellt - **THEN** tritt Laufzeitfehler 4 auf ### Requirement: Unterbrechbare step()-Ausführung Die VM SHALL als zustandsbehaftete, vom Einbetter getriebene Struktur ausführbar sein: An Anweisungsgrenzen prüft sie ein Flag-Wort und kann anhalten (Breakpoint auf (Modul, Zeile), Einzelschritt, Abbruchwunsch). Im angehaltenen Zustand MUST der Einbetter Variableninhalte (Skalare, Arrayelemente, UDT-Felder) inspizieren und die Ausführung fortsetzen können. Die Tick-Prüfung erfolgt nur an Anweisungsgrenzen, nicht pro Opcode. Ein Breakpoint SHALL durch Modulidentität und physische Quellzeile bestimmt sein. Gleiche Zeilennummern anderer Module MUST NOT denselben Breakpoint auslösen. #### Scenario: Breakpoint hält an - **WHEN** ein Breakpoint auf Zeile 10 gesetzt ist und die Ausführung Zeile 10 erreicht - **THEN** hält die VM vor der Anweisung an und meldet Modul und Zeile #### Scenario: Inspektion im Halt - **WHEN** die VM an einem Breakpoint steht - **THEN** liefert die Inspektions-API den aktuellen Wert einer benannten Variablen #### Scenario: Gleiche Zeile in zwei Modulen - **WHEN** ein Breakpoint auf Modul A, Zeile 10 gesetzt ist und Modul B Zeile 10 ausführt - **THEN** läuft B weiter; erst A Zeile 10 hält an ### Requirement: Programmende im Runner Außerhalb der IDE SHALL `END` (und das Erreichen des Programmendes) den Prozess mit Exit-Code 0 beenden; `STOP` SHALL mit der Meldung `STOP in line n` (bzw. Modul/Zeile) und einem Exit-Code ungleich 0 terminieren (Projektentscheidung 2026-09-02). `SYSTEM` verhält sich wie `END`. `CONT` existiert nur als IDE-Konzept (Phase 5) und ist kein Programmbestandteil. #### Scenario: STOP im Runner - **WHEN** ein per `tbc run` gestartetes Programm `STOP` in Zeile 7 ausführt - **THEN** endet der Prozess mit Exit-Code ungleich 0 und die Meldung nennt Zeile 7 ### Requirement: Ereigniszustellung an Anweisungsgrenzen Die VM SHALL an Anweisungsgrenzen prüfen, ob ein Ereignis zuzustellen ist, und in diesem Fall das `GOSUB`-Ziel des zugehörigen Traps ausführen. Der unterbrochene Zustand — Aufrufkette, Schleifen, Locals, `GOSUB`-Stapel und Fehlerzustand — MUST dabei erhalten bleiben; mit `RETURN` SHALL die Ausführung an der unterbrochenen Stelle fortfahren. Die Prüfung MUST NOT pro Opcode erfolgen; solange kein Trap definiert ist, MUST sie auf einen einzelnen Test hinauslaufen und MUST NOT die Uhr lesen. Ohne klassische Traps, aktive Forms-Timer oder zeitabhängige Mausauswertung SHALL auch die Forms-Integration keine Hostzeit abfragen. #### Scenario: Trap aus einer Prozedur heraus - **WHEN** ein Ereignis zugestellt wird, während eine `SUB` läuft, und das Trap-Ziel im Modulrumpf liegt - **THEN** läuft der Handler und nach `RETURN` setzt die `SUB` an der unterbrochenen Anweisung fort #### Scenario: Ohne aktiven Trap wird die Uhr nicht gelesen - **WHEN** ein Programm ohne jede `ON …`-Trap-Definition ausgeführt wird - **THEN** fragt die Ausführung an keiner Anweisungsgrenze die Zeit ab, und der Durchsatz bleibt im Rahmen der Messgenauigkeit unverändert #### Scenario: Einfaches Programm liest keine Ereigniszeit - **WHEN** ein Programm nur Zuweisungen und END ausführt und ein Testhost Zeitabfragen zählt - **THEN** ist der Zähler am Ende 0 ### Requirement: Jede Schleife trägt eine Anweisungsgrenze im Kreis Der zurückspringende Zweig jeder Schleife (`FOR`/`NEXT`, `WHILE`/`WEND`, `DO`/`LOOP` in allen Bedingungsformen) MUST über mindestens eine Anweisungsgrenze führen, auch wenn der Schleifenrumpf leer ist. Andernfalls gäbe es in einer Warteschleife keinen Zustellpunkt: kein Ereignis, kein Breakpoint und kein Abbruch würden dort wirken. #### Scenario: Leere Warteschleife - **WHEN** `FOR i% = 1 TO 10000` unmittelbar von `NEXT` gefolgt wird - **THEN** führt der Rücksprung über eine Anweisungsgrenze und ein aktiver Zeit-Trap wird darin zugestellt #### Scenario: Leere Schleife in jeder Form - **WHEN** eine `WHILE`/`WEND`-, `DO WHILE`/`LOOP`- oder `DO`/`LOOP UNTIL`-Schleife ohne Rumpf übersetzt wird - **THEN** liegt zwischen Rücksprungziel und Rücksprung eine Anweisungsgrenze ### Requirement: DOEVENTS gibt die Kontrolle ab `DOEVENTS` SHALL anstehende Ereignisse zustellen und danach einen Wert liefern. Solange keine Formulare geladen werden können, SHALL der Wert 0 sein. #### Scenario: DOEVENTS in einer Warteschleife - **WHEN** ein Programm in einer Schleife `DOEVENTS` aufruft und ein Trap fällig wird - **THEN** läuft der Handler innerhalb dieser Schleife