Files
TerminalBasic/docs/tbvm-design.md
Chili Palmer 1ce371b96b Leitbild und Performance-Anforderungen verankern
- README: Leitbild Re-Imagination (Stufe 1 originalgetreu, Stufe 2
  additive Anreicherung fuer CLI-/TUI-Tool-Entwicklung)
- PLAN: Leitplanken (Online-Doku als Primaerquelle, Performance als
  Anforderung: Instant-Compile, Projekt-Build in Sekunden, Executable
  ohne Toolchain), Stufe-2-Ideenspeicher, Benchmark-Aufgaben in Phase 2
- tbvm-design: Performance-Abschnitt (Single-Pass, inkrementell,
  typisierte Opcodes, Slot-Aufloesung zur Compilezeit, Messlatten)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 16:45:59 +02:00

5.2 KiB
Raw Blame History

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):

enum Value {
    Int(i16),          // INTEGER
    Long(i32),         // LONG
    Single(f32),       // SINGLE
    Double(f64),       // DOUBLE
    Currency(i64),     // CURRENCY, Festkomma ×10 000
    Str(Rc<str>),      // 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<RefCell<…>>).
}
  • 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.

Performance

Compiler (Instant-Compile, Anforderung siehe PLAN.md):

  • Single-Pass pro Modul: Lexen, Parsen und Codegen in einem Durchlauf; Vorwärtsreferenzen (Prozeduren, Labels) über Fixup-Listen statt zweitem Pass. Die Sprache ist dafür gemacht — das Vorbild kompilierte auf 1992er-Hardware gefühlt sofort.
  • Modulweise inkrementell: .BAS/.FRM werden unabhängig zu Bytecode- Einheiten übersetzt und beim Build nur zusammengebunden; die IDE recompiliert nur geänderte Module (Hash über Quelltext).
  • Keine Optimierungspasses. Erlaubt sind nur Gratis-Optimierungen im Codegen (Konstantenfaltung im Ausdruck, Peephole beim Emit).
  • Budget als Test verankern (Phase 2): Benchmark-Projekt (~50k Zeilen) muss unter 1 s kompilieren (Release-Build der Toolchain, Referenzrechner); einzelnes Modul < 50 ms.

VM-Ausführung:

  • Alle Namen werden zur Compilezeit aufgelöst: Variablen/Parameter sind Slot-Indizes, Prozeduren Tabellenindizes — zur Laufzeit keine Hash-Lookups.
  • Statische Typen des Dialekts ausnutzen: typisierte Opcodes (ADD_I16, ADD_F64, CONCAT …) statt generischem Dispatch über Value-Tags in heißen Pfaden.
  • Dichte Opcodes, match-Dispatch in einer engen Schleife; Tick-Prüfung (Events/Breakpoints) nur an Anweisungsgrenzen über ein einzelnes Flag-Wort, nicht pro Opcode.
  • Strings immutabel via Rc<str> (Kopien sind Pointer-Kopien); Konstantenpool dedupliziert.
  • Messlatte (Phase 2, Benchmarks in benches/): typische Schleifen-/String-Lasten mindestens ~100× schneller als das Vorbild auf Originalhardware; Richtwert grob Lua-Interpreter-Klasse, gemessen und dokumentiert statt geraten.

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?