263 lines
14 KiB
Markdown
263 lines
14 KiB
Markdown
# 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 <Klassenname> <Name> … 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 <Typ> <Name> … 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 `<Menüname>.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 `'`. `tbc check`, `build` und
|
||
`run` lösen die Einträge relativ zur `.MAK`-Datei und ohne Beachtung der
|
||
DOS-Großschreibung auf; Formularobjekte aller `.FRM`-Einträge stehen dem
|
||
gemeinsamen Modulverbund zur Verfügung.
|
||
|
||
## 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/<tabelle>` | `u64 → Bytes` | ein Satz je Eintrag, Schlüssel ist die Satz-ID |
|
||
| `idx/<tabelle>/<index>` | `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<n>` (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, `name<TAB>eindeutig<TAB>spalten`, 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.
|
||
|
||
**Datenbankidentität und Tabellenlöschung.** ISAM löst bestehende Datenbankpfade
|
||
kanonisch auf; bei einer Neuanlage wird zuerst der Elternpfad aufgelöst.
|
||
Nach der Anlage wird die endgültige Dateiidentität erneut bestimmt, sodass
|
||
auch Symlinks auf zuvor fehlende Zieldateien korrekt gebunden bleiben.
|
||
Relative Pfade beziehen sich auf das aktuelle Arbeitsverzeichnis. Ein späteres
|
||
`CHDIR` ändert bestehende Bindungen nicht. Relative, absolute und über Symlinks
|
||
auflösbare Aliase derselben vorhandenen Datei teilen einen Schreibkontext.
|
||
`DELETETABLE` schließt alle Bindungen der entfernten Tabelle. Zugriffe über die
|
||
alten Nummern melden Fehler 52. Nach Rücknahme der Löschung ist der Bestand
|
||
wieder vorhanden, muss aber mit `OPEN` neu gebunden werden.
|
||
|
||
**Transaktionen und Sicherungspunkte.** Außerhalb von `BEGINTRANS` wird
|
||
jede Änderung einzeln festgeschrieben. Innerhalb einer Transaktion teilen
|
||
alle Bindungen derselben Datenbank einen Schreibkontext; auch ein weiteres
|
||
`OPEN` sieht die bisherigen Änderungen und schreibt sie nicht fest.
|
||
`CLOSE #n`, `CLOSE` und `RESET` lösen nur Bindungen. `COMMITTRANS` schreibt
|
||
fest, `ROLLBACK ALL` bricht die Transaktion ab; eine beim Programmende
|
||
offene Transaktion verfällt. Bei mehreren Datenbankdateien erfolgt die
|
||
Festschreibung je Datei, ohne datenbankübergreifende Atomaritätsgarantie.
|
||
|
||
`SAVEPOINT` liefert eine Kennung. `ROLLBACK kennung` nimmt die danach
|
||
erfolgten Satzänderungen, Indexanlagen/-löschungen und Tabellenanlagen/-löschungen
|
||
einschließlich Layout, Indexdefinitionen und gelöschtem Tabellenbestand zurück.
|
||
Die Transaktion bleibt offen; der gewählte und alle jüngeren Sicherungspunkte
|
||
werden verbraucht. `ROLLBACK` ohne Kennung verwendet den letzten
|
||
Sicherungspunkt oder, wenn keiner besteht, den Transaktionsbeginn.
|
||
Die Rücknahme funktioniert auch nach dem Schließen aller Bindungen.
|
||
Bei Satz-Rücknahmen bleibt die ID-Vergabe monoton; das Wiederherstellen
|
||
einer gelöschten Tabelle stellt auch ihren gespeicherten ID-Zähler wieder her.
|
||
|
||
Nach einer Rücknahme sind Cursor unpositioniert; nicht mehr vorhandene aktive
|
||
Indizes werden auf den NULL-Index zurückgesetzt. Bindungen an zurückgenommene
|
||
Tabellenanlagen oder an ein zurückgenommenes Layout werden gelöst. Ein erneutes
|
||
`OPEN` bindet den wiederhergestellten Bestand beziehungsweise legt eine fehlende
|
||
Tabelle neu an. Die von `FREEFILE` gelieferten Nummern berücksichtigen gemeinsam
|
||
gewöhnliche Dateien und ISAM-Bindungen; doppelte Nummern werden mit Fehler 55
|
||
abgewiesen.
|
||
|
||
Das Rücknahmeprotokoll hält Layouts unabhängig von offenen Bindungen.
|
||
Für Strukturänderungen speichert es die betroffenen Metadaten und, bei Löschungen,
|
||
den gelöschten Index beziehungsweise Tabellenbestand, keine vollständige
|
||
Datenbankkopie je Sicherungspunkt. Diese gespeicherten Daten zählen zum
|
||
ISAM-Puffer (`SETMEM`); reicht er nicht, scheitert die Strukturänderung vor
|
||
dem Schreiben mit Fehler 89. Rücknahme und Transaktionsende geben diesen
|
||
Puffer wieder frei. Auch DELETE prüft seine Satzbytes vor der Änderung gegen
|
||
die verfügbare Grenze; Fehler 89 lässt Sätze, Indizes und Cursor unverändert.
|
||
Das Dateiformat bleibt Version 1.
|
||
|
||
## Kompilat: `.tbc` (neu, eigenes Format)
|
||
|
||
Version 4 ist ein eigenständig ausführbarer Bytecode-Container; das vollständige
|
||
Format steht in [tbvm-design.md](tbvm-design.md). `tbc build app.mak` erzeugt
|
||
`app.tbc`, `tbc run app.tbc` führt es ohne BAS-/FRM-/MAK-/Include-Quellen aus.
|
||
Quellorte, Prozedursignaturen und Formular-Anfangswerte einschließlich
|
||
Designzeit-Control-Arrays bleiben erhalten. Ältere Versionen 1–3 und unbekannte
|
||
Versionen werden mit Versionsangabe abgewiesen. Native Executable-Verpackung
|
||
ist noch nicht implementiert.
|