REFERAT-MenüDeutschGeographieGeschichteChemieBiographienElektronik
 EnglischEpochenFranzösischBiologieInformatikItalienisch
 KunstLateinLiteraturMathematikMusikPhilosophie
 PhysikPolitikPsychologieRechtSonstigeSpanisch
 SportTechnikWirtschaftWirtschaftskunde  



Der Speicheraufbau unter DOS - TSR-Programmierung




Der Speicheraufbau

unter DOS

&

TSR-Programmierung

1. Der Speicheraufbau unter DOS




Der PC kam zunächst mit einer Speicherausstattung von 16 KByte auf den Markt, die man auf der Hauptplatine bis zu 64 KByte ausbauen konnte. Außerdem vertrieb IBM-Speichererweiterungskarten, die jeweils 64 KByte faßten und in einem der fünf Erweiterungssteckplätze plaziert wurden. Maximal drei dieser Karten konnten installiert werden, um den PC auf die damals gigantische Speichermenge von 256 KByte aufzurüsten.


Die Entwickler der PCs wußten jedoch, daß das nicht das Ende der Entwicklung sein würde und legten deshalb ein Speicher-Layout fest, daß den Ausbau des RAM-Speichers bis zu einer Marke von 640 KByte erlaubte. Sie glaubten dabei zukunftsweisend zu sein, wurden von der Zukunft aber nur allzu schnell überholt, wie jeder DOS-Anwender heute weiß.


Neben dem RAM planten sie in dem 1 MByte umfassenden Adreßraum der 8088-CPU Raum für den RAM-Bereich der Video-Karten (den sog. Video-RAM), für das ROM-BIOS und einige ROM-Erweiterungen ein. Ob sich hinter einer Speicherstelle RAM oder ROM befindet, ist für den Prozessor gleichgültig, mit dem einzigen Unterschied, daß sich Speicherbereiche im ROM nun einmal nicht beschreiben lassen. Der Prozessor lehnt auch nicht ab, Speicherstellen anzusprechen, die physikalisch gar nicht vorhanden sind. Denn die Tatsache, daß der Prozessor bis zu 1 MByte Speicher verwalten kann, bedeutet noch lange nicht, daß sich hinter jeder Speicheradresse auch wirklich ein RAM- oder ROM-Baustein verbirgt.


Wie die folgende Tabelle (Abbildung 1) zeigt, erfolgte die Planung des Speicherlayouts auf Basis von 64-KByte-Segmente, denn der 8088 und seine Nachfolger verwalten den Speicher in Blöcken dieser Größe. 16 dieser Blöcke faßt der Adreßraum von 1 MByte.


Die ersten zehn Speichersegmente sind für das Hauptsspeicher-RAM reserviert, wodurch es in seiner Größe auf maximal 640 KByte beschränkt ist. Dem Speichersegment 0 kommt dabei eine besondere Rolle zu, da es wichtige Daten und Routinen des Betriebssystems aufnimmt.


Block

Adresse

Inhalt


F000:0000 - F000:FFFF

BIOS-ROM


E000:0000 - E000:FFFF

frei für ROM-Cartridges


D000:0000 - D000:FFFF

frei für ROM-Cartridges


C000:0000 - C000:FFFF

zusätzliches BIOS-ROM


B000:0000 - B000:FFFF

Video-RAM


A000:0000 - A000:FFFF

zusätzliches Video-RAM (EGA/VGA)


9000:0000 - 9000:FFFF

RAM von 576 KB bis 640 KB


8000:0000 - 8000:FFFF

RAM von 512 KB bis 576 KB


7000:0000 - 7000:FFFF

RAM von 448 KB bis 512 KB


6000:0000 - 6000:FFFF

RAM von 384 KB bis 448 KB


5000:0000 - 5000:FFFF

RAM von 320 KB bis 384 KB


4000:0000 - 4000:FFFF

RAM von 256 KB bis 320 KB


3000:0000 - 3000:FFFF

RAM von 192 KB bis 256 KB


2000:0000 - 2000:FFFF

RAM von 128 KB bis 192 KB


1000:0000 - 1000:FFFF



RAM von 64 KB bis 128 KB


0000:0000 - 0000:FFFF

RAM von 0 KB bis 64 KB


Abbildung 1: Die Aufteilung des PC-RAM-Speichers


Auf den RAM-Speicher folgt das Speichersegment A, das mit einer EGA- und VGA-Grafik-Karte installiert wird. Es dient als Speicher für den Bildschirmaufbau in den verschiedenen Grafikmodi dieser Karten.


Das Speichersegment B ist den monochromen Videokarten MDA und Herclues sowie der Farbgrafikkarte CGA zugeordnet. Beide teilen sich dieses Segment als Speicher für den Bildschirmaufbau, wobei die monochrome Karte die unteren 32 KByte und die Color-Karte die oberen 32 KByte dieses Segments in Anspruch nimmt.


Die Speichersegmente hinter dem Video-RAM werden nicht mehr mit RAM, sondern mit ROM belegt, wobei das C-Segment den Anfang macht. In diesem Segment sind bei einigen Rechnern BIOS-Routinen untergebracht, die nicht Teil des ursprünglichen BIOS-Kerns sind. Beim XT sind dies zum Beispiel die Routinen zur Unterstützung der mit ihm eingeführten Festplatte.


Die Segmente D und E waren ursprünglich für ROM-Cartridges vorgesehen, wie man sie bei Homecomputer- und Telespielen zur Einbringung von Software in das System nutzt. Davon wurde allerdings nie richtig Gebrauch gemacht, so daß dieser Bereich fast immer ungenutzt blieb und heutzutage als zusätzlicher RAM oder für die Einblendung von EMS-Speicher verwendet wird.


Das Segment F enthält schließlich die eigentlichen BIOS-Routinen, den Ur-Lader des Systems, sowie das nur noch bei alten Rechnern vorhandene ROM-BASIC.


