# 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. Offene Detailfragen sind ausgeräumt
(Stand 2026-09-04); wo die Original-Dokumentation schweigt, steht an Ort
und Stelle eine datierte Entscheidung mit Begründung.

**Primärquellen:** Original-Hilfedateien des Vorbilds als HTML unter
https://dos-help.soulsphere.org/ (`qck.hlp` Quick Reference, `advr.hlp`
Details, `ener.hlp` Fehlermeldungen; Vergleich PDS 7.1: `bas7qck.hlp`)
sowie das Original-README der Professional Edition. Aussagen ohne
ausgewiesene Entscheidung sind daraus belegt; die Themenlisten von
`bas7advr.hlp` und `qb45advr.hlp` bilden zusammen das
Vollständigkeits-Inventar (docs/inventar.md).

**Guiding Principle:** Die Original-Dokumentation führt. Jedes dort
dokumentierte Sprachelement wird unterstützt oder — nur nach expliziter
Non-Feature-Entscheidung des Projektinhabers — mit einer klaren
Fehlermeldung abgewiesen. **Bewusste Abweichungen und Non-Features**
stehen am Ende dieses Dokuments; nur diese Liste rechtfertigt Abweisungen.

---

## 1. Lexik

- **Zeilenorientiert.** Eine logische Zeile enthält eine oder mehrere
  Anweisungen, getrennt durch `:`. **Zeilenfortsetzung:** `_` als letztes
  Zeichen (mit vorangehendem Leerzeichen/Tab) setzt die Zeile fort — im
  Vorbild für extern erstellten Code gedacht (der IDE-Editor fügt
  fortgesetzte Zeilen beim Laden zusammen); Terminal Basic akzeptiert es
  überall.
- **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.
  (`_` in Bezeichnern ist eine dokumentierte Neuerung des Vorbilds.)
- **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 erzwingen den Typ (`10%`, `10&`,
  `1.5!`, `1.5#`, `2.5@`). Ohne Suffix gilt: Ganzzahl im INTEGER-Bereich →
  INTEGER; Hex/Oktal ohne `&`-Suffix → 16 Bit (INTEGER), mit `&` → LONG;
  `E`-Exponent → SINGLE, `D`-Exponent → DOUBLE; Dezimalpunkt-Wert:
  **Entscheidung (2026-09-02):** bis 7 signifikante Stellen → SINGLE,
  darüber → DOUBLE. (Die Original-Hilfe nennt widersprüchlich eine
  15-Stellen-Schwelle, die SINGLE-Präzision verlöre; wir legen die Schwelle
  auf die SINGLE-Genauigkeit — präzisionserhaltende Auslegung.)
  Ganzzahl außerhalb des LONG-Bereichs wird Gleitkommawert (kein Fehler).
- **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 a–z` (buchstabenbereichsweise,
  wirkt pro Modul/Prozedur ab Deklaration).
- `OPTION EXPLICIT` **existiert** (Neuerung des Vorbilds gegenüber der
  QB/PDS-Familie): erzwingt Deklaration aller Variablen (über COMMON,
  CONST, DEF FN, DIM, FUNCTION, REDIM, SHARED, STATIC oder SUB); gilt
  auch für das Direktfenster.
- **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. **60 Dimensionen**,
  Indizes −32 767 … 32 767 (mit `TO`). `LBOUND`/`UBOUND` liefern Grenzen.
  (DOS-Speichergrenzen der Arrays — 64K/128K-Regeln — übernehmen wir nicht.)
- **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). Die vollständige Konvertierungsmatrix (Ergebnistypen
  der Operatoren, Rundungs- und Überlaufregeln, Textdarstellung) steht
  in docs/tbvm-design.md und ist über `tests/compat/konvertierung.bas`
  ausführbar verankert (Phase 2, 2026-09-02).

## 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; ohne `CHAIN` bleibt `COMMON` die
  Verknüpfung mehrerer Module eines Projekts — geklärt mit der
  Projektverwaltung in Phase 5).
- `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.
- `'$INCLUDE: 'datei'` — bindet eine Textquelle relativ zur einschließenden
  Datei ein; Namen werden wie unter DOS ohne Beachtung der Großschreibung
  aufgelöst, Zyklen werden namentlich abgewiesen.

## 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").
  Runner-Verhalten (Entscheidung 2026-09-02): außerhalb der IDE beendet
  `STOP` das Programm mit der Meldung `STOP in line n` und Exit-Code ≠ 0;
  `END`/`SYSTEM`/Programmende liefern Exit-Code 0. `CONT` existiert nur
  als Direktmodus-Befehl der IDE (Phase 5) und ist nicht kompilierbar.
