Phase 0: Referenzdokumente, Fehlerkatalog, Testkorpus, Ratatui-Spike

Entscheidungen festgehalten: TBVM bestaetigt und eingebettet in die
Executables; durchgaengig UTF-8 statt CP437 (dokumentierte Abweichung).

- docs/: Sprachreferenz, Forms-Referenz, Dateiformate, TBVM-Design
- tb-runtime::errors: klassischer Laufzeitfehler-Katalog (implementiert)
- tb-ui::screen: 80x25-Unicode-Zellenpuffer mit 16-Farben-Abbildung,
  Scrollbereich (VIEW PRINT), Letterboxing; Ratatui-Widget + Tests
- Spike: cargo run -p tb-ui --example spike (Farben, Unicode, Tasten, Maus)
- tests/compat/: erste Referenzprogramme mit byte-genauer Sollausgabe

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 16:36:45 +02:00
parent 5821ace0cf
commit 7e6fa6ea88
24 changed files with 1148 additions and 25 deletions

55
docs/dateiformate.md Normal file
View File

@@ -0,0 +1,55 @@
# Dateiformate
Terminal Basic liest und schreibt die Textformate des Vorbilds, durchgängig
in UTF-8 (Abweichung: das Vorbild nutzte die DOS-Codepage). Binäre
„Fast-Load"-Varianten des Vorbilds sind Nicht-Ziel — nur Textformate.
## Quelltext: `.BAS`
Reiner Text, eine Anweisung(sfolge) pro Zeile. Optionale Kopfzeilen der IDE
(`DECLARE`-Prototypen) werden beim Speichern erzeugt/aktualisiert.
Metabefehle in Kommentaren: `'$INCLUDE: 'datei.bi'`, `'$STATIC`, `'$DYNAMIC`.
## Formular: `.FRM`
Textformat, zwei Abschnitte: Formular-Beschreibung, dann Code.
```
VERSION 1.00
Begin Form Form1
Caption = "Beispiel"
Height = 15
Left = 10
Top = 4
Width = 50
Begin CommandButton cmdOK
Caption = "&OK"
Height = 1
Left = 18
Top = 11
Width = 10
End
End
SUB cmdOK_Click ()
UNLOAD Form1
END SUB
```
- `VERSION`-Zeile, dann verschachtelte `Begin <Typ> <Name> … End`-Blöcke
mit `Eigenschaft = Wert`-Zeilen (Strings in `"…"`).
- Danach normaler BASIC-Code des Formular-Moduls.
- TODO: exakte Eigenschaftsnamen/-reihenfolge und Einrückung des Vorbilds
an Originaldateien verifizieren (Ziel: Roundtrip-Kompatibilität).
## Projekt: `.MAK`
Zeilenweise Liste der Projektdateien (`.BAS`, `.FRM`, `.BI`), TODO:
Optionszeilen des Vorbilds prüfen. Terminal Basic akzeptiert zusätzlich
Kommentarzeilen mit `'`.
## Kompilat: `.tbc` (neu, eigenes Format)
Container für TBVM-Bytecode, Entwurf in [tbvm-design.md](tbvm-design.md).
`tbdosc build` erzeugt wahlweise `.tbc` oder ein eigenständiges Executable
(Runner + eingebettetes `.tbc`).

85
docs/forms-referenz.md Normal file
View File

