# TBVM — Designentwurf Eigene Stack-basierte Bytecode-VM (Entscheidung siehe PLAN.md, 0.2). Die VM ist in `tbdos` (IDE) und in von `tbdosc` erzeugte Executables eingebettet — identischer Code, identisches Verhalten, schneller Turnaround. ## Werte-Modell Tagged Enum, kein NaN-Boxing (Einfachheit und Debugbarkeit vor Mikro- performance): ```rust enum Value { Int(i16), // INTEGER Long(i32), // LONG Single(f32), // SINGLE Double(f64), // DOUBLE Currency(i64), // CURRENCY, Festkomma ×10 000 Str(Rc), // STRING (immutabel geteilt; Copy-on-Write bei MID$-Anweisung) // Arrays und TYPE-Instanzen leben im Heap-Bereich der VM, // Variablen-Slots referenzieren sie per Handle (Rc>). } ``` - Kein GC nötig: der Dialekt kennt keine Zyklen (keine Objektreferenzen in TYPEs, keine Closures) → `Rc` genügt. - Feste Strings (`STRING * n`) und TYPE-Records werden als eigene Speicherobjekte mit fester Zeichen-/Elementstruktur geführt. ## Bytecode - Stack-Maschine, Opcodes 1 Byte + Operanden variabler Länge (u8/u16/u32, little-endian). - **Modul-Struktur:** Konstantenpool (Strings, Zahlen), Typtabelle (TYPE-Layouts), Prozedurtabelle (Signatur, Locals-Anzahl, Code-Offset), globale Slots, DATA-Segment (für READ/RESTORE), Zeilentabelle. - **Zeilentabelle:** Abbildung Code-Offset → (Moduldatei, Zeile). Grundlage für `ERL`, Fehlermeldungen, Breakpoints und Einzelschritt. - **Aufrufkonventionen:** - SUB/FUNCTION: eigener Frame (Locals-Slots, Operandenstack-Basis); BYREF-Parameter als Referenz-Slots (Handle auf Variablen-Slot). - GOSUB: **kein** Frame — Rücksprungadresse auf separatem GOSUB-Stack im aktuellen Frame (RETURN prüft diesen zuerst). - **Fehlerbehandlung:** Pro Frame ein Handler-Zustand (`ON ERROR GOTO x`). Laufzeitfehler → VM sucht aktiven Handler im aktuellen Modulkontext (Vorbild: Handler sind modul-/prozedurlokal, TODO: exakte Scoping-Regel testen), setzt `ERR`/`ERL`, springt. `RESUME` nutzt gemerkten Anweisungs-Offset. ## Ausführungsmodell / Unterbrechbarkeit - Die Interpreterschleife läuft in **Ticks**: nach jeder Anweisung (Grenze aus der Zeilentabelle) prüft sie ein Flag-Set: Breakpoint? Einzelschritt? Strg+Untbr? Ereignis-Queue nicht leer und Zustellung erlaubt? - Ereigniszustellung (Forms, Timer) erfolgt kooperativ: nur an Zustellpunkten (`DOEVENTS`, `SLEEP`, blockierende Eingabe, Ende einer Ereignisprozedur) — wie im Vorbild. - Die VM ist eine gewöhnliche zustandsbehaftete Struktur, `step()`-basiert; die einbettende Schleife (IDE-Debugger oder Runner) treibt sie. Kein eigener Thread nötig; Terminal-Events werden zwischen Ticks gepollt. ## `.tbc`-Container (Entwurf) ``` Magic "TBC\0" · Formatversion u16 · Flags Abschnittstabelle: [ (Kennung, Offset, Länge) ] Abschnitte: CONSTS, TYPES, PROCS, CODE, DATA, LINES, FORMS (serialisierte .FRM-Beschreibungen) ``` Serialisierung mit einfachem eigenem Writer/Reader (kein serde nötig, Format bleibt stabil und dokumentiert). ## Eigenständige Executables `tbdosc build --exe` kopiert den vorkompilierten Runner (dieselbe tb-vm/tb-runtime/tb-ui-Bibliothek wie die IDE) und hängt das `.tbc` als Ressource an (Anhängen ans Binary + Fußzeile mit Offset/Magic; portabel für alle drei Plattformen). Alternative — `include_bytes!` + Cargo-Build beim Nutzer — verworfen: erfordert Rust-Toolchain beim Anwender. ## Offene Punkte - Opcode-Satz konkret ausformulieren (mit Phase 2) - Zahlenkonvertierungs-Matrix (implizite Casts, Rundung, Überlauf) als Tabelle - Verhalten von `STOP`/`CONT` im Runner (ohne IDE): Abbruch mit Meldung?