- `SLEEP [sekunden]`, `DO EVENTS`/`DOEVENTS` (Ereignisse verarbeiten —
  zentral für Forms)
- `RUN` startet das aktuelle Programm neu; `RUN zeile` startet es nach den
  globalen Deklarationen an der angegebenen Zeile, `RUN datei$` lädt relativ
  zur laufenden Quelle eine `.BAS`-, `.FRM`- oder `.MAK`-Datei neu.

## 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; `BYVAL` allgemein zu erlauben wäre
  eine Erweiterung über den Kernstandard hinaus → Stufe 2, PLAN.md).
- 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` (mit
  `EXIT DEF`); Aufruf `FNname(…)`. Nur auf Modulebene definier- und
  nutzbar, **nicht rekursiv**, Argumente by value, keine Arrays/Records/
  Fixed-Strings als Argumente.

## 7. Fehlerbehandlung

- Vollständige Syntax: `ON [LOCAL] ERROR {GOTO zeile/label | RESUME NEXT |
  GOTO 0}`. `LOCAL` installiert einen **prozedurlokalen** Handler;
  ohne `LOCAL` gilt der Handler modulweit. `GOTO 0` deaktiviert (in einem
  aktiven Handler: Fehler weiterreichen → Programmabbruch).
- Im Handler: `RESUME` (fehlerauslösende Anweisung wiederholen),
  `RESUME NEXT`, `RESUME zeile/label`.
- `ERR` (Code), `ERL` (Zeilennummer, nur numerische Zeilennummern!),
  `ERROR n` (Fehler auslösen). Fehlernummer 105 ist im Vorbild intern
  reserviert und sollte nicht via `ERROR n` verwendet werden.
- Fehler im aktiven Handler → sofortiger Abbruch. Fehler ohne Handler →
  Abbruch mit Meldung „Fehlertext in Zeile n" bzw. Debugger in der IDE.
- Fehlerkatalog: klassische Codes 1–76, ISAM-Codes 80–89, Forms-Codes
  260–480 — vollständig implementiert in `tb-runtime/src/errors.rs`.

## 8. Ereignis-Traps (klassisch, ohne Forms)

Vorhanden im Vorbild: `ON COM/KEY/PEN/PLAY/SIGNAL/STRIG/TIMER/UEVENT(…)
GOSUB label` mit Steueranweisungen `… ON/OFF/STOP`, dazu das
`EVENT`-Statement (auch für Forms-Ereignisse) und `SetUEvent`.
Timer-Events und Trapping sind ausgesetzt, solange ein Menü-Control den
Fokus hat. Für Terminal Basic relevant: `TIMER`, `KEY`, `UEVENT`,
`SIGNAL`; `COM/PEN/PLAY/STRIG`: Nicht-Ziel (Fehler 73).

**Traps und Wertebereiche**

| Anweisung | Wertebereich | Quelle |
|---|---|---|
| `ON TIMER(n&) GOSUB ziel` | `n&` = 1–86 400 Sekunden | verstrichene Zeit |
| `ON KEY(n%) GOSUB ziel` | `n%` = 0 (alle), 1–10 (F1–F10), 11 ↑, 12 ←, 13 →, 14 ↓, 15–25 (benutzerdefiniert), 30–31 (F11–F12) | Tastendruck |
| `ON UEVENT GOSUB ziel` | — | `SetUEvent` |
| `ON SIGNAL(n%) GOSUB ziel` | `n%` = 1 (`SIGINT`), 2 (`SIGTERM`) | Betriebssystem |

`GOSUB 0` schaltet den jeweiligen Trap ab. Ein Wert außerhalb des
Bereichs wird namentlich abgewiesen — zur Übersetzungszeit, wenn er
konstant ist, sonst mit Fehler 5.

**Maskierung.** Je Quelle `ON` (zustellen), `OFF` (verwerfen — auch ein
unter `STOP` bereits gemerktes Ereignis) und `STOP` (höchstens eines
merken und beim nächsten `ON` zustellen). `EVENT ON`/`EVENT OFF`
klammern einen Abschnitt, in dem keine Ereignisse erkannt werden müssen;
ein `EVENT STOP` gibt es nicht.

**Zustellpunkte.** Anweisungsgrenze, `DOEVENTS`, während `SLEEP` und vor
blockierender Eingabe — sonst nirgends. Ein zugestelltes Ereignis
beendet ein laufendes `SLEEP`.

**Wiedereintritt.** Der Eintritt in einen Handler führt selbsttätig
`STOP` seiner Quelle aus, das `RETURN` selbsttätig `ON` — außer im
Handler stand ein ausdrückliches `OFF`, dann bleibt die Quelle aus.

**Benutzerdefinierte Trap-Tasten.** `KEY n, CHR$(flag) + CHR$(scancode)`
erklärt für `n` = 15–25, welche Taste der zugehörige `ON KEY(n)`-Trap
fängt — anders als für 1–10 und 30–31, wo dieselbe Anweisung ein Makro
zuweist. `flag` ist das Tastaturflagbyte (1 rechte Umschalttaste, 2 linke,
4 Strg, 8 Alt), `scancode` der Scancode der Taste. Beispiel: `KEY 15,
CHR$(4) + CHR$(30)` fängt Strg+A.

**Festlegungen ohne Beleg im Vorbild** (2026-09-04; die Original-Hilfe
verweist dazu auf ein nicht vorliegendes Kapitel des Programmer's Guide):

- Eine Taste mit aktivem `ON KEY`-Trap erscheint **nicht** zusätzlich bei
  `INKEY$`/`INPUT`; sonst sähe ein Programm dieselbe Taste zweimal.
- Sind mehrere Ereignisse zugleich fällig, gilt die Reihenfolge `KEY`
  (aufsteigend nach `n`), `TIMER`, `UEVENT`, `SIGNAL`; zugestellt wird
  eines je Zustellpunkt.
- Zugestellt wird an jeder Anweisungsgrenze, auch innerhalb von
  `SUB`/`FUNCTION`; das Sprungziel liegt im Modulrumpf — dieselbe Regel
  wie beim modulweiten `ON ERROR`.
- Die Bits des Tastaturflagbytes folgen der BIOS-Belegung. Linke und
  rechte Umschalttaste sind im Terminal nicht unterscheidbar; beide Bits
  gelten gleich. NumLock und CapsLock werden ignoriert.
- Zeichen ohne Eintrag in der Scancode-Tabelle des Vorbilds können keinen
  benutzerdefinierten Trap auslösen.

## 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 (offizielle Liste):
  numerisch `#` Ziffer, `.` Dezimalpunkt, `,` Tausendertrennung, `+` Vorzeichen-
  position, `-` nachgestelltes Minus, `$$` führendes $, `**` Füllsterne,
  `**$` kombiniert, `^^^^` Exponent; String `&` ganz, `!` erstes Zeichen,
  `\ \` n Zeichen (Leerzeichen + 2); `_` Literal-Escape; alle anderen
  Zeichen literal.
  - **Feldüberlauf** (geklärt 2026-09-04): Passt ein Wert nicht in sein
    Feld, wird er vollständig ausgegeben und die Ausgabe mit `%`
    eingeleitet (`PRINT USING "##"; 12345` → `%12345`). Die Original-Hilfe
    listet das nicht; wir übernehmen das Verhalten der QB-Familie, weil ein
    stilles Abschneiden Zahlen verfälschen würde.
  - Vorzeichenangaben (`+` vorn, `+`/`-` hinten) belegen eine **eigene**
    Position neben den Ziffernstellen; `$$` und `**` bringen ihre Stellen
    dagegen mit.
  - Die Formatzeichenkette wird wiederholt, solange Werte übrig sind. Eine
    Zeichenkette ohne Feld bei vorhandenen Werten löst Fehler 5 aus.
  - `SetFormatCC(ländercode)` setzt das Zeichen für `$$`. Nur wenige Codes
    sind belegt (1 = `$`, 33 = `F`, 39 = `L`, 44 = `£`, 49 = `DM`,
    81 = `¥`); alle übrigen behalten `$` — die Tabelle des Vorbilds ist
    nicht überliefert (dokumentierte Abweichung).
- `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])` liest genau `n` Unicode-Codepoints, ohne Echo. Der
  Rest einer Sondertastenfolge bleibt lesbar. Dateien werden als UTF-8
  decodiert; zu wenige Zeichen → Fehler 62, beschädigtes UTF-8 → Fehler 57.
- `LOCATE [zeile][,spalte][,cursor an/aus][,start,ende]`, `CSRLIN`, `POS(0)`
  — arbeiten auf der tatsächlichen Bildschirmgröße (dynamisch, s. Abweichungen).
- `COLOR [vg][,hg]` (vg 0–31: 16–31 = blinkend — bei uns als „hell"
  simuliert, Entscheidung 2026-09-02; hg 0–7), `CLS`,
  `WIDTH` (hebt den Puffer höchstens an, s. Abweichungen),
  `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).
