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.
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.
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.
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.
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ß.
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.
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.