← ST-Computer 06 / 1988

Extended VT52-Emulator Teil 3

Grundlagen

Bild 1: Arbeiten mit zwei Textfenstern bei Verwendung von xVT52

Einige der neu hinzugekommenen Steuerzeichen wurden bereits im zweiten Teil dieser Serie behandelt. In dieser Folge werden auch noch die restlichen ESCapes unter die Lupe genommen, bevor es dann um den Geschwindigkeitszuwachs gehen wird...

... und der ganze Rest

Beginnen wir also mit dem Komplettieren der xVT52-Sequenzen. In Programmen ist es durchaus nicht unĂŒblich, daß es mehrere Prozeduren oder Funktionen gibt, die in irgendeiner Weise auf den (“TOS”-) Bildschirm zugreifen, um z.B. Texte auszugeben oder auch Eingaben anzufordern. Nun ist das mit dem Ein- und Ausschalten des Cursors so eine Sache. Angenommen, das Hauptprogramm möchte verhindern, daß der Cursor innerhalb einer Prozedur oder Funktion sichtbar werden wird. Dann lĂ€ĂŸt sich dies mit den konventionellen Möglichkeiten des VT52-Emulators nicht mehr erreichen. Ein Blick ins VDI-Handbuch zeigt aber anhand der Maus-Routinen, daß es auch anders geht. Unter der Nummer 122 lĂ€ĂŸt sich die Funktion SHOW CURSOR ansprechen, der man eine Variable mitgeben kann. Je nach Wert dieser Variablen wird die Maus entweder sofort sichtbar (0), oder es wird zuerst ein ZĂ€hler dekrementiert, der von der Funktion HIDE CURSOR (VDI 123) inkrementiert wird (1). Erst wenn dieser ZĂ€hler auf Null liegt, wird die Maus wieder angezeigt. Was liegt also nĂ€her als diesen Mechanismus auch fĂŒr den Cursor einzufĂŒhren? Hierzu dient die Sequenz:

  • ESC a: Cursor einschalten mit Hide-Cursor-Check.

Der Cursor wird also erst wieder sichtbar, wenn genausoviele ESC a-wie ESC f-Aufrufe erfolgt sind. Der ZĂ€hler wird durch ESC e auf Null gesetzt, so daß nach diesem Aufruf der Cursor garantiert sichtbar ist, und bei jedem ESC f inkrementiert (wobei dann der Cursor garantiert unsichtbar wird).

Schwarz auf Weiß und umgekehrt

Um die Augen und/oder den Bildschirm zu schonen, kann es nĂŒtzlich sein, die Darstellung zu invertieren, d.h. alles, was vorher weiß war, wird jetzt schwarz und umgekehrt. FĂŒr diesen Fall kennt der erweiterte Emulator folgende Steuerzeichen:

  • ESC m: Umschalten auf weiße Schrift auf schwarzem Grund
  • ESC n: Umschalten auf Normalmodus (schwarz auf weiß)

Intern bewirken diese beiden Funknonen lediglich eine Umprogrammierung des Video-Shifters; mit jeweils zwei Assembler-Befehlen kann man sie getrost als die “weniger Code-intensiven” Teile des Emulators bezeichnen. Bemerkenswert ist noch, daß wegen der Shifter-Programmierung natĂŒrlich ALLES invertiert wird, also auch das Desktop und alles, was mit GEM zu tun hat. Um also nicht gewollte Effekte zu verhindern, sollten Sie Ihre TOS-Programme noch ein ESC n ausgeben lassen, bevor sie terminieren (sofern die Darstellungsart zwischenzeitlich gewechselt wurde, versteht sich).

X=Print C$ oder: Als die Ausgabe zur Funktion wurde...

Zugegebenermaßen ist es reichlich ungewöhnlich, daß man bei der Zeichenausgabe ĂŒber Funktionen des Betriebssystems einen RĂŒckgabewert erhĂ€lt. Aber einerseits liefern eigentlich alle GEMDOS-/BIOS- und XBIOS-Funktionen irgendwelche Werte (das haben Funktionen halt so an sich), andererseits wissen die Kenner der Sprache C, daß printf() auch etwas zurĂŒckgibt, nĂ€mlich die Anzahl der ausgegebenen Argumente. FĂŒr den erweiterten Emulator gab es zwei Probleme, die eine Werte-RĂŒckgabe notwendig machten. Zum einen mußte es eine “legale” Möglichkeit fĂŒr Programme geben, um feststellen zu können, ob der xVT52-Treiber installiert ist (um gegebenenfalls mit einer Meldung abbrechen zu können; oder andere Routinen fĂŒr den Standard-VT52-Emulator anzustoßen). Zum anderen sollte der Benutzer ebenfalls eine “legale” Möglichkeit erhalten, um auf alle relevanten Variablen des Emulators zugreifen zu können. Beides lĂ€ĂŸt sich durch die EinfĂŒhrung zweier Sequenzen erreichen:

  • ESC g: Bestimmen des Zeigers auf den TCB.
  • ESC h: Bestimmen des Zeigers auf den CCB.

