Turbo Pascal unterstützt nur ein Speichermodell, das von seinem Aufbau her der Implementierung von TSR-Programmen entgegenkommt.
Die Abbildung 4 zeigt, daß hinter dem PSP der Programmcode und die benötigten Routinen aus den verschiedenen Units sowie aus der Run-Time-Library folgen. Daran schließen sich die vordefinierten Konstanten, die globalen Daten sowie das Stacksegment an. Während die Größe dieser Programmbestandteile bei der Kompilierung festgelegt wird und sich nicht mehr ändert, gilt dies für die Größe des Heap, der sich an das Stacksegment anschließt, nicht. Werden neue Objekte mit NEW erzeugt, wächst der Heap, während mit RELEASE der Heap wieder auf das Ende des Stacksegments zugeht.
Turbo Pascal bietet den großen Vorteil, die maximale Größe des Heap sowie die Stackgröße einstellen zu können. Es handelt sich dabei um die $M-Direktive, die mit folgenden Parametern aufgerufen werden muß:
{$M Stackgröße, minimale Heap-Größe, maximale Heap-Größe}
Alle Angaben beziehen sich auf ein Byte, so daß die Direktive
{$M 2048, 0, 5000}
die Erzeugung eines 2 KByte großen Stacks und eines maximal 5000 Byte großen Heap bei der Kompilierung zur Folge hat. Fehlt eine solche Direktive innerhalb des Programms, ist dem Wachstum des Heap keine Grenze gesetzt, und er kann sich bis zum Ende des Hauptspeichers ausdehnen. Dies hätte jedoch die katastrophale Folge, daß der gesamte Hauptspeicher für das TSR-Programm nach dessen Beendigung reserviert werden müßte und kein Speicher für weitere Programme mehr zur Verfügung stände. Indem dem Programm aber die $M-Direktive vorangestellt wird, läßt sich die maximale Größe des Programms im Speicher und damit die Anzahl der Paragraphen, die nach der Beendigung des Programms resident im Speicher verbleiben müssen, genau berechnen.
Auch hier bietet Turbo Pascal den Vorteil, daß die Anzahl zu reservierender Paragraphen bereits aus dem Pascal-Programm heraus berechnet werden kann, die komplizierte Berechnung innerhalb der Assemblerschnittstelle entfällt also. Für diese Zwecke wichtig sind dabei die Anfangsadresse des PSP und das Ende des Heap, da sie den Anfang und das Ende des TSR-Programms im Speicher markieren. Turbo Pascal definiert diese Informationen als ganz normale Variablen, die in Form von Pointern für ein Pascal-Programm zugänglich sind.
Die Abbildung 4 zeigt, daß die Segmentadresse des PSP in der Variablen PrefixSeg zu finden ist, während das Ende des Heap bis zur Version 6.0 mit Hilfe der (Pointer-)Variablen FreePtr ermittelt werden kann. Zwar zeigt diese Variable nicht direkt auf das Ende des Heap, doch enthält der Segmentteil dieses Pointers die Endadresse des Heap minus $1000. Unter der Version 6.0 wurde die Verwaltung des Heap etwas verändert: Hier ist es der Zeiger HeapEnd, der direkt auf das Ende des Heap zeigt.
Die Verfügbarkeit dieser Informationen macht sich innerhalb des TSR-Programms die Prodzedur ResPara zunutze, indem sie mit Hilfe der genannten Variablen die Anzahl der Paragraphen berechnet, die nach der Installation des TSR-Programms resident im Speicher verbleiben müssen. Mit Hilfe konditionaler Kompilierung wird dabei je nach der Turbo-Pascal-Version auf den Zeiger HeapPtr oder HeapEnd zurückgegriffen.
Bei der Pascal-TSR-Funktion, deren Adresse TsrInit als erster Parameter übergeben werden muß, muß es sich um eine Prozedur handeln, die sich innerhalb des Hauptprogramms und nicht innerhalb einer Unit befindet. Darüber hinaus darf sie nicht mit Hilfe der $F+-Compiler-Direktive in eine FAR-Procedure verwandelt werden, da die Assembler-Schnittstelle davon ausgeht, daß sie es hier mit einer NEAR-Procedure zu tun hat. Aus diesem Grund muß die Adresse diesr Prozedur mit Hilfe der Funktion OFS ermittelt und an die Funktion TsrInit übergeben werden, da Turbo Pascal sonst neben der Offset-Adresse der Prozedur auch die Segment-Adresse auf den Stack ablegen würde.
FAR müssen hingegen die Prozeduren und Funktionen sein, die in der installierten Kopie des Programms aufgerufen werden sollen. Das Casting von Funktionszeigern in Turbo Pascal ist nicht erlaubt. Aus diesem Grund liefert TsrSetPtr kein Ergebnis an ihren Aufrufer zurück, und der Aufruf von TsrSetPtr und TsrCall kann nicht miteinander kombiniert wrden.
Allerdings müssen zunächst Code-Zeiger deklariert wrden, die die aufzurufenden Prozeduren bzw. Funktionen und vor allem ihre Argumente beschreiben. Wie der folgende Auszug aus dem Programm-Listing von TSRP.PAS zeigt, tragen diese Zeiger die Namen OAProzT und SHKProzT. OAProzT stellt dabei einen Zeiger auf eine Prozedur dar, die keine Argumente erwartet, während SHKProzT auf die Bedürfnisse der Prozedur TsrSetHotkey zugeschnitten wurde.
type OAProzT = procedure;
SHKProzT = procedure ( Keymask:word; ScCode:byte );
PPtrT = record
case integer of
1 : ( OAProz : OAProzT );
2 : ( SHKProz : SHKProzT );
end;
const Call : PPtrT = ( OAProz:TsrCall );
Zusammengefaßt werden diese Typen in einem Varianten-Record, der für jeden Typen einen Eintrag enthält. Sie tragen hier die namen OAProz für OAProzT und SHKProz für SHKProzT. Um die Funktionen aufrufen zu können, die mit diesen Typen verbunden sind, wird eine globale Variable mit dem Namen Call definiert, deren OAProz-Komponente gleich mit einem Zeiger auf die TsrCall-Prozedur initialisiert wird.
Über diese Variable kann die gewünschte Prozedur oder Funktion anschließend aufgerufen werden, sofern ihre Offset-Adresse zuvor an TsrSetPtr übergeben wurde. Dies zeigen auch die folgenden beiden Programmzeilen, in denen zunächst TsrSetHotkey beim Aufruf von TsrSetPtr als die auszurufende Routine festgelegt, und TsrCall anschließend mit den Argumenten für TsrSetHotkey aufgerufen wird.
TsrSetPtr (ofs (TsrSetHotKey));
Call.SHKProz (Keymask, ScCode);
Dies wird möglich, weil der Compiler durch Verwendung der SHKProz-Komponente von Call davon ausgeht, daß tatsächlich eine derartige Prozedur aufgerufen wird. Tatsächlich wird aber TsrCall aufgerufen.
Nach dem Start des Hochsprachen-Programms TSRP.PAS werden zunächst einmal die Parameter aus der Kommandozeile mit Hilfe der Funktion ParamGetHotKey ausgewertet. Sie erkennt als Hotkeys alle Parameter an, die mit dem Präfix \"/t\" beginnen. Dahinter darf der Name einer Umschalttaste (lshift, rshift, alt, ctrl etc.) oder die Nummer einer Taste folgen, die als Scan-Code herangezogen werden soll. Dabei wird eine Zahl im Dezimalformat erwartet.
Zur Auswahl der linken [Shift] Taste, der [Alt]-Taste und der [Leertaste] als Hotkey müssen also folgende Parameter angegeben werden
Als Resultat trägt ParamGetHotkey den gewünschten Status der Umschalttasten und den Scan-Code des Hotkeys in die beiden Variablen KeyMask und ScCode, die ihr zu diesem Zweck übergeben werden.
Wird bei der Auswertung der Kommandozeile ein Fehler entdeckt, wird die Programmausführung mit einer entsprechenden Bildschirmmeldung gestoppt. Andernfalls wird mit Hilfe der Funktion TsrIsInst aus dem Assembler-Modul überprüft, ob das Programm bereits installiet war. Dabei wird auch die Funktionsnummer festgelegt, über die später der MUX-Handler des Programms erreicht werden kann. Über die Konstante I2F_CODE ist bisher die Funktion C4h eingestellt, doch können Sie durchaus eine andere Funktion wählen. Von dieser Möglichkeit sollten Sie auf jeden Fall Gebrauch machen, wenn Sie mehrere TSR-Programme mit Hilfe der Assembler-Schnittstellen entwickeln. Denn sonst besetzen die verschiedenen Programme die gleiche MUX-Funktion und kommen sich dadurch in die Quere.
Doch bei der Auswahl neuer MUX-Funktionsnummern ist Vorsicht geboten, denn zahlreiche Werte werden bereits durch andere Programme verwendet, und Werte kleiner als C0h sollten gar nicht eingesetzt werden.
Zeigt der Aufruf von TsrIsInst, daß das Programm noch nicht installiert war, wird anhand der Variablen KeyMask und ScCode überprüft, ob bei der Auswertung der Kommandozeile /t-Parameter entdeckt wurden. Wenn nicht, weisen die beiden Variablen Default-Werte auf, und als Hotkey wird durch Aufruf der Assembler-Routine TsrSetHotkey die Tastenkombination [Alt]+[H] festgelegt. Andernfalls wird der vom Anwender vorgegebene Hotkey ebenfalls über TsrSetHotkey eingestellt.
Was bleibt, ist dann nur noch der Aufruf der Assembler-Routine TsrInit, die das Programm in ein TSR-Programm verwandelt. Als TSR-Prozedur wird dabei die Hoschsprachen-Routine TSR angegeben, doch dazu gleich mehr.
Hat der Aufruf von TsrIsInst gezeigt, daß das Programm bereits installiert war, hängt das weitere vorgehen vom Anwender ab. Hat er beim Aufruf des Programms einen Hotkey angegeben, wird dieser Hotkey mittels TsrSetHotkey in der bereits installierten Kopie des Programms als neuer Hotkey eingestellt und das Programm anschließend ohne die Reinstallation des TSR-Programms beendet. Wurde jedoch kein neuer Hotkey angegeben, wird durch den Aufruf von TsrCanUninst zunächst überprüft, ob die bereits installierte Kopie des Programms wieder deaktiviert werden kann. Wenn ja, wird anschließend TsrUninst aufgerufen, um das Programm zu deaktivieren.
Zuvor wird in der installierten Kopie des Programms jedoch noch eine Hochsprachen-Routine mit dem Namen EndePrz aufgerufen, die die internen Ressourcen des Programms wieder frei- und eine Bildschirmmeldung ausgibt, aus der die Anzahl der Aktivierungen des TSR-Programms hervorgeht.
Sie bezieht sich dabei auf die globale Variable ATimes, die mit jeder Aktivierung des TSR-Programms inkrementiert wird. Dies geschieht in der TSR-Prozedur des Programms, die den Namen TSR trägt. Hier wird zunächst der Tastaturpuffer geleert, um den Hotkey von dort zu entfernen. Anschließend wird der Bildschirm des unterbrochenen Programms gesichert und der Anwender dann durch Ausgabe einer Bildschirmmeldung zur Betätigung einer beliebigen Taste aufgefordert. Sobald dies geschehen ist, wird wieder der Bildschirm des unterbrochenen Programms zum Vorschein gebracht und die TSR-Prozedur beendet, wodurch auch das unterbrochene Programm wieder zur Ausführung kommt.
|