- **Funktionstasten-Makros** (Umfang geklärt 2026-09-03 aus der
  Original-Hilfe):
  - `KEY n, text$` belegt ein Makro. `n` ist 1–10 für F1–F10 sowie 30 und
    31 für F11/F12; andere Werte lösen Fehler 5 aus. `text$` wird nach
    **15 Zeichen** abgeschnitten.
  - `KEY LIST` gibt alle zwölf Makros untereinander aus.
  - `KEY ON` blendet die Softkey-Zeile in der untersten Bildschirmzeile ein
    (Tastennummer plus die ersten sechs Zeichen des Makros), `KEY OFF`
    blendet sie aus. Solange sie sichtbar ist, endet der Scrollbereich
    darüber, damit die Ausgabe sie nicht überschreibt; ein danach gesetztes
    `VIEW PRINT` hat Vorrang.
  - `KEY(n) ON|OFF|STOP` **mit** Index ist Ereignissteuerung und kommt mit
    der Forms-Engine (Phase 4).
  - Abweichung: ein kompiliertes Programm startet mit **leeren** Makros;
    die Vorbelegung des Vorbilds gehörte zu dessen Interpreter-Oberfläche.

## 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
  `"…"`, innere Anführungszeichen verdoppelt), `INPUT #`, `LINE INPUT #`,
  `EOF(n)`. `INPUT #` bewahrt übrige Felder und liest über Zeilen hinweg;
  fehlende geforderte Felder lösen Fehler 62 aus. Die drei Datei-Lesearten
  `INPUT #`, `LINE INPUT #`, `INPUT$` teilen einen Cursor.
