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

Auf unserer Rundreise durch das Betriebssystem befassen wir uns zum letzten Mal mit den Systemvariablen. Dabei lernen Sie unter anderem alle Variablen für die Verwaltung der Video-Hardware und der Massenspeicher kennen.

Die Systemvariable »palmode« ($448.W) repräsentiert die Bildwiederholfrequenz in den farbigen Auflösungen. Der Wert 0 steht für 50 Hz, der Wert 1 für 60 Hz. In $44c.W liegt die Systemvariable »sshiftmd«. Sie enthält die aktuelle Videoauflösung. Die Werte entsprechen der XBIOS-Funktion »Getrez«. Beachten Sie in diesem Zusammenhang, daß Computer wie der Atari TT mehr Auflösungen besitzen und sich somit der Wertebereich erweitert. »Saubere« GEM-Programme sollten deshalb eine andere Methode benutzen, um die Auflösung in Frfahrung zu bringen: Nach Öffnung einer Virtual Workstation (VDI-Funktion »v_opnvwk«) erhalten Sie in intout[0], intout[1] und intout[13]/intout[39] die Rasterbreite, Rasterhöhe und Anzahl der Farben des geöffneten Gerätes zurück.

In welcher Auflösung befinden Sie sich, wenn Sie von Monochrom nach Farbe umschalten? Diese Frage klärt die Systemvariable »defshiftmod« ($44a.W). Bei 0 befinden Sie sich nach dem Umschalten in der niedrigsten, bei 1 in der mittleren Auflösung.

Damit Sie und das Betriebssystem auch ohne langwierige XBIOS-Aufrufe stets wissen, wo eigentlich der Bildschirmspeicher liegt, hält sich TOS die Systemvariable »_v_bas_ad« ($44e.L). Genau genommen enthält »_v_bas_ad« die logische Bildschirmbasis. Dorthin erfolgen alle Grafikausgaben. Daneben existiert noch die physikalische Bildschirmbasis; dies ist die Adresse, welche die Video-Hardware tatsächlich darstellt. Vorteil: Der physikalische ist sichtbar, während der logische Bildschirm gerade aufgebaut wird. Somit erhalten Sie flimmerfreie Animationen, wie es in vielen Spielen üblich ist. Die physikalische Basis hält sich das Betriebssystern in keiner Systemvariable. Allerdings existiert »screenpt« ($45e.L), die indirekt die physikalische und logische Bildschirmadresse auf die dort hineingeschriebene Adresse setzt. Indirekt deswegen, weil diese Systemvariable laufend vom VBI aus überprüft wird. Ist sie ungleich Null, so liest die VBI-Routine die Adresse aus und setzt die beiden Bildschirmbasen.

Neben »screenpt« überprüft der VBI noch die Systemvariable »colorptr« ($45a.L). Steht dort ein Wert ungleich Null, so interpretiert ihn die VBI-Routine als Zeiger auf eine Farbtabelle und setzt die Farben entsprechend der Tabelle.

Stellt das Betriebssystem fest, daß der Anwender zwischen Farb- und Monochrom-Monitor umgeschalten hat, macht der Computer einen Reset. Doch dieses Verhalten läßt sich über die Systemvariable »swv_vec« ($46e.L) steuern. An die dort eingetragene Adresse springt das Betriebssystem - oder genauer gesagt: die VBI-Routine -, nachdem ein Monitorwechsel stattgefunden hat und nachdem die neue Auflösung über »defshiftmod« gesetzt wurde. Normalerweise zeigt der »swv_vec«-Vektor direkt auf den Betriebssystembeginn (Offset $1e) - hier startet TOS mit dem Reset. Um etwa zu verhindern, daß TOS das laufende Programm bei einem Monitorwechsel unterbricht, verwenden Sie in GFA-Basic folgende Befehle:

	SDPOKE &H700, &H4e75	! rts
	SLPOKE &H46e, &H700	! setze swv_vec

Eigene Massenspeicher programmieren

Das BIOS des Betriebssystems nutzt die Vektoren »hdv_bpb« ($472.L), »hdv_rw« ($476.L) und »hdv_mediach« ($47e.L) als Zeiger auf die Funktionen »Getbpb«, Rwabs« und »Mediach«. Um etwa eine RAM-Disk oder einen Harddisk-Treiber zu schreiben, muß der Programmierer nicht das Betriebssystem verändern, sondern lediglich die drei Vektoren umbiegen und das der neuen Laufwerksnummer entsprechende Bit in »drvbits« ($4c2.L) setzen. In Listing 1 sehen Sie die für eine RAM-Disk-Verwaltung angepaßten drei neuen Routinen. Auf der Diskette finden Sie unter »RAMDISK.S« den vollständigen Quelltext.