Die Hardware des PCs ist nicht an ein bestimmtes Speicherlayout gebunden, schon gar nicht an das von IBM, und trotzdem setzte IBM bereits mit dem ersten PC den Maßstab, der bis heute von allen Anbietern eingehalten wird. DAS ist jedoch hauptsächlich eine Frage der Software, denn es sind das BIOS und das DOS, die sich auf die Lage bestimmter Speicherbereiche (z.B. des Video-RAMs) eingestellt haben. Zementiert wurde dieses Layout zusätzlich durch jedes Programm, das auf den Aufbau des Speichers aus und arbeiten nicht einwandfrei, wenn diese sich als falsch erweisen.



2. TSR-Programmierung


Seit seiner Entwicklung haftet DOS der Makel an, nicht Multitasking-fähig zu sein, also jeweils nur ein Programm ausführen zu können. Erst jetzt, nach mehr als zehn Jahren DOS-Geschichte, stehen mit Windows und OS/2 zwei Betriebssysteme zur Verfügung, die diese Lücke füllen. Einen Hauch von Multitasking haben jedoch seit jeher die sogenannten TSR-Programme (TSR = Terminate and Stay Resident) in die DOS-Welt gebracht. Diese Speicherresidenten Programme schlummern, einmal gestartet, im Hintergrund und warten auf ihre Aktivierung.


Zwar findet auch bei dieser Art von Programmen kein echtes Multitasking statt, werden also nicht mehrere Programme gleichzeitig ausgeführt, doch kann der Anwender das im Hintergrund wartende TSR-Programm jederzeit durch die Betätigung einer bestimmten Tastenkombination, dem sog. 'Hotkey', aktivieren. Das TSR-Programm unterbricht dann die Ausführung des gerade aktiven Programms, merkt sich den Bildschirminhalt und bietet dem Anwender seine Dienste an. Beendet dieser das TSR-Programm anschließend wieder, restauriert es den Bildschirm des unterbrochenen Programms und setzt desen Ausführung fort.


Mit Hilfe von TSR-Programmen kann sich der Anwender per Knopfdruck jederzeit Zugang zu so nützlichen Hilfsmitteln wie Taschenrechner, Kalender oder Notizbuch verschaffen. Manche TSR-Programme sind sogar in der Lage, mit den von ihnen unterbrochenen Programm zu interagieren und Daten zu transferieren. Obwohl die unterschiedlichsten Anwendungen als TSR-Programme implementiert werden können, liegt allen TSR-Anwendungen eine einheitliche Funktionsweise und ein einheitlicher Aufbau zu Grunde.


TSR-Programme sind bereits von ihrer Definition her Programme, die die Konzeption von DOS als Singletask-System ständig unterlaufen. Denn 'Singletask' bedeutet, daß immer nur ein Programm allein auf die Systemressourcen (RAM-Speicher, Bildschirm, Festplatte usw.) zugreift, was eben nicht der Fall ist, wenn neben dem eigentlichen Vordergrund-Programm noch ein TSR-Programm im Hintergrund seine Aufgaben wahrnimmt. Ein TSR-Programm muß es deshalb innerhalb des Systems mit zahlreichen Gegnern aufnehmen, zu denen das BIOS, das DOS, das unterbrochene Programm, aber auch andere TSR-Programme zählen.


Dies zu bewältigen, ist keine ganz einfache, dafür aber eine faszinierende Herausforderung, die jedoch nur in der Assemblersprache realisiert werden kann. Allerdings haftet dieser Sprache der Makel an, für die Programmierung von typischen TSR-Anwendungen wie Taschenrechner oder Notizbüchern, die man einfacher und schneller in Hochsprachen wie Pascal und C programmieren kann, nur wenig geeignet zu sein. Aus diesem Grund erstellen wir ein Assemblerprogramm, mit desen Hilfe ein ganz normales Pascal-Programm ohne großen Aufwand in ein TSR-Programm verwandelt werden kann.



Die Aktivierung von TSR-Programmen


Wenn wir an ein TSR-Programm die Forderung stellen, unmittelbar nach Betätigung seines ganz persönlichen Hotkeys in den Vordergrund zu rücken, müssen wir natürlich eine Art Auslösemechanismus installieren, der mit der Tastatur verbunden ist. Hier bieten sich die Interrupts 09h und 16h an, die beide im Zusammenhang mit der Tastatur aufgerufen werden. Interrupt 16h ist der bekannte BIOS-Tastatur-Interrupt, der Programmen zur Abfrage der Zeichen und des Tastaturstatus dient. Sich in diesen Interrupt einzuklinken und ihn im Hinblick auf die Aktivierung des TSR 'umzubauen', führt jedoch nicht zu dem gewünschten Erfolg. Denn dadurch kann das TSR-Programm nur aktiviert werden, wenn dieser Interrupt durch ein Programm zur Tastaturabfrage aufgerufen wird. Schließlich erhält das TSR-Programm erst dann die Möglichkeit, den Inhalt des Tastaturpuffers zu betrachten und sich in den Vordergrund zu rücken, wenn es darin seinen Hotkey entdeckt.


Besser eignet sich deshalb der Interrupt 09h, der von der Tastatur mit jeder Betätigung einer Taste ausgelöst wird. Es gilt also diesen Interrupt auf eine eigene Routine umzuleiten, die dann bei jedem Tastenanschlag überprüfen kann, ob das TSR-Programm aktiviert werden soll oder nicht. Bevor dies geschieht, sollte die Routine jedoch den Interrupt-Handler aufrufen, der vor der Installation des TSR-Programms für den Interrupt 09h zuständig war. Dafür gibt es zwei wichtige Gründe. Der erste hängt mit der Aufgabe diese Interrupts zusammen. Er soll dem System anzeigen, daß die Tastatur die Aufmerksamkeit des Systems benötigt, um Informationen über die Betätigung oder das Loslassen einer Taste an das System zu übertragen. Konkret heißt das, wenn nicht zunächst die ursprüngliche Routine aufgerufen wird, wären keine Eingaben von der Tastatur mehr möglich.


