Files
TerminalBasic/openspec/specs/textbildschirm/spec.md

224 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# textbildschirm Specification
## Purpose
Der emulierte Textbildschirm ist die Rendering-Grundlage für `PRINT`,
`LOCATE`, `COLOR`, `CLS` und später die Forms-Engine: Unicode-Zellenpuffer
mit dynamischer Terminalgröße und klassischer 16-Farben-Palette auf
Ratatui.
## Requirements
### Requirement: Dynamische Terminalgröße mit Mindestmaß
Der Bildschirm SHALL der Terminalgröße folgen (Puffer per `resize`
anpassbar; Inhalt bleibt oben links erhalten, Cursor wird geklemmt).
Die Größe SHALL zu jedem Zeitpunkt aus der Darstellungsfläche abgeleitet
und nicht fest vorgegeben werden. Die Mindestgröße ist 80×25: kleinere
Werte werden auf 80×25 geklemmt, und ist die Render-Fläche kleiner als
80×25, SHALL nur ein Hinweis „Terminal zu klein" mit Ist- und
Mindestgröße gerendert werden. Dieses Mindestmaß ist ausschließlich eine
untere Schranke der Darstellung; es MUST NOT als Bildschirmgröße im
Verhalten des Programms auftreten, solange die Fläche größer ist.
#### Scenario: Vergrößertes Terminal
- **WHEN** der Bildschirm auf 120×40 gesetzt wird
- **THEN** sind alle 120 Spalten und 40 Zeilen adressierbar (`LOCATE 40, 120`)
#### Scenario: Zu kleines Terminal
- **WHEN** die Render-Fläche 60×20 misst
- **THEN** erscheint statt des Puffers der Hinweis mit Minimum 80×25
### Requirement: PRINT-Semantik mit Umbruch und Scrollen
`print` SHALL Zeichen an der Cursorposition ausgeben, am rechten Rand
umbrechen und am unteren Rand des Scrollbereichs den Bereich um eine
Zeile nach oben scrollen; `\n` bricht um, `\r` setzt an den Zeilenanfang.
`VIEW PRINT oben TO unten` SHALL das Scrollen auf den Bereich begrenzen;
ohne eigene Einstellung folgt der Scrollbereich der Bildschirmgröße.
#### Scenario: Scrollen in VIEW-PRINT-Bereich
- **WHEN** der Scrollbereich Zeilen 35 umfasst und in Zeile 5 ein Umbruch erfolgt
- **THEN** scrollen nur die Zeilen 35; Kopfzeilen außerhalb bleiben unverändert
### Requirement: 1-basierte Cursor-API mit Bereichsprüfung
`LOCATE`, `CSRLIN` und `POS` SHALL 1-basiert arbeiten. `LOCATE`
außerhalb der aktuellen Bildschirmgrenzen SHALL Laufzeitfehler 5
„Illegal function call" auslösen. Ausgelassene Argumente von `LOCATE`
SHALL den jeweiligen Wert unverändert lassen; die Argumente für
Cursorsichtbarkeit und Cursorform SHALL entgegengenommen und, soweit das
Terminal sie nicht abbilden kann, folgenlos bleiben.
#### Scenario: Grenzprüfung
- **WHEN** bei 80×25 `LOCATE 26, 1` aufgerufen wird
- **THEN** tritt Laufzeitfehler 5 auf
#### Scenario: Ausgelassenes LOCATE-Argument
- **WHEN** der Cursor auf (5, 9) steht und `LOCATE , 3` ausgeführt wird
- **THEN** steht der Cursor auf (5, 3)
### Requirement: Klassische Farbpalette und Blink-Simulation
Der Bildschirm SHALL die klassische Palette abbilden (Vordergrund 015,
Hintergrund 07) als feste 24-Bit-RGB-Werte ausgeben, unabhängig von Terminalprofil
und Farberkennung (klassisch 1 = #0000AA, 4 = #AA0000). Dieselbe
Abbildung SHALL für IDE, Formularvorschau und BASIC-Ausgabe gelten;
programmbestimmte Farbnummern SHALL unverändert bleiben. Blinkende Vordergrundfarben (1631) SHALL als „hell"
simuliert werden (Farbe 16, Intensitätsbit gesetzt) — kein echtes
Terminal-Blinken.
#### Scenario: Blink wird hell
- **WHEN** `COLOR 17, 0` gesetzt wird (blinkend Blau)
- **THEN** wird mit heller Vordergrundfarbe 9 gerendert
#### Scenario: Programmbestimmte absolute Farbe
- **WHEN** ein Programm COLOR 7, 1 oder entsprechende ForeColor-/BackColor-Eigenschaften verwendet
- **THEN** wird #AAAAAA auf #0000AA ausgegeben, auch bei abweichender ANSI-Palette und ohne COLORTERM
### Requirement: Unicode-Zellenmodell
Der Puffer SHALL Unicode-Zeichen speichern (keine CP437-Emulation).
Zeichen mit Darstellungsbreite 2 (u. a. Emoji, CJK) SHALL zwei
nebeneinanderliegende Zellen belegen (Entscheidung 2026-09-02): die erste
trägt das Zeichen, die zweite ist als Fortsetzung markiert und MUST NOT
eigenständig beschrieben werden. Der Cursor SHALL nach der Ausgabe eines
breiten Zeichens um zwei Spalten vorrücken; `POS` SHALL die Spalte des
Zeichenanfangs zählen. Passt ein breites Zeichen nicht mehr in die letzte
Spalte, SHALL es vollständig in die nächste Zeile umgebrochen werden und
die letzte Spalte leer bleiben. `LOCATE` auf die Fortsetzungszelle SHALL
auf den Zeichenanfang wirken.
#### Scenario: Umlaute und Symbole
- **WHEN** `Ä☃` ausgegeben wird
- **THEN** belegen `Ä` und `☃` je genau eine Zelle
#### Scenario: Breites Zeichen belegt zwei Zellen
- **WHEN** an Spalte 1 ein CJK-Zeichen ausgegeben wird
- **THEN** ist Spalte 2 als Fortsetzung belegt und der Cursor steht auf Spalte 3
#### Scenario: Breites Zeichen am rechten Rand
- **WHEN** bei 80 Spalten der Cursor auf Spalte 80 steht und ein breites Zeichen ausgegeben wird
- **THEN** bleibt Spalte 80 leer und das Zeichen steht in Spalte 1 der Folgezeile
### Requirement: Zellenpuffer ohne Terminalabhängigkeit
Der Zellenpuffer mit der vollständigen Bildschirmsemantik (Cursor,
Farbattribute, Umbruch, Scrollen, Scrollbereich, Größenänderung) SHALL
ohne Terminal instanziierbar, veränderbar und auslesbar sein. Die
Anbindung an ein konkretes Terminal SHALL ausschließlich in der
Darstellungsschicht liegen. Damit MUST jedes Bildschirmverhalten in
automatischen Tests ohne Terminal prüfbar sein, und die Ausführungsschicht
MUST NOT von einer Terminal-Bibliothek abhängen.
Ein separater Einbetter SHALL die VM einschließlich Forms ohne Terminalbackend bauen und ausführen können. Der CLI-Runner SHALL das Backend ausdrücklich zuschalten.
#### Scenario: Bildschirmverhalten im Test ohne Terminal
- **WHEN** ein Testprogramm in einer Umgebung ohne Terminal `LOCATE 5, 10 : PRINT "x"` ausführt
- **THEN** trägt die Zelle (5, 10) das Zeichen `x` und der Test benötigt kein Terminal
#### Scenario: Terminalfreier Einbetter
- **WHEN** ein separates Programm ausschließlich die VM mit einem Capture-Host einbindet
- **THEN** enthält sein aufgelöster Abhängigkeitsbaum keine Terminalbibliothek und die Forms-Tests können darin laufen
### Requirement: Bildschirmanweisungen des Dialekts
`CLS`, `COLOR`, `LOCATE`, `WIDTH`, `VIEW PRINT` und die Anweisungsform von
`SCREEN` SHALL auf dem Zellenpuffer wirken. `CLS` SHALL den Scrollbereich
löschen und den Cursor an dessen Anfang setzen; `CLS 2` SHALL nur den
Textbereich löschen. `COLOR` SHALL Vordergrund und Hintergrund für
nachfolgende Ausgaben setzen, ausgelassene Argumente lassen den bisherigen
Wert unverändert. `WIDTH` SHALL die Spalten- und Zeilenzahl setzen, soweit
die Darstellungsfläche es zulässt. `VIEW PRINT oben TO unten` SHALL den
Scrollbereich begrenzen, `VIEW PRINT` ohne Argumente ihn auf den ganzen
Bildschirm zurücksetzen.
#### Scenario: CLS setzt Cursor zurück
- **WHEN** nach Ausgaben in Zeile 10 `CLS` ausgeführt wird
- **THEN** ist der Puffer leer und `CSRLIN` liefert 1, `POS(0)` liefert 1
#### Scenario: COLOR wirkt nur auf Folgeausgaben
- **WHEN** `PRINT "a" : COLOR 14, 1 : PRINT "b"` ausgeführt wird
- **THEN** trägt die Zelle mit `a` das vorherige Attribut und die Zelle mit `b` Vordergrund 14 auf Hintergrund 1
#### Scenario: Ausgelassenes COLOR-Argument
- **WHEN** nach `COLOR 14, 1` die Anweisung `COLOR , 4` ausgeführt wird
- **THEN** bleibt der Vordergrund 14 und der Hintergrund wird 4
### Requirement: Bildschirm-Abfragefunktionen
`CSRLIN` SHALL die aktuelle Cursorzeile liefern, `POS(0)` die aktuelle
Cursorspalte, beide 1-basiert. Die Funktionsform `SCREEN(zeile, spalte
[, farbe])` SHALL das Zeichen an der genannten Position als Codepoint
liefern, bei gesetztem dritten Argument stattdessen dessen Farbattribut.
Positionen außerhalb des Bildschirms MUST Laufzeitfehler 5 „Illegal
function call" auslösen.
#### Scenario: Zeichen zurücklesen
- **WHEN** `LOCATE 3, 7 : PRINT "Q";` ausgeführt und danach `SCREEN(3, 7)` ausgewertet wird
- **THEN** liefert `SCREEN(3, 7)` den Codepoint von `Q`
#### Scenario: Abfrage außerhalb des Bildschirms
- **WHEN** bei 80×25 `SCREEN(30, 1)` ausgewertet wird
- **THEN** tritt Laufzeitfehler 5 auf
### Requirement: Tastatureingabe ohne Zeilenmodell
`INKEY$` SHALL ohne zu blockieren die nächste anstehende Taste liefern:
den leeren String bei leerem Puffer, ein Zeichen bei einer
Zeichentaste, eine zwei Zeichen lange Folge mit führendem Nullzeichen bei
einer Sondertaste. `INPUT$(n [, #dateinummer])` SHALL genau `n` Zeichen
lesen und dabei blockieren, ohne sie am Bildschirm zu wiederholen.
INPUT$ SHALL n Unicode-Codepoints liefern. Nicht verbrauchte Zeichen einer Sondertastenfolge und noch nicht gelesene Dateizeichen SHALL für Folgeaufrufe erhalten bleiben. Dateieingabe SHALL UTF-8 vor der Zeichenzählung decodieren.
#### Scenario: INKEY$ bei leerem Tastaturpuffer
- **WHEN** `INKEY$` ohne anstehende Taste ausgewertet wird
- **THEN** liefert es den leeren String und blockiert nicht
#### Scenario: Sondertaste als zwei Zeichen
- **WHEN** F1 gedrückt wurde und `INKEY$` ausgewertet wird
- **THEN** hat das Ergebnis die Länge 2 und beginnt mit dem Nullzeichen
#### Scenario: Sondertastenfolge in zwei Reads
- **WHEN** F1 ansteht und zweimal INPUT$(1) ausgeführt wird
- **THEN** liefert der erste Aufruf das Nullzeichen und der zweite den Scancode, jeweils genau ein Zeichen
#### Scenario: Unicode aus Datei
- **WHEN** eine UTF-8-Datei mit ä beginnt und INPUT$(1,#1) aufgerufen wird
- **THEN** liefert der Aufruf ä ohne Ersatzzeichen
### Requirement: Keine feste Bildschirmgröße im Verhalten
Kein beobachtbares Verhalten SHALL eine feste Spalten- oder Zeilenzahl
voraussetzen. Wo die Referenz des Vorbilds von 80×25 spricht, ist stets
der volle aktuelle Bildschirm gemeint. Insbesondere SHALL `CLS` den
vollen aktuellen Bildschirm löschen, der Scrollbereich ohne eigene
`VIEW PRINT`-Einstellung den vollen aktuellen Bildschirm umfassen, der
Zeilenumbruch an der aktuell letzten Spalte erfolgen, das Scrollen an
der aktuell letzten Zeile des Bereichs auslösen und die Grenzprüfung von
`LOCATE` und der Funktionsform von `SCREEN` gegen die aktuellen
Abmessungen prüfen. Die Werte 80 und 25 MUST NOT als Grenze in
beobachtbarem Verhalten auftreten.
#### Scenario: Löschen und Scrollen auf großem Bildschirm
- **WHEN** der Bildschirm 120×40 misst, in Zeile 40 ein Umbruch erfolgt und danach `CLS` ausgeführt wird
- **THEN** scrollt der Bildschirm erst an Zeile 40 und `CLS` löscht alle 40 Zeilen
#### Scenario: Adressierbarkeit jenseits von 80×25
- **WHEN** der Bildschirm 120×40 misst und `LOCATE 40, 120` ausgeführt wird
- **THEN** entsteht kein Fehler und `CSRLIN` liefert 40, `POS(0)` liefert 120
### Requirement: Größenänderung zur Laufzeit
Ändert sich die Größe der Darstellungsfläche während ein Programm läuft,
SHALL die Größenänderung als Ereignis bis zum Bildschirmzustand
durchgereicht und der Zellenpuffer angepasst werden. Der Inhalt SHALL
oben links erhalten bleiben; der Cursor SHALL in die neuen Grenzen
geklemmt werden; ein `VIEW PRINT`-Bereich, der nicht mehr vollständig in
den Bildschirm passt, SHALL auf die neuen Grenzen geklemmt und, falls er
dadurch leer würde, auf den vollen Bildschirm zurückgesetzt werden. Alle
programmseitig sichtbaren Größen — `CSRLIN`, `POS`, die Grenzen von
`LOCATE` und der Funktionsform von `SCREEN` — MUST unmittelbar nach der
Änderung die neuen Abmessungen widerspiegeln.
#### Scenario: Vergrößerung während der Ausführung
- **WHEN** ein laufendes Programm bei 80×25 ausgibt und die Fläche auf 120×40 wächst
- **THEN** bleibt der bisherige Inhalt oben links stehen und `LOCATE 40, 120` ist danach zulässig
#### Scenario: Verkleinerung klemmt den Cursor
- **WHEN** der Cursor auf Zeile 40 steht und die Fläche auf 80×25 schrumpft
- **THEN** liegt der Cursor danach innerhalb der neuen Grenzen
#### Scenario: Scrollbereich überlebt die Verkleinerung
- **WHEN** `VIEW PRINT 30 TO 38` gesetzt ist und die Fläche auf 25 Zeilen schrumpft
- **THEN** ist der Scrollbereich danach gültig und liegt vollständig innerhalb des Bildschirms