Die Schlüsselfunktion ist »hdv_rw«. Sie liest eine bestimmte Anzahl von Sektoren vom Massenspeicher oder schreibt diese darauf. Die nötigen Parameter übergibt das Betriebssystem der Funktion auf dem Stack (siehe Tabelle 1). So sollten Sie dabei stets die Laufwerksnummer zuerst auswerten. Denn ist diese ungleich Ihrer eigenen, so müssen Sie unverzüglich die ursprüngliche »hdv_rw«-Routine anspringen. Aus diesem Grund eignet sich hier das XBRA-Protokoll (siehe Listing) besonders gut. Um Informationen über die angeschlossenen Massenspeicher zu erhalten, nutzt das Betriebssystem die Funktion »Getbp« und somit indirekt »hdv_bpb«. Als Ergebnis erhält es die Adresse auf einen sogenannten »BIOS Parameter Block« - kurz BPB - (siehe Tabelle 2) zurück. Listing 1 verbiegt »hdv_bpb« auf »mybpb«. Darin prüft das Programm zuerst, ob die RAM-Disk angesprochen wird. Wenn ja, dann gibt es die Adresse des konstanten BPB »protobpb« zurück.

Den dritten Vektor »hdv_mediach« nutzt das Betriebssystem, um festzustellen, ob in dem bestimmten Laufwerk in der Zwischenzeit eine Diskette gewechselt wurde. Das Ergebnis ist entweder 0 (Diskette nicht gewechselt), 1 (Diskette vielleicht gewechselt) oder 2 (Diskette gewechselt). Die RAM-Disk aus Listing 1 gibt stets den Wert 0 zurück. Beim Thema Massenspeicher-Verwaltung darf auch die Systemvariable »punptr ($516.L) nicht fehlen. Diese zeigt auf die sogenannte »Physical Unit Strukture«, die wichtige Daten über die angeschlossenen Festplatten und deren Partitionen enthält. Normalerweise legt der aktive Harddisk-Treiber (»AHDI«) »CBHD« etc.) die PUN-Struktur an. Leider kocht jedoch jeder Festplatten-Treiber sein eigenes Süppchen. Seit AHDI (»Atari Harddisk Driver«) 3.00 gibt es jedoch seitens Atari den AHDI-Standard, auf den ich mich im folgenden beziehe. Die Strukturkomponenten und wie Sie die Struktur erhalten, ersehen Sie aus Listing 2.

Der Struktureintrag »puns« enthält die Anzahl der vom Harddisk-Treiber gefundenen Fest- und Wechselplatten. In »pun« finden Sie zu jedem logischen Laufwerk die entsprechende ACSI-Gerätenummer. Dabei bedeutet der Wert $FF, daß der Treiber das entsprechende Gerät ignoriert. In »part_start« finden Sie für jede Partition den Startsektor auf der Platte. »p_max_sector« enthält schließlich die größte auftretbare Sektorgröße für BIOS-Sektoren.

Doch bevor Sie diese Informationen aus der PUN-Struktur auswerten, sollten Sie sicherstellen, daß sie auch im Sinne des AHDI 3.0-Standards gültig sind. Dazu dienen die beiden Einträge »p_cookie« und »p_cookptr«. »p_cookie« sollte die Zeichenkette »AHDI« enthalten und »p_cookptr« sollte auf »p_cookie« zeigen. Um endgültig sicher zu gehen, können Sie zudem die Versionsnummer »p_version« auf größer/gleich 3.0 testen.

Die Cache-Technik des GEMDOS

Auch das GEMDOS des Betriebssystems hält zwei äußerst wichtige Zeiger im Systenivariablen-Bereich: »_bufl[0]« ($4b2.L) und »_bufl[1]« ($4b6.L). Sie zeigen auf zwei Listen von Buffer Control Blocks (BCBs). Die BCBs verwendet GEMDOS um gelesene Sektoren vom Massenspeicher im RAM abzulegen und somit einen erneuten Zugriff auf diese Sektoren zu beschleunigen. Diese Technik nennt man auch »Caching« oder »Puffern«.

Den Aufbau eines BCBs finden Sie in Tabelle 3. Jeder BCB verwaltet einen Sektor. Die Verkettung der BCBs zu einer Liste erfolgt jeweils über den ersten Eintrag »next«, der auf den nächsten BCB zeigt. Besteht die Liste etwa aus zehn BCBs, so puffert GEMDOS zehn Sektoren.

