Per Anhalter durch das Betriebssystem: Einblick in die System - Variablen, Teil 1

Die Reise durchs Betriebssystem führt uns zu den reichhaltigen Funktionen des GEM und TOS sowie den Datenstrukturen. Im ersten Teil stoßen wir auf die System-Variablen, in die das Betriebssystem Informationen ablegt und durch die wir das System fast beliebig konfigurieren können.

Wie jedes Programm, so benutzt auch das Betriebssystem Variablen, um sich Adressen und Flags zu merken. Alle Variablen liegen im unteren Speicherbereich ab $380. Dabei gibt es zwei Kategorien: Die offiziellen, von Atari veröffentlichten Variablen haben eine feste Adresse. Die inoffiziellen wurden zum Teil von Hackern herausgefunden, kamen aber auch teils direkt von Atari. Ihre Adressen sind von der TOS-Version abhängig und somit am besten zu meiden.
Die offiziellen Systemvariablen liegen im Bereich zwischen $380 (896) und $800 (2048). Eine Liste finden Sie z.B. im Profibuch des Sybex-Verlags. Bevor wir sie in unseren Programmen einsetzen können, müssen wir eine Hürde meistern: Der 68000-Prozessor erlaubt den Zugriff auf den Speicherbereich zwischen 0 und $800 nur im sogenannten Supervisor-Modus. Dies ist ein privilegierter Betriebsmodus, in den wir über zwei Betriebssystem-Funktionen wechseln. Einerseits können wir der XBIOS-Funktion 38 (»Supexec«) die Adresse einer im Supervisor-Modus auszuführenden Routine übergeben. Die Alternative steht uns mit der GEMDOS-Funktion 32 (»Super«) zur Verfügung. Mit ihr schalten wir zwischen dem User- und dem Supervisor-Modus hin und her. In GFA-Basic gibt es die Befehle »SPEEK« und »SPOKE«, die im Supervisor-Modus Daten auslesen bzw. schreiben und somit für unsere Zwecke geeignet sind. Listing 1 und 2 zeigen die Anwendung beider Funktionen in Assembler bzw. C.

Post mortem-Informationen

Im Speicherbereich $380 bis $400 legt das Betriebssystem nach einem Absturz für Assembler-Programmierer wichtige Daten ab, bevor es die Bomben auf den Bildschirm schreibt. So bestimmt die Variable »proc_lives« ($380.L), ob die folgenden Daten gültig sind. Wenn ja, dann enthält sie den magischen Wert $12345678. Das ».L« hinter der Adresse $380 besagt daß die Variable ein Langwort (4 Byte) umfaßt. Entsprechend gelten die Kürzel für Byte- (».B«) bzw. \Nord-Länge (».W«). Im Langwort-Array »proc_regs« ($384 bis $3c3) legt das Betriebssystem nach einem Absturz die Register D0 bis D7, A0 bis A6 und den Supervisor-Stapelzeiger (SSP) ab. »proc_pc« ($3c4.L) enthält den Programmzähler, in dessen höchstwertigem Byte die Nummer der auslösenden Exception liegt. »proc_usp« ($3c8.L) speichert den User-Stapelzeiger (USP) und das Wort-Array »proc_stk« ($3cc bis $3ff) die obersten sechs Worte des Stapels.

»Daten auf Disk A defekt...«

Eine sehr interessante System-Variable namens »etv_critic« (»Critical Error Handler«) liegt in $404.L. »etv_critic« enthält die Adresse einer Routine, die das Betriebssystem beim Auftreten von Diskettenfehlern aufruft. Diese Routine gibt z.B. die Meldung »Diskette schreibgeschützt ... « aus.
Hat Ihr Programm einen Diskettenfehler festgestellt, so können Sie über die »etv_critic«-Routine eine entsprechende Meldung auf dem Bildschirm erzwingen. Dazu übergeben Sie zwei Wort-Parameter auf dem Stack: die Kennung des fehlerhaften Laufwerks und eine BIOS-Fehlermeldung. Die Laufwerkskennung beginnt mit 0 (A:), und die BIOS-Fehlermeldungen entsprechen denen der XBIOS-Funktion 8 (»Floprd «): - 13 bedeutet etwa »Diskette schreibgeschützt« und -2 »Laufwerk antwortet nicht«. Als Ergebnis bekommen Sie im Datenregister D0 entweder eine 0 für »Abbruch« oder $10000 (65536) für »Weiter« zurück. Listing 3 zeigt, wie Sie »etv_critic« in Assembler aufrufen.

Speicherkontrolle