Der zweite Grund hängt mit der Möglichkeit zusammen, daß vor einem TSR-Programm bereits andere TSR-Programme installiert wurden, die den Interrupt 09h auf eine eigene Routine umgeleitet haben. Da sich das zuletzt gestartete TSR-Programm in der Kette der Interrupt-Handler vor diesen Programmen befindet, werden ihrer Interrupt-Routinen nicht mehr aufgerufen, wenn man auf einen Aufruf des alten Interrupt-Handlers verzichtet. Die Folge: Diese TSR-Programme können nicht mehr aktiviert werden.
















































Abbildung 2: Kette der TSR-Programme am Beispiel des Interrupts 09h



Aus diesem Grund gilt für TSR-Programme, daß beim Aufruf von umgeleiteten Interrupt-Routinen vor oder nach der eigenen Interrupt-Abarbeitung der alte Interrupt-Handler aufgerufen werden sollte. Dieser Aufruf darf dabei jedoch nicht über den Maschinensprachebefehl INT erfolgen, da dadurch womöglich doch wieder nur der letzte Interrupt-Handler aufgerufen würde, der zusammen mit einem TSR-Programm installiert wurde. Dies hätte in den meisten Fällen eine unendliche Rekursion und damit verbunden einen Stacküberlauf zur Folge, was fast zwangsläufig zu einem Absturz des Systems führt. Um dem entgegenzuwirken, muß die Adresse des alten Interrupt-Handlers bei der Installation des TSR-Programms in Erfahrung gebracht und in einer Variablen gespeichert werden. Über diese Adresse kann der alte Interrupt-Handler dann mit Hilfe eines FAR-CALL-Befehls aufgerufen werden. Um jedoch den Aufruf dieses Handlers über den INT-Befehl zu simulieren, muß zuvor der Inhalt des Flag-Registers mittels PUSHF auf den Stack gebracht werden, wie es automatisch beim INT-Befehl geschieht.


Bevor der alte Interrupt-Handler innerhalb des neuen Interrupt-Handlers aufgerufen wird, wird zunächst einmal der Inhalt des Ports 60h ausgelesen. Das ist der Port, an den die Tastatur jeweils den Scan-Code der betätigten Taste anlegt. Auch der später aufgerufene alte Interrupt-Handler wird diesen Port auslesen, was das Programm allerdings nicht beeinträchtigt, weil der Port immer den gleichen Wert zurückliefert, bis eine neue Taste betätigt wurde. Mit dem Inhalt des Tastatur-Ports 60h kann der neue Interrupt-Handler des TSR-Programms feststellen, ob der Anwender einen Teil des Hotkeys betätigt hat, denn ein Hotkey setzt sich in der Regel aus einer Buchstaben- oder Ziffern-Taste in Verbindung mit einer oder mehreren Umschalttasten ([Shift], [Ctrl], [Alt] etc.) zusammen. Trifft der Interrupt-Handler auf den Code der Buchstaben- oder Zifferntasten, muß noch sichergestellt werden, daß der Anwender auch gleichzeitig die Umschalttasten niedergedrückt hält, die zum Hotkey gehören. Deshalb wird der Inhalt des BIOS-Tastatur-Flags inspiziert, das an der Adresse 17h innerhalb des BIOS-Variablensegments (Segmentadresse 0040h) zu finden ist. Die verschiedenen Umschalttasten werden dabei durch jeweils ein Bit repräsentiert. Trifft man dort auf die gewünschte Bit-Maske, versucht der Anwender gerade, das TSR-Programm zu aktivieren. Das TSR-Programm darf sich in diesem Fall aber nicht unter allen Umständen aktivieren, denn dem stehen verschiedene Probleme gegenüber, die unter dem Stichwort 'DOS-Reentranz' zusammengefaßt werden.



DOS ist nicht reentrant


Da das TSR-Programm über die Tastatur jederzeit aktiviert werden kann, ist es möglich, daß es die Ausführung einer gerade aufgerufenen DOS-Funktion unterbricht. Dies muß noch nicht zwangsläufig zu Problemen führen, solange das TSR-Programm nach seiner Beendigung ordnungsgemäß in die unterbrochene DOS-Funktion zurückkehrt. Ein Problem entsteht jedoch dann, wenn das TSR-Programm seinerseits DOS-Funktionen aufruft, was praktisch nicht zu vermeiden ist. Hier tritt das Problem der Reentranz auf.


Dieser Begriff steht für die Fähigkeit eines Systems, seinen Programmcode von mehreren Programmen gleichzeitig aufrufen und ausführen zu lassen. Gerade diese Reentranz ist bei DOS nicht gegeben, da es als Singletask-System davon ausgeht, daß die einzelnen DOS-Funktionen nicht gleichzeitig (parallel), sondern eine nach der anderen (seriell) aufgerufen wird.


Um dem Reentranz-Problem aus dem Weg zu gehen, bleibt einem TSR-Programm nur die Möglichkeit, die Aktivierung erst dann zu erlauben, wenn gerade keine DOS-Funktion ausgeführt wird. Dabei kommt DOS den Entwicklern solcher Programme entgegen, indem es das sogenannte INDOS-Flag bereitstellt. Es handelt sich dabei um einen Zähler, der die Verschachtelungstiefe von DOS-Aufrufen zählt. Enthält er den Wert 0, wird gerade keine DOS-Funktion ausgeführt, während der Wert 1 die aktuelle Ausführung einer DOS-Funktion anzeigt. Wenn eine DOS-Funktion bei ihrer Ausführung eine andere DOS-Funktion aufruft, kann diese Flag auch größere Werte enthalten.


