← ST-Computer 11 / 1987

Schnell wie der Wind: Neue Form_Dial Routine

Programmierpraxis

In der Juni-Ausgabe der Zeitschrift wurde eine elegante File-Select-Box vorgestellt, die aber nur in eigenen Programmen verwendet werden kann. Mit dem hier vorliegenden Artikel wird gezeigt, daß es einfch ist, solche Änderungen bleibend (bis zum Reset) im Betriebssystem zu verankern, sodaß alle Programme davon profitieren. Als Beispiel wird hier die Form__Dial Routine des AES verwendet, die beschleunigt wird.

Zunächst werden wir die Arbeitsweise dieser Funktion erläutern. Soll in einem GEM-Programm eine Formular- oder Alert-Box dargestellt werden, wird vorher der dazu benötigte Bildschirmbereich durch den Aufruf der Form__Dial Routine reserviert. Äußerlich ist davon nichts zu merken, intern jedoch wird die Größe des Bereichs gespeichert.

Sobald der Programm-Nutzer seine Eingaben abgeschlossen hat und die Box nicht mehr benötigt wird, wird vom Programm erneut die Form_Dial-Routine aufgerufen, diesmal um den Bildschirmbereich wieder freizugeben. Jetzt kommt es jedoch zu einer unmittelbaren Reaktion des Betriebssystems: für alle von diesem Bildschirmbereich betroffenen Fenster (auch Accessories) wird eine Nachricht ausgesandt, damit die zerstörten Bereiche wieder neu gezeichnet werden.

Diese Wiederherstellung des Bildschirms geht bei einigen Programmen nur sehr langsam voran. Wenn beispielsweise beim Textverarbeitungsprogramm 1st-Word ein komplizierter Bildschirminhalt (mit verschiedenen Schriftarten, ...) erneuert werden muß, so kann dies einige Sekunden dauern. Diese unnötigen Wartezeiten werden mit dem im folgenden beschriebenen Programm vermieden.

Überblick

Die Funktionsweise ist dabei die: Das Programm wird zuerst resident in den Speicher geladen, wo es dann etwa 30 KByte wegnimmt. Wird die Form_Dial Routine zum Reservieren eines Bildschirmbereichs aufgerufen, wird die AES-Routine umgangen, statt-dessen wird der Bildschirmausschnitt in den, im Programm vorgesehenen Speicher geschrieben. Wenn der Bereich mit einem erneuten Aufruf der Funktion wieder freigegeben werden soll, wird er durch das Programm blitzartig wieder hergestellt. Es kommt zu keinen Meldungen des Betriebssystems an das Programm, daß ein Bildbereich zu erneuern ist.

Nun zu den Details: Das Programm besteht eigentlich aus zwei Teilen: dem Initialisierungsteil,-der nur einmal durchlaufen wird, und dem eigentlichen Programm, das bei jedem Aufruf einer AES- oder VDI Routine angesprungen wird.

Der Initialisierungsteil

Im Initialisierungsteil wird zuerst (wie in allen GEM-Programmen) der notwenige Speicherbedarf berechnet. Dabei wird auch ein beim Eingeben des Programms festzulegender Betrag von Bytes berücksichtigt, der als Pufferspeicher verwendet wird, um den Bildschirmausschnitt zwischenzuspeichern. Die Größe kann grundsätzlich frei gewählt werden - je mehr Bytes dazu verwendet werden dürfen, desto größere Bereiche können gesichert werden. Wenn zur Speicherung eines Bildbereiches nicht genügend Speicher vorhanden ist, wird die 'normale’ AES Routine verwendet. Für die meisten Anwendungen reichen etwa 20 KByte. Die obere Grenze stellen 32 KByte dar (das reicht für den gesamten Bildschirminhalt, größer wird ein Formular sicher nie sein).

Im Initialisierungsteil wird überprüft, ob das Programm bereits im Speicher verankert wurde - in diesem Fall wird die Initialisierung abgebrochen. Es wird durch diese Abfrage sichergestellt, daß das Programm nicht mehrfach im Speicher ist.

