550 lines
30 KiB
Markdown
550 lines
30 KiB
Markdown
# 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.
|
||
|
||
## 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)
|
||
|
||
## 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])`.
|
||
- `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
|
||
`"…"`), `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.
|
||
|
||
## 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.eigenschaft = wert`, `form!steuerelement.eigenschaft`,
|
||
Ereignisprozeduren `SUB name_Ereignis (…)`, `LOAD`/`UNLOAD form`,
|
||
`form.SHOW [style%]` (0 = modeless, Standard; 1 = modal), `form.HIDE`,
|
||
`DOEVENTS()`, `SCREEN`-Objekt, Datentypen `FORM`/`CONTROL`, `IF TYPEOF`,
|
||
`$FORM`-Metabefehl, Include-Dateien (Konstanten-`.BI`).
|
||
|
||
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-Anweisungen** (`SCREEN n>0`, `PSET`, `PRESET`, `LINE`,
|
||
`CIRCLE`, `PAINT`, `DRAW`, Grafik-`GET`/`PUT`, `PALETTE`, `WINDOW`,
|
||
`VIEW`, `PMAP`, `PCOPY`, `POINT`) sind Nicht-Ziel und werden **zur
|
||
Compile-Zeit** mit „Feature unavailable" abgewiesen. Die Formen, die
|
||
sich ein Schlüsselwort mit einer unterstützten Anweisung teilen, erkennt
|
||
der Compiler an ihrer Syntax: `LINE` ohne `INPUT`, `GET`/`PUT` mit
|
||
`(`, `VIEW` ohne `PRINT`. Hinweis: das Vorbild **hatte** die
|
||
Grafikmodi 0–13, aber nicht gleichzeitig mit Forms (dort Fehler 272
|
||
„Invalid when forms are showing") — wir übernehmen nur den
|
||
Forms-/Textmodus-Zweig.
|
||
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 …"). `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.
|