# 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"-Formulare des Vorbilds werden nur durch den Konverter gelesen, aber nie geschrieben. ## 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` Das Vorbild kannte zwei Speicherformate: **binär** („Fast load and save", Standard) und **Text** („Readable by other programs"). Terminal Basic schreibt das Textformat und liest das Binärformat ausschließlich zur Konvertierung. Textformat, zwei Abschnitte: Formular-Beschreibung, dann Code. Schema wie beim Windows-Schwesterprodukt: `VERSION`-Zeile, verschachtelte `Begin … End`-Blöcke (Klassennamen siehe forms-referenz.md), `Eigenschaft = Wert`-Zeilen. Der Parser des Vorbilds akzeptiert Version 1.00 und 2.00 (Import aus dem Windows-Produkt via Übersetzer). ``` 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 … End`-Blöcke mit `Eigenschaft = Wert`-Zeilen (Strings in `"…"`). - Danach normaler BASIC-Code des Formular-Moduls. - Terminal Basic schreibt `VERSION` groß, Klassen- und Eigenschaftsnamen in der Schreibweise der Forms-Referenz und drei Leerzeichen je Blockebene. - Der Eigenschaftsname belegt 16 Spalten; danach folgen ` = ` und der Wert. - Eigenschaften stehen in der alphabetischen Reihenfolge der Forms-Referenz und werden nur geschrieben, wenn ihr Wert vom Vorgabewert der Klasse abweicht. - Unverändert gelesene Dateien werden bytegleich zurückgegeben; für neu erzeugte oder veränderte Beschreibungen gilt die kanonische Form oben. - Zeichenketten verdoppeln ein enthaltenes `"`. Wahrheitswerte werden als `-1` und `0` geschrieben. Die minimale kanonische Referenz ist vollständig: ``` VERSION 1.00 Begin Form Form1 Caption = "Beispiel" Height = 15 Begin CommandButton cmdOK Caption = "&OK" End End ``` ### Binäres VBDOS-1.0-Formular Der Binärleser ist ausschließlich ein Importpfad. Er erkennt keine Datei an der Endung, sondern an `FC 08 01 00`. Aus den Originaldateien und dem `cout/vbdos`-Bestand ergibt sich folgender Aufbau: | Bereich | Kodierung | Bedeutung | |---|---|---| | `0x0000` | `FC 08 01 00` | Kennung und VBDOS-Formatversion | | `0x001c` | `u16` little-endian + `0x16` | Dateiposition des Objektkatalogs | | `0x001e` | `u16` little-endian | Länge des versionsgebundenen Objekt-/Eigenschaftsbereichs | | `0x0020` | Wurzelkopf und klassenabhängiger Datensatz; danach je Objekt ein 7-Byte-Kopf und der klassenabhängige Datensatz | Der Objektkopf nennt Katalogindex, Klasse und Flags; der Datensatz enthält Eigenschaften, Array-Index und Containerverweis | | danach | Folgen aus `u16 Länge` und CP437-Bytes | Zeichenkettenpool; ein Datensatzverweis `p` bezeichnet den Längeneintrag bei Dateiposition `p + 0x16`, die Bytes werden beim Import nach UTF-8 gewandelt | | danach | `u16 Verweis`, `u8 Klasse`, `u8 Länge`, Name | Objektkatalog; Bit 7 der Klasse kennzeichnet ein Steuerelementfeld, die unteren sieben Bit entsprechen der Klassen-ID; `Verweis = 0` beendet den Katalog | | Ende des Objektbereichs | aufsteigende `u16`-Verweise | Verweise auf Datensätze bei `Datensatzanfang + 3`, unter anderem für Feldinstanzen und physisch umgeordnete Objekte; jeder Verweis muss auf einen gelesenen Datensatz zeigen | | Rest | Symboltabelle, Modulblöcke und tokenisierter BASIC-Code | Bezeichner und der in Text zurückübersetzte BASIC-Code | In den Datensätzen liegen Containerverweis, `Tag`-Verweis und Arrayindex bei `+0`, `+2` und `+4`, die Geometrie bei `+8` bis `+11` und `TabIndex` bei `+13`. Beim Form-Root liegen `CurrentX`/`CurrentY` bei `+19`/`+20` und `BackColor`/`ForeColor` bei `+21`/`+22`. Klassenabhängig folgen unter anderem `TextBox.BorderStyle`/`ScrollBars` bei `+19`/`+20`; bei TextBoxen kodiert außerdem das Common-Flag `0x08` `MultiLine`. `ComboBox.Style` liegt bei `+31`, `Label.BorderStyle`/`Alignment` bei `+19`/`+20` und `PictureBox.BorderStyle`/`CurrentX`/`CurrentY` bei `+16`/`+17`/`+19`; `Timer.Interval` ist ein `u16` bei `+17`. Das Common-Flag `0x10` eines CommandButton kodiert `Cancel`. Das `AutoRedraw`-Bit `0x08` von Form und PictureBox steht im Objektkopf; bei Forms kodiert `0x02` zusätzlich `FormType = 1`. Dort kodiert `0x80` bei ListBoxen `Sorted` und `0x20` bei Labels `AutoSize`; bei Menüs kodieren `0x01` und `0x40` `Separator` und `Checked`. Scrollbars verwenden ab `+14` ein eigenes Layout: `Attached`, `SmallChange`, `LargeChange`, `Max` und `Min`; ihr Anfangswert ist `Min`. Diese Bytes sind ausdrücklich keine Farbwerte. Der Import ordnet jeden physischen Datensatz über dessen 7-Byte-Kopf dem Katalogeintrag zu; die Katalogreihenfolge ist dafür ausdrücklich nicht maßgeblich. Array-Indizes müssen eindeutig, Containerverweise auf bereits gelesene Objekte gerichtet und Zeichenkettenverweise exakt auf einen Pooleintrag auflösbar sein; auch bei Custom Controls darf kein Pooleintrag unbelegt bleiben. Nicht unterstützte Custom Controls der binären Klasse 17 werden nicht als `Screen` ausgegeben, sondern mit Objektname und tatsächlicher Byteposition gemeldet. Eine gespeicherte, vom Objektmodell nicht unterstützte Menü-Tastenkombination liegt im erweiterten Menüdatensatz als `u16` little-endian bei `+19`; sie wird als `.Shortcut` mit ihrer Byteposition gemeldet und übersprungen. Eine unbekannte Klasse, ein ungültiger Verweis, ein unbelegter String oder ein abgeschnittener Bereich führt an der Fundstelle zum Abbruch; eine Ausgabedatei wird erst nach erfolgreichem Lesen angelegt. ## Projekt: `.MAK` Reine Textdatei ohne Kopfzeile, **eine Projektdatei pro Zeile** (`.BAS`/`.FRM`; belegt durch die Originalbeispiele). Terminal Basic akzeptiert zusätzlich Kommentarzeilen mit `'`. ## Record-Dateien (`GET`/`PUT`, `OPEN … FOR RANDOM`) Feste Strings (`STRING * n`) in Records werden als **UTF-32LE** gespeichert (4 Bytes pro Zeichen → feste Record-Länge bleibt erhalten). Entscheidung 2026-09-02: Die Binärdateien sind damit bewusst inkompatibel zu Dateien des Vorbilds; numerische Felder behalten ihr klassisches Layout (INTEGER i16, LONG i32, SINGLE f32, DOUBLE f64, CURRENCY i64, little-endian). ## ISAM-Datenbank (`OPEN … FOR ISAM`) Eigenes Format, **bewusst nicht binärkompatibel** zu Datenbankdateien des Vorbilds — dieselbe Linie wie bei den UTF-32-Records oben. Ein Konvertierungswerkzeug ist Nicht-Ziel; das Vorbildformat wird weder gelesen noch geschrieben. **Träger.** Die Datei ist eine [`redb`](https://crates.io/crates/redb)-Datei (eingebetteter transaktionaler B-Baum). `redb` liefert Seitenverwaltung, Transaktionen und Crash-Sicherheit; die ISAM-Semantik darüber ist eigener Code (siehe `tb-runtime::isam`). **Formatversion.** Die Datei trägt in ihrer Metatabelle unter `version` eine 32-Bit-Formatversion (little-endian), derzeit **1**. Eine Datei mit höherer Version wird beim Öffnen mit Laufzeitfehler 88 („ISAM - Database inconsistent") abgewiesen, statt fehlinterpretiert zu werden. Ebenso abgewiesen wird eine bestehende Datei ohne Versionsmarke sowie jede strukturell unlesbare Datei. **Tabellen in der Datei.** Je Datenbankdatei: | `redb`-Tabelle | Schlüssel → Wert | Inhalt | |---|---|---| | `tb_meta` | `&str → Bytes` | Formatversion, Layout, Indexdefinitionen, ID-Zähler | | `satz/` | `u64 → Bytes` | ein Satz je Eintrag, Schlüssel ist die Satz-ID | | `idx//` | `Bytes → u64` | Indexeintrag: Schlüsselbytes → Satz-ID | Die Einträge in `tb_meta` je Tabelle `T`: - `tab:T` — Layoutkurzform `spalte:typ;…` mit den Typkürzeln `I2`, `I4`, `R4`, `R8`, `CY`, `T` (fester Text mit `n` Zeichen). Beim Öffnen einer bestehenden Tabelle wird sie gegen den angegebenen Satztyp geprüft; Abweichung ist Fehler 88. - `idx:T` — eine Zeile je Index, `nameeindeutigspalten`, Spalten als Feldnummern mit `-` für absteigend. - `seq:T` — nächste Satz-ID (u64, little-endian). IDs werden **monoton** vergeben und **nie wiederverwendet**: ein Cursor merkt sich eine Satz-ID, und eine nach `DELETE` neu vergebene ID ließe ihn still auf einen fremden Satz zeigen. **Satzbytes.** Ein Satz liegt genau so im Speicher wie ein Record bei `PUT` auf eine `RANDOM`-Datei: numerische Felder klassisch und little-endian, feste Strings als UTF-32LE. Daher gilt die Inkompatibilität zum Vorbild aus demselben Grund wie oben. **Indexschlüssel.** Die Schlüsselbytes sind **ordnungserhaltend** kodiert — ihr Byte-Vergleich entspricht dem fachlichen Vergleich, weil `redb` nach Bytes ordnet: - Ganzzahlen: Big-Endian fester Breite mit gekipptem Vorzeichenbit. - Gleitkommazahlen: Big-Endian der Bitdarstellung; bei negativem Vorzeichen alle Bits invertiert, sonst das oberste Bit gesetzt. - Text: UTF-8 (dessen Bytereihenfolge ist die Codepoint-Reihenfolge, siehe „Sortierordnung" der Sprachreferenz). `00` im Text wird zu `00 FF` verdoppelt, den Abschluss bildet `00 00` — damit ordnet ein echtes Präfix vor jedem längeren Text. - Absteigende Spalten: alle Bytes dieser Spalte invertiert. - Mehrspaltig: Verkettung in Spaltenreihenfolge. An jeden Indexschlüssel sind acht Bytes Satz-ID (Big-Endian) angehängt. Das macht auch Einträge eines mehrdeutigen Index eindeutig, hält Dubletten in Einfügereihenfolge und macht die Präfixsuche zur Bereichsabfrage. ## Kompilat: `.tbc` (neu, eigenes Format) Container für TBVM-Bytecode, Entwurf in [tbvm-design.md](tbvm-design.md). `tbc build` erzeugt wahlweise `.tbc` oder ein eigenständiges Executable (Runner + eingebettetes `.tbc`).