Kennen Sie das? Sie möchten in Ihrem Programm mehrzeilige Texteingaben ermöglichen, wobei z.b. die ZeilenlÀnge aufgrund der Bildschirmaufteilung begrenzt ist oder sich sogar von Fall zu Fall Àndert.
Kein Problem... mit FORM INPUT mĂŒĂte es gehen: ZeilenlĂ€nge wird angegeben, BACKSPACE und DELETE geht, Cursor hin und her geht auch. Aber was ist mit Cursor rauf und runter oder den Funktions- oder Sondertasten? Dann ist da noch die Sache mit dem Bildschirm-Scrolling und der Speicherverwaltung; auĂerdem soll das Hauptprogramm nicht unnötig aufgeblĂ€ht werden, und die Textzeilen mĂŒssen leicht zugĂ€nglich sein. Doch ein Problem? Nicht mit ED_IT!
Das nachstehende Modul erfĂŒllt die gestellten Anforderungen auf einfache und durchschaubare Weise: Tastendruck abwarten - Taste aus werten - falls ASCII-Zeichen, dieses an Zeile anhĂ€ngen - falls Steuertaste, Aktionen ausfĂŒhren - fertig! Cursor- und Bildschirmsteuerung werden mit VT52-Sequenzen erledigt, so daĂ das ganze âGrafikverwaltungs- und Cursor-wo-bist-Du-Geraffelâ entfĂ€llt. Diese Sequenzen werden normalerweise fĂŒr die Ansteuerung von Bildschirmterminals des Typs 52 verwendet. Solche Terminals werden in GroĂrechneranlagen verwendet, wo mehrere ArbeitsplĂ€tze mit einem Zentralcomputer verbunden sind, und bestehen eigentlich nur aus Bildschirm, Tastatur und Zwischenspeicher. In den empfangenen Texten, die auf den Bildschirm sollen, sind bestimmte Steuerzeichen versteckt, mit denen z.B. Cursor-Position und Schriftfarbe verĂ€ndert werden oder Teile des Bildspeichers gelöscht werden können. Sie bestehen aus einer Folge von zwei oder mehr Zeichen, wobei das erste immer den Zeichencode 27 (= ASCII-Code fĂŒr Escape) hat.
Eben diese Escape-Sequenzen werden auch vom Betriebssystem des Atari ST verstanden (VT52-Emulator). Soll zum Beispiel der Cursor in die linke obere Ecke gesetzt werden, sendet man an den Bildschirm ESCH:
PRINT CHR$(27);"H";
Oder soll an der momentanen Cursor-Position eine Zeile eingefĂŒgt werden, sendet man einfach ESC L:
PRINT CHR$(27);"L";
Der untere Teil des Bildschirmes wird dann automatisch verschoben. Eine Ăbersicht der möglichen VT52-Funktionen gibt die Tabelle.
Das Konzept
Auch wenn der Emulator die meiste Bildschirmarbeit erledigt, geht es doch nicht ganz ohne Verwaltungskram. ED_IT verwendet hierfĂŒr einige Variablen, in denen die aktuelle Zeilennummer (ZNR), die Zeilennummer der obersten Bildschirmzeile (TOPLINE) und die Position des Cursors innerhalb einer Zeile (ZEIGER) vermerkt sind (siehe auch Bild 1). Die Zeileninhalte liegen in dem Stringarray ZEILEN$(). Bei jeder Tastenaktion werden nun diese Variablen aktualisiert. Beispiel: mit jeder Eingabe eines Textzeichens wird ZEIGER um eins erhöht, das Zeichen an ZEILEN$() angehĂ€ngt und der Cursor weiterbewegt. Wird eine Cursor-Taste (z.B. hoch) bedient, wird ZNR verringert; befindet sich der Cursor am oberen Bildschirmrand, wird TOPLINE zurĂŒckgezĂ€hlt, der Bildschirm heruntergescrollt und die oberste Zeile neu eingetragen. Die Unterscheidung, ob eine Zeichen- oder Steuertaste gedrĂŒckt wurde, erfolgt ĂŒber sogenannte Scan-Codes. Bei Tastendruck wird von der Tastatur ein 32-Bit-Wert mit der Tastenkombination geliefert, der wie folgt aufgebaut ist:
Bits 0-7 ASCII-Code der Taste
Bits 16-23 SCAN-Code der Taste
Bits 24-31 Umschalttastenstatus
Durch Ausmaskieren mit der AND-Funktion werden die benötigten Bits isoliert (siehe auch DEFFN in der Prozedur EDI_INIT). So kann man sich alle benötigten Informationen ĂŒber das, was auf der Tastatur los ist, herausholen.
Die Initialisierung
In der Prozedur EDI-INIT werden die Konstanten fĂŒr Bildschirmhöhe, Bildschirmbreite usw. eingelesen sowie der âTextspeicherâ ZEILEN$() dimensioniert. Auf diese Weise ist eine Anpassung an individuelle BedĂŒrfnisse kein Problem. Die Funktionsdefinitionen ASCII(x), FTASTE(x) und SCAN(x) filtern die zur Steuerung benötigten Bits aus dem Wert, der vom Tastaturprozessor gesendet wird. Die neue Bildschirmhöhe wird dem System ĂŒber eine LINE-A (L~a)-Variable mitgeteilt. In den Line-A-Variablen stehen Informationen, die GEM und TOS fĂŒr Bildschirmaktionen usw. dringend benötigen. Also Vorsicht beim Experimentieren! Versehentliche Ănderungen in diesem Bereich können einen Absturz, mindestens aber merkwĂŒrdiges und unberechenbares Verhalten des Rechners zur Folge haben (Sie kennen sicher das Naturgesetz, nach dem sich der Rechner aufhĂ€ngt, bevor man die Arbeit von Stunden abgespeichert hat). Um den Ursprungszustand bei Verlassen des Editors wieder herstellen zu können, wird die Variable OLD_ZEILEN als Zwischenspeicher genutzt. Zum SchluĂ der Vorbereitungen wird der Cursor eingeschaltet und in die linke obere Ecke gesetzt. Die Sequenz ESC E löscht nur den eingestellten Bildschirmbereich. Auf diese Weise kann man unterhalb des scrollfĂ€higen Teiles Help-Texte, Bedienungshinweise oder Programminformationen anzeigen, ohne daĂ diese bei Anwendung des Editors verschoben werden. Grafikfunktionen und Anzeigen mit TEXT x,y,X$ sind in diesem Bereich unbeschrĂ€nkt benutzbar, so daĂ laufende Ănderungen kein Problem sind.
Der Editor
ZunĂ€chst langweilt sich der Rechner in einer Schleife. Die Hilfsvariable X hat solange den Wert Null, bis die Tastatur den oben angesprochenen 32-Bit-Wert liefert. AnschlieĂend wird der zugehörige ASCII-Code herausgezogen und in der Variablen TASTE gespeichert. Sollte TASTE den Wert Null haben, war es eine Sondertaste (Up, Down, Help, usw.); also wird nun der SCAN-Code ermittelt und anhand der SELECT-CASE-Anweisung verzweigt. Wurde beispielsweise die Cursor-Down-Taste (SCAN 80) gedrĂŒckt, wird die Zeilennummer ZNR um eins erhöht, ZEIGER auf den Zeilenanfang der nĂ€chsten Zeile gesetzt und geprĂŒft, ob gescrollt werden muĂ. Dies ist dann der Fall, wenn sich der Cursor in der untersten Zeile des eingestellten Bildschirmbereiches befindet; also mit PRINT einen Upscroll erzwingen und die TOPLINE um eins erhöhen. Befand sich aber der Cursor oberhalb der Unterkante, wird er mit ESC B um eine Zeile nach unten bewegt. RETURN und Cursor-Up funktionieren entsprechend. ED_IT merkt sich immer die höchste Zeilennummer in der Variablen LASTLINE, damit ein Durchfahren des Textes nur bis zur letzten Zeile möglich ist. Wollen Sie den Editor um weitere Funktionen erweitern, brauchen Sie nur den SCAN-Code der Taste zu ermitteln und eine weitere âCASE-xy-Schachtel" hinzuzufĂŒgen, in der dann Ihre Funktionen liegen.
Der Editor kann mittels UNDO-Taste wieder verlassen werden. Wie schon oben erwĂ€hnt, ist es wichtig, die Line-A-Variable Int{L~a-42} auf den alten Wert zurĂŒckzusetzen, um böse Ăberraschungen zu vermeiden.
Literatur:
[1] Handbuch GFA-BASIC 3.0, GFA Systemtechnik GmbH
[2] BrĂŒckmann/Englisch/Gerits: ST-Intern, DATA BECKER GmbH
Ăbersicht der VT52-Escape-Sequenzen
ESC âAâ Cursor hoch ohne Scrolling
ESC âBâ Cursor runter ohne Scrolling
ESC âCâ Cursor rechts
ESC âDâ Cursor links
ESC âEâ Clear Home (löscht Bildschirm)
ESC âHâ Cursor Home (linke obere Ecke)
ESC âIâ Cursor hoch mit Scrolling
ESC âJâ löscht den Rest des Bildschirmes ab Cursor-Position.
ESC âKâ löscht den Rest der Zeile, in der sich der Cursor befindet.
ESC âLâ Zeile an Cursor-Position einfĂŒgen. Unterer Bildschirmteil wird um eine Zeile nach unten verschoben.
ESC âMâ entfernt die Zeile, in der der Cursor steht, und zieht den Rest des Bildschirmes hoch.
ESC âYâ Positionieren des Cursors. Die folgenden zwei Zeichen werden als Zeile und Spalte interpretiert, wobei jeweils 32 addiert werden muĂ. Soll der Cursor beispielsweise auf Zeile 8, Spalte 12 gesetzt werden, schreibt man:
PRINT CHR$(27);âYâ;CHR$(8+32);CHR$(12+32);
ESC âbâ Einstellung der Schriftfarbe. Je nach Modus 2, 4 oder 16 Farben (zB. schwarz: ESC âb1â)
ESC âcâ Einstellung der Hintergrundfarbe
ESC âdâ Löschen des Bildschirmes bis Cursor-Position
ESC âeâ Einschalten des Cursors
ESC âfâ Abschalten des Cursors
ESC âjâ speichert die augenblickliche Cursor-Position
ESC âkâ setzt Cursor wieder auf gespeicherte Position.
ESC âlâ löscht nur die aktuelle Zeile.
ESC âoâ löscht von Zeilenanfang bis Cursor-Position
ESC âpâ inverse Schrift einschalten
ESC âqâ inverse Schrift abschalten
ESC âvâ Zeilenumbruch an. Bei Erreichen des Zeilenendes wird Ausgabe in der nĂ€chsten Zeile fortgesetzt.
ESC âwâ Zeilenumbruch aus
Bild 1: Zusammenhang zwischen Variablen und Textspeicher
'
' ED_IT.GFA von Rolf Stelljes
' Mini-Texteditor
' GFA 3.07
' (c) MAXON Computer 1991
'
edi_init
editor
'
PROCEDURE edi_init ! Vorbereitung der Editorfunktionen
'
' Alle Numerischen Variablen Integer-Format %
' Die Bildschirmsteuerungen erfolgen ĂŒber VT52-Sequenzen
'
DIM zeilen$(500) ! Array fĂŒr Textzeilen (GröĂe abhĂ€ngig '
' ! von Verwendungszweck)
DEFFN ascii(x%)=x% AND 255 ! Ermittelt ASCII-Code einer Taste
DEFFN ftaste(x%)=@scan(x%)-58 ! Ermittelt F-Tasten-Nummer
DEFFN scan(x%)=((x% AND &HFFOOOO)/65536) ! Ermittelt Scan-Code
scr_ho%=10 ! Bildschirmhöhe
scr_br%=30 ! Bildschirmbreite
znr%=1 ! Erste Z-Nr.
zeiger%=1 ! Zeilenanfang
tabs%=8 ! Tabulatorschritte
topline%=1 ! Oberste Zeile des Bildschirms
raus!=FALSE ! Merker zurĂŒcksetzen
WHILE INKEY$<>"" ! Tastaturpuffer leeren
WEND
'
old_zeilen%=INT{L~A-42} ! Maximale Anzahl Zeilen merken
INT{L~A-42}=scr_ho%-1 ! Neue max Zeilenanzahl setzen
'
PRINT CHR$(27);"e"; ! Cursor ein
PRINT CHR$(27);"H"; ! Cursor in linke obere Ecke
RETURN
PROCEDURE editor ! Texteditor mit Scrolling, Ins, Del.
REPEAT ! Hauptschleife
REPEAT ! Warten auf Tastendruck
KEYTEST x% ! und Tastencode merken
UNTIL x%>0
taste%=@ascii(x%) ! ASCII-Code ausfiltern
IF taste%=0 ! Falls Steuertaste bedient
SELECT @scan(x%) ! Scan-Code ermitteln
CASE 82 ! ** Insert **
PRINT CHR$(27);"L"; ! Leerzeile an CrsPosition einfĂŒgen
INSERT zeilen$(znr%)="" ! Platz in Array einfĂŒgen
INC lastline% ! Anzahl der Zeilen erhöhen
zeiger%=1 ! Crs an linken Rand
CASE 80 ! ** Down **
IF znr%<lastline% ! Runter nur bis letzte Zeile
INC znr% ! NĂ€chste Zeilennummer (max Lastline)
zeiger%=1 ! Zeilenanfang
IF CRSLIN=>scr_ho% ! Falls Unterkante Bildschirm erreicht
INC topline% ! Topline verschieben
PRINT ! Bildschirm hochscrollen
ELSE ! ansonsten nur
PRINT CHR$(27);"B"; ! Crs down
ENDIF
PRINT AT(1,CRSLIN);zeilen$(znr%);AT(1,CRSLIN); ! Zeileninhalt zeigen
ENDIF
CASE 72 ! ** Up **
znr%=MAX(1,znr%-1) ! Vorige Zeilennummer (minimal 1)
zeiger%=1 ! Zeilenanfang
IF CRSLIN=1 ! Falls oberer Bildschirmrand
IF topline%>1 ! Hochscrollen nur bis Oberkante Text
PRINT CHR$(27);"I"; ! Crs up mit Scrolling down
ENDIF
topline%=MAX(1,topline%-1)! Topline verschieben
ELSE ! Falls nicht Bildschirmoberkante
PRINT CHR$(27);"I"; ! Crs hoch
ENDIF
PRINT AT(1,CRSLIN);zeilen$(znr%);AT(1,CRSLIN); ! Zeileninhalt anzeigen
CASE 77 ! ** Right **
IF zeiger%<LEN(zeilen$(znr%))+1 ! Zeiger maximal bis Zeilenende
PRINT CHR$(27);"C"; ! Crs rechts
zeiger%=MIN(zeiger%+l,scr_br%) ! Zeiger nach rechts verschieben
ENDIF !
CASE 75 ! ** Left **
PRINT CHR$(27);"D"; ! Crs links
zeiger%=MAX(zeiger%-1,1) ! Zeiger nach links verschieben
CASE 97 ! ** Undo **
INT{L~A-42}=old_zeilen% ! Alten Zustand wieder herstellen
PRINT CHR$(27);"f"; ! Crs ausschalten
raus!=TRUE ! Fertig-Flag setzen
ENDSELECT ! Ende Steuertastenverteiler
ELSE
x$=zeilen$(znr%) ! Aktuelle Zeile in Hilfsstring legen
y$=x$
SELECT taste% ! Falls ASCII-Zeichen
CASE 32 TO 126,129,132,142,148,153,154,158 ! ** Zeichentaste
IF zeiger%<scr_br%
x%=LEN(x$)-zeiger%+1 ! RestlÀnge der Zeile ab Zeiger
x$=LEFT$(x$,zeiger%-1)+CHR$(taste%) ! Neues Zeichen vor Zeiger einfĂŒgen
IF x%>0 ! Falls Rest vorhanden
x$=x$+RIGHT$(y$,x%) ! Restzeile anhÀngen
ENDIF
zeiger%=MIN(zeiger%+1,scr_br%) ! Zeiger weiter bewegen
PRINT AT(1,CRSLIN);x$; ! Zeile anzeigen
PRINT AT(zeiger%,CRSLIN);! Crs positionieren
zeilen$(znr%)=x$ ! Neue Zeile merken
ENDIF
CASE 9 ! ** Tab **
x%=((zeiger%+tabs%) DIV tabs%)*tabs% ! NĂ€chste Tab-Position berechnen
zeiger%=MAX(1,MIN(LEN(zeilen$(znr%))+1,x%)) ! Zeiger positionieren
PRINT AT(zeiger%,CRSLIN); ! Crs posit.
CASE 13 ! ** Return **
INC znr% ! Neue Zeilennummer
IF CRSLIN=>scr_ho% ! Falls Unterkante Bildschirm erreicht
INC topline% ! Topline verschieben
PRINT ! Bildschirm hochscrollen
ELSE ! ansonsten nur...
PRINT CHR$(27);"B"; ! Crs runter
ENDIF !
PRINT AT(1,CRSLIN);zeilen$(znr%);AT(1,CRSLIN); ! Zeileninhalt anzeigen
zeiger%=1 ! Zeiger auf Zeilenanfang setzen
CASE 25 ! ** Ctl Y ** (Zeile löschen)
PRINT CHR$(27);"M"; ! Zeile auf Bildschirm löschen mit upscroll
DEC lastline% ! Anpassung Zeilenanzahl
DELETE zeilen$(znr%) ! Zeile aus Array entfernen
zeiger%=1 ! Zeiger auf Zeilenanfang
CASE 27 ! ** Escape ** (Zeileninhalt löschen)
PRINT CHR$(27);"l"; ! Zeile auf Bildschirm löschen
zeilen$(znr%)="" ! Inhalt löschen
zeiger%=1 ! Zeiger auf Zeilenanfang
CASE 8,127 ! ** Delete/Backspace ** (Zeichen löschen)
x$=zeilen$(znr%) ! Aktuelle Zeile an Hilfsstring ĂŒbergeben
x%=LEN(x$) ! LĂ€nge merken
IF taste%=8 ! Falls Backspace-Taste
zeiger%=MAX(zeiger%-1,1) ! voriges Zeichen anvisieren
ENDIF
IF zeiger%<=x% ! Falls Zeichen innerhalb Zeile
zeilen$(znr%)=LEFT$(x$,zeiger%-1)+RIGHT$(x$,x%-zeiger%) ! dieses isolieren
PRINT AT(1,CRSLIN);zeilen$(znr%);" "; ! Zeile neu anzeigen
PRINT AT(zeiger%,CRSLIN);! Crs positionieren
ENDIF
ENDSELECT ! Ende ASCII-Tastenverteiler
ENDIF
lastline%=MAX(lastline%,znr%) ! Höchste Zeilennummer merken
UNTIL raus!
RETURN