»_bufl[0]« enthält den Zeiger auf eine BCB-Liste, welche die FAT-Sektoren der Massenspeicher puffert. Die FAT ist ein Datenbereich, der Informationen über die Lage der Cluster aller Dateien auf dem Massenspeicher enthält. »_bufl[1]« zeigt auf eine BCB-Liste, die zur Pufferung von Inhaltsverzeichnis- und Datensektoren dient. Normalerweise verwendet GEMDOS pro Liste je zwei BCBs bzw. zwei Sektoren. Um die Listen zu erweitern, müssen Sie lediglich drei Schritte durchführen:

Aus $4b2 oder $4b6 den Zeiger auf den Listenbeginn lesen und diesen Zeiger auf die eigene Listenerweiterung umbiegen (Kette von BCBs). Zuletzt setzen Sie den »next«-Zeiger in der letzten BCB-Struktur der Listenerweiterung auf den alten Listenbeginn.

In den neuen BCBs müssen Sie nur die »next«-, »drive«- und »buffer«-Einträge initialisieren; den Rest erledigt GEMDOS für Sie. Setzen Sie »next« auf den nächsten BCB und »drive« auf $ffff. Bei »buffer« taucht ein Problem auf. Wie groß ist überhaupt ein Sektor? Ganz einfach: normalerweise 512 Byte, es sei denn, die PUN-Struktur (»punptr«) gemäß AHDI-Standard existiert. In diesem Fall ergibt sich die Sektorgröße aus dem Eintrag »p_max_sector«. Listing 3 ist eine C-Prozedur, welche die benötigte Sektorgröße für Sie berechnet.

Der Vollständigkeit halber finden Sie in Tabelle 4 die noch nicht vorgestellten Systemvariablen. Diese sind abgesehen von Spezialanwendungen - nicht weiter interessant, so daß wir sie lediglich mit einer kurzen Erklärung versehen. Im nächsten Teil unserer Rundreise durch das Betriebssystem beschäftigen wir uns dann intensiv mit dem GEMDOS. (ah)

Literatur
Die Programmierung rund um Massenspeicher Brod, Stepper, »Scheibenkleister II - Massenspeicher am ST“, Eschborn: MAXON Computer GmbH, 1989, ISBN 3-927065-00-5

	hdv_bpb	$472	; getbpb-Vektor
	hdv_rw	$476	; RWABS-vektor
	hdv_medich	$47e	; mediach-Vektor
	secs = 1024		; = 512 KB groesse (secs+18)*512
	ramdrv = 15		; P:
	...
		move.l	hdv_bpb, o_bpb
		move.l	#mybpb, hdv_bpb
		move.l	hdv_rw, o_rw
		move.l	#myrabs, hdv_rw
		move.l	hdv_mediach, o_media
		move.l	#mymedia, hdv_mediach
	...
	; mybpb
		dc.b	„XBRATOSD“
	o_bpb:
		dc.l	0
	mybpb:
		move.w	#ramdrv, d0	; ist Ramdisk
		cmp.w	4(sp),d0	; gemeint?
		beq	newbpb
		move.l	o_bpb, a0
		jmp	(a0)
	newbpb:
		move.l	#protobpb, d0
		rts
	; myrwabs
		dc.b	„XBRATOSD“
	o_rw:
		dc.l	0
	myrwabs:
		move.w	#ramdrv, d0
		cmp.w	14(sp), d0
		beq	newrw
		move.l	o_rw, a0
		jmp	(a0)
	newrw:
		moveq	#O, d0
		move.w	12(sp),d0	; secno
		lsl.l	#8, d0	; * 512
		lsl.l	#1, d0	; offset
		move.l	6(sp), a0
		move.w	10(sp), d1	; seccnt
		subq.w	#1, d1
		move.l	puffer, a1
		add.l	d0, a1
		move.w	4(sp), d0	; rw-flag
		btst	#0, d0
		beq	copysec
		exg	a0, a1	; Quelle/Ziel vertauschen
	; mymedia
		dc.b	„XBRATOSD“
	o_media:
		dc.l	0
	mymedia:
		move.w	#ramdrv, d0
		cmp.w	4(sp), d0
		beq	newmedia
		move.l	o_media, a0
		jmp	(a0)
	newmedia:
		moveq	#0, d0
		rts
	protobpb:		; konstante BPB-Struktur
		dc.w	512, 2, 1024, 7, 5, 6, 18, secs/2, 0, 0, 0