Sollten Sie nicht mehr wissen, wozu die beiden Strukturen dienen, hier nochmal die Kurzfassung. Der Terminal Control Block (TCB) enthĂ€lt alle zum Organisieren der Ausgabe wichtigen Parameter, wie z.B. die aktuelle Cursorposition, den aktiven Font, Attribute (wrapping, invers...) u.Ă€., wĂ€hrend der Cursor Control Block die fĂŒr die Darstellung des Cursors benötigten Variablen enthĂ€lt (wie Blinkrate, aktuelle Position und Cursorattribute). Wenn ein Programm nun feststellen möchte, ob der xVT52-Emulator bereits geladen wurde, kann es sich einer Sequenz bedienen, wie dies in Listing 2 exemplarisch anhand eines GFA-ProgrĂ€mmchens dargestellt ist. Nur bei installiertem Treiber liefern die Ausgabefunktionen die gewĂŒnschten Zeiger. Die Differenz zwischen den Zeigern muß genau 66 betragen, ansonsten kann man sicher davon ausgehen, daß nur der Original-Emulator seinen Dienst verrichtet. Damit wĂ€ren die erwĂ€hnten Probleme auch schon aus der Welt geschafft, denn gleichzeitig mit den Zeigern hat man ja auch den Offset auf die xVT52-Variablen und somit die Möglichkeit, auf diese zuzugreifen. Aber Vorsicht!

Fummel, fummel...

Sicher ist es richtig, daß man oft erst durch gezielte Eingriffe in programminterne Bereiche Effekte erreichen kann, die legal unmöglich zu realisieren gewesen wĂ€ren. Genauso sicher ist es aber auch, daß der am hĂ€ufigsten dabei erzielte Effekt ein Systemabsturz ist. Der erweiterte Emulator gestattet dem Programmierer einen sehr tiefen Einblick in seine Arbeit. Das Lesen der Variablen ist natĂŒrlich absolut ungefĂ€hrlich; so ist sicherlich nichts dagegen einzuwenden, wenn man z.B. die aktuelle Schreibposition ermitteln möchte. Wohl aber, wenn Variablen geĂ€ndert werden, ohne sich der Folgen bewußt zu sein! Das Ändern des einen oder anderen Bytes im TCB und CCB kann wunderschöne Effekte mit sich bringen, einschließlich des oben beschriebenen, versteht sich. FĂŒr die meisten denkbaren Änderungen stehen ohnehin ESCape-Sequenzen zur VerfĂŒgung; allerdings lĂ€ĂŸt sich ein selbstgeschriebener Zeichensatz (muß denselben Aufbau wie der des GEM und eine konstante Breite von 8 Pixeln haben) mittels direktem EintrĂ€gen in den TCB schneller und wohl auch einfacher installieren als ĂŒber das VDI.Man sollte vor derartigen “Fummeleien” aber dafĂŒr sorgen, daß alle Daten, die man evtl, spĂ€ter nochmal benötigen könnte, einer sorgfĂ€ltigen Rettungsaktion unterzogen werden; sonst - HUAAaaahh!

Um hier allerdings keine falschen EindrĂŒcke aufkommen zu lassen: xVT52 lĂ€uft bereits seit Monaten unter den verschiedensten Programmen ohne Fehl und Tadel. Es ist also bei Aktivieren des erweiterten Emulators keineswegs “bloß noch eine Zeitfrage”, wann sich der Rechner wohl verabschieden wĂŒrde. Insofern bezieht sich meine Mahnung nur auf die vom Anwender eigenmĂ€chtig vorgenommenen Manipulationen und deren eventuelle Folgen.

Text-” Windows”

Wohl jedes benutzerfreundliche Programm beinhaltet auf die eine oder andere Art die Möglichkeit, dem Benutzer Hilfestellungen zu geben, wenn er an einer Stelle nicht mehr weiter weis. Innerhalb von GEM-Programmen lĂ€ĂŸt sich dies mittels eines zusĂ€tzlichen Fensters erreichen (sofern nicht schon vier geöffnet sind; aber dann gibt es ja auch noch Alert-und Dialogboxen...). Unter TOS muß man da schon grĂ¶ĂŸere “Verrenkungen” anstellen, weil es halt mal nur einen Bildschirm gibt. Und alle VT52-Funktionen - speziell diejenigen zum EinfĂŒgen und Löschen einer Zeile - beziehen sich prinzipiell auf den kompletten Bildschirm, wodurch der Einsatz eines “geschĂŒtzten” Bildschirmbereiches unmöglich gemacht wird. Zwei Sequenzen des xVT52-Emulators erlauben es nun, zwei voneinander unabhĂ€ngige Textfenster zu unterhalten, indem sie die bereits bestehenden ESCapes L (zum EinfĂŒgen) und M (zum Löschen) quasi “im gemischten Doppel” nachvollziehen:

Tabelle 1: Alle Scrollfunktionen auf einen Blick
  • ESC s: EinfĂŒgen einer Zeile, wobei der Bildschirminhalt inklusive der aktuellen Cursorzeile NACH OBEN geschoben wird. Die oberste Zeile geht dabei verloren.
  • ESC t: Löschen der aktuellen Cursorzeile, wobei der Rest des Bildschirminhaltes VON OBEN nachrĂŒckt. Hierdurch wird die oberste Zeile frei.

Nach diesen Sequenzen befindet sich der Cursor am Anfang (Spalte 0) der eingefĂŒgten bzw. gelöschten Zeile. Sie arbeiten somit genauso wie die bisherigen Steuer-Sequenzen, nur in der Behandlung des restlichen Bildschirminhaltes genau umgekehrt. Damit kann man nun problemlos zwei Bildschirmbereiche steuern. Weil es aber einigermaßen umstĂ€ndlich ist zu erklĂ€ren, in welchem Bereich wie gearbeitet werden muß, möchte ich Sie auf Bild 1 hinweisen, in dem die benötigten ESCapes den beiden Fenstern zugeordnet sind. Es bleibt lediglich Ihrer Phantasie ĂŒberlassen, was Sie mit dem zweiten Fenster alles anstellen: Sie können damit ebenso Hilfstexte einblenden wie auch in Editormanier zwei Texte bearbeiten. Sie mĂŒssen nur darauf achten, daß der Bildschirminhalt gepuffert wird, damit ein durch das zweite Fenster eventuell zerstörter Text wiederhergestellt werden kann. Ansonsten können mit den neuen Sequenzen auch hĂŒbsche Effekte erzielt werden: Wenn Sie - ausgehend von Zeile 12 -abwechselnd mit ESC s und ESC L Zeilen einfĂŒgen oder mit ESC t und ESC M Zeilen löschen, entsteht der Eindruck, der Bildschirm wĂŒrde “explodieren” bzw. “implodieren”. Besonders lustig sieht das “Implodieren” bei Verwendung der pixelorientierten Sequenzen aus, die Sie gleich kennenlernen werden.