- 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. Fehlende Datei
  bei vorhandenem Elternverzeichnis → Fehler 53; fehlender Elternpfad → 76.
- `CVI/CVL/CVS/CVD/CVC` verlangen exakt 2/4/4/8/8 Bytezeichen mit
  Codepoints 0–255, sonst Fehler 5. Die zugehörigen `MK…$`-Funktionen
  liefern diese Darstellung; Recordfelder bleiben UTF-32.

## 10a. ISAM (indizierter Satzzugriff)

ISAM der Professional-Edition des Vorbilds wird unterstützt (Entscheidung
2026-09-02). Das Dateiformat beschreibt
[dateiformate.md](dateiformate.md#isam-datenbank-open--for-isam).

- `OPEN datenbank$ FOR ISAM typname tabellenname$ AS [#]n` bindet eine
  Tabelle an eine Dateinummer; die Felder des benannten `TYPE` sind ihre
  Spalten. Die Tabelle entsteht beim ersten Öffnen. `CLOSE` löst die
  Bindung; jede Operation außerhalb einer Transaktion ist für sich schon
  festgeschrieben. Eine laufende Transaktion lässt `CLOSE` unberührt —
  über ihr Ende entscheiden `COMMITTRANS` und `ROLLBACK ALL`; eine beim
  Programmende offene Transaktion bleibt wirkungslos.
- Indizes: `CREATEINDEX [#]n, indexname$, eindeutig%, spalte$[, spalte$]…`,
  `DELETEINDEX [#]n, indexname$`, `SETINDEX [#]n[, indexname$]`,
  `GETINDEX$(n)`. `SETINDEX` ohne Namen wählt den NULL-Index
  (Einfügereihenfolge); `GETINDEX$` liefert dafür einen leeren String.
- Sätze: `INSERT`, `RETRIEVE`, `UPDATE` (je `[#]n, satzvariable`) und
  `DELETE [#]n` auf dem aktuellen Satz.
- Cursor: `MOVEFIRST`, `MOVELAST`, `MOVENEXT`, `MOVEPREVIOUS` (je `[#]n`)
  in der Ordnung des aktiven Index; `EOF(n)` und `BOF(n)` melden das
  Überlaufen am Ende bzw. am Anfang.
- Suche: `SEEKEQ`, `SEEKGT`, `SEEKGE` (je `[#]n, schlüsselwert[, …]`).
  Ein mehrspaltiger Index lässt sich mit einem Präfix seiner Spalten
  durchsuchen; `SEEKEQ` mit unvollständigem Schlüssel schlägt dabei immer
  fehl (wie im Vorbild), `SEEKGE` und `SEEKGT` positionieren auf dem ersten
  passenden Satz. Über den NULL-Index lässt sich nicht suchen (Fehler 87).
- Tabellen: `DELETETABLE datenbank$, tabellenname$`.
- Transaktionen: `BEGINTRANS`, `COMMITTRANS`, `SAVEPOINT` (Funktion,
  liefert die Kennung), `ROLLBACK [kennung]` und `ROLLBACK ALL`.
- `LOF(n)` liefert für eine ISAM-Dateinummer die Satzzahl der Tabelle,
  `LOC(n)` die Kennung des aktuellen Satzes (0 = unpositioniert).
- Cursorlage wie im Vorbild: nach `SETINDEX` ist der erste Satz der neuen
  Ordnung der aktuelle; nach `DELETE` der auf den gelöschten folgende, und
  war der gelöschte der letzte, steht der Cursor am Ende der Tabelle ohne
  aktuellen Satz.
- Fehlercodes 81–89 (`ERR`): 81 ungültiger Name, 82 Tabelle nicht
  gefunden, 83 Index nicht gefunden, 84 ungültige Spalte, 85 kein aktueller
  Satz, 86 Verstoß gegen einen eindeutigen Index, 87 unzulässige Operation
  auf dem NULL-Index (Suche), 88 Datenbank inkonsistent, 89 ISAM-Puffer zu
  klein.

**Sortierordnung der Indizes.** Textspalten ordnen nach
**Unicode-Codepoints**; Groß- und Kleinschreibung werden **unterschieden**.
Es ist dieselbe Ordnung, die der Vergleichsoperator des Dialekts auf
Strings anwendet — ein Programm kann die Satzreihenfolge damit
vorhersagen. Praktisch heißt das: alle Großbuchstaben `A`–`Z` stehen vor
allen Kleinbuchstaben `a`–`z`, und Zeichen jenseits von ASCII (`Ä`, `ß`,
`€`) stehen nach beiden, in der Reihenfolge ihrer Codepoints. Beispiel:
`Apfel` < `Birne` < `apfel` < `birne` < `Äpfel`.

Eine sprachabhängige (locale-abhängige) Sortierung gibt es bewusst nicht:
sie machte die Reihenfolge plattformabhängig. Das Vorbild ordnete nach den
Zeichencodes einer festen Codepage; Codepoints sind die direkte
Entsprechung unter der Unicode-Entscheidung dieses Projekts.

**Absteigende Indexspalten** (Erweiterung). Die Original-Hilfe kennt bei
`CREATEINDEX` keine Sortierrichtung. Terminal Basic ordnet eine Spalte
absteigend, wenn ihrem Namen ein `-` vorangestellt ist:

```basic
CREATEINDEX #1, "NachPreis", 0, "Nachname", "-Preis"
```

Das ist kollisionsfrei, weil ein Feldname einer `TYPE`-Anweisung nie mit
`-` beginnen kann; Programme des Vorbilds bleiben unverändert gültig.

**`SETMEM` und Fehler 89** (Abweichung). Im Vorbild veränderte
`SETMEM(n&)` den DOS-Fern-Heap um `n` Bytes. Ein DOS-Speichermodell gibt
es hier nicht; stattdessen ist `SETMEM` die Obergrenze des ISAM-Puffers:
sie wird um `n` Bytes verändert (negativ verkleinert, positiv vergrößert)
und die danach verfügbare Größe geliefert. Der Vorgabewert ist **65536
Bytes**. Gegen die Grenze zählen die Satzbytes einer laufenden Operation
und das Rücknahmeprotokoll einer offenen Transaktion. Reicht der Puffer
für eine Operation nicht aus, tritt Laufzeitfehler 89 („ISAM -
Insufficient ISAM buffers") auf — nicht ein generischer Fehler und kein
stiller Abbruch.


## 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]`,
`CINT`, `CLNG`, `CSNG`, `CDBL`, `CCUR`.

**Zufallszahlen.** Der Generator ist der lineare Kongruenzgenerator des
Vorbilds auf 24 Bit:
`zustand = (zustand · &H FD43FD + &H C39EC3) MOD 2^24`, Ergebnis
`zustand / 2^24` als SINGLE im Bereich [0, 1).
`RND` und `RND(n)` mit `n > 0` liefern die nächste Zahl, `RND(0)`
wiederholt die zuletzt gelieferte, `RND(n)` mit `n < 0` setzt den Zustand
aus dem Argument. `RANDOMIZE saat` bildet aus der Saat 16 Bit, die die
Bits 8–23 des Zustands besetzen; das niederwertige Byte ist fest — damit
liefert dieselbe Saat stets dieselbe Folge, unabhängig von vorherigen
`RND`-Aufrufen. `RANDOMIZE` ohne Argument fragt den Startwert mit
`Random Number Seed (-32768 to 32767)?` ab.

**Datum/Zeit:** `DATE$` (Funktion und Anweisung — Setzen: Nicht-Ziel),
`TIME$`, `TIMER` (Sekunden seit Mitternacht, SINGLE); dazu die nativen
Schlüsselwörter des Vorbilds (Neuerung ggü. QB/PDS): `NOW`, `DATESERIAL`,
`DATEVALUE`, `TIMESERIAL`, `TIMEVALUE`, `DAY`, `MONTH`, `YEAR`, `WEEKDAY`,
`HOUR`, `MINUTE`, `SECOND` (serielle Datumswerte als DOUBLE) sowie
`FORMAT$` (Zahlen-, Datums-/Zeitformatierung).

**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` kennzeichnet ein Formularmodul. Formulare und die aus seiner
Beschreibung bekannten Controls sind benannte, statisch typisierte Objekte.
Der Punkt liest oder schreibt eine explizite Eigenschaft
(`Text1.Text = "x"`); es gibt keine Standardeigenschaft. Der
Container-Operator `Form1!Text1.Text` wählt dasselbe Control innerhalb
seines Formulars. Ein `!` vor einem Bezeichner ist dieser Operator, sonst
bleibt es das SINGLE-Suffix. Unbekannte Objekte, Eigenschaften und Methoden
werden bei der Übersetzung mit Namen abgewiesen; UDT-Feldpfade behalten ihre
bisherige Bedeutung.

`FORM` und `CONTROL` sind als Parametertypen zulässig; ihre Eigenschaften
werden anhand der zur Laufzeit übergebenen Objektklasse gelesen und geschrieben. Mit
`IF TYPEOF ausdruck IS Klasse THEN` wird die konkrete, bekannte Klasse
geprüft. `SCREEN` ist das globale Objekt mit `ActiveForm`, `ActiveControl`,
`Height`, `Width`, `MousePointer`, `ControlPanel` sowie `SHOW`/`HIDE`;
`Height` und `Width` folgen der aktuellen Terminalgröße und sind read-only.

`LOAD form` lädt unsichtbar und ruft zuerst `Form_Load` auf. `UNLOAD form`
ruft `Form_Unload(Cancel AS INTEGER)` auf; `Cancel <> 0` verhindert das
Entladen. `form.HIDE` ändert nur die Sichtbarkeit. Ein Eigenschaftszugriff
lädt ein noch nicht geladenes Formular implizit. `form.SHOW [style%]` zeigt
es mit 0 (Standard) modeless oder mit 1 modal; bei modal wird der aufrufende
VM-Frame bis `HIDE`/`UNLOAD` markiert, während die Ereignisschleife weiter
zustellt. Stapelverstöße liefern Fehler 400–403.

Steuerelement-Arrays verwenden `LOAD name(index)` und
`UNLOAD name(index)`; Eigenschaften werden als `name(index).Eigenschaft`
adressiert. Entwurfszeit-Elemente liefern beim Entladen Fehler 362.
Ihre Ereignisprozedur erhält `Index AS INTEGER` als erstes Argument.
Ansonsten heißt ein Handler `SUB Name_Ereignis (…)` und muss exakt die
Signatur der Forms-Referenz tragen. Fehlt er, verfällt das Ereignis. Der
Dispatch nutzt ausschließlich die vorhandenen Zustellpunkte der
Ereignissteuerung. Mausargumente sind Taste und Shift als INTEGER sowie X/Y
als SINGLE in 1-basierten Textzellen.

Dialoge:
- `MSGBOX msg$ [,type% [,title$]]` (Anweisung) bzw. Funktion → INTEGER.
  `type%` = Buttons (0 OK · 1 OK/Cancel · 2 Abort/Retry/Ignore ·
  3 Yes/No/Cancel · 4 Yes/No · 5 Retry/Cancel) + Default-Button
  (0/256/512). Rückgabe 1 OK · 2 Cancel/Esc · 3 Abort · 4 Retry ·
  5 Ignore · 6 Yes · 7 No. Keine Icon-Flags (Textmodus). msg$ max. 1024
  Zeichen, modal.
- `INPUTBOX$(prompt$ [,title$ [,default$ [,xpos%, ypos%]]])` → STRING;
  Cancel liefert `""`; Position in **Zeichen**, sonst zentriert; Dialog
  fest 46×16 Zeichen; prompt$ max. 255 Zeichen.

---

## 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 2 (Emoji, CJK) **belegen zwei Zellen**
     (Entscheidung 2026-09-02, umgesetzt): der Cursor rückt um zwei Spalten
     vor, `POS` nennt die Anfangsspalte, `LOCATE` auf die zweite Hälfte
     wirkt auf den Zeichenanfang. Passt ein breites Zeichen nicht mehr in
     die Zeile, wandert es als Ganzes in die nächste und die letzte Spalte
     bleibt leer. Wird eine Hälfte überschrieben, verschwindet die andere.
     Zeichen der Breite 0 (kombinierende Zeichen) belegen vorerst eine
     eigene Zelle.
   - `GET`/`PUT`-Records: feste Strings (`STRING * n`) werden als **UTF-32**
     gespeichert (4 Bytes/Zeichen, feste Record-Länge) — Binärdateien sind
     dadurch bewusst inkompatibel zum Vorbild (Entscheidung 2026-09-02).
2. **Keine Hardware-Nähe** (bestätigt 2026-09-02): `PEEK`/`POKE`/`INP`/
   `OUT`/`WAIT`/`BLOAD`/`BSAVE`/`VARPTR`/`VARSEG`/`SADD`/`CALL ABSOLUTE`/
   `CALL INTERRUPT`/`IOCTL` werden bereits **zur Compile-Zeit** mit der
   Meldung „Feature unavailable" abgewiesen (kein Laufzeitfehler).
   Ebenso abgewiesen: die Interlanguage-Schnittstelle (`CALLS`, `SSEG`,
   `SSEGADD`, `StringAddress`/`StringAssign`/`StringLength`/
   `StringRelease`), die Eingabegeräte `PEN`, `STRIG`, `STICK` und die
   serielle Schnittstelle (`COM`, `ON COM`, `OPEN "COMn:"`).
3. **`INKEY$` für erweiterte Tasten** liefert `CHR$(0) + code`-Sequenzen
   mit den klassischen Scancodes, damit bestehender Code funktioniert
   (festgelegt 2026-09-04): F1–F10 = 59–68, Pos1 = 71, ↑ = 72, Bild↑ = 73,
   ← = 75, → = 77, Ende = 79, ↓ = 80, Bild↓ = 81, Einfg = 82, Entf = 83.
   Zeichentasten liefern ein Zeichen, Eingabe `CHR$(13)`, Rückschritt
   `CHR$(8)`, Esc `CHR$(27)`, Tabulator `CHR$(9)`. Strg+C und Strg+Untbr
   sind Abbruch und erscheinen nicht in `INKEY$`. Tasten heutiger
   Tastaturen ohne Entsprechung im Vorbild (etwa F11/F12 oder Meta-
   Kombinationen) liefern nichts — eine eigene Kodierung dafür wäre eine
   Erweiterung und gehört in Stufe 2.
4. **Kein `CHAIN`/Overlay-Mechanismus**; `SHELL` startet die System-Shell.
5. **Grafik im Zellenpuffer:** `SCREEN 0` bis `SCREEN 13`, `LINE` (auch
   `B`/`BF` und `STEP`), `PAINT` und `VIEW` werden ausgeführt. Historische
   Pixelkoordinaten werden dabei in 8×8-Blöcken auf Terminalzellen
   abgebildet. `PSET`, `PRESET`, `CIRCLE`, `DRAW`, Grafik-`GET`/`PUT`,
   `PALETTE`, `WINDOW`, `PMAP`, `PCOPY` und `POINT` bleiben Nicht-Ziel und
   werden zur Compile-Zeit mit „Feature unavailable" abgewiesen.
6. **`PLAY`/`SOUND`**: `BEEP` = Terminal-Bell; Rest Nicht-Ziel (Fehler 73).
5a. **`LPRINT` schreibt in eine Datei** (2026-09-03): einen
   plattformübergreifenden Druckerkanal gibt es nicht. Der Druckerpuffer
   geht am Programmende in `LPT1.TXT` im aktuellen Verzeichnis; `LPOS`
   nennt die Spalte in diesem Puffer.
5b. **Satzsperren** (`LOCK`/`UNLOCK`) prüfen nur, ob die Datei offen ist;
   eine echte Sperre bietet die Plattform nicht einheitlich. `FILEATTR`
   liefert nur die Modus-Kennzahl (Argument 1); das
   Betriebssystem-Handle bildet der Dialekt nicht ab und liefert 0.
5c. **`CHDRIVE`** ist außerhalb von Windows folgenlos — Laufwerksbuchstaben
   gibt es dort nicht.
6a. **Uhr in Ortszeit, mit Rückfall auf UTC** (2026-09-04): `DATE$`,
   `TIME$`, `TIMER` und `NOW` rechnen in der Zeitzone des Rechners;
   `TIMER` zählt ab der **lokalen** Mitternacht. Der Versatz wird für den
   jeweils abgefragten Zeitpunkt bestimmt, nicht einmal beim Start — ein
   Programm, das über eine Zeitumstellung hinweg läuft, folgt ihr.
   Lässt sich die Zone des Rechners nicht ermitteln, gilt UTC; das
   Programm läuft weiter, und der Runner meldet es einmal beim Start auf
   der Fehlerausgabe („Zeitzone nicht ermittelbar …"). Die additive
   TerminalBasic-Erweiterung `TIMEZONEKNOWN` (ohne Argument, `INTEGER`)
   liefert `-1` für eine ermittelte Zone, `0` für UTC-Rückfall. Der Status
   folgt jeder aktuellen Ermittlung, auch späterem Ausfall und Erholung;
   eine ermittelte UTC-Zone liefert ebenfalls `-1`. `DATE$ = …` und
   `TIME$ = …` stellen nicht die Systemuhr, sondern einen programmeigenen
   Versatz auf die Ortszeit; alle Zeitfunktionen rechnen ihn mit ein.
6b. **Serielle Datumswerte** zählen Tage ab dem 30.12.1899 (Serie 2 =
   1.1.1900), der Nachkommaanteil ist die Tageszeit. `WEEKDAY` liefert 1
   für Sonntag. Zweistellige Jahre gelten in `DATESERIAL` als 19xx, in
   `DATEVALUE` als 20xx unter 80 und sonst als 19xx.
7. **Dynamische Bildschirmgröße** (2026-09-02): kein festes 80×25 — der
   Bildschirm folgt der Terminalgröße (Mindestgröße 80×25, darunter nur
   ein Hinweis). `CSRLIN`/`POS`/`LOCATE`/`VIEW PRINT` und das
   `SCREEN`-Objekt arbeiten auf der tatsächlichen Größe. `WIDTH 40/80`
   wird akzeptiert, ändert aber nichts (die Größe bestimmt das Terminal).
   80×25 ist überall nur die **untere Schranke der Darstellung**, nie eine
   Bildschirmgröße im Verhalten des Programms. Ändert sich die Größe
   **während der Ausführung**, wird das zugestellt: der Inhalt bleibt oben
   links erhalten, der Cursor wird in die neuen Grenzen geklemmt, und ein
   `VIEW PRINT`-Bereich wird am unteren Rand geklemmt bzw. — wenn er ganz
   vom Schirm fällt — auf den vollen Bildschirm zurückgesetzt.

8. **`SIGNAL` liegt auf Betriebssystemsignalen** (2026-09-04): Im Vorbild
   trappte `ON SIGNAL(n%)` OS/2-Signale — Prozessflags A/B/C, ausgelöst
   über `DOSFLAGPROCESS` aus einem anderen Prozess; einen Wertebereich
   für `n%` dokumentierte die Hilfe nicht. Diese Quelle existiert auf
   keiner Zielplattform. Abgebildet wird deshalb die minimale Menge, die
   auf allen dreien besteht: `SIGNAL(1)` = `SIGINT`, `SIGNAL(2)` =
   `SIGTERM`. Die vier übrigen Signale des C-Standards (`SIGABRT`,
   `SIGFPE`, `SIGILL`, `SIGSEGV`) entstehen aus dem eigenen
   Programmzustand und sind nach einem `RETURN` nicht fortsetzbar; sie
   werden nicht abgebildet. Unter Windows ist `SIGTERM` von außen nicht
   zustellbar — `SIGNAL(2)` bleibt dort wirkungslos. Trifft `SIGINT` ohne aktiven Trap
   ein, bleibt es beim bisherigen Abbruch.

9. **Forms-Darstellung und Dateisystem-Steuerelemente** (2026-09-05):
   Controls zeichnen ausschließlich in den Unicode-Zellenpuffer. Mangels
   vollständiger Original-Screenshots gelten die Zeichenbilder und
   Fokus-/Deaktiviert-Farben aus `forms-referenz.md` als unsere Festlegung.
   Dasselbe gilt für die Z-Reihenfolge (Erzeugungsreihenfolge, später liegt
   oben) und gleichzeitig fällige Timer (aufsteigend nach `CtlName`).
   `DirListBox` und `FileListBox` verwenden native Pfade und `*`/`?`-Muster;
   `DriveListBox` zeigt unter Unix `/` und unter macOS zusätzlich Mounts aus
   `/Volumes` statt DOS-Laufwerksbuchstaben.