Der Inhalt dieses Flags kann direkt aus dem Speicher ausgelesen werden, da sich seine Adresse nach dem Booten des Systems nicht mehr ändert. Es ist sinnvoll diese Adresse während der Installation des TSR-Programms mit der DOS-Funktion 34h, die nach ihrem Aufruf die Adresse des INDOS-Flags im Registerpaar ES:BX zurückliefert, in einer Variablen zu speichern. Die Abfrage dieses Flags wird nun so in den Interrupt-Handler für den Interrupt 09h eingegliedert, daß er zwar weiterhin zunächst die Betätigung des Hotkeys überprüft, in diesem Fall jedoch die Aktivierung des TSR-Programms nur dann erlaubt, wenn das INDOS-Flag den Wert 0 enthält.


Ein weiteres Problem bringt die Aktivierung des TSR-Programms von der DOS-Oberfläche heraus. Da sich der Befehlsinterpreter des DOS (COMMAND.COM) selbst einiger DOS-Funktionen zur Ausgabe des Prompts und der Entgegennahme von Eingaben durch den Anwender bedient, enthält das INDOS-Flag hier fortwährend den Wert 1. In diesem speziellen Fall wäre die Unterbrechung zwar relativ sicher, doch müßte festgestellt werden, ob das INDOS-Flag den Wert 1 enthält, weil eine DOS-Funktion von einem transienten Programm oder vom DOS-Befehlsinterpreter aufgerufen wird.


Doch auch für diesen Fall gibt es eine Lösung. Sie beruht darauf, daß das DOS in periodischen Abständen den Interrupt 28h aufruft, der für die kurzzeitige Aktivierung von Hintergrundprozessen verantwortlich ist. Wird dieser Interrupt aufgerufen, kann man grundsätzlich davon ausgehen, das DOS unbeschäftigt und es relativ sicher ist, das TSR-Programm zu aktivieren. Dieser Interrupt trägt deshalb auch den Namen DOS-Idle (dt. idle = unbeschäftigt).


Dieses Verhalten ausnutzend, wird beim Start des TSR-Programms ein neuer Handler für den Interrupt 28h installiert. Er ruft zunächst den alten Handler für diesen Interrupt auf und prüft dann, ob der Anwender den Hotkey betätigt hat. Trifft dies zu, kann das TSR-Programm aktiviert werden, auch wenn das INDOS-Flag einen Wert ungleich 0 enthält.


Eine weitere Einschränkung muß hier jedoch insofern gemacht werden, als daß eine Aktivierung des TSR-Programms nur dann erlaubt werden sollte, wenn innerhalb des Systems keine zeitkritischen Aktionen durchgeführt werden.



Zeitkritische Aktionen


Hierunter fallen Aktionen, die aus bestimmten Gründen nicht unterbrochen, also in einer relativ kurzen Zeit beendet werden müssen. Beim PC zählen dazu vor allem die Zugriffe auf Diskette und Festplatte, die auf unterster Ebene über den BIOS-Interrupt 13h gesteuert werden. Wird ein Zugriff auf diese Geräte nicht in kürzester Zeit abgeschlossen, so kann es zu ernsthaften Störungen im Systemablauf kommen. Eine dramatische Situation ergibt sich dann sogar, wenn das TSR-Programm einen Zugriff auf diese Geräte durchführt, während ein anderer Zugriff (ausgelöst durch das unterbrochene Programm) noch nicht abgeschlossen ist. Dies kann, wenn vielleicht auch nicht zum Systemabsturz, so doch auf jeden Fall zu Datenverlust führen.


Vermieden wird dies wiederum durch Installation eines eigenen Interrupt-Handlers für den BIOS-Interrupt 13h. Dieser Handler inkrementiert bei seinem Aufruf ein internes Flag, das anzeigt, daß der BIOS-Disk-Interrupt gerade aktiv ist. Danach ruft er den alten Handler für diesen Interrupt auf, der den Zugriff auf das Diskettenlaufwerk oder die Festplatte durchführt. Kehrt er dann wieder in den Handler des TSR-Programms zurück, dekrementiert dieser das zuvor gesetzte Flag und signalisiert dadurch das Ende der Aktivität des BIOS-Disk-Interrupts.


Um eine Unterbrechung dieses Interrupts zu verhindern, nehmen die anderen Interrupt-Handler innerhalb des TSR-Programms auf dieses Flag insofern Rücksicht, als daß sie das TSR-Programm nur dann aktivieren, wenn aus seinem Inhalt hervorgeht, daß der BIOS-Disk-Interrupt nicht aktiv ist.



Rekursion


Da die Betätigung des Hotkeys nach der Aktivierung des TSR-Programms weiterhin möglich ist, muß vermieden werden, daß das TSR-Programm erneut aktiviert wird, ohne zuvor beendet worden zu sein. Diese Rekursion kann man leicht verhindern, indem auch hier ein Flag installiert wird, das bei der Aktivierung des Programms gesetzt und bei seiner Beendigung wieder gelöscht wird. Stellt einer der Interrupt-Handler bei der Betätigung des Hotkeys mit Hilfe dieses Flags fest, daß das TSR-Programm bereits aktiv ist, so ignoriert er die Betätigung des Hotkeys.


Werden alle genannten Bedingungen erfüllt, so steht der Aktivierung des TSR-Programms nichts mehr im Wege.



Verzögerte Aktivierung


Weil ein TSR-Programm aufgrund der Aktivitäten des DOS oder des BIOS nicht zu jedem Zeitpunkt in den Vordergrund geschaltet wrden kann, installieren die meisten TSR-Programme auch einen Interrupt-Handler für den Timer-Interrupt 08h, der eine Verzögerung der Aktivierung möglich macht. Dazu wird bei der Entdeckung des Hotkeys innerhalb des Tastatur-Interrupt-Handlers ein spezielles Flag gesetzt, wenn die Aktivierung des TSR-Programms gerade nicht möglich ist.