SeitensprĂŒnge (ESCapaden)

Daß man Texte nach oben und unten scrollen kann, ist sicherlich ein Standard-Feature eines jeden Emulators. In einer pluralistischen Gesellschaft wie der unseren sollte es aber fast schon eine SelbstverstĂ€ndlichkeit sein, in allen vier Himmelsrichtungen FreizĂŒgigkeit zu genießen... Also wurden beim xVT52 zwei weitere Sequenzen implementiert, die ein spaltenweises Scrollen ermöglichen:

  • ESC u: Inhalt der aktuellen Zeile um 1 Spalte nach links durchlaufen lassen
  • ESC x: Inhalt der aktuellen Zeile um 1 Spalte nach rechts durchlaufen lassen.

In beiden FĂ€llen bleibt der Cursor an derselben Position stehen, an der er auch stand, bevor gescrollt wurde; er Ă€ndert also weder Zeilen- noch Spaltenposition. Das “Durchlaufenlassen” bedeutet hier, daß das Zeichen, welches durch das Scrollen links oder rechts “herausgefallen” ist, auf der jeweils anderen Seite wieder “hereingeschoben” wird. Wozu lĂ€ĂŸt sich das gebrauchen? Sie können beispielsweise damit eine Eingaberoutine schreiben, die - sobald die letzte Spalte der aktuellen Zeile erreicht ist - den bisherigen Inhalt um eine Spalte nach links schiebt und die letzte Spalte zum Eingeben eines neuen Zeichens löscht. Wenn Sie sich den Editor von GFA-BASIC einmal nĂ€her betrachten, werden Sie feststellen, daß dort genau dies passiert. Nur: Das Feuerwerk an ESCape-Sequenzen in Kombination mit wiederholter Textausgabe lĂ€ĂŸt erahnen, welche Verrenkungen Sie sich mit ESC u/x sparen können!

Wem diese beiden Sequenzen nicht genĂŒgen, der dĂŒrfte - Assembler-Kenntnisse vorausgesetzt - keine Probleme haben, die entsprechenden Prozeduren SCR_LEFT bzw. SCR_RIGHT auf seine BedĂŒrfnisse umzustricken, so daß es dann möglich ist, ab der aktuellen Spalte den Rest (ohne Turn-around) nach links oder rechts zu scrollen.

Bit By Bit - Built-In Smooth Scrolling

Hinter diesem netten Zungenbrecher verbirgt sich eine Besonderheit von xVT52, die bei vielen Effekten wertvolle Hilfe bieten kann. Gemeint ist das pixelweise Verschieben des Bildschirminhaltes. Zu allen Steuerzeichen, die der erweiterte Emulator kennt und die dem EinfĂŒgen/Löschen und horizontalen Scrollen dienen, gibt es ein “pixelweises Pendant“. In allen FĂ€llen bleibt der Cursor auf seiner alten Position stehen. Soll - wie bei den normalen Sequenzen - der Cursor im Anschluß an die jeweilige Operation am Anfang der bearbeiteten Zeile stehen, so kann man dies durch nachtrĂ€gliches Ausgeben eines ZeilenrĂŒcklaufes (Carriage Return, ASCII-Code 13) erreichen. Tabelle 1 zeigt den Zusammenhang zwischen Sequenzen zum zeilen-/spaltenweisen Scrollen und den entsprechenden, pixelweise arbeitenden Verschieberoutinen. Anzumerken ist hier noch, daß bei den spalten weisen Verschieberoutinen (ESC T/U) tatsĂ€chlich nur um ein Pixel nach links bzw. rechts gescrollt wird. Möchten Sie also eine Zeile um eine komplette Spalte verschieben, mĂŒssen Sie die entsprechende Sequenz achtmal abschicken. Warum? Weil der Prozessor abgeht wie die Feuerwehr! WĂŒrden diese Sequenzen jeweils eine komplette Spalte verschieben, wĂ€ren sie ĂŒberhaupt nicht mehr von den spaltenorientierten Routinen zu unterscheiden...

Alphabet (fast) komplett!

Wer mitgezĂ€hlt hat, wird auf bisher 51 (!) ESCape-Sequenzen kommen, die der erweiterte VT52-Emulator “versteht”, nĂ€mlich von A-Z und a-z. Die letzte Sequenz, ESC r, dient dazu, den Emulator grafikfĂ€hig zu machen. Es ist sicherlich die reizvollste Erweiterung des VT52, mit der sich etliches anstellen lĂ€ĂŸt. Um den Rahmen nicht zu sprengen, muß ich Sie aber leider auf den vierten und letzten Teil dieser Serie vertrösten. Da Sie sowieso erst mit dem Emulator arbeiten können, wenn Sie ihn komplett eingegeben haben, dĂŒrfte dies aber leicht zu verschmerzen sein. Es ließ sich nicht vermeiden, den Artikel in vier Teile zu splitten, weil er “en bloc” schlicht-weg zu groß geworden wĂ€re. Andererseits war es aber unmöglich, das Programm zu modularisieren, so daß Sie also portionsweise immer ein “StĂŒckchen mehr vom Emulator” gehabt hĂ€tten. Denn: Entweder lĂ€uft alles oder es lĂ€uft eben nichts. So ist das nunmal im DigitalgeschĂ€ft...

