Der speicheraufbau unter dos & tsr-programmierung
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
15
F000:0000 - F000:FFFF
BIOS-ROM
14
E000:0000 - E000:FFFF
frei für ROM-Cartridges
13
D000:0000 - D000:FFFF
frei für ROM-Cartridges
12
C000:0000 - C000:FFFF
zusätzliches BIOS-ROM
11
B000:0000 - B000:FFFF
Video-RAM
10
A000:0000 - A000:FFFF
zusätzliches Video-RAM
(EGA/VGA)
9
9000:0000 - 9000:FFFF
RAM von 576 KB bis 640 KB
8
8000:0000 - 8000:FFFF
RAM von 512 KB bis 576 KB
7
7000:0000 - 7000:FFFF
RAM von 448 KB bis 512 KB
6
6000:0000 - 6000:FFFF
RAM von 384 KB bis 448 KB
5
5000:0000 - 5000:FFFF
RAM von 320 KB bis 384 KB
4
4000:0000 - 4000:FFFF
RAM von 256 KB bis 320 KB
3
3000:0000 - 3000:FFFF
RAM von 192 KB bis 256 KB
2
2000:0000 - 2000:FFFF
RAM von 128 KB bis 192 KB
1
1000:0000 - 1000:FFFF
RAM von 64 KB bis 128 KB
0
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
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.
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.
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.
Anmerkungen: |
| impressum | datenschutz
© Copyright Artikelpedia.com