Dieses Flag wird nun innerhalb des neuen Timer-Interrupt-Handlers abgefragt, der 18,2 mal in der Sekunde aufgerufen wird, sofern das Vordergrund-Programm ihn nicht auf eine andere Taktfrequenz eingestellt hat. Wird dabei festgestellt, daß das TSR-Programm tatsächlich auf seine Aktivierung wartet und daß derzeit weder das DOS noch das BIOS aktiv ist, steht der Aktivierung des TSR-Programms nichts mehr im Wege.


Allerdings sollte die Zeitdauer, die zwischen der Betätigung des Hotkeys und der Aktivierung des TSR-Programms vergeht, begrenzt werden. Sonst kann es bei langwierigen DOS-Operationen dazu kommen, daß das TSR-Programm erst mehrere Sekunden nach der Betätigung des Hotkeys aktiviert wird. Fehlt eine zeitliche Begrenzung, weiß der Anwender nie, ob das TSR-Programm den Hotkey nicht erkannt hat oder ob es nur auf eine Möglichkeit wartet, das TSR-Programm zu aktivieren.


Das Flag, das für die verzögerte Aktivierung des TSR-Programms gesetzt wird, dient deshalb gleichzeitig als Zeitmesser, indem es mit jedem Aufruf des Timer-Interrupts dekrementiert wird. Nur, solange es dabei einen Wert größer als 0 aufweist, wird der Versuch zur Aktivierung des TSR-Programms unternommen. Initialisiert der Tastatur-Interrupt-Handler dieses Flag beispielsweise mit 6, muß die Aktivierung innerhalb der nächsten 6 Aufrufe des Timer-Interrupts erfolgen, was einer Zeitdauer von einer drittel Sekunde entspricht. Wenn nicht, bleibt die Betätigung des Hotkeys ohne Wirkung.



Kontextwechsel


Die Abläufe bei der Aktivierung des TSR-Programms bezeichnet man in der Sprache der Informatiker als einen Kontextwechsel. Zum Kontext, oder einfacher gesagt zur Umgebung eines Programms zählen dabei alle Informationen, die zum Betrieb des Programms benötigt werden. Dazu zählen zum Beispiel der Inhalt der Prozessor-Register, wichtige Informationen des Betriebssystems, aber auch der vom Programm belegte Speicher. Um letzteren muß man sich beim Kontextwechsel in TSR-Programmen jedoch nicht kümmern, weil ein TSR-Programm bei seiner Installation als resident markiert wird und der von ihm belegte Speicher vom Betriebssystem deshalb nicht mehr an andere Programme vergeben wird.


Die Prozessor-Register, allen voran die Segmentregister, müssen jedoch mit den Werten geladen werden, die das TSR-Programm bei seiner Ausführung erwartet. Dazu sind sie bei der Installation des TSR-Programms in interne Variablen gespeichert worden und können nun mit Hilfe dieser Variablen wieder restauriert wrden. Da der Inhalt dieser, aber auch der Inhalt aller anderen Register bei der Ausführung des TSR-Programms verändert wird, müssen sie zuvor jedoch gesichert werden, da sie zum Kontext des unterbrochenen Programms gehören und bei seiner Wiederaufnahme nicht verändert worden sein dürfen.


Gleiches gilt für die kontextabhängigen Betriebssysteminformationen, wobei für DOS hier lediglich der PSP (Program Segment Prefix) des Programms und der DTA (Disk Transfer Area) von Bedeutung ist. Die Adressen beider Strukturen müssen bei der Installation des TSR-Programms ermittelt, gespeichert und beim Kontextwechsel zum TSR-Programm dann wieder gesetzt wrden. Nicht vergessen darf man hier natürlich die Adresse des PSP und der DTA des unterbrochenen Programms vor dem Kontextwechsel zum TSR-Programm zu sichern, damit sie nach dessen Beendigung wieder auf ihren ursprünglichen Wert zurückgesetzt werden können.


Während die Adresse des DTA mit Hilfe zweier DOS-Funktionen gesetzt (Funktion 1Ah) und ermittelt (Funktion 2Fh) werden kann, existieren entsprechend dokumentierte Funktonen für den PSP nicht in allen DOS-Versionen. Zwar gibt es ab der DOS-Version 3.0 die dokumentierte Funktion 62h, mit der die Adresse des aktuellen PSP ermittelt werden kann, doch fehlt weiterhin eine Funktion, mit der diese Adresse gesetzt werden kann. Im Bereich der undokumentierten Funktionen finden sich jedoch die benötigten Funktionen bereits seit der DOS-Version 2.0. Es handelt sich dabei um die Funktion 50h (Adresse des PSP sezten) und um die Funktion 51h (Adresse des PSP ermitteln).


Eine letzte Vorsichtsmaßnahme muß in bezug auf die Aktivierung des Programms über den Interrupt 28h getroffen werden. Geht die Aktivierung von diesem Interrupt aus, wird sehr wohl eine aktive DOS-Funktion unterbrochen, deren Stack-Inhalt nicht zerstört werden darf. Aus diesem Grund werden generell die obersten 64 Words vom aktuellen Stack geholt und auf dem Stack des TSR-Programms gesichert. Damit ist der Kontextwechsel zum TSR-Programm vollzogen, und das TSR-Programm kann gestartet werden.


Von diesem Moment an kann das TSR-Programm als ganz normales Programm betrachtet werden, das beliebige DOS- und BIOS-Funktionen aufrufen darf. Von den vielen Gegenspielern innerhalb des Systems ist jetzt nur noch das Vordergrundprogramm übrig, mit dem sich das TSR-Programm insofern arrangieren muß, als daß es seinen Bildschirminhalt nicht zerstören bzw. bei seiner Beendigung nicht verändert hinterlassen darf.