Wenn das Programm noch nicht resident ist, dann wird mit der BIOS Funktion Set_Exception der Ansprungvektor für AES- und VDI-Routinen auf den folgenden Programmteil GEMNEW gerichtet. Die alte Startadresse wird in der Program invariablen OLDGEM gespeichert und benötigt, wenn eine andere Routine als AES-Form_Dial aufgerufen wird.

Anschließend werden die Program invariablen STARTMEM und ENDMEM mit der Start- und Endadresse des verfügbaren Pufferspeichers belegt - dieser Speicher beginnt nach dem Ende des Programms, seine Länge wird durch PUFFLEN angegeben. Danach erfolgt mit der GEMDOS-Funktion Keep_Proccess der Rücksprung in das Programm, aus dem das Programm gestartet wurde (gewöhnlich GEM-Desktop). Mit dieser GEMDOS-Funktion wird für das Programm der in D4 angegebene Speicherbedarf reserviert, der Rest wird an die GEM-Speicherverwaltung zurückgegeben. Das Programm bleibt im Speicher und kann jederzeit aufgerufen werden.

Das Hauptprogramm

Der zweite Programmteil beginnt bei dem Label GEMNEW. Dort wird eine Identifikationsnummer gespeichert, mit der verhindert werden soll, daß das Programm mehrfach installiert werden kann (siehe oben).

Dieser Programmteil wird nun (wegen der Veränderung des Einsprungvektors) bei jedem Aufruf einer AES- öder VDI-Routine aufgerufen. Es muß zuerst festgestellt werden, ob eine AES-oder VDI-Routine aufgerufen wurde. Dazu wird das Register DO ausgewertet, das den Wert $C8 haben muß, wenn eine AES-Routine aufgerufen wird. Wenn dies nicht der Fall ist, dann wird (unter Verwendung der gespeicherten alten Einsprungadresse) die zuständige Routine des Betriebssystems aufgerufen. Gleiches geschieht, wenn zwar eine AES-Routine aufgerufen wurde, aber eine andere Funktiosnummer, als die von Form__Dial verwendet wird.

Handelt es sich bei dem Aufruf hingegen tatsächlich um die Form_Dial-Routine, muß das Intin-Feld für AES-Routinen ausgewertet werden. Dort wird im ersten Wort angegeben, auf welche Weise die Funktion verwendet werden soll: mit 0 zum Reservieren eines Bildschirmteiles, mit 3 zum Freigeben desselben. Möglich sind auch die Werte 1 und 2 - so wird die Funktion dazu verwendet, schrumpfende oder wachsende Rechtecke zu zeichnen, damit es aussieht, als kämen die Boxen,... aus der Unendlichkeit oder würden eben dorthin wieder verschwinden.

In den ersten beiden Fällen werden die, bei den Labeln GET und PUT beginnenden Programmteile aufgerufen, die den Bildschirm speichern oder wiederherstellen sollen. In den beiden anderen Fällen erfolgt der Rücksprung ins Programm, von dem die Form_Dial Routine aufgerufen wurde. Es werden also keine schrumpfenden,... Rechtecke gezeichnet. Wenn jemand darauf Wert legt, muß lediglich die Anweisung BRA RETURN durch BRA CONTAES ersetzt werden - es erfolgt in diesen Fällen der Aufruf der normalen Form_Dial Routine des Betriebssystems.

Der Programmteil GET

Statt einen Bildbereich zu reservieren, wird er in diesem Programmteil im RAM zwischengespeichert. Dazu muß zuerst die Maus abgeschaltet werden, damit diese (falls sie bewegt wird), keine Störungen verursacht. Das dazu verwendete Unterprogramm benutzt die Line-A Grafik-Befehle. Es wird darin auch die Adresse des Control-Feldes dieser Befehle ermittelt und in A3 gespeichert - die Adresse wird später benötigt, wenn die Maus wieder eingeschaltet werden soll.

Im Unterprogramm CALC werden dann aus den Daten im Intin-Block der AES-Funktion die Breite des zu rettenden Biidschirmbereiches in Worten (D1), die Zahl der Zeilen dieses Bereiches (D2), der Speicherbedarf zur Speicherung des Rechtecks (D3), die Startadresse des Bereiches am Bildschirm (A0), sowie eine Kenn-Nummer (D0) errechnet.