Tabelle 2: Ausgabegeschwindigkeiten mit und ohne xVT52 bei verschiedenen Konfigurationen und TOS-Versionen. Alle Zeiten in Sekunden.

Speed It Up

Wenn man ein Programm schreibt, das ein bereits bestehendes Programm ablösen soll, muß man dafĂŒr natĂŒrlich gute GrĂŒnde haben. So auch bei der Vorstellung eines neuen (alten, weil kompatiblen) Terminal-Emulators. Nun kann der erweiterte Emulator mit einer Vielzahl wirklich neuer Features aufwarten, aber das hĂ€tte man sicher auch anders erreichen können; beispielsweise durch EinfĂŒhrung neuer GEMDOS-Funktionen. Die Einbindung in den Emulator ist natĂŒrlich die wesentlich elegantere Methode, weil hierbei die logisch zueinander gehörenden Funktionen auch auf dieselbe Weise angesprochen werden können. Aber es gibt noch ein weiteres Argument, warum xVT52 dem “gewöhnlichen” VT52 ĂŒberlegen ist: die Geschwindigkeit. Diese liegt nĂ€mlich im Vergleich zum “Alten” um mindestens Faktor zwei höher; in SpezialfĂ€llen kann die AusfĂŒhrung auch sechsmal und mehr beschleunigt werden. Um diese Steigerungen zu erreichen, wurden einige programmtechnische Tricks angewandt, die ich Ihnen nicht vorenthalten und zur Nachahmung empfehlen möchte. Aber erst das nĂ€chste Mal; jetzt möchte ich Ihnen zunĂ€chst einmal zeigen, wie schnell er denn nun wirklich ist, der erweiterte Emu.

Vorher/nachher und die Benchmarks

Wann immer es darum geht, Programme und deren Leistung zu objektivieren, geht es frĂŒher oder spĂ€ter um deren Geschwindigkeit. So auch diesmal. Auch wenn man nicht zu den “Geschwindigkeits-Fanatikern” gehört, die Leistungen (nur noch) nach der Anzahl der gewonnenen Nanosekunden beurteilen, so muß man sich doch darĂŒber im klaren sein, daß eine tausendmalige Einsparung einer Millisekunde bereits eine Sekunde ausmacht und sich somit viele Wenig zu einem Viel aufsummieren. Wer fast ausschließlich die grafische BenutzeroberflĂ€che und sonstige GEM-Programme benutzt, wird sich ĂŒber die teilweise drastischen Geschwindigkeitszunahmen durch den Blitter-Einsatz freuen, obwohl dieser in einzelnen Funktionsaufrufen “nur unwesentlich” schneller als die softwaremĂ€ĂŸigen Emulationen ist. Umgekehrt gibt es aber auch viele ST-Besitzer, die die grafischen FĂ€higkeiten zwar nicht verachten, aber aus GrĂŒnden der Effizienz lieber mit TOS-Programmen und Kommando-Interpretern arbeiten. Da in diesen Programmen der Bildschirmausgabe per VT52-Emulator eine tragende Rolle zukommt, spielt es hier sehr wohl eine Rolle, ob ein Bildschirm nun in einer oder in einer fĂŒnftel Sekunde aufgebaut ist, da sich dieser Vorgang leicht ein paarhundertmal in der Stunde wiederholen kann. Und eine durchschnittliche “Session” kann viele Stunden dauern...

Um nun also ein Maß fĂŒr die AusfĂŒhrungsgeschwindigkeiten zu bekommen, mußte wieder einmal einer der so beliebten Benchmarks herhalten. FĂŒr das Messen der Bildschirmausgabe gelten zwei Tests als Standard (zumindest fĂŒr die IBM-Welt), so daß ich sie auch fĂŒr den xVT52 verwendet habe (s. Listing 3). Es galt zehn volle Bildschirmseiten ohne und mit Scrollen auszugeben - bloß: wie? Denn mit Benchmarks ist das immer so eine heikle Geschichte. Wenn man nicht aufpaßt, mißt man mit diesen Tests alle möglichen Nebeneffekte - nur halt eben nicht das, was man eigentlich wollte! Tatort: GFA-BASIC. Wenn man hier mißt, wie lange es dauert, um 250 Zeilen mittels PRINT -Anweisung auszugeben, wird man lediglich herausbekommen, wie fix der BIOS-Traphandler ist. Grund: Frank Ostrowski hat schon frĂŒh erkannt, daß das BIOS wesentlich hurtiger arbeitet als die GEMDOS-Funktionen, in denen u.a. auf Ausgabeumlenkung geprĂŒft wird, bevor dann am Ende ohnehin BIOS- oder XBIOS-Funktionen aktiviert werden. So wird also alles, was hinter dem PRINT-Befehl steht, Zeichen fĂŒr Zeichen direkt ĂŒber das BIOS ausgegeben. Also mußte ich direkt die GEMDOS-Funktion PRINT LINE (0x9) ansprechen, die ja einen beliebig langen String ausgeben kann und dabei nur einmal den Trap-Handler zu durchlaufen braucht. Somit konnte gewĂ€hrleistet werden, daß nur die Ausgabegeschwindigkeiten und nicht die Nebeneffekte (inklusive der Interpretergeschwindigkeit) gemessen wurden.