Durch sechs System-Variablen informiert sich das Betriebssystem, wieviel Speicher zur Verfügung steht. Enthält die Variable »memvalid« ($420.L) den »magischen« Wert $752019f3, »memvalid2« ($43a.L) den Wert $237698aa und »memvalid3« ($51a.L) den Wert $5555aaaa, so ist der Inhalt von »phystop« ($42e.L) gültig. In »phystop« wiederum ist das physikalische Ende des RAM festgehalten.
»_membot« ($432.L) und »_memtop« ($436.L) bestimmen Anfang und Ende des Benutzerspeichers (TPA, »transient program area«). Beide Werte ermittelt das Betriebssystem während des Startvorgangs in Abhängigkeit von »phystop«. »_membot« liegt normalerweise im Bereich zwischen $10000 und $20000.
Stellen Sie sicher, daß »memvalid«, »memvalid2« und »memvalid3« ihre magischen Werte besitzen, so können Sie etwa einen kleineren ST simulieren. Sie müssen lediglich »phystop« entsprechend reduzieren und einen Warmstart des Betriebssystems (Reset) durchführen (Listing 4).
Über die beiden System-Variablen »resvalid« ($426.L) und »resvector« ($42a.L) installieren Sie problemlos eine neue Reset-Routine. Diese ruft das Betriebssystem u.a. dann auf, wenn der Anwender den Reset-Knopf betätigt. In »resvector« steht dabei die aufzurufende Routine. Diese ist allerdings nur gültig, wenn »resvalid« den magischen Wert $31415926 enthält. Die neue Routine kann einerseits den Reset komplett abfangen und etwa eine Bildschirmgrafik anzeigen. Sie kann andererseits auch wichtige Dinge erledigen und anschließend wieder mit der normalen Reset-Routine weitermachen (siehe auch Listing 5). Problem: Die Rücksprungadresse in Adressregister 0(A6) zeigt bei einer TOS-Version auf eine falsche Adresse, die den Wert $9bcd (sub.l a5, a5) enthält. In diesem Fall sieht die korrekte Adresse in $24(A6).

# Massenspeicher-Informationen

Wenn Sie den Laufwerks-Controller (FDC) direkt programmieren, sollten Sie die System-Variable »flock« ($43e.W, Floppy Lock) auf einen Wert ungleich 0 setzen. Dadurch signalisieren Sie dem Betriebssystem, daß gerade Laufwerks-Zugriffe stattfinden. Ist der Wert dagegen 0, so initialisiert das Betriebssystem 72 mal pro Sekunde den FDC. »seekrate« ($440.W) enthält die sog. Steprate der Diskettenlaufwerke A und B. Die Steprate ist die Zeit, die der Schreib/Lesekopf benötigt, um sich eine Spur zu bewegen. »seekrate« nimmt vier Werte an: 0 (6 ms), 1 (12 ms), 2 (2 ms) und 3 (3 ms). Das Betriebssystem beschreibt die Variable mit den Wert 3, also 6 ms. Besitzer von 5.25 Zoll-Laufwerken mit 40 Track sollten »seekrate« auf 1 setzen.
Das Betriebssystem greift allerdings nicht direkt auf »seekrate« zu, sondern verwendet inoffizielle Variablen. Damit es den neuen Wert anerkennt, müssen Sie noch die Massenspeicher-Initialisierungsroutine aufrufen. Die Adresse der Routine finden Sie in der Variablen »hdv_init« ($46a.L). Listing 6 zeigt die Vorgehensweise. Beachten Sie, daß es ab TOS 1.4 eine neue Betriebssystem-Funktion namens »Floprate« (XBIOS 41) gibt, die zwei Wort-Parameter erwartet: das zu konfigurierende Laufwerk (0 oder 1) und die neue Steprate. Als Ergebnis erhalten Sie die alte Steprate zurück.
Die Systemvariablen: »_fverify« ($444.W) dient dem Betriebssystem als Flag, ob es nach Laufwerks-Schreibzugriffen eine Überprüfung (engl »Verify«) durchführen soll. Normalerweise enthält »_fverify« den Wert $ff00, d.h. das Verify ist aktiv. Setzen Sie die Variable auf 0, so beschleunigen Sie zwar sämtliche Schreibzugriffe um ca. 40 bis 50 Prozent, gefährden jedoch die Datensicherheit.
Direkt anschließend, in $446.w, liegt die Variable »bootdev«. Sie gibt Ihnen darüber Auskunft, von welchem Laufwerk das Betriebssystem gebootet hat, falls Sie nach einem Reset nicht verändert wurde. Zudem bootet der ST beim nächsten Reset vom hier angegebenen Laufwerk. Der Wert 0 steht für A: und 2 entsprechend für C:.
Die Variable »hdv_init« ($46a.L) haben wir bei der Stepraten-Einstellung bereits angesprochen: Sie enthält die Adresse einer Routine, die prüft, welche Laufwerke vorhanden sind, und initialisiert diese. Außerdem setzt »hdv_init« auch die Stepraten entsprechend der Variablen »seekrate«.
Zwei weitere, vor allem etwa für Anti-Viren-Programmierer interessante Variablen sind »hdv_boot« ($47a.L) und »_dskbufp« ($4c6.L). »hdv_boot« zeigt auf eine Routine, die den Boot-Sektor von Laufwerk A oder B lädt und im Datenregister D0 das Ergebnis zurückgibt: 0 (Bootsektor ist ausführbar), 1 (Kann Boot-Sektor nicht laden; allgemeiner Fehler), 2 (Kein gültiges Laufwerk), 3 (Boot-Sektor nicht lesbar) oder 4 (Nicht ausführbarer Boot-Sektor). Das zu testende Laufwerk bestimmen Sie mit »_bootdev« ($446.W). Den Boot-Sektor schreibt das Betriebssystem in einen TOS-internen Datenpuffer, dessen Adresse Sie in »_dskbufp« finden.
»_nflops« ($4a6.W) und »_drvbits« ($4c2.L) geben Ihnen schließlich Auskunft über die angeschlossenen Massenspeicher. »_nflops« enthält die Anzahl der angeschlossenen Laufwerke (1 oder 2). In »_drvbits« beherbergt das Betriebssystem ein Langwort (32 Bit), in dem jedes Bit ein Laufwerk repräsentiert: Ist Bit 0 eins, so ist Laufwerk A verfügbar, ist Bit 3 eins, so gibt es ein Laufwerk D usw. Im nächsten Teil dieser Serie gehen wir auf die restlichen offiziellen und inoffiziellen System-Variablen ein. Dabei entdecken wir bereits viele der wichtigsten Datenstrukturen des Betriebssystems. (ah)