Der Grund, weshalb die Breite des Rechteckes in Worten angegeben wird, ist leicht zu verstehen: es wird nicht genau das angegebene Rechteck gespeichert, sondern aus Gründen der Einfachheit und Geschwindigkeit ein (meistens) etwas größerer Bereich mit Wort-Grenzen. Andernfalls hätten einzelne Bits am Rand separat berücksichtigt werden müssen.

Der Speicherbedarf (in Byte) ergibt sich grundsätzlich aus der Multiplikation Breite in Worten mal Zeilenanzahl mal zwei. Es muß aber berücksichtigt werden, daß sowohl die Zeilenanzahl als auch die Breite um eins reduzierte Werte sind, und daß außer der eigentlichen Bildinformation auch noch die eine Adresse, sowie die Kenn-Nummer gespeichert werden muß.

Die Kenn-Nummer ist notwendig, damit der PUT-Programmteil feststellen kann, ob der freizugebende Bildschirmbereich mit dem gespeicherten Bereich identisch ist (dies ist dann nicht der Fall, wenn nicht ausreichend Speicher vorhanden war, um das Rechteck zu speichern).

Nachdem alle diese Werte ermittelt wurden, sollte man feststellen, ob genug Speicher vorhanden ist, um den Bereich zu speichern. Wenn dies der Fall ist, dann wird in einer einfachen Schleife der Bildschirminhalt wortweise in den Pufferspeicher übertragen. Außerdem wird noch die Kenn-Nummer und die Startadresse des gerade gespeicherten Blocks im Puffer abgelegt - beide Informationen werden vom Programmteil PUT benötigt. Auch die Startadresse des Pufferspeichers muß erhöht werden, damit es möglich ist, auch mehrere (kleinere) Boxen zugleich zwischenzuspeichern (dies ist möglich, kommt in der Praxis aber ziemlich selten vor).

Anschließend verzweigt das Programm zum Label MAUSEIN, wo die Maus wieder sichtbar gemacht wird, und die Rückkehr in das Programm erfolgt, von dem die Form_Dial Routine aufgerufen wurde. Dabei ist zu beachten, daß der Rücksprung über RTE und nicht RTS erfolgt, weil Betriebssystem-Routinen beim ST über Traps aufgerufen werden und sich der Prozessor dann im ’Exception-Modus’ befindet.

Wenn der Speicher nicht ausreicht, erfolgt über den Programmteil ERROR der Aufruf der Form_Dial-Routine des Betriebssystems, wozu die Maus wieder sichtbar gemacht werden muß.

Der Programmteil PUT

Dieser Programmteil wird aufgerufen, um einen Bildschirmbereich wieder frei zu geben. Wie bei GET wird zuerst die Maus unsichtbar gemacht und das Unterprogramm CALC aufgerufen. Sodann wird festgestellt, ob die errechnete Kenn-Nummer mit dem letzten Wert im Pufferspeicher identisch ist - wenn dies nicht der Fall ist, wird (über ERROR) die Form_Dial Routine des Betriebssystems aufgerufen.

Andernfalls wird aus dem Pufferspeicher die Startadresse des Blocks gelesen, in einer ähnlichen Schleife wie bei GET wird der Bildschirminhalt wieder erneuert. Die Startadresse des Pufferspeichers wird verkleinert - der Speicher kann somit wieder verwendet werden. Anschließend erfolgt, wie bei GET der Rücksprung in das Programm, welches die Form_Dial Routine verwendet hat.

Anwendung des Programmes