Wow!

Ich glaube, daß man mit den Zeiten recht zufrieden leben kann. Es sind zwar keine Tempus-Geschwindigkeiten erreicht worden (was fĂŒr ein Betriebssystem zugegebenermaßen auch unmöglich ist), aber dennoch liegen die Zeiten von xVT52 deutlich unter denen des Original-Emulators. In Tabelle 2 können Sie die Ergebnisse fĂŒr die verschiedenen ST-Konfigurationen nachlesen. Je nach TOS-Version und Blitter-Einsatz liegt der Geschwindigkeitszuwachsfaktor zwischen zwei und fĂŒnf. Im “normalen” Einsatz ist eine Steigerung um das zwei- bis dreifache als realistisch zu betrachten. NatĂŒrlich werden mir jetzt einige Leser vorwerfen, daß die Benchmark-Tests alles andere als objektiv sind, weil die verwendete Funktion des GEMDOS so umgeschrieben wurde, daß sie die Strings nun “am StĂŒck” und somit ohne weitere BIOS-Aufrufe ausgeben kann. Aber: Ist es denn nicht gerade Sinn und Zweck von (System-) Patches, MĂ€ngel in Programmen auszurĂ€umen (auch mangelnde Geschwindigkeit)? Und: Die meisten TOS-Programme benutzen genau diese Funktion, um ihre Ausgaben auf den Bildschirm zu realisieren und kommen somit auch in den Genuß des erweiterten Emulators. Und schließlich hat der xVT52 ja auch noch ganz andere QualitĂ€ten und VorzĂŒge...

Soweit fĂŒr diesmal. Ich darf natĂŒrlich auch heute nicht versĂ€umen, Ihnen viel Spaß (und wenig Blasen) beim Abtippen des Listings zu wĂŒnschen. Vielleicht hat der eine oder andere Tippfaule unter der Leserschar mit Blick auf Benchmarks und ESCapes nun doch noch Appetit bekommen. WĂŒrde mich natĂŒrlich freuen...

MS