@@ -0,0 +1,85 @@
# Forms-Referenz Terminal Basic
Rekonstruierte Referenz der Forms-Engine des Vorbilds (textbasierte,
ereignisgesteuerte Oberflächen). Grundlage für `tb-ui::forms` und den
Formular-Designer der IDE. Unklare Punkte sind mit `TODO` markiert.
## Koordinatenmodell
Alle Maße in **Textzellen** (Zeilen/Spalten), Ursprung links oben.
Formulare liegen auf dem 80×25-Bildschirm; Steuerelemente relativ zum
Formular-Innenbereich. Eigenschaften `Top`, `Left`, `Height`, `Width`.
## Steuerelemente (Übersicht)
| Steuerelement | Zweck | Schlüssel-Ereignisse |
|---|---|---|
| Form | Fenster (mit/ohne Rahmen, Titel, verschiebbar) | Load, Unload, Activate, Deactivate, Resize, Paint, Click, KeyPress |
| Label | statischer Text | Click |
| TextBox | ein-/mehrzeilige Texteingabe | Change, KeyPress, GotFocus, LostFocus |
| CommandButton | Schaltfläche | Click |
| CheckBox | Mehrfachauswahl (Value 0/1/2) | Click |
| OptionButton | Einfachauswahl in Gruppe (Value TRUE/FALSE) | Click |
| Frame | Gruppierungsrahmen (Container) | — |
| ListBox | Liste (List, ListCount, ListIndex, AddItem/RemoveItem) | Click, DblClick |
| ComboBox | Eingabe + Liste (Style 0/1/2) | Change, Click, DblClick |
| HScrollBar / VScrollBar | Bildlauf (Min, Max, Value, SmallChange, LargeChange) | Change, Scroll |
| PictureBox | Text-Zeichenfläche (PRINT/CLS auf Steuerelement) | Click, Paint |
| Timer | unsichtbar; Interval in ms (Vorbild: 55-ms-Ticks) | Timer |
| Menu | Menüleiste/-einträge (Designer-definiert) | Click |
## Gemeinsame Eigenschaften
`Name` (Entwurfszeit), `Caption`/`Text`, `Top`/`Left`/`Height`/`Width`,
`Visible`, `Enabled`, `TabIndex`, `TabStop`, `Tag`, `Index` (Steuerelement-
Arrays), `ForeColor`/`BackColor` (015 bzw. 07), `MousePointer` (TODO).
Zugriff zur Laufzeit: `form.eigenschaft`, `form!element.eigenschaft`;
Standard-Eigenschaft (z. B. `Text` bei TextBox, `Caption` bei Label) bei
Zuweisung ohne Eigenschaftsname. TODO: exakte Standard-Eigenschaften prüfen.
## Gemeinsame Methoden
`Move top, left [, height, width]` · `SetFocus` · `Refresh` · `Drag` (Nicht-
Ziel?) · Form zusätzlich: `Show [modal]`, `Hide`, `Cls`, `Print`, `Line`
(Textmodus-Variante? TODO). ListBox/ComboBox: `AddItem text$ [, index]`,
`RemoveItem index`, `Clear` (TODO: hieß es `Clear`?).
## Ereignismodell
- Ereignisprozeduren heißen `SUB elementname_Ereignis (argumente)` und liegen
im Formular-Modul.
- Tastatur: `KeyDown(KeyCode%, Shift%)`, `KeyPress(KeyAscii%)`, `KeyUp`
KeyAscii ist bei uns ein Unicode-Codepoint (Abweichung UTF-8).
- Maus: `MouseDown/MouseMove/MouseUp(Button%, Shift%, X!, Y!)` in Zellen.
- Fokusreihenfolge über `TabIndex`; Tab/Shift+Tab wechseln, Access-Keys per
`&` im Caption (z. B. `"&OK"` → Alt+O).
- Zentrale Schleife: Ereignisse werden nur bei `DOEVENTS`, `SLEEP`, `INPUT`-
artigen Wartezuständen oder nach Ende einer Ereignisprozedur zugestellt
(kooperativ, wie im Vorbild — keine Präemption).
## Globale Objekte und Funktionen
- `SCREEN`-Objekt: `SCREEN.ActiveForm`, `SCREEN.ActiveControl`,
`SCREEN.Height`/`Width` (25/80), `SCREEN.MousePointer` (TODO: Umfang).
- `MSGBOX(text$ [, typ% [, titel$]])` als Anweisung und Funktion
(Rückgabe: gedrückte Schaltfläche), `INPUTBOX$(prompt$ [, titel$
[, standard$ [, spalte%, zeile%]]])`.
- `LOAD form` (Load-Ereignis, unsichtbar), `UNLOAD form` (Unload-Ereignis),
Formular-Referenzen sind statisch (keine Instanziierung wie in späteren
Nachfolgern).
## Farben und Zeichensatz
Rahmen, Schatten und Bedienelemente werden mit Unicode-Box-Drawing gezeichnet
(Abweichung: UTF-8 statt CP437, identische Optik). Standard-Farbschema des
Vorbilds (grauer Dialog, schwarze Schrift, weiße Akzente) wird als Default
nachgebildet. TODO: exakte Attributtabelle je Steuerelement-Zustand
(normal/fokussiert/deaktiviert) aus Screenshots/Emulator ableiten.
## Offene Detailfragen (per Emulator-Session zu klären)
- Exakte Standardwerte aller Eigenschaften je Steuerelement
- Z-Reihenfolge/Überlappung von Steuerelementen und Formularen
- Verhalten `Show 1` (modal): welche Ereignisse laufen weiter?
- Timer-Auflösung und -Reihenfolge bei mehreren Timern
- Genauer Umfang von PictureBox im Textmodus