Sicherung des Bildschirmkontextes


Während die bisher beschriebenen Aufgaben der Assembler-Schnittstelle zufallen, gehört die Sicherung des Bildschirmkontextes zur Aufgabe des Hochsprachen-Programms, das das eigentliche TSR-Programm darstellt. Zum Bildschirmkontext zählen der aktuelle Videomodus, die Cursor-Position und der Bildschirminhalt. Darüber hinaus müssen bei Grafikkarten auch die Inhalte der Farbauswahl-Register und anderer Register der Video-Karte gesichert werden, sofern hier Veränderungen vorgenommen werden.


Der aktuelle Viedeomodus kann leicht mit Hilfe der Funktion 00h des BIOS-Video-Interrupts 16h abgefragt werden. Stellt sich dabei heraus, daß sich der Bildschirm im Textmodus befindet (diese Modi tragen die Nummern 0, 1, 2, 3 und 7), muß lediglich die erste Textseite aus dem Video-RAM durch das TSR-Programm gesichert werden. Hierzu kann es sich ebenfalls des Video-BIOS bedienen oder direkt auf das Video-RAM zugreifen. Ergibt die Abfrage des Videomodus hingegen, daß ein Grafikmodus aktiv ist, wird die Sicherung des Videomodus schon aufgrund der Tatsache sehr kompliziert, daß das Video-RAM bei EGA- und VGA-Karten in bestimmten Videomodi eine Größe von bis zu 256 KByte erreichen kann. Hat das TSR-Programm dann ein transientes Programm bei seiner Arbeit unterbrochen, dürfte es kaum möglich sein, einen Puffer von derartiger Größe zu allokieren.


Aus diesem Grund verzichten viele TSR-Programme auf ihre Aktivierung innerhalb eines Grafikmodus und lassen sich nur innerhalb der verschiedenen Textmodi starten. Da unter DOS meistens im Textmodus gearbeitet wird, läßt sich mit diesem Makel in der Regel gut leben. Eine Ausnahme bilden hier jedoch die grafischen Benutzeroberflächen, allen voran Windows, die ausschließlich im Grafikmodus arbeiten. Da Windows jedoch eigene Mechanismen zur parallelen Ausführung von Programmen bzw. zur Einbindung von Taschenrechnern, Notizbüchern etc. anbietet, ist der Einsatz von TSR-Programmen hier ohnehin wenig sinnvoll.



Die Funktionen der Assembler-Schnittstelle


Die Assembler-Schnittstelle bietet ihrem Aufrufer die Möglichkeit, das TSR-Programm bei seinem ersten Aufruf von der DOS-Ebene aus zu installieren und es bei einem erneuten Aufruf wieder zu reinstallieren. Darüber hinaus bietet die Assembler-Schnittstelle ihrem TSR-Programm bei einem Aufruf von der Kommandozeile aus die Möglichkeit, mit einer bereits im Speicher installierten Kopie ihrer selbst in Verbindung zu treten. Dadurch besteht z.B. die Möglichkeit, einen neuen Hotkey einzustellen, ohne das Programm aus dem Speicher zu entfernen und dann wieder installieren zu müssen. Aber auch andere Parameter lassen sich auf diese Art und Weise einrichten, denn es kann jede beliebige Pascal-Routine in dem bereits installierten TSR-Programm aufgerufen werden.


Um die genannten Mechanismen zu unterstützen, bietet die Assembler-Schnittstelle dem Hochsprachenprogramm sieben Routinen an, die in der folgenden Tabelle (Abbildung 3) aufgeführt werden.



Name

Aufgabe

TsrInit

Verwandelt das Programm in ein TSR-Programm, installiert die Interrupt-Handler, beendet es und verankert es dabei resident im Speicher.

TsrIsInst

Stellt fest, ob bereits eine Kopie des Programms im Speicher installiert ist.

TsrCanUninst

Stellt fest, ob die bereits installierte Kopie wieder entfernt werden kann.

TsrUnInst

Entfernt eine zuvor installierte Kopie des Programms wieder aus dem Speicher.

TsrSetPtr

Hält die Adresse der Routine fest, die in der bereits installierten Kopie des Programms aufgerufen werden soll.

TsrCall

Ruft die beim Aufruf von TsrSetPtr angegebene Routine auf.

TsrSetHotkey

Stellt den Hotkey des Programms ein.


Abbildung 3: Die Routinen der Assembler-Schnittstelle



Abfrage des Installations-Status


Von den genannten Routinen sollte das Hochsprachen-Programm zunächst die Routine TsrIsInst aufrufen, denn mit ihrer Hilfe läßt sich feststellen, ob bereits eine Kopie des Programms als TSR-Programm im Speicher verankert wurde. Die Routine bedient sich dabei einer Funktion des DOS-Multiplexer-Interrupts 2Fh (MUX). Der Interrupt-Handler für den Interrupt 2Fh reagiert jedoch nur auf eine ganz bestimmte Funktion, deren Funktionsnummer beim Aufruf von TsrIsInst festgelegt wird. Wird dieser Interrupt mit einer anderen Funktionsnummer aufgerufen, leitet das TSR-Programm diesen Aufruf einfach an den alten Handler weiter. Der neue MUX-Interrupt-Handler unterstützt zwei Unterfunktionen mit den Nummern AAh und BBh. Erstere hilft beim Auffinden einer bereits installierten Kopie.


Wie bei MUX-Funktionen üblich, vertauscht sie bei ihrem Aufruf dazu einfach die Funktions- und Unterfunktionsnummern im Registerpaar AH/AL. Ist das TSR-Programm nicht installiert, unterbleibt dieser Tausch, da sich keiner der bisherigen MUX-Handler für diese Funktion verantwortlich fühlt und den Inhalt des AX-Registers deshalb unverändert zurückgibt.


