259 lines
13 KiB
Markdown
259 lines
13 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)
|
|
|
|
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`).
|