Listing 1: In den Supervisor-Modus durch die XBIOS-Funktion 38 (Supexec)

/* Superexec in (Turbo) C */
#include <tos.h>
...
Supexec (MyProc);	/* MyProc aufrufen */

/* Wird im Supervisor-Modus ausgeführt */
MyProc ()
{
	*((int *) 0x444L) = 0;
}

Listing 2: In den Supervisor-Modus durch die GEMDOS-Funktion 32 (Super)

; in Assembler:
	clr.l	-(sp)
	move.w	#$20, -(sp)	; in Supervisor schalten
	trap	#1
	add.l	#6, sp
	clr.w	$444	; Sys.Variable »fverify«
	move.l	d0, (-sp)
	move.w	#$20, -(sp)
	trap	#1	; in Usermode zurück
	addq.l	#6, sp

/* und das ganze in C: */
#include <tos.h>
...
{
	long spvi_stack = Super (0);
	* ((int *) 0x444L) = 0;
	Super (spvi_stack);
}

Listing 3: Durch »etv_critic« geben Sie jederzeit Diskettenfehlermeldungen auf dem Bildschirm aus (Assembler)

	move.w	#0, -(sp)	; Laufwerk A:
	move.w	#-13, -(sp)	; Schreibgeschützt!
	move.q	#-1, d0
	move.l	$404, a0	; etv_critic auslesen
	jsr	(a0)	; die Routine Anspringen
	addq.l	#4, sp	; Stack aufräumen
	tst.w	d0
	beq	abbruch	; d0 = 0 -> Abbruch
	bra	weiter	; d0 = $10000 -> Weiter

Listing 4: Von nun an bis zum Ausschalten besitzt ihr ST nur noch 512 kByte (GfA-Basic)

SLPOKE $42E, $80000	! $80000 = 512 kByte
SLPOKE $420, $752019F3	! magic 1
SLPOKE $43A, $237698AA	! magic 2
SLPOKE $51A, $5555AAAA	! magic 3
a% = SLPEEK (4)	! RESET-Adresse
CALL a%	! Warmstart ausführen

Listing 5: Auch eigene Routinen lassen sich in die RESET-Routine einklinken (Assembler)

	move.l	#$31415926, $426
	move.l	#myreset, $42A
	...
	; während dem RESET soll eine kurze
	; Zeit der Bildschirm Blinken
myreset:
	move.w	#10000, d0	; kleine Schleife
loop:
	not.w	$ff8240	; Bildschirm invertieren
	dbf	d0, loop
	cmpa.l	$9bcd, (a6)	; Adresse prüfen
	beq.s	error_tos	; fehlerhaftes TOS
	jmp	(a6)	; Reset
error-tos:
	jmp	#24 (a6)	; Reset

Listing 6: Ändern der Steprate der Diskettenlautwerke auf 12 ms (GFA-Basic)

SWPOKE $440,1	! Seekrate - 12 ms
a% = SLPEEK ($46A)	! hdv_init
CALL a%	! Laufwerk-Initialisierung

Martin Backschat
Aus: TOS 02 / 1991, Seite 86

Links

Copyright-Bestimmungen: siehe Über diese Seite