Ist das Programm bereits installiert, wird innerhalb von TsrIsInst dann auch gleich die zweite MUX-Funktion aufgerufen, die die Segmentadresse des TSR-Programms im Speicher zurückliefert. Dieser Wert wird, wie alle anderen Variablen des Assembler-Moduls, im Codesegment des Moduls untergebracht. Dies bietet die Gewähr, daß diese Variablen auch innerhalb der Interrupt-Handler angesprochen werden können, wenn das Datensegment des Programms nicht erreichbar ist.



Einstellung des Hotkeys und Installation


Stellt das Programm mit Hilfe von TsrIsInst fest, daß es noch nicht installiert war, wird in der Regel die Installation folgen. Dabei handelt es sich um einen zweistufigen Prozeß, in dessen Verlauf zunächst der Hotkey des Programms mit Hilfe von TsrSetHotkey eingestellt wird. Anschließend wird das Programm mit Hilfe von TsrInit als TSR-Programm im Speicher verankert und seine Ausführung zunächst beendet.

Diese TsrSetHotkey-Routine erwartet als Argument die beiden Parameter, die den Hotkey bestimmen: Die Bit-Maske für die Steuertasten und den Scan-Code der zugehörigen Buchstaben- oder Zifferntaste. Beide Parameter können mit Hilfe zahlreicher Konstanten konstruiert werden, die sich am Anfang des Pascal-Programms finden.


Für die Umschalttasten tragen diese Parameter die Namen LSHIFT (linke Schift-Taste), RSHIFT (rechte Shift-Taste), ALT, CTRL etc. Mehrere dieser Konstanten können durch ein binäres ODER miteinander verknüpft werden, wenn mehrere Umschalttasten gleichzeitig betätigt werden müssen, um das TSR-Programm zu aktivieren. Das binäre ODER entspricht auf der Anwender-Seite also einem logischen UND.


Die verschiedenen Konstanten für die Scan-Codes der einzelnen Tasten beginnen alle mit dem Präfix SC_, auf den dann der Buchstabe oder die Ziffer bzw. deren Name folgt. (z.B. SC_5, SC_X oder SC_SPACE). Soll das Programm lediglich durch Betätigung einer oder mehrerer Umschalttasten aktiviert werden können, ohne daß dazu eine Buchstaben- oder Zahlentaste betätigt werden muß, können Sie sich der Konstante SC_NOKEY bedienen.


Nach der Einstellung der Hotkeys wird das Programm durch den Aufruf von TsrInit in ein TSR-Programm verwandelt. TrsInit erwartet von seinem Aufrufer dabei zwei Parameter: Die Offsetadresse der eigentlichen TSR-Routine und eine Information, die über den Speicherbedarf des Programms Auskunft gibt.


Aufgabe von TsrInit ist es, zunächst die Adressen der Interrupt-Handler 08h, 09h, 13h, 28h und 2Fh zu ermitteln und zu speichern. Danach werden die Daten ermittelt, die zum Kontext des Hochsprachen-Programms gehören. Auch sie werden in Variablen innerhalb des Codesegments gespeichert, damit sie später für die Interrupt-Handler und zur Aktivierung des TSR-Programms zugänglich sind. Im nächsten Schritt werden die neuen Interrupt-Handler installiert. Als Funktionsnummer für den MUX-Interrupt 2Fh wird dabei die Funktionsnummer eingesetzt, die beim vorhergehenden Aufruf von TsrIsInst angegeben wurde.


Bevor das Programm dann über die DOS-Funktion 31h als residentes Programm im Speicher verankert werden kann, muß die Größe des Programms und damit die Anzahl der Paragraphen berechnet werden, die nach der Programmbeendigung, resident im Speicher verbleiben sollen. Die Installation ist damit abgeschlossen, und das Programm wird resident beendet.



Entfernung aus dem Speicher


TSR-Programme arbeiten oft nach der Logik: Beim ersten Aufruf installieren, beim nächsten  wieder aus dem Speicher entfernen. Wurde nach dem Aufruf von TsrIsInst am Anfang des Programms daher festgestellt, daß sich bereits eine Kopie des Programms resident im Speicher befindet, wird man diese Kopie in der Regel wieder deaktivieren wollen.


Dazu muß zunächst einmal die Funktion TsrCanUnist aufgerufen werden. Sie stellt fest, ob das Programm überhaupt reinstalliert werden kann, denn nicht immer ist dies möglich. Das gilt vor allem, wenn nach der Installation des Programms noch ein anderes TSR-Programm installiert wurde, das die Interrupt-Vektoren des Timers, der Tastatur etc. ebenfalls umleitet. Denn auch dieses Programm wird bei seiner Installation die Adressen der bisherigen Handler ermittelt haben und diese innerhalb seiner eigenen Interrupt-Handler anspringen. Bei den bisherigen Handlern handelt es sich aber um die Handler des zu deinstallierenden TSR-Programms, die damit aus dem Speicher entfernt würden. Da es jedoch leider keine Möglichkeit gibt, das andere TSR-Programm von der Entfernung dieser Handler in Kenntnis zu setzen, werden sie weiterhin aufgerufen, was nur eines zur Folge haben kann: den Absturz des Systems.




TsrCanUninst überprüft deshalb, ob noch alle umgeleiteten Interrupts direkt auf die Interrup-Handler der bereits installierten Kopie des TSR-Programms verweisen und liefert dementsprchend TRUE oder FALSE zurück. Nur bei TRUE darf das Programm anschließend TsrUninst aufrufen, um die installierte Kopie wieder aus dem Speicher zu entfernen.


Dabei werden zunächst wieder die alten Interrupt-Handler für die Interrupts 08h, 09h, 13h, 28h und 2Fh installiert. Anschließend wird der Speicher des Programms wieder freigegeben, damit er von DOS wieder an andere Programme verteilt wreden kann, Das Programm hinterläßt dadurch keinerlei Spuren im Speicher.