; ************************************* ; * >EXTENDED VT52-TERMINAL EMULATOR< * 3. Teil ; *===================================* ; * Entwickelt mit PROFIMAT-Assembler * ; * M.Schumacher, (p) 12/87 * ; ************************************* SCR_LEFT: ; ESC ’u' move.l 6(a4),a1 ; abs. Cursorposition suba.w (a0),a1 ; -akt, Spalte=AZeilenanfang move.w 36(a0),d2 ; Zeichenhöhe (Pixels) subq.w #1,d2 ; in dbra-ZĂ€hler wandeln \lp1: movea.l a1,a2 ; a1=Ziel, a2=Quelle addq.w #1,a2 ; Quelle=Ziel+1 moveq #12,d1 ; 13*6=78 Bytes verschieben move.b (a1),d3 ; vorderstes Bytes merken \lp2: move.b (a2)+,(a1)+ ; 6 Bytes nach links verschieben move.b (a2)+,(a1)+ move.b (a2)+,(a1)+ move.b (a2)+,(a1)+ move.b (a2)+,(a1)+ move.b (a2)+,(a1)+ dbra d1,\lp2 move.b (a2),(a1)+ ; Byte 78 verschieben move.b d3,(a1)+ ; Anfangsbyte ans Ende (79) setzen dbra d2,\lp1 ; nĂ€chste Pixelzeile verschieben rts SCR_RIGHT: ; ESC 'x' move.l 6(a4),a1 ; abs. Cursorposition suba.w (a0),a1 ; -akt. 5palte=^Zeilenanfang adda.w 38(a0),a1 ; +Bytes/Textzeile=^Anfang nĂ€chster Textzeile move.w 36(a0),d2 ; Zeichenhöhe (Pixels) subq.w #1,d2 ; in dbra-ZĂ€hler wandeln \lp1: movea.l a,a2 ; a1=Ziel, a2=Quelle subq.w #1,a2 ; Quelle=Ziel-1 moveq #12,d1 ; 13*6=78 Bytes verschieben move b (a2),d3 ; letztes Bytes merken \lp2: move.b -(a2),-(a1) ; 6 Bytes nach rechts verschieben move.b -(a2),-(a1) move.b -(a2),-(a1) move.b -(a2),-(a1) move.b -(a2),-(a1) move.b -(a2),-(a1) dbra d1,\lp2 move.b -(a2),-(a1) ; Byte 78 verschieben move.b d3,-(a1) ; letztes Byte an den Anfang setzen dbra d2,\lp1 ; nĂ€chste Pixelzeile verschieben rts SAVE_CRS: ; ESC 'j' move.w 10(a0),d0 ; Anzahl gespeicherter Positionen cmpl.w #3,d0 ; schon alle 3 besetzt? bne.s \set ; nein subq.w #1,d0 ; sonst 1 abziehen subq.w #1,10(a0) move.l 16(a0),12(a0) ; neue Position 1=alte P. 2 (alte 1 geht verloren) move.l 20(a0),16(a0) ; neue Position 2-alte P. 3 \set: lsl.w #2,d0 ; Anzahl in Longpointer wandeln move.l (a0),12(a0,d0.w); Spalte und Zeile merken addq.w #1,10(a0) ; Anzahl inkrementieren rts ; fertig REST_CRS: ; ESC 'k' move.w 10(a0),d0 ; Anzahl gespeicherter Positionen beq.s \zurĂŒck ; fertig, falls 0 subq.w #1,d0 ; sonst Anzahl dekrementieren subq.w #1,10(a0) lsl.w #2,d0 ; und in Longpointer Handeln move.l 12(a0,d0.w),(a0); alte Position zurĂŒckholen move.w 6(a0),d0 ; grĂ¶ĂŸte Zeilennummer cmp.w 2(a0),d0 ; < aktueller? (Hegen EVTL.Fontwechsel) bpl.s \zurĂŒck ; nein, alles ok move.w d0,2(a0) ; sonst in letzte Zeile positionieren \zurĂŒck: rts GET_TCB: ; ESC 'g' bsr UPDATE_END ; Cursor wieder freigeben move.l a0,d0 ; d0:=^TerminalControlBlock addq.l #4,a7 ; und direkt zum Trap-Handler zurĂŒckspringen rts ; (also nicht ĂŒber ESC_SEQ) GET_CCB: ; ESC 'h' bsr UPDATE_END ; Cursor wieder freigeben move.l a4,d0 ; d0:=^CursorControlBlock addq.l #4,a7 ; und direkt zum Trap-Handler zurĂŒckspringen rts ; (also nicht ĂŒber ESC_SEQ) WHITE_BLK: ; ESC 'm' clr.w VID_R0 ; inverse Darstellung (weiß auf schnarz) rts BLK_WHITE: ; ESC 'n' move.w #1,VID_R0 ; normale Darstellung (schwarz auf weiß) rts DEL_TO_CRS: ; ESC 'd' move.w 2(a0),d0 ; akt. Zeile beq.s L_TO_CRS ; falls in Zeile 0, nur noch bis Cursor löschen move.w 36(a0),d1 ; Zeichenhöhe in Pixeln subq.w #8,d1 ; - 8 beq.s \del ; 8x8-Font aktiv? add.w d0,d0 ; sonst doppelt so viele Zeilen löschen \del: subq.w #1,d0 ; Anzahl Zeilen in dbra-ZĂ€hler Handeln move.l (a0),-(a7) ; akt. Spalte und Zeile retten pea \weiter ; RĂŒcksprungadresse merken movem.l a0/a6,-(a7) ; Register retten movea.l 6(a4),a6 ; abs. Cursorposition suba.w (a0),a6 ; - akt. Spalte=^Zeilenanfang bra CLS_ENTRY ; Zellen löschen \weiter: move.l (a7)+,(a0) ; Spalte und Zeile zurĂŒck L_TO_CRS: ; ESC 'o' move.w (a0),d1 ; akt. Spalte move.w d1,d2 ; merken moveq #80,d3 ; Bytes/Pixelzeile move.l 6(a4),a1 ; abs. Cursorposition suba.w d1,a1 ; -akt. Spalte=Zeilenanfang movea.l a1,a2 ; merken adda.l d3,a2 ; und Offset fĂŒr nĂ€chste Pixelzeile addieren move.w 36(a0),d0 ; Zeichenhöhe subq.w #1,d0 ; in dbra-ZĂ€hler Handeln \lp_pixz: clr.b (a1)+ ; Byte löschen dbra d1,\lp_pixz ; nĂ€chstes Byte movea.l a2,a1 ; Anfang der nĂ€chsten Pixelzeile adda.l d3,a2 ; nieder Offset addieren move.w d2,d1 ; ZĂ€hler nieder laden dbra d0,\lp_pixz ; und nĂ€chste Pixelzeile löschen \zurĂŒck: rts GRAPHMODE: ; ESC 'r' lea VEC_BASE,a1 ; Ausgabevektor lea GET_WIDTH,a2 ; auf Holen der Grafikbreite move.l a2,(a1) ; umbiegen st 9(a0) ; Grafikmodus einschalten rts GET_WIDTH: ; Grafikbreite fĂŒr ESC 'r' bestimmen lea GET_HEIGHT_H,a2 ; Ausgabevektor auf Holen der Grafikhöhe move.l a2,(a1) ; umbiegen lea TCB,a0 ; ^TerminalControlBlock subi.w #32,d0 ; Offset von Wert fĂŒr Breite abziehen move.w d0,44(a0) ; und merken moveq #80,d1 ; max. Spalte sub.w (a0),d1 ; -akt. Spalte move.w d1,40(a0) ; als erlaubte Breite ĂŒbernehmen rts GET_HEIGHT_H: ; Grafikhöhe, Hunderter lea GET_HEIGHT_L,a2 ; Ausgabevektor auf Holen der Grafikhöhe move.l a2,(a1) ; umbiegen lea TCB+46,a0 ; Speicher fĂŒr Grafikhöhe in Pixels subi.w #32,d0 ; Offset vom Höhenwert abziehen mulu #100,d0 ; mal 100 move.w d0,(a0) ; merken rts GET_HEIGHT_L: ; Grafikhöhe, Zehner+Einer lea TCB,a0 ; ^TerminalControlBlock subi.H #32,d0 ; Offset vom Höhenwert abziehen move.w d0,d1 ; und zwischenspeichern andi.w #$F,d1 ; Einer isolieren lsr.w #4,d0 ; Zehner andi.n #$F,d0 ; isolieren mulu #10,d0 ; mal 10 add.w d1,d0 ; + Einer add.w d0,46(a0) ; + Hunderter tst.w 44(a0) ; Breite=0? bne.s \weiter ; nein \fail: lea STD_VEC,a2 ; sonst Ausgabe wieder auf Standard move.l a2,(a1) ; umbiegen rts \weiter: tst.w 46(a0) ; Höhe=0? beq.s \fail ; ja. abbrechen lea GRAPHICS,a2 ; sonst Ausgabe auf Grafik move.l a2,(a1) ; stellen clr.l 48(a0) ; ZĂ€hler initialisieren lea CCB,a4 ; ^CursorControlBlock move.l 6(a4),52(a0) ; abs. Cursorpos. als linken Offset merken move.w #400,d1 ; max. Anzahl Pixelzeilen move.w 2(a0),d2 ; akt. Zeile mulu 36(a0),d2 ; 4 Zeichenhöhe in Pixels sub.w d2,d1 ; von max. Anzahl subtrahieren move.w d,42(a0) ; * Anzahl erlaubter Pixelzeilen rts GRAPHICS: ; Grafikausgabe lea TCB,a0 ; TerminalControlBlock move.w 48(a0),d1 ; akt. hör. Position move.w 50(a0),d2 ; akt. Pixelzeile cmp.w 42(a0),d2 ; mit erlaubter Anzahl vergleichen bpl.s \next ; zu groß, nicht ausgeben cmp.w 40(a0),d1 ; hör. Pos. mit max. Position vergleichen bpl.s \next ; zu groß, nicht ausgeben move.l 52(a0),a2 ; linker Zeilenoffset move.b d0.0(a2,d1.w) ; Grafikbyte ausgeben btst #0,8(a0) ; Invertierung eingeschaltet? beq.s \next ; nein not.b 0(a2,d1.w) ; sonst halt invertieren \next: addq.w #1,d1 ; hör. Position ++ cmp.w 44(a0),d1 ; mit Breite vergleichen beq.s \more ; letzte Position schon erreicht move.w d1,48(a0) ; sonst merken rts ; fertig \more: clr.w 48(a0) ; nieder bei Position 0 anfangen addi.l #80,52(a0) ; Offset fĂŒr nĂ€chste Pixelzeile addieren move.w 50(a0),d1 ; akt. Pixelzeile addq.w #1,d1 ; ++ cmp.w 46(a8),d1 ; =Höhe der Grafik? beq.s \fertig ; ja, Grafikmodus beenden move.w d1,50(a0) ; sonst neue Position merken rts \fertig: lea STD_VEC,a2 ; Ausgabe wieder auf Standard move.l a2,(a1) ; umbiegen sf 9(a0) ; und Grafik ausschalten rts CRS_UP: ; ESC 'A' tst.w 2(a0) ; Zeile 0? beq.s \zurĂŒck ; ja, ignorieren subq.w #1,2(a0) ; sonst -- \zurĂŒck: rts CRS_DOWN: ; ESC 'B' move.w 2(a0),d0 ; akt. Zeile cmp.w 6(a0),d0 ; =letzte? bpl.s \zurĂŒck ; ja, ignorieren addq.w #1,2(a0) ; sonst ++ \zurĂŒck: rts CRS_RIGHT: ; ESC 'C' cmpi.w #79,(a0) ; letzte Spalte? bpl.s \zurĂŒck ; ja, ignorieren addq.w #1,(a0) ; sonst ++ \zurĂŒck: rts CRS_UP_SCR: ; ESC 'I' tst.w 2(a0) ; Zeile 0? bne.s Nrunter ; nein move.w (a0),-(a7) ; akt. Spalte merken bsr SCROLL_DOWN ; sonst runterscrollen bsr DEL_LINE ; und Zeile löschen move.w (a7)+,(a0) ; akt. Spalte zurĂŒck bra s \zurĂŒck \runter: subq.w #1,2(a0) ; Zeile -- \zurĂŒck: rts BIG_FONT: ; ESC 'F' move.l 24(a0),d0 ; ^8x16-Fontdaten cmp.l 32(a0),d0 ; Font schon aktiu? beq.s \zurĂŒck ; ja, fertig move.l d0,32(a0) ; sonst als aktuellen Font ĂŒbernehmen move.w #24,6(a0) ; max. Zeile=24 move.l #$100500,36(a0) ; Höhe=16 Pixel: Bytes/Textzeile=1280 lsr 2(a0) ; akt. Zeile halbieren clr.w 10(a0) ; gemerkte Cursorpositionen löschen \zurĂŒck: rts SML_FONT: ; ESC 'G' move.l 28(a0),d0 ; ^8x8-Fontdaten cmp.l 32(a0),d0 ; Font schon aktiu? beq.s \zurĂŒck ; ja, fertig move.l d0,32(a0) ; sonst als aktuellen Font ĂŒbernehmen move.w #49,6(a0) ; max. Zeile=49 move.l #$80280,36(a0) ; Höhe=16 Pixel: Bytes/Textzeile=648 lsl 2(a0) ; akt. Zeile uerdoppeln clr.w 10(a0) ; gemerkte Cursorpositionen löschen \zurĂŒck: rts DEL_FROM_CRS: S ESC J' move.w 6(a0),d0 ; letzte Zeile sub.w 2(a0),d0 ; Cursor schon unten? beq.s L_TO_ENO ; ja, nur noch ab Cursor löschen move.w 36(a0),d1 ; Zeichenhöhe in Pixeln subq.w #8,d1 ; - 8 beq.s \del ; 8x8-Font aktiu add.w d0,d0 ; sonst doppelt so viele Zeilen löschen \del: subq.w #1,d0 ; in dbra-ZĂ€hler wandeln move.l (a0),—(a7) ; Spalte und Zeile retten pea \weiter ; RĂŒcksprungadresse auf Stack movem.l a8/a6,-(a7) ; Register retten move.l LOGBASE,a6 ; ^Video-RAM adda.w #32000,a6 ; ^Bildschirm-Ende bra CLS_ENTRY ; Zeilen löschen \weiter: move.l (a7)+,(a0) ; Spalte und Zeile zurĂŒck L_TO_END: ; ESC 'K' moveq #79,d1 ; max. Spalte sub.w (a0),d1 ; -akt. 5palte=Anzahl zu löschender Zeichen move.w d1,d2 ; merken moveq #80,d3 ; Bytes/Pixelzeile move.l 6(a4),a1 ; abs. Cursorposition movea.l a1,a2 ; merken adda.l d3,a2 ; und Offset fĂŒr nĂ€chst Pixelzeile addieren move.w 36(a0),d0 ; Zeichenhöhe subq.w #1,d0 ; in dbra-ZĂ€hler wandeln \lp_pixz: clr.b (a1)+ ; Byte löschen dbra d1,\lp_pixz ; nĂ€chstes Byte movea.l a2,a1 ; Anfang der nĂ€chsten Pixelzeile adda.l d3,a2 ; wieder Offset addieren move.w d2,d1 ; ZĂ€hler wieder laden dbra d0,\lp_pixz ; und nĂ€chste Pixelzeile löschen \zurĂŒck: rts DEF_TABS: ; ESC 'N' lea TABS,a0 ; ^Bitvektor fĂŒr Tabulatoren move.l #$80808080,d0 ; Bitmaske: Jede 8. Position move.l d0,(a0)+ ; Tabulatoren setzen move.l d0,(a0)+ move.w d0,(a0) rts CLR_TABS: ; ESC 'Q' lea TABS,a0 ; Miami weiß clr.l (a0)+ ; Tabulatoren löschen clr.l (a0)+ clr.w (aB) rts SET_TAB: ; ESC 'P' lea VEC_BASE,a1 ; Vektor lea SGET_TAB,a2 ; zum Holen der Position move.l a2,(a1) ; umbiegen rts ; fertig SGET_TAB: lea VEC_BASE,a1 ; Vektor wieder lea STD_VEC,a2 ; auf normale Ausgabe move.l a2,(a1) ; umschalten bsr.s CALC_TAB ; Tab-Position bestimmen bset d2.0(a0,d0.w) ; und Tab setzen \zurĂŒck: rts CLR_TAB: ; ESC 'Q' lea VEC_BASE,a1 ; Vektor lea CGET_TAB,a2 ; zum Holen der Position move.l a2,(a1) ; umbiegen rts ; fertig CGET_TAB: lea VEC_BASE,a1 ; Vektor wieder lea STD_VEC,a2 ; auf normale Ausgabe move.l a2,(a1) ; umschalten bsr.s CALC_TAB ; Bitvektorposition berechnen bclr d2.0(a0,d0.w) ; und Tab löschen \zurĂŒck: rts CALC_TAB: ; Tabulatorposition berechnen subi.w #32,d0 ; Offset von Tabulatorposition abziehen bmi.s \fail ; zu klein, ignorieren cmpi.w #80,d0 ; grĂ¶ĂŸer als grĂ¶ĂŸte Spalte? bpl.s \fail ; ja, ignorieren lea TABS,a0 ; ^Tab-Vektor ext.w d0 ; Position auf WortlĂ€nge bringen move.w d0,d1 ; und retten asr.w #3,d0 ; durch 8 diuidieren moveq #7,d2 and.w d2,d1 ; Rest isolieren sub.w dl,d2 ; und Offset berechnen rts \fail: addq.l #4,a7 ; direkt zum Trap-Handler rts ; zurĂŒckspringen LIGHT_ON: ; ESC 'R' bset #3,8(a0) ; halbe Helligkeit einschaiten rts LIGHT_OFF: ; ESC 'S' bclr #3.8(a0) ; intensive Darstellung einschalten rts