Listing 1. Die Hauptbestandteile einer typischen RAM-Disk typedef struct { int puns; unsigned char pun[16]; unsigned long partstart[16]; long P_cookie; long *P_cookptr; unsigned int P_version; unsigned int P_max_sector; } PUNINFO;

PUN_INFO *GetPunPtr(void)
{
	PUN_INFO *P;
	long oldsp;

	oldsp = Super(0L);
	P = *((PUN_INFO **)(0x516L));
	Super((void*)oldsp);

	if (P && (P->P_cookie == 0x41484449L) &&
	   (P->P_cookptr & (P->P_cookie)) &&
	   (P->P_version >= 0x300))
		return P;	/* punptr war gueltig */
	return 0L;
}

int getBCBSize()
{
	PUN_INFO *p:
	if (( p = GetPunPtr()) != 0L)
		return p->P_max_sector;
	return 512;
}

Listing 2. Die Funktion »GetPunPtr« liest die vom AHDI-kompatiblen Festplatten-Treiber angelegte PUN-Struktur aus

int getBCBSize()
{
   	PUN_INFO *p;
   	if ((p = GetPunPtr()) != 0L)
   		return p->P_max_sector;
 	return 512;
}

Listing 3. Als Erweiterung zu Listing 2 erhalten Sie mit »getSize« die für die Buffer Control Blocks (BCBs) nötige Sektorgröße

Offset Länge Bedeutung
4(SP) WORD Lese (0)/Schreib (1)-Flag
6(SP) LONG Pufferadresse
10(SP) WORD Anzahl der Sektoren
12(SP) WORD Startsektor
14(SP) WORD Laufwerksnummer

Tabelle 1. Diese Belegung weist der Stack beim Aufruf von hdv_rw auf

Offset Länge Bedeutung
0 RECSIZ Sektorgröße in Byte
2 CLSIZ Clustergrüße in Sektoren
4 CLSIZI3 Clustergröße in Byte
6 RDLEN Wurzelverzeichnislänge in Sektoren
8 FSIZ Größe der FAT in Sektoren
10 FATREC erste Sektornummer der zweiten FAT
12 DATREC Nummer des ersten Datensektors
14 NUMCL Anzahl der Datencluster
16 BFLAGS 0: 12-Bit-FAT 1: 16-Bit-FAT

Tabelle 2. Aufbau der BIOS Parameter Block-Struktur (BPB)

Offset Name Bedeutung
0 next Zeiger auf nächsten BCB
4 drive Laufwerksnummer oder A
6 typ Puffertyp (0:FAT, 1:Verzeichnis, 2:Daten)
8 rec interne GEMDOS-Sektornummer
10 dirty Änderungsflag <>0 - geändert)
12 dmd Zeiger auf Drive Media Deskriplor
16 buffer Zeiger auf den Sektorpuffer

Tabelle 3. Aufbau eines Block-Control-Blocks (kurz BCB)

Spezielle Systemveriablen

o o o
$48e.4L themd 4 Langworte für Ur-Memory-Deskriptor
$49e.2W __md 2 Worte für zusätzliche Memory Deskriptoren
$4a2.L savptr Zeigt auf BIOS-Puffer für Prozessregister
$4ac.W save_row Puffer für Cursorzeile
$4ao.L sav_context Zeigt auf Exception-Pufferspeicher
$4ba.L hz_200 200 Hz-Zähler
$4be.L the_env Voreinstellung der Programmumgebung (Environment)
$4f6.L _shell_p Zeigt auf eine Shell-Routine, die Kommando-Zeilen verarbeitet
$506.L prt_stat Zeigt auf Routine, die den Druckerstatus holt
$50a.L prt_vec Zeigt auf Druckausgabe-Routine
$50e.L aux_stat Zeigt auf Routine, die RS232-Status holt
$512.L aux_vec Zeigt auf RS232-Ausgabe-Routine
$51e.8L bconst_ptrs 8 Zeiger für jedes BIOS-Gerät auf Routinen, die den Eingabestatus holen
$53e.8L bconin_ptrs 8 Zeiger für jedes BIOS-Gerät auf Routinen, die ein Zeichen einlesen
$55e.8L bcostat_ptrs 8 Zeiger für jedes BIOS-Gerät auf Routinen, die den Ausgabestatus holen
$57e.8L bconout_ptrs 8 Zeiger für jedes BIOS-Gerät auf Routinen, die ein Zeichen ausgeben

Tabelle 4. Diese Systemvariablen sind nur für Spezialanwendungen interessant


Martin Backschat
Aus: TOS 05 / 1991, Seite 86

Links

Copyright-Bestimmungen: siehe Über diese Seite