240
docs/sprachreferenz.md Normal file
View File

@@ -0,0 +1,240 @@
# Sprachreferenz Terminal Basic
Rekonstruierte Referenz des Dialekts (DOS-BASIC, Stand 1992: prozedurale
BASIC-Familie mit Forms-Erweiterung). Dieses Dokument ist die verbindliche
Grundlage für Frontend und Runtime. Unklare Detailfragen sind mit `TODO`
markiert und werden per Testkorpus geklärt bzw. entschieden.
**Bewusste Abweichungen vom Vorbild** stehen am Ende des Dokuments.
---
## 1. Lexik
- **Zeilenorientiert.** Eine logische Zeile enthält eine oder mehrere
Anweisungen, getrennt durch `:`. Keine Zeilenfortsetzung im Vorbild
(`_` ist eine spätere Erfindung) — TODO: als Erweiterung erlauben?
- **Zeilennummern** sind optional und wirken als Labels. Alphanumerische
**Labels** enden mit `:` am Zeilenanfang (`Fehler:`).
- **Kommentare:** `REM` (ganze Anweisung) und `'` (bis Zeilenende).
`REM`/`'` am Zeilenanfang mit `$STATIC`/`$DYNAMIC`/`$INCLUDE: 'datei'`
sind **Metabefehle**.
- **Bezeichner:** Buchstabe, dann Buchstaben/Ziffern/`.`, max. 40 Zeichen,
case-insensitiv. Optionales Typ-Suffix als letztes Zeichen.
- **Typ-Suffixe:** `%` INTEGER · `&` LONG · `!` SINGLE · `#` DOUBLE ·
`$` STRING · `@` CURRENCY. `name`, `name%`, `name$` sind
**verschiedene Variablen**.
- **Keywords** sind reserviert und case-insensitiv; die IDE normalisiert
auf Großschreibung.
- **Numerische Literale:** dezimal (`123`, `1.5`, `1.5E3`, `1D3` für DOUBLE),
hexadezimal `&HFF`, oktal `&O777`; Suffixe wie bei Variablen (`10%`, `10&`,
`1.5#`, `2.5@`). Ohne Suffix: kleinster passender Typ (Ganzzahl → INTEGER,
sonst SINGLE/DOUBLE je nach Präzision; TODO: exakte Regel testen).
- **String-Literale:** `"…"`; doppeltes `""` ergibt ein Anführungszeichen.
## 2. Typsystem
| Typ | Suffix | Repräsentation | Wertebereich |
|---|---|---|---|
| INTEGER | `%` | i16 | 32 768 … 32 767 |
| LONG | `&` | i32 | 2 147 483 648 … 2 147 483 647 |
| SINGLE | `!` | f32 | ±3.4E38 |
| DOUBLE | `#` | f64 | ±1.8E308 |
| STRING | `$` | dynamischer Unicode-String | Länge 0 … 32 767 Zeichen |
| STRING * n | — | fester String, n Zeichen | nur in `TYPE`/`DIM` |
| CURRENCY | `@` | i64, Festkomma ×10 000 | ±922 337 203 685 477.5807 |
- **Benutzerdefinierte Typen:** `TYPE name … END TYPE` mit Elementen fester
Größe (numerische Typen, `STRING * n`, verschachtelte TYPEs). Kein
dynamischer STRING in TYPE.
- **Implizite Deklaration:** Erstverwendung deklariert die Variable. Ohne
Suffix gilt der Standardtyp SINGLE, änderbar per
`DEFINT/DEFLNG/DEFSNG/DEFDBL/DEFSTR/DEFCUR az` (buchstabenbereichsweise,
wirkt pro Modul/Prozedur ab Deklaration).
- `OPTION EXPLICIT` gibt es im Vorbild **nicht** — TODO: als opt-in
Erweiterung anbieten?
- **Arrays:** `DIM a(10)`, `DIM a(1 TO 10, 0 TO 5)`. Untergrenze standardmäßig
0, per `OPTION BASE 1` änderbar. `$STATIC`/`$DYNAMIC` bzw. Kontext bestimmen
statisch/dynamisch; `REDIM` (dynamisch, löscht Inhalt), `ERASE`
(reinitialisiert statisch / gibt dynamisch frei). Max. 8 Dimensionen
(TODO: prüfen). `LBOUND`/`UBOUND` liefern Grenzen.
- **Konvertierung:** implizit zwischen numerischen Typen mit Rundung
(Banker's Rounding bei `CINT`/`CLNG` und Zuweisung an Ganzzahl); Überlauf
→ Fehler 6. Keine implizite Konvertierung Zahl ↔ String (Fehler 13,
Type mismatch).
## 3. Deklarationen und Sichtbarkeit
- `DIM [SHARED] var[(dims)] [AS typ]``AS`-Klausel: INTEGER, LONG, SINGLE,
DOUBLE, STRING, STRING * n, CURRENCY, benutzerdefinierter Typ.
- `COMMON [SHARED] [/blockname/] liste` — modulübergreifend (Kette
CHAIN-kompatibel im Vorbild; TODO: Relevanz ohne CHAIN klären).
- `SHARED` (in Prozedur): Zugriff auf Modulebene-Variablen.
- `STATIC` (in Prozedur): Variablen behalten Werte zwischen Aufrufen;
`STATIC`-Attribut an `SUB`/`FUNCTION` macht alle lokalen Variablen statisch.
- `CONST name = ausdruck` — Konstanten (konstante Ausdrücke zur Compilezeit).
- `DECLARE SUB/FUNCTION name (parameter)` — Prototyp; die IDE erzeugt sie
automatisch beim Speichern.
## 4. Operatoren (nach Priorität, hoch → niedrig)
1. `^` (Potenz)
2. unäres `-`
3. `*`, `/` (Fließkommadivision)
4. `\` (Ganzzahldivision; Operanden werden gerundet auf INTEGER/LONG)
5. `MOD` (Ganzzahlrest, Vorzeichen wie Dividend)
6. `+`, `-` (`+` auch String-Verkettung)
7. Vergleiche `= <> < > <= >=` (Zahlen und Strings; Strings
codepoint-weise — Abweichung, s. u.)
8. `NOT`
9. `AND`
10. `OR`
11. `XOR`
12. `EQV`
13. `IMP`
Logische Operatoren sind **bitweise** auf Ganzzahlen; Vergleichsergebnis ist
INTEGER 1 (wahr) / 0 (falsch).
## 5. Kontrollfluss
- `IF b THEN … [ELSE …]` (einzeilig) und Block-`IF … THEN / ELSEIF / ELSE /
END IF`
- `SELECT CASE ausdruck` mit `CASE wert`, `CASE a TO b`, `CASE IS >= x`,
`CASE ELSE`
- `FOR i = a TO b [STEP s] … NEXT [i]` (Grenzen werden einmal ausgewertet;
Schleifenvariable numerisch)
- `DO [WHILE|UNTIL b] … LOOP [WHILE|UNTIL b]`, `WHILE … WEND`
- `EXIT FOR / EXIT DO / EXIT SUB / EXIT FUNCTION / EXIT DEF`
- `GOTO ziel`, `GOSUB ziel` / `RETURN [ziel]`
- `ON n GOTO liste`, `ON n GOSUB liste` (berechneter Sprung, 1-basiert;
0 oder > Anzahl: kein Sprung; negativ/>255: Fehler 5)
- `END` (Programmende), `STOP` (Unterbrechung → im IDE-Kontext Debugger),
`SYSTEM` (Programmende, im Vorbild „zurück zu DOS")
- `SLEEP [sekunden]`, `DO EVENTS`/`DOEVENTS` (Ereignisse verarbeiten —
zentral für Forms)
## 6. Prozeduren
- `SUB name (p1 AS t, p2(), …) [STATIC] … END SUB`; Aufruf `CALL name(args)`
oder `name args` (ohne Klammern).
- `FUNCTION name (…) [STATIC] … name = wert … END FUNCTION`; Typ über Suffix
oder `DEF…`-Regel.
- **Parameterübergabe standardmäßig BYREF.** Klammern um ein Argument
(`CALL f((x))`) erzwingen Wertübergabe. `BYVAL` nur in `DECLARE` für
externe Routinen (entfällt bei uns; TODO: `BYVAL` allgemein erlauben?).
- Arrays werden mit `name()` übergeben, TYPEs BYREF.
- Rekursion erlaubt (außer bei `STATIC`-Semantik-Konflikten).
- `DEF FNname (args) = ausdruck` und Block-`DEF FN … END DEF`; Aufruf
`FNname(…)`. Modulweit, kein eigener Namensraum.
## 7. Fehlerbehandlung
- `ON ERROR GOTO label` (aktiviert Handler), `ON ERROR GOTO 0` (deaktiviert;
in einem aktiven Handler: Fehler weiterreichen → Programmabbruch),
`ON ERROR RESUME NEXT` — TODO: prüfen, ob das Vorbild das kennt
(QB-Familie: nein; Forms-Dialekt: ja?).
- Im Handler: `RESUME` (fehlerauslösende Anweisung wiederholen),
`RESUME NEXT`, `RESUME label`.
- `ERR` (Code), `ERL` (Zeilennummer, nur numerische Zeilennummern!),
`ERROR n` (Fehler auslösen).
- Fehler im aktiven Handler → sofortiger Abbruch. Fehler ohne Handler →
Abbruch mit Meldung „Fehlertext in Zeile n" bzw. Debugger in der IDE.
- Fehlerkatalog: siehe `tb-runtime/src/errors.rs` (implementiert).
## 8. Ereignis-Traps (klassisch, ohne Forms)
`ON TIMER(n) GOSUB label` + `TIMER ON/OFF/STOP`; analog `ON KEY(n)`,
`ON PLAY`, `ON COM(n)`, `ON PEN`, `ON STRIG(n)`. Für Terminal Basic relevant:
`TIMER` und `KEY`; Rest: Nicht-Ziel (siehe Abweichungen).
## 9. Konsolen-E/A
- `PRINT [#n,] liste` — Trennzeichen `;` (direkt anschließend) und `,`
(nächste 14-Zeichen-Druckzone). Zahlen: führendes Leerzeichen bzw. `-`,
nachgestelltes Leerzeichen. Abschluss ohne `;`/`,` → Zeilenumbruch.
- `PRINT USING "format"; liste` — Formatzeichen: `#` Ziffer, `.` Dezimalpunkt,
`,` Tausendertrennung, `+`/`-` Vorzeichen, `$$` Währung, `**` Füllsterne,
`^^^^` Exponent, `&` String ganz, `!` erstes Zeichen, `\ \` n Zeichen,
`_` Literal-Escape.
- `INPUT ["prompt"{;|,}] var, …` (mit `;` vor Prompt: kein „? ");
`LINE INPUT` (ganze Zeile in String).
- `INKEY$` (nicht blockierend; "" wenn leer; erweiterte Tasten:
2-Zeichen-Sequenz `CHR$(0)+code` im Vorbild — Abweichung s. u.),
`INPUT$(n [,#f])`.
- `LOCATE [zeile][,spalte][,cursor an/aus][,start,ende]`, `CSRLIN`, `POS(0)`.
- `COLOR [vg][,hg]` (vg 031: 1631 = blinkend; hg 07), `CLS`,
`WIDTH` (80/40 — wir: nur 80), `VIEW PRINT oben TO unten` (Scrollbereich).
- `TAB(n)`, `SPC(n)` in PRINT-Listen.
- `BEEP`, `SOUND freq, dauer` (Terminal-Bell / Nicht-Ziel, s. Abweichungen),
`PLAY` (Nicht-Ziel).
- `KEY n, text$` / `KEY LIST` / `KEY ON/OFF` (Funktionstasten-Makros +
Statuszeile) — TODO: Umfang klären.
## 10. Datei-E/A
- `OPEN datei$ [FOR modus] [ACCESS zugriff] [lock] AS [#]n [LEN=reclen]`
Modi: `INPUT`, `OUTPUT`, `APPEND` (sequenziell), `RANDOM` (Standard),
`BINARY`.
- `CLOSE [#n, …]`, `RESET` (alle schließen).
- Sequenziell: `PRINT #`, `PRINT # USING`, `WRITE #` (CSV-artig, Strings in
`"…"`), `INPUT #`, `LINE INPUT #`, `EOF(n)`.
- Random: `FIELD #n, breite AS var$…` (klassisch) **und** `GET/PUT #n
[,satznr] [,var]` mit TYPE-Variablen; `LSET`/`RSET` für Feldpuffer.
- Binary: `GET/PUT #n, [pos], var`, `SEEK #n, pos` / `SEEK(n)`,
`LOC(n)`, `LOF(n)`.
- Verwaltung: `NAME alt$ AS neu$`, `KILL datei$`, `FILES [muster$]`,
`CHDIR`, `MKDIR`, `RMDIR`, `FILEATTR`, `FREEFILE`.
- Pfade: plattformneutral; `/` und `\` werden akzeptiert.
## 11. Eingebaute Funktionen (Katalog)
**Strings:** `LEN`, `LEFT$`, `RIGHT$`, `MID$` (Funktion **und** Anweisung),
`INSTR([start,] s$, such$)`, `UCASE$`, `LCASE$`, `LTRIM$`, `RTRIM$`,
`SPACE$`, `STRING$(n, zeichen|code)`, `ASC`, `CHR$`, `STR$`, `VAL`,
`HEX$`, `OCT$`, `LSET`/`RSET` (Anweisungen).
**Mathematik:** `ABS`, `SGN`, `INT` (abrunden), `FIX` (Richtung 0), `SQR`,
`EXP`, `LOG`, `SIN`, `COS`, `TAN`, `ATN`, `RND[(n)]`, `RANDOMIZE [saat]`
(kompatibler PRNG! → Testkorpus), `CINT`, `CLNG`, `CSNG`, `CDBL`, `CCUR`.
**Datum/Zeit:** `DATE$` (Funktion und Anweisung — Setzen: Nicht-Ziel),
`TIME$`, `TIMER` (Sekunden seit Mitternacht, SINGLE).
**Sonstiges:** `LBOUND`, `UBOUND`, `FRE(…)` (freier Speicher — liefert bei
uns Pseudowerte), `VARPTR`/`VARSEG`/`SADD`/`PEEK`/`POKE` → Nicht-Ziel
(Fehler 73), `ENVIRON$`, `COMMAND$`, `SHELL [cmd$]`.
**DATA:** `DATA konstanten`, `READ var, …`, `RESTORE [label]`.
## 12. Forms-Anbindung (Details in forms-referenz.md)
`form.eigenschaft = wert`, `form!steuerelement.eigenschaft`,
Ereignisprozeduren `SUB name_Ereignis (…)`, `LOAD`/`UNLOAD form`,
`form.SHOW [modal]`, `form.HIDE`, `MSGBOX`/`INPUTBOX$`-Funktionen,
`DOEVENTS`, `SCREEN`-Objekt (aktives Formular/Steuerelement).
---
## Abweichungen vom Vorbild (beschlossen)
1. **UTF-8/Unicode statt CP437** (2026-09-01). Konsequenzen:
- STRING ist eine Folge von Unicode-Zeichen (Codepoints); `LEN` zählt
Zeichen, nicht Bytes. `ASC`/`CHR$` arbeiten auf Codepoints
(`CHR$(9731)` = „☃"). `ASC("")` bleibt Fehler 5.
- String-Vergleich codepoint-weise (keine CP437-Sortierung).
- Der Bildschirmpuffer speichert Unicode-Zeichen; Rahmen werden mit
Unicode-Box-Drawing gezeichnet.
- Zeichen mit Darstellungsbreite ≠ 1: offen (siehe PLAN.md).
2. **Keine Hardware-Nähe:** `PEEK`/`POKE`/`INP`/`OUT`/`CALL ABSOLUTE`/
Interrupts lösen Fehler 73 (Feature unavailable) aus.
3. **`INKEY$` für erweiterte Tasten** liefert weiterhin
`CHR$(0) + code`-Sequenzen mit den klassischen Scancodes (F1 = `CHR$(0)+";"`
usw.), damit bestehender Code funktioniert. Zusätzliche moderne Tasten:
TODO.
4. **Kein `CHAIN`/Overlay-Mechanismus**; `SHELL` startet die System-Shell.
5. **Grafik-Anweisungen** (`SCREEN n>0`, `PSET`, `LINE`, `CIRCLE`, `PAINT`,
`DRAW`) sind Nicht-Ziel (Fehler 73) — das Vorbild war im Forms-Modus
ebenfalls textonly.
6. **`PLAY`/`SOUND`**: `BEEP` = Terminal-Bell; Rest Nicht-Ziel (Fehler 73).
7. **`WIDTH 40`** wird nicht unterstützt (nur 80×25).

85
docs/tbvm-design.md Normal file
View File

@@ -0,0 +1,85 @@
# 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<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.
## 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?