Das Programm läuft in dieser Form nur in der höchsten Auflösungsstufe des ST. Es kann jedoch an die anderen Modi angepaßt werden, wozu im Unterprogramm CALC die Berechnung der Breite des Rechtecks in Worten geändert werden muß (in Mid-Res: die beiden Rotierbefehle ASR mit #3 statt #4, in Low-Res mit #2).

Auch die Berechnung der Bildschirm-Startadresse muß ein wenig verändert werden: die Multiplikation muß sowohl in Low-Res, als auch in Mid-Res mit =#=160 statt #80 erfolgen.

Abgesehen davon läuft das Programm ohne Einschränkung mit allen anderen Programmen, die die Form_Dial Routine vorschriftsmäßig verwenden. Es gibt allerdings einige Programme, bei denen eine ähnliche Pufferung des Bildinhaltes schon von sich aus durchgeführt wird (z. B. GfA-BASIC bei A1ert-Boxen, Tempus-Editor bei kleinen Boxen). Bei diesen Programmen kann daher keine Beschleunigung erreicht werden.

Accessories, die nur aus einem Formular bestehen, arbeiten gewöhnlich problemlos mit dem Programm zusammen. Keinen Effekt hat das Programm hingegen auf Gem-Desktop. Dort wird die Form_Dial Routine anscheinend nicht verwendet, oder der Einsprung erfolgt nicht über den GEM-Vektor. Auch die GEM-File-Select Box, die ebenfalls einen Bildschirmausschnitt reserviert und später wieder frei gibt, nützt die Form__Dial Routine entweder gar nicht, oder umgeht zumindest den GEM-Vektor.

Grundsätzlich kann nach dem hier demonstrierten Muster jede AES- oder VDI-Funktion durch eigene, schnellere oder vielseitigere Routinen ersetzt werden. Neben der Initialisierung, bei der kaum Änderungen notwendig sind, muß darauf geachtet werden, daß die alte Startadresse der Betriebssystem-Routinen gespeichert wird, daß dorthin mit JMP gesprungen wird, wenn eine andere Routine, als die zu ersetzende aufgerufen wurde, und daß ansonsten der Rücksprung ins Programm, aus dem der Aufruf kommt, mit RTE erfolgt. Es können auch mehrere solche Programme zugleich im Speicher sein. Nur sollte darauf geachtet werden, daß diese möglichst rasch wieder verlassen werden, wenn eine andere Funktion aufgerufen wird - sonst wird das gesamte Betriebssystem noch langsamer.

; Michael Kofler, August 87 ; das Programm umgeht AES-Routine Form_Dial: ; der zu reservierenden Bildschirmbereich wird im RAM zwischenspeichert ; und diesen Bereich bei der Freigabe blitzartig erneuert; dies bringt ; bei GEM-Programmen mit langsamen Bildschirmaufbau eine klare ; Beschleunigung des Programmablaufs, wenn Formulare, Alert-Boxen,.. ; verwendet werden SECTION TEXT INITGRAF EQU $A000 ;Codes der Line-A Grafik Routinen SHOWMAUS EQU $A009 HIDEMAUS EQU $A0OA GEMDOS EQU 1 ;Trap-Nummer für GEMDOS-Funktionen KEEP EQU $31 ;Fn.nummer für KEEP PROCESS BIOS EQU 13 ;Trap-Nummer für BIOS-Funktionen SETEXC EQU 5 ;Fn.nummer für SETEXEC GEMVEC EQU 34 ;Vektor des GEM-Traps SCRADR EQU $44E ;hier steht die Bildschirmstartadresse PUFFLEN EQU 25000 ;Größe des Pufferspeichers ; INITIALISIERUNGSTEIL MOVE.L 4(A7),A0 ;Programmbeginn MOVE.L #$100,D4 ; + Base Page ADD.L 12(A0),D4 ;+ Prg.Länge ADD.L 20(A0),D4 ; + Datensegement-Länge ADD.L 28(A0),D4 ;+ Blocksegment-Länge ADD.L #PUFFLEN,D4 ;+ Raum für Pufferspeicher und Stack ;D4 gibt benötigten Speicher an MOVE.L #-1,-(A7) ;GEM Exception-Vektor bestimmen MOVE.W #GEMVEC,-(A7) MOVE.W #SETEXC,-(A7) TRAP #BIOS ADDQ.L #8,A7 MOVE.L D0,A0 ;Test, ob Programm bereits resident CMP.L #$1A2B3C4D,2(A0) ;2. und 3. Wort der Execptionroutine ; mit S1A2B3C4D vergleichen BEQ.S ABBRUCH ;wenn identisch ->> Programm abbrechen MOVE.L #GEMNEW,-(A7) ;GEM Exception-Vektor ändern: MOVE.W #GEMVEC,-(A7) ; zeigt jetzt auf den unten folgenden MOVE.W #SETEXC,-(A7) ; Programmteil TRAP #BIOS ADDQ.L #8,A7 MOVE.L D0,OLDGEM ;alte GEM-Startadresse retten LEA PUFFER,A0 ,Adressen des Pufferspeicher bestimmen MOVE.L A0,STARTMEM ;und in den vorgesehenen Programm- ADDA.L #PUFFLEN,A0 ; variablen speichern MOVE.L A0,ENDMEM CLR.W -(A7) ;Initialisierung beenden (Programm MOVE.L D4,-(A7) ;bleibt aber im Speicher resident) MOVE.W #KEEP,-(A7) TRAP #GEMDOS ABBRUCH CLR.L (A7) ;Programm abbrechen (Programm wird au: CLR.W -(A7) ;Speicher entfernt) TRAP #GEMDOS ; Start der Routine, die künftig immer dann durchlaufen wird, wenn eine ; AES oder VDI Routine aufgerufen wird GEMNEW BRA.S WEITER DC.L $1A2B3C4D ;Kennung der Routine WEITER CMPI.B #$C8,D0 ;wurde AES-Funktion aufgerufen ? BNE.S JUMPAES ;nein, weiter im Betriebssystem MOVEM.L D0-D4/A0-A3,-(A7) ;Register retten MOVEA.L D1,A0 ;A0 zeigt auf AES-Parameterblock MOVEA.L (A0),A1 ;A1 zeigt auf CONTRL-Feld CMPI.W #51, (A1) ;-wurde Form_Dial aufgerufen ? BNE.S CONTAES ;nein, weiter in AES MOVEA.L 8(A0),A2 ;A2 zeigt auf INTIN-Feld, dort wird ;Funktion der Form_Dial Routine bestimmt CMPI.W #0,(A2) ;bei 0 GET-Programmteil aufrufen BEQ.S GET CMPI.W #3,(A2) ;bei 3 PUT-Programmteil aufrufen BEQ.S PUT BRA RETURN ;ansonsten Rücksprung ins Hauptprogramm ; (d.h. schrumpfende Rahmen,., werden ; nicht gezeichnet) ERROR MOVE.W #0,2(A3) ;bei Fehler in GET/PUT Routinen: MOVE.W #1,6(A3) ; Maus wieder sichtbar machen, DC.W SHOWMAUS ; weiter in Betriebssystemroutinen CONTAES MOVEM.L (A7)+,D0-D4/A0-A3 ;Register zurück JUMPAES MOVE.L OLDGEM,A0 ; .. Startadresse der GEM-Routinen JMP (A0) ; dorthin springen MAUSAUS MOVE.L A2,-(A7) ;A2 (zeigt auf Intin) retten DC.W INITGRAF ;Grafik initialisieren MOVE.L 4(A0),A3 ;A3 zeigt auf CONTRL-Array (für später DC.W HIDEMAUS ;Maus ausschalten MOVE.L (A7)+,A2 ;A2 wieder zurück RTS GET ;zu reservierenden Bild-Bereich lesen und im Puffer speichern BSR MAUSAUS ;Maus abschalten BSR.S CALC ;Adressen, Speicherbedarf,., errechnen ADD.L STARTMEM,D0 ; Feststellung, ob genug Speicher für CMP.L ENDMEM,D0 ; den Bildausschnitt vorhanden ist BGT ERROR ;zuwenig Speicher - daher normale AES- ; Routine verwenden MOVE.L STARTMEM,A1 ;A1 zeigt auf Beginn des freien Speichers MOVE.L A0,A2 ;Bildschirmbereich in Pufferspeicher LOOP1 MOVE.W D1,D4 ; schreiben; Register aus CALC bekannt LOOP2 MOVE.W (A0)+,(A1)+ ;innere Schleife für eine Bildschirm- DBF D4,LOOP2 ; Zeile, deren Länge in D4 bestimmt isc ADD.L #80,A2 MOVE.L A2,A0 DBF D2,LOOP1 ;äußere Schleife für Zahl der Zeilen(D2 MOVE.L STARTMEM,(A1)+ ;Startadresse des Blocks speichern MOVE.L D3,(A1)+ ;-zuletzt Kennnummer speichern MOVE.L D0,STARTMEM ;neuer Start BRA.S MAUSEIN ;Maus wieder sichtbar, Rücksprung PUT ;Bild-Bereich aus Puffer auf den Bildschirm schreiben BSR MAUSAUS ;Maus abschalten BSR.S CALC ;Adressen,., ausrechnen MOVE.L STARTMEM,A1 CMP.L -4(A1),D3 ;Kennnummer vergleichen BNE ERROR ;nicht passend, daher weiter im AES MOVE.L -8(A1),A1 ;in A1: Startadresse des Blocks MOVE.L A0,A2 ;Bildschirmausschnitt vom Puffer- LOOP3 MOVE.W D1,D3 ;Speicher auf Bildschirm übertragen LOOP4 MOVE.W (A1)+,(A0)+ ;innere Schleife für eine Bildschirm- DBF D3,LOOP4 ; Zeile ADD.L #80,A2 MOVE.L A2,A0 DBF D2,LOOP3 ;äußere Schleife für Zahl der Zeilen SUB.L D0,STARTMEM ;Start des freien Speicher reduzieren MAUSEIN MOVE.W #0,2(A3) ;Maus wieder sichtbar machen MOVE.W #1,6(A3) ;A3 zeigt auf Grafik-Parameter DC.W SHOWMAUS RETURN MOVEM.L (A7)+,D0-D4/A0-A3 ;Register zurück, danach Rücksprung ins RTE ;Programm, aus dem Aufruf erfolgte CALC ;Koordinaten auslesen, Wortgrenzen, Speicherbedarf ;gegeben: A2: zeigt auf INTIN Feld ;gesucht: D0: Speicherbedarf in Byte D3: Kenn-Nummer ; D1: Breite in Worten D2: Höhe in Zeilen ; A0: Startadresse des Rechtecks am Bildschirm LEA 10(A2),A0 ;A0 zeigt auf Beginn der Parameterliste CLR.L D0 ; im Intin-Feld MOVE.W (A0),D0 ;D0: X1-Koordinate in Punkten MOVE.W D0,D1 ADD.W 4(A0),D1 ;D1: X2-Koordinate (Xl+Breite)in Punkten ASR.W #4,D0 ;D0: X1 in Worten (Division durch 16) ASR.W #4,D1 ;D1: X2 in Worten SUB.W D0,D1 ; —> D1: Breite minus 1 in Worten MOVE.W 6(A0),D2 ; —> D2: Y-Breite MOVE.L SCRADR,D3 ;D3: Bildschirmstartadresse ADD.W D0,D0 ;D0: X1 in Byte ADD.L D0,D3 ;zur Startadr. addieren MOVE.W 2(A0),D0 ;D0: Y1 in Zeilen MULU #80,D0 ;mal 80 .. in Byte ADD.L D0,D3 ;zur Startadr. addieren MOVE.L D3,-(A7) ;am Stack Zwischenspeichern MOVE.W D1,D0 ;D0: Speicherbedarf in Wörtern ADDQ.W #1,D0 ;aus D1: Breite in Worten MULU 6(A0),D0 ;mit Höhe (Zeilenanzahl) multiplizieren ADD.W D1,D0 ;nochmal Breite in Worten addieren ADD.W #5,D0 ;plus 5 Worte für Kenn-Nummer,... ADD.W D0,D0 ;mal 2 --> D0:Speicherbedarf in Byte MOVE.L (A0),D3 ;Kenn-Nummer bestimmen EOR.W D0,D3 ; --> D3: Kenn-Nummer MOVE.L (A7)+,A0 ;Startadresse vom Stack in --> A0 RTS ;Programmvariablen STARTMEM DS.L 1 ;Beginn des Pufferspeichers ENDMEM DS.L 1 ;Ende des Pufferspeichers OLDGEM DS.L 1 ;Zeiger auf gewöhnliche GEM-Routinen PUFFER DS.W 1 ;hier beginnt der Pufferspeicher, dessen ;durch PUFFLEN festgelegt ist

Listing 1: Die neue Form_Dialroutine zeigt dem Blitter wo es langgeht.

Michael Kofler