218 lines
11 KiB
Markdown
218 lines
11 KiB
Markdown
# 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 3–5 umfasst und in Zeile 5 ein Umbruch erfolgt
|
||
- **THEN** scrollen nur die Zeilen 3–5; 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 0–15,
|
||
Hintergrund 0–7) und auf ANSI-Indexfarben mappen (klassisch 1 = Blau ↔
|
||
ANSI 4 usw.). Blinkende Vordergrundfarben (16–31) 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
|
||
|
||
### 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
|