← ST-Computer 04 / 1991

Kompakter Texteditor als Modul

Programmierpraxis

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