Aufruf von Routinen im installierten TSR


Spätestens bei der Reinstallation eines TSR-Programms wird man von der Möglichkeit Gebrauch machen müssen, Routinen innerhalb der installierten Kopie aufzurufen. Denn auch der Hochsprachen-Teil eines TSR wird oftmals Betriebsmittel (Speicher, Interrupt-Vektoren, Dateien) für sich in Anspruch nehmen, die bei seiner Entfernung aus dem Speicher wieder an das Betriebssystem zurückgegeben werden müssen.


Da sich die Segmentadresse der installierten Kopie problemlos über den MUX-Handler ermitteln läßt und die Offsetadresse der jeweiligen Routine die gleiche ist, wie in der gerade ausgeführten Kopie des Programms, ließe sich leicht ein FAR-Ziger auf die Routine in der installierten Kopie des Programms konstruieren, über den die gewünschte Routine aufgerufen werden kann. Voraussetzung dafür wäre lediglich, daß die aufgerufene Routine vom Typ FAR ist, aber auch das ließe sich bewerkstelligen.


Die ganze Sache hat jedoch einen großen Hacken, denn durch den direkten Aufruf  der Routine findet kein Kontextwechesl zu der installierten Kopie des Programms im Speicher statt. Es bleibe also weiterhin das Datensegment der aktuell ausgeführten Kopie aktiv, genau wie deren PSP und DTA. Dadurch bliebe der aufgerufenen Routine aber der Zugriff auf ihre Variablen im Datensegment versperrt, denn sie würde auf die Variablen in der gerade aufgerufenen Kopie des Programms zugreifen.


Dashalb muß man sich für den Aufruf einer Routine in der bereits installierten Kopie des Programms eines Mittlers bedienen, der vor dem Aufruf der gewünschten Routine den Kontextwechsel zur installierten Kopie ausführt und auch den anschließenden Kontextwechsel zurück zum gerade ausgeführten Programm übernimmt.


Eine solche Routine würde als Argument lediglich die Offsetadresse der auszuführenden Routine in der installierten Kopie des TSR benötigen. Das funktioniert zwar, ist jedoch nicht besonders komfortabel, weil dabei keine Argumente an die aufzurufende Routine übergeben werden können und auch die Entgegennahme eines Rückgabewertes nicht möglich ist.


Um aber auch die Übergabe von Parametern und die Entgegennahme eines Funktionsergebnisses möglich zu machen, wurde für den Aufruf von Routinen in der bereits installierten Kopie des TSR-Programms ein anderer Weg gewählt, der in den zwei Routinen der Assembler-Schnittstelle involviert sind: TsrSetPtr und TsrCall.


Von diesen beiden Routinen muß zunächst TsrSetPtr aufgerufen werden, welche die Adresse der aufzurufenden Routine entgegennimmt und sie in einer Variablen der Assembler-Schnittstelle speichert. Anschließend erfolgt der Aufruf der Routine TsrCall, die den Kontextwechsel durchführt und sich der zuvor gepeicherten Adresse bedient, um die gewünschte Routine aufzurufen.


Das Problem dabei ist nur, das TsrCall natürlich innerhalb des Hochsprachen-Moduls deklariert und dabei auch die verschiedenen Parameter aufgeführt werden müssen, die über TsrCall an die jeweilige Routine übergeben werden sollen. Die Anzahl der Parameter und ihr Typ hängt von der auszuführenden Routine ab und variiert.



Das Hochsprachen-Programm


Turbo Pascal unterstützt nur ein Speichermodell, das von seinem Aufbau her der Implementierung von TSR-Programmen entgegenkommt.




























Abbildung 4: Speicheraufbau eines Pascal-Programms unter Turbo Pascal ab Version 4.0



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




Alle Angaben beziehen sich auf ein Byte, so daß die Direktive




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:


/tlshift /talt /t57


wobei die Reihenfolge der Parameter keine Bedeutung hat.


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.



Das Beispielprogramm


Das Hauptprogramm trägt den Namen TSRP.PAS, die dazugehörigen Assembler-Routinen sind im Programm TSRPA.ASM zu finden.



Noch ein paar Tips zum Schluß


Es empfiehlt sich für die Entwicklung von TSR-Programmen einige besondere Vorgehensweisen, die mit dem speziellen Charakter dieser Programme zusammenhängen. Zunächst sollte das Programm als ganz normales Programm entwickelt werden, das von der DOS-Oberfläche oder aus einer integrierten Umgebung kompiliert und zur Ausführung gebracht wird.


Um bei der Umwandlung in ein TSR-Programm möglichst wenige Umstellungen vornehmen zu müssen, kann man dabei bereits eine Initialisierungsroutine und die eigentliche TSR-Routine entwickeln, die später bei der Betätigung des Hotkeys aufgerufen werden soll. Im Gegensatz zur TSR-Version läßt man diese Routinen jedoch innerhalb der Hauptprozedur (bzw. Funktion) des Programms explizit aufrufen, macht ihre Aktivierung also nicht von der Betätigung des Hotkeys abhängig. Auf diese Art und Weise sollte das Programm zunächst komplett entwickelt und ausgetestet werden. Erst wenn es sich als fehlerfrei erwiesen hat, sollte die Umwandlung in ein TSR-Programm erfolgen, da nach dieser Umwandlung das Aufspüren von Fehlern (auch mit Hilfe eines Debuggers) kaum mehr möglich ist.


Die Umwandlung in ein TSR-Programm selbst ist relativ simpel, da dazu lediglich die Assembler-Schnittstelle in das Programm eingebunden und die entsprechenden Funktionen aus dem Assembler-Modul aufgerufen werden.











Haupt | Fügen Sie Referat | Kontakt | Impressum | Nutzungsbedingungen







Neu artikel