Es folgen noch zwei Listings auf der nÀchsten Seite.

Listing 2: So einfach kann man prĂŒfen, welcher Emulator gerade aktiv ist (in GFA-BASIC).

' ***************************** ' * xVT52-INSTALLATIONSTEST * ' ***************************** PRINT CHR$(27); ! ESCape ausgeben tcb=BIOS(3,2,ASC("g")) ! Pointer auf TCB holen PRINT CMR$(27); ! ESCape ausgeben ccb=BIOS(3,2,ASC("h")) ! Pointer auf CCB holen PRINT HEX$(tcb)'HEXS(ccb) ! Adressen ausgeben IF (ccb-tcb)<>66 ! schon installiert? PRINT "Nur Original-UT52 installiert!" ELSE PRINT CHRS(27);"R>>> xVT52 installiert! <<<";CHR$(27);"S" ENDIF

Listing 3: So wurden die Ausgabezeiten ermittelt (in GFA-BASIC)

' ************************ ' * BENCHMARK-TEST FÜR * ' * xVTS2-EMULATOR * ' ************************ ' b$=SPACE$(1999)+CHR$(8) ! Platz schaffen b=VARPTR(b$) ! Adresse besorgen FOR i=b 10 b+1998 ! Mit Zufalls- POKE i,32+RANDOM(224) ! Zeichen belegen NEXT i PRINT CHR$(27);"v" ! Wrapping ein ' ' *** TEST 1 *** t=TIMER FOR i=1 TO 10 ! zehn Seiten CLS ! ausgeben VOID GEMDOS(9,L:VARPTR(b$)) NEXT i t1=TIMER PRINT PRINT (t1-t)/200 ! Zeit ausgeben REPEAT UNTIL inkey$<>"" ' ' *** TEST 2 *** ' PRINT AT(1,24); ! Cursor nach unten t=TIMER FOR i=1 TO 10 ! zehn Seiten VOID GEMDOS(9,L:VARPTR(b$)) NEXT i ! Scrollen t1=TIMER PRINT PRINT (t1-t)/200 ! Zeit ausgeben