Auf der Schwelle zum Licht: Laufwerksverwaltung

Diesmal widmen wir uns der Verwaltung der Laufwerke unter GEMDOS. Dabei wird, wie versprochen, der Drive-Media-Descriptor erklärt. Außerdem ist das Thema “Cluster- und Sektornumerierung” vom letzten Mal auch noch nicht ausgeschöpft.

Nachtrag

Doch zuerst noch ein kleiner Nachtrag zur FAT-Verwaltung. Es ist vielleicht noch ganz interessant, den Algorithmus zu kennen, mit dem ein freier Sektor in der FAT ausgewählt wird, wenn eine Datei verlängert wird.

Die Strategie ist eigentlich sehr einfach. Beim letzten Cluster der Datei beginnend, wird einfach der erste freie Cluster gesucht. Wenn dabei der letzte Cluster (nach Meinung des GEMDOS der letzte, eigentlich der drittletzte) erreicht wird, setzt man die Suche beim ersten Cluster (der Nummer 2) fort. Wenn ‘numcl’- Einträge erfolglos untersucht wurden, ist das Speichermedium voll, und die Funktion bricht ab.

Nach dem Kreieren einer Datei beginnt die Suche für den ersten Cluster von vorne.

Anmeldung von Laufwerken

Damit GEMDOS mit den verschiedenen Massenspeicher-Laufwerken Zusammenarbeiten kann, müssen diese bei ihm “angemeldet” sein. Dies geschieht aber nicht explizit dadurch, daß ein Treiberprogramm eine spezielle Funktion aufruft oder irgendwelche Systemvariablen manipuliert, sondern wird von GEMDOS automatisch durchgeführt, wenn das Laufwerk zum ersten Mal mittels einer GEMDOS-Funktion angesprochen wird.

GEMDOS führt Buch darüber, welche Laufwerke ihm schon bekannt sind. Dazu dient eine Bit-Maske (‘drvmap’), wobei ein gesetztes Bit bedeutet, daß das Laufwerk angemeldet ist. Bit 0 ist dabei Laufwerk A: zugeordnet, Bit 1 gehört zu Laufwerk B:, usw.

Bei jeder GEMDOS-Funktion, die auf ein Laufwerk zugreifen muß (insbesondere also die Datei- und Disk-Funktionen F... bzw. D...), wird von einer Routine überprüft, ob der Zugriff auf das gewünschte Laufwerk möglich ist. Diese Routine wird im folgenden ‘d_chkdrv’ genannt.

Wenn das Laufwerk schon bekannt ist, also das zugehörige Bit in ‘drvmap’ gesetzt ist, geht man davon aus, daß es auch ansprechbar ist. Man überprüft nur noch, ob für das Laufwerk schon ein Standardpfad existiert. Ist dies nicht der Fall, wird das Root Directory als aktuelles Directory festgelegt (dazu kommen wir noch einmal in einer späteren Folge). Ist das Laufwerk dagegen GEMDOS noch unbekannt, wird das BIOS mit ‘Getbpb’ um Hilfe gefragt. Wenn hier ein Fehler zurückgeliefert wird, ist entweder kein Treiber für das Laufwerk installiert oder es kann nicht an-, gesprochen werden (z.B. wenn der Boot-Sektor der Diskette einen Lesefehler hat).

Im Normalfall gibt ‘Getbpb’ die Adresse des “BIOS-Parameter-Blocks” (BPB) zurück, an dessen Aufbau Sie sich vielleicht noch aus der letzten Folge erinnern. Der BPB wird nun ausgewertet und der “Drive Media Descriptor” aufgebaut (siehe nächsten Abschnitt).

Anschließend definiert wie oben beschrieben, das Root-Directory als aktueller Pfad.

Interessant ist in diesem Zusammenhang noch, daß ‘d_chkdrv’, wenn alles geklappt hat, die Laufwerkskennung an den Aufrufer zurückgibt. ERROR (-1L) zeigt einen eventuellen Fehler an. Wenn die aufrufenden Routinen diese Kennung und nicht die, die sie ‘d_chkdrv’ übergeben haben, für die eigentlichen Operationen benutzen würden, könnten in ‘d_chkdrv’ Laufwerke auf andere umgeleitet werden, bzw. es könnten “virtuelle Laufwerke” verwaltet werden.

An der Verwendung des Konjunktivs merken Sie schon, daß dies ein Wunschtraum bleibt, denn die Rückgabe von ‘d_chkdrv’ wird nicht weiter beachtet (außer daß die Fehlermeldung abgefragt wird).

Dieser in ‘d_chkdrv’ auftretende interne Fehler, nämlich daß ein Laufwerk nicht angesprochen werden kann, ist die in der Januar-Folge als “Laufwerk unbekannt” bezeichnete Ursache von GEMDOS-Fehlermel-dungen (am besten nochmal nachlesen).

Der “Drive Media Descriptor”

Jeder eifrige Leser von Original ATARI-Dokumentationen ist schon über die Bezeichnung “Drive Media Descriptor” gestolpert: Bei der Angabe (von Dokumentation kann man hier wohl kaum sprechen) des “Buffer Control Blocks” (BCB) taucht dieser Begriff auf, ohne daß ein weiteres Wort über ihn verloren wird. Hier soll nun sein Geheimnis, das eigentlich gar keins ist, gelüftet werden.

Sein Aufbau ist in Abb. 1 dargestellt. Die Komponenten lassen sich am besten erläutern, indem man die Initialisierung des DMD bei der Anmeldung eines Laufwerks betrachtet.

Wie der Name schon sagt, beschreibt der DMD ein Laufwerk bzw. das damit verbundene Speichermedium. Er erlaubt GEMDOS, die Kommunikation mit den unterschiedlichsten Laufwerken und Medien, da er recht flexibel ist. Wie letzten Monat schon deutlich wurde, ist dies aber alles nur graue Theorie, da hier so manches nicht funktioniert.

‘d_drive’ enthält, wie man sich beinahe denken kann, die Kennung des Laufwerks, das der DMD beschreibt. Einige Werte im DMD werden direkt aus dem BPB übernommen, was durch die ähnliche Namensgebung angedeutet werden soll. So ist ‘d_clsiz’ die Cluster-Größe in Sektoren (wie ‘b_clsiz’), ‘d_clsizb’ gibt die Cluster-Größe in Bytes an (wie ‘b_clsizb’), ‘d_recsiz’ ist die Sektor-Größe in Bytes (wie ‘b_recsiz‘), ‘d_fsiz’ ist die FAT-Größe in Sektoren (wie ‘b_fsiz’) und ‘d_numcl’ ist die Anzahl der Daten-Cluster (wie ‘b_numcl’). ‘d_flag' enthält eine Kopie des Bit 0 von ‘b_flags[0]\ gibt also Auskunft, ob eine 12-oder 16-Bit-FAT vorliegt. Die anderen Bits von ‘d_flag’ sind immer Null, unabhängig von ‘b_flags[0]'

Bei der Berechnung von Datei-Positionen, Sektor-Nummern, usw. rechnet GEMDOS fleißig mit diesen Werten herum. Dabei ist es oft notwendig, durch sie zu dividieren bzw. bestimmte Bits auszublenden. Dies wird mit Shift-Operationen und Logisch-AND-Befehlen erreicht. Damit man die hierbei benötigten Shift-Operanden (Zweierlogarithmen) und Bit-Masken nicht jedesmal neu berechnen muß, merkt sich GEMDOS einige davon im DMD.

In ‘djclsiz’ wird der Zweierlogarithmus von ‘d_clsiz’ gespeichert, ‘d_mclsiz’ ist eine Bit-Maske zum Ausblenden niederwertiger Bits (Tab. 1). Die entsprechenden Werte für ‘d_recsiz’ sind in ‘djrecsiz’ und ‘d_mrecsiz’ abgelegt (wer hätte das gedacht?). Die Bit-Masken sind zwar gerade die um eins verminderten Werte selbst, aber GEMDOS holt sie sich extra aus einer Tabelle ('f_masks’, s.u.)!

'clsiz' ’lclsiz' 'mclsiz' bzw. bzw. bzw. 'recsiz' ’lrecsiz' 'mrecsiz'

1 0 $0000 2 1 $0001 4 2 $0003 : : : 512 9 $01FF 1024 10 $03FF : : :

Tab. 1: Shift- und Maskier-Werte

typedef struct
{ int d_roff[3];	/* Sektornummer-Offsets für FAT,DIR,DATA */
	int d_drive;	/* Laufwerkskennung 0..15 */
	int d_fsiz;		/* FAT-Größe in Sektoren */
	int d_clsiz;	/* Cluster-Größe in Sektoren */
	int d_clsizb;	/* Cluster-Größe in Bytes */
	int d_recsiz;	/* Sektor-Größe in Bytes */
	int d_numcl;	/* Zahl der Datencluster */
	int d_lclsiz;	/* 2er-Logarithmus von clsiz	* */
	int d_mclsiz;	/* Bit-Maske für clsiz */
	int d_lrecsiz;	/* 2er-Logarithmus von recsiz */
	int d_mrecsiz;	/* Bit-Maske für recsiz */
	int d_lclsizb;	/* 2er-Logarithmus von clsizb */
	FD *d_fatfd;	/* Zeiger auf FD der FAT */
	long d_dummy;	/* unbenutzt */
	DD *d_rdd;	/* Zeiger auf DD des Root Directorys */
	int d_flag;	/* FAT-Typ: 0: 12-Bit, 1: 16-Bit */
} DMD;

Abb. 1 : Drive Media Descriptor (DMD)

Tab. 2 : Beispiele zur GEMDOS-Sektor-Zählung

Zur Verwaltung von Dateien (einschl. Directories und FAT) gibt es weitere Strukturen (“File Descriptoren” und “Directory Descriptoren”), um die wir uns ein andermal kümmern. Hier sei nur erwähnt, daß sie zusammen mit dem DMD ein kompliziertes Netz bilden, welches vom DMD aus gut “aufgerollt” werden kann.

Beim Einrichten des DMD werden die File Descriptoren für das Root Directory und die FAT sowie der Directory Descriptor des Root Directories ebenfalls initialisiert.

Übrig bleiben nun noch die "d_roff[0..2]’-Werte. Dabei handelt es sich um die letzten Monat erwähnen Sektornummer-Offsets für FAT-, DIR- und DATA-Sektoren. Die GEMDOS-Sektornummer plus entsprechendem Offset ergibt die BIOS-Sektornummer.

In der letzten Folge waren die beiden Zählweisen einander gegenübergestellt, wobei auffiel, daß die GEMDOS-Sektornummern nicht durchgehend waren, sondern daß es merkwürdige Lücken in der Numerierung gab. Heute soll der Algorithmus erklärt werden, mit dem diese Zählweise festgelegt wird.

Bisher habe ich Ihnen eine weitere Komplikation noch verschwiegen. Obwohl eigentlich nur Daten-Sektoren zu Clustern zusammengefaßt werden, gibt es auch Cluster-Nummern für Root Directory (RD) und FAT. Das liegt daran, daß RD und FAT intern wie Dateien behandelt und daher bei der Berechnung von Dateipositionen (Seek-Funktion) wie bei Dateien Cluster in Sektoren umgerechnet werden. Da es in Wirklichkeit aber gar keine Cluster gibt, müssen Cluster- und Sektornumerierung so angelegt sein, daß die Pseudo-Cluster-Nummern eindeutig auf GEMDOS-Sektornummem abgebildet werden.

Zum besseren Verständnis der etwas komplizierten Zusammenhänge dient Tab. 2, wo Beispiele für die nun folgenden Erklärungen angegeben sind. Aus der Länge des RD in Sektoren (‘b_rdlen’) errechnet sich seine Länge in Clustern (‘rdcl’). Da die Zahl der RD-Sektoren kein Vielfaches der Clustergröße sein muß, wird diese Clusterzahl aufgerundet. Die genaue Formel lautet:

rclcl_rdlen + clsiz - 1 clsiz

(Der bei der Division auftretende Rest wird ignoriert). Der erste “Cluster” des RD ist ‘rdsf:

rdst = - 1 - rdcl

Das RD belegt damit die Clusternummern ‘rdst’ bis -2; die Clusternummer -1 existiert nicht, ebenso die Cluster 0 und 1. Die erste GEMDOS-Sektornummer ergibt sich durch Multiplikation mit der Anzahl der Sektoren pro Cluster. Durch die Aufrundung bei der Berechnung von ‘rdcl’ ergeben sich in der “Ausnutzung” der GEMDOS-Sektornummern Lücken (s. Beispiele in Tab. 2).

Entsprechend verfährt man mit der FAT:

fl_fsiz + clsiz - 1 clsiz
fst = rdst - fcl

Die FAT-Cluster liegen unmittelbar vor den RD-Clustem, denn sie haben die Nummern ‘fst’ bis ‘rdst’-1. Auch hier ergibt sich die erste GEMDOS-Sektornummer durch Multiplikation von ‘fst’ mit ‘clsiz’.

Nachdem nun festgelegt wurde, welchen Pseudo-Clustern die RD- und FAT-Sektoren zugeordnet werden, kann GEMDOS die gesamte Verwaltung wie bei User-Dateien erledigen. Beim tatsächlichen Zugriff werden nun die GEMDOS-Sektornummern, mit denen sich die Sektorpuffer-Verwaltung herumschlagen muß, mit den ‘d_roff’-Offsets zu BIOS-Nummern zurückgerechnet. Diese Offsets müssen nun so berechnet werden, daß diese Rücktransformation klappt.

Für Daten-Sektoren:
d_roff[2] = datrec - (2 * clsiz)

‘datrec’ ist die aus dem BPB entnommene BIOS-Nummer des ersten Da-ten-Sektors. Da der erste Cluster immer die Nummer 2 hat, ergeben sich für Daten-Sektoren die Nummern ab ‘datrec’ aufwärts.

Für FAT-Sektoren:
d_roff[0] = fatrec - (fst * clsiz)

‘fatrec’ ist die ebenfalls im BPB gefundene BIOS-Nummer des ersten Sektors der zweiten FAT, die, wie Sie seit einem Monat wissen, eigentlich die erste FAT ist. Zu beachten ist, daß ‘fst’ negativ ist, und damit ‘d_roff[0]’ größer als ‘fatrec’ wird. Dies ist aber korrekt, da die bei der Umrechnung addierten GEMDOS-Sektornummern negativ sind, so daß sich BIOS-Nummern ab ‘fatrec’ aufwärts ergeben.

Für RD-Sektoren:
d_roff[1] = fatrec + fsiz - (rdst * clsiz)

Das RD muß unmittelbar nach der zweiten FAT liegen, beginnt also bei ‘fatrec’ plus ‘fsiz’ (‘fsiz’ ist die Zahl der Sektoren pro FAT, zu finden im BPB - wo wohl sonst?). Der Rest ist wie bei der FAT.

So, nun wissen Sie hoffentlich alles, was man über Sektor- und Clusternummern wissen muß. Bei den bisherigen Erfahrungen mit GEMDOS muß man sich wundern, daß das ganze Hin- und Hergerechne so reibungslos funktioniert; zumindest ist mir in dieser Hinsicht noch kein Fehler bekannt.

Noch eine Ergänzung für diejenigen, die den Artikel über die Speicherverwaltung kennen:

Die DMDs werden im “internen GEMDOS-Speicher” abgelegt. Da es hiervon aber nur relativ wenige gibt, ist die Anzahl der Laufwerke selbst nicht so entscheidend. Allerdings wird man bei vielen Laufwerken auch viele Ordner ansprechen, so daß die Chance, daß einem der GEMDOS-Speicher ausgeht, bei jedem neuen Laufwerk steigt (eine zweite Floppy wirkt hier schon Wunder).

Da ein DMD 42 Byte groß ist, was aufgerundet drei 8-Wort-Einheiten (382 = 48) sind, finden sich freigegebene DMDs in der ‘mifl’-Liste 3. Der DMD ist übrigens die einzige Struktur, die über die “3er-Liste” verwaltet wird.

Abmelden von Laufwerken

GEMDOS erkennt zwar selbständig neue Laufwerke und richtet dafür die internen Datenstrukturen ein, doch ist die Anmeldung eines Laufwerks nicht vorgesehen.

Aber auch hierfür gibt es einen “automatischen” Mechanismus, nämlich den Mediumwechsel. Bei einem durch das BIOS gemeldeten Mediumwechsel werden zuerst alle Datenstrukturen des betroffenen Laufwerks freigegeben. Dies sind der DMD, die File und die Directory-Descriptoren. Weiterhin werden alle Pfade zu Directories sowie die Sektor-Puffer für ungültig erklärt (siehe hierzu auch die erste und zweite Folge).

Anschließend wird mit der BIOS-Funktion ‘Getbpb’ überprüft, ob das Laufwerk (mit einem anderen Medium) überhaupt noch verfügbar ist. Ist dies der Fall, werden die Strukturen zur Disk-Verwaltung neu initialisiert, so daß das Laufwerk GEMDOS wieder bekannt ist.

Wenn das Laufwerk nicht mein ansprechbar ist, wird es für GEMDOS abgemeldet, indem das entsprechende Bit von ‘drvmap’ gelöscht wird. Einer erneuten Anmeldung durch Zugriff steht nichts im Wege.

Dieses Verfahren kann man sich zu Nutze machen, wenn man gezielt einzelne Laufwerke abmelden will. Dies kann sinnvoll sein, wenn das Laufwerk unter verschiedenen Kennungen angesprochen werden soll. Würde die Abmeldung nicht erfolgen und der Treiber einfach auf seine alte Kennung nicht mehr reagieren, würden die Datenstrukturen als “Leichen” im Speicher herumliegen und kostbaren internen Speicher verbrauchen.

Außerdem kann ein simulierter Mediumwechsel bei allen Laufwerken einfach dazu dienen, internen Speicher freizugeben, was nach ausgiebigem Öffnen von vielen Ordnern empfehlenswert ist. Dazu läßt sich eine solche Routine leicht in ein Accessory einbauen.

Listing 1 zeigt nun, wie man’s macht. In der Funktion ‘free=drv’ werden zunächst die Vektoren der elementaren Disk-Routinen ‘Mediach’, ‘Getbpb’ und ‘Rwabs’ auf eigene Routinen umgesetzt. Durch den Aufruf der GEMDOS-Funktion ‘Dffee’ (andere tun’s auch) wird GEMDOS zu einem Zugriff auf das Laufwerk gezwungen. Die eigenen Disk-Routinen geben nun Meldungen zurück, wie sie bei einem Diskettenwechsel auftreten.

Die Funktion ‘free_all’ generiert Diskettenwechsel für alle 16 möglichen Laufwerke.

Da die Disk-Vektoren im geschützten Speicherbereich liegen, darf auf sie nur im Supervisor-Mode zugegriffen werden. Da man dieses Problem als “Betriebssystem-Hacker” öfter hat, sind die allgemeinen "Peek & Poke”-Routinen für solche Dinge ganz brauchbar. Wer erinnert sich hier nicht an seine alten 8-Bitter, bei denen die Kenntnis geheimnisvoller “Poke-Befehle” unerläßlich war?

Bei der Anwendung dieses kleinen Programms sollten Sie darauf achten, daß auf den betroffenen Laufwerken keine Dateien mehr geöffnet sind, da diese bei dem “Mediumwechsel” nicht mehr ordnungsgemäß geschlossen werden können, und daher Datenverlust droht. Ferner gehen alle Standardpfad-Einstellungen verloren.

Systemvariable ‘drvbits’

Wie wir nun wissen, steht in der GEMDOS-Variablen ‘drvmap’, welche Laufwerke bekannt sind. Andererseits gibt es noch die BlOS-Systemvariable ‘drvbits’ ($4C2). ‘drvbits’ ist ebenfalls eine Bit-Maske für vorhandene Laufwerke (sogar 32 Bit!), hat aber nichts mit GEMDOS zu tun.

Während ‘drvmap’ angibt, welche Laufwerke schon einmal angesprochen wurden, steht in ‘drvbits’, ob das Laufwerk überhaupt verfügbar ist. Massenspeicher-Treiber sollten allerdings die von ihnen belegten Laufwerkskennungen hier eintragen, ‘drvbits’ wird nämlich von der BIOS-Funktion ‘Drvmap’ zurückgeliefert, damit Anwenderprogramme feststellen können, welche Laufwerke ansprechbar sind. Vom AES wird diese Möglichkeit während der Reset-Phase auch ausgiebig genutzt.

GEMDOS-Variablen für Disk-Verwaltung

Die globalen GEMDOS-Variablen für die Disk-Verwaltung sind in Abb. 2 dargestellt, zusammen mit denen für die Sektor-Pufferung, die ich letztes Mal vergessen habe. Die erste Adresse gilt für das alte TOS (6.2.1986), die zweite für das Blitter-TOS. Überflüssig zu erwähnen, daß diese Adressen nicht dokumentiert sind und nicht in Programmen verwendet werden sollten.

BCB bcbx[4];			/* $60a4/	$8846:	BIOS: 4-Standard-BCBs */
char secbuf [4][512];	/* $4e22/	$75c4:	BIOS: 4 Puffer für je 1 Sektor */

int drvmap;	/* $5fe2/	$8784:	Bit-Map für angemeldete Drives */
DMD *dmdx[16];	/* $5bde/	$8780:	DMD-Zeiger für alle Drives */
int f_masks[]	/* $fd1bf6/$fd3022: Masken für Datei-Operationen */
= { 0x0000, 0x0001, 0x0003, 0x0007,
	0x000f, 0x001f, 0x003f, 0x007f,
	0x00ff, 0x01ff, 0x03ff, 0x07ff,
	0xffff, 0x1fff, 0x3fff, 0x7fff };

Abb. 2 : GEMDOS-Variablen zur Puffer- und Disk-Verwaltung

‘bcbx’ und ‘secbuf’ sind die vom BIOS eingerichteten 4 BCBs mit den dazugehörigen Sektor-Puffern. Sie sind legal über die Pufferlisten (‘bufl[]’) zugänglich.

‘drvmap’ wurde oben schon erläutert. GEMDOS hat eine Tabelle (‘dmdx’), in der für jedes Laufwerk der Zeiger auf den DMD steht. Wenn ein Laufwerk unbekannt ist, enthält die Tabelle einen Nullzeiger (OL). Gesetzt bzw. gelöscht werden diese Zeiger bei der automatischen An- und Abmeldung der Laufwerke.

‘f_masks’ ist eine Tabelle mit Bit-Masken, die für Berechnungen verschiedenster Art benötigt werden. Bei der Anmeldung von Laufwerken ergeben sich die Werte für ‘b_mrecsiz’ und ‘b_mclsiz’ aus dieser Tabelle durch Indizierung mit den log2-Werten (‘b_lrecsiz’, ‘bjclsiz’).

Im folgenden wird die Struktur des Prozeßdescriptors noch gebraucht werden, daher ist sie für diejenigen unter Ihnen, die den Artikel “Programmverwaltung” nicht gelesen haben, in Abb. 3 noch einmal dargestellt. Für die genaue Erklärung muß ich jedoch leider wieder auf das ST-Extra-Heft verweisen.

Mehr als 16 Laufwerke?

GEMDOS kann in seiner derzeitigen Version entgegen sämtlicher Gerüchte maximal 16 Laufwerke verwalten. Dies scheint viel zu sein, doch gibt es Leute, denen das noch zu wenig ist. Die Gründe für die Beschränkung auf 16 Laufwerke sind die folgenden:

‘drvmap’ ist nur eine ‘int’-Variable (16 Bit). Durch eine einfache Änderung im GEMDOS-Quelltext könnte dies auf Tong’ (32 Bit) geändert werden.

Das ‘dmdx’-Feld müßte für 32 Laufwerke erweitert werden (für die Atari-Entwickler ebenfalls kein Problem). Das ‘p_drvx’-Feld im Prozeßdeskriptor (PD), in dem Handles für die Standardpfade abgelegt sind (dazu kommen wir in einer späteren Folge), ist für 16 Laufwerke dimensioniert. Da danach aber noch Platz im PD ist, wäre eine Erweiterung auf 32 Laufwerke ohne Änderungen möglich. Allerdings gibt es Routinen, die alle Laufwerke aus ‘p_drvx’ in einer Schleife durchgehen (z.B. bei der Vererbung an Tochterprozesse). Hier müßte überall der Schleifenendwert auf 32 statt 16 gesetzt werden.

Bei Benutzung so vieler Laufwerke müßte aber auch der interne Speicher nochmals vergrößert werden, da GEMDOS hier ganz schön zuschlägt.

GEMDOS-Funktionen (TRAP #1)

Hier möchte ich noch kurz auf die GEMDOS-Funktionen eingehen, die mit der Disk-Verwaltung Zusammenhängen.

Funktion $0e Dsetdrv

long Dsetdrv(int drv)
Das Default-Laufwerk wird auf ‘drv’ gesetzt (0,1.. für A:, B:, usw.). Dieses Laufwerk wird angesprochen, wenn in Pfadnamen keine Laufwerkskennung explizit angegeben ist.
Rückgabewert
Es wird eine 32-Bit-Maske der angemeldeten Laufwerke zurückgegeben (Bit 0,1,.. für A:,B:,..). Dabei handelt es sich jedoch nicht um die GEMDOS-Variable ‘drvmap’, sondern um die oben erwähnte BlOS-Systemvariable ‘drvbits’.

typedef struct
{	char *p_lowtpa;	/* Zeiger auf Beginn des reservierten Bereichs */
	char *p_hitpa;	/* Zeiger auf Ende des reservierten Bereichs */
	char *p_tbase;	/* Zeiger auf TEXT-Segment */
	long p_tlen;	/* Länge TEXT-Segment */
	char *p_dbase;	/* Zeiger auf DATA-Segment */
	long p_dlen;	/* Länge DATA-Segment */
	char *p_bbase;	/* Zeiger auf BSS-Segment */
	long p_blen;	/* Länge BSS-Segment */
	DTA *p_dta;		/* Zeiger auf DTA-Puffer */
	PD *p_parent;	/* Zeiger auf PD des Parent-Prozesses */
	long resl;		/* nicht benutzt */
	char *p_env;	/* Zeiger auf Environment-String */
	char p_devx[6];	/* Handles für Standard-Devices */
	char res2;		/* nicht benutzt */
	char p_defdrv;	/* aktuelles Laufwerk (Default-Laufwerk) */
	char res3[8];	/* nicht benutzt */
	char p_drvx[16);	/* Pfad_Handles für alle Laufwerke */
	char res4[0xl8];	/* nicht benutzt */
	long p_d0;		/* Prozessorregister DO */
	long p_a3;		/* Prozessorregister A3 */
	long p_a4;		/* Prozessorregister A4 */
	long p_a5;		/* Prozessorregister A5 */
	long p_a6;		/* Prozessorregister A6 */
	long *p_reg;	/* Zeiger auf restliche Prozessor-Register */
	char p_cmdlin[128]; /* Kommando-Zeile (übergebener Parameter) */
} PD;

Abb. 3: Der Prozeßdescriptor (PD)

typedef struct
{	long b_free;	/* freie Cluster */
	long b_total;	/* Gesamtzahl der Cluster */
	long b_secsiz;	/* Sektor-Größe in Byte */
	long b_clsiz;	/* Cluster-Größe in Sektoren */
} DISKINFO;

Abb. 4: DISKINFO-Struktur

Arbeitsweise
GEMDOS merkt sich das aktuelle Laufwerk im PD als ‘p_defdrv’. Dies bedeutet, daß jeder Prozeß sein eigenes aktuelles Laufwerk hat.

Die Funktion ‘Dsetdrv’ besteht nur aus zwei ‘C’-Befehlen. ‘drv’ wird direkt nach ‘p_defdrv’ des PD kopiert. Dabei erfolgen keinerlei Kontrollen. Wenn das Laufwerk gar nicht existiert, wird dies frühestens beim versuchten Zugriff bemerkt (in der oben erläuterten Routine ‘d_chkdrv’). Als zweites wird die BIOS-Funktion ‘Drvmap’ aufgerufen, deren Ergebnis direkt an den Aufrufer von ‘Dsetdrv’ zurückgegeben wird.

Bei der Gelegenheit gleich noch ein Wort zu unerlaubten Laufwerkskennungen: GEMDOS bemerkt zwar früher oder später, wenn ein Laufwerk nicht ansprechbar ist. Es reagiert allerdings ausgesprochen allergisch auf Kennungen, die nicht zwischen 0 und 15 liegen. Da keine Überprüfungen erfolgen, können unvorhersehbare Zugriffe erfolgen (durch Index-Überlauf in ‘p_drvx’). Natürlich gibt es auch Schwierigkeiten beim Abfragen des Laufwerk-Bits in ‘drvmap’.

Die GEMDOS-Programmierer waren sicherlich der Ansicht, daß es in Anwenderprogrammen keine Programmierfehler gibt, die illegale Kennungen erzeugen. Wie üblich muß der Anwender selbst nach den Bombenlegern im GEMDOS suchen. Bei Accessories ist wieder einmal zu beachten, daß diese unter dem PD der Hauptapplikation, also unter AES/ Desktop, laufen, wodurch sich Konflikte ergeben können.

Funktion $19 Degetdrv

int Dgetdrv()
Hiermit erhält man die Kennung des für den aktiven Prozeß gültigen aktuellen Laufwerks. Dies kann entweder die zuletzt mit ‘Dsetdrv* gesetzte oder die vom Parent-Prozeß “geerbte” Kennung sein.

Arbeitsweise
‘Dgetdrv’ ist ein Einzeiler. Es erfolgt direkt die Rückgabe von ‘p_defdrv’ aus dem PD.

Funktion $36 Dfree

void Dfree
(DISKINFO *info, int drv)

Diese Funktion gibt Auskunft über den freien Speicherplatz auf dem Laufwerk ‘drv’ (Ofür aktuelles Laufwerk; 1,2,... für A:,B:,..). ‘info’ muß dabei auf freien Speicherplatz zeigen, der von ‘Dfree’ mit einer DISKINFO-Struktur (Abb. 4) gefüllt wird. Daraus kann der belegte und noch freie Speicherplatz in Bytes berechnet werden:

free = b_free * b_clsiz * b_secsiz  
used =(b_total - b_free)*b_clsiz *b_secsiz

Dabei ist zu berücksichtigen, daß‘Dfree’ nur die Informationen der FAT berücksichtigt, wo der Speicherplatz nach Clustern verbucht wird.

Arbeitsweise

Nach der Ermittlung des gewünschten Laufwerks (Berücksichtigung des aktuellen Laufwerks) und der Überprüfung mit ‘d_chkdrv’ werden alle FAT-Einträge (von 2 bis‘numcl’) gelesen. Alle Einträge gleich Null werden als frei gezählt (dies ergibt ‘b_free’).

Dann wird die DISKINFO-Struktur erzeugt. Die Werte ‘b_total\ ‘b_secsiz’ und ‘b_clsiz’ entnimmt man dabei dem DMD (‘d_numd\ 'd_recsiz\ ‘d_clsiz’).

Für das Lesen der FAT wurde keine große, programmtechnische Mühe “vergeudet”. Dies erledigt nämlich die allgemeine FAT-Lese-Routine, die mit ‘f_seek’ (der internen Version von GEMDOS-Tseek’) und ‘f_read’ jeden Eintrag einzeln liest und auch noch gleich den Folge-Cluster bestimmt, was hier ja überflüssig ist. Dieser enorme Overhead erklärt, warum ‘Dfree’ so berüchtigt langsam ist (gar nicht zu reden vom DESK-

TOP-”Disk-Info”, das auch noch sämtliche Directories liest!). Eine auf ‘Dfree’ zugeschnittene Routine, die effektiver arbeitet, würde hier schon Wunder wirken.

Vorausschau

Das nächste Mal kehren wir wieder zur Dateiverwaltung zurück. Dabei geht es um die nächsthöhere Ebene -die Ebene der File-Descriptoren.

/*	Routinen zum Abmelden von Laufwerken
	bzw. zur Freigabe internen GEMDOS-Speichers
	by A. Esser, Juni 1987 
	entwickelt mit MEGAMAX C
*/

#define pokeb(a,b)	poke(0,(long)a,(int)b)
#define pokew(a,b)	poke(1, (long)a,(int)b)
#define pokel(a,b)	poke(3,(long)a,(long)b) 
#define peekb(a)	((int)peek(0,(long)a))
#define peekw(a)	((int)peek(1,(long)a))
#define peekl(a)	peek(3,(long)a)

/*	allgemeine Funktionen zum Speicherzugriff 
	ermöglicht Zugriff auf geschützte Bereiche 
	und auf ungerade Adressen */

asm
{
poke:
	pea		spoke
	move.w	#38,-(A7)	;XBIOS-Supexec
	trap	#14
	addq.w	#6,A7
	rts 
spoke:
	move.l	14(A7),A0	;Poke-Adresse
	lea		18(A7),a1	;Adresse des zu schreibenden Wertes
	move.w	12(A7),d1	;Zahl der Bytes -1 (0,1,3)
	bne.s	spokel		;-> int oder long
	addq.w	#1,A1		;char wurde als int übergeben
spokel:
	move.b	(A1)+,(A0)+ 
	dbf		D1,spokel
	rts
;
peek:
	pea		speek
	move.w	#38,-(A7)	;XBIOS-Supexec
	trap	#14
	addq.l	#6,A7
	rts 
speek:
	move.l	14(A7),A0	;Peek-Adresse
	moveq	#0,D0
	move.w	12(A7),D1	;Zahl der Bytes -1 (0,1,3)
speekl:
	asl.l	#8,D0
	move.b	(A0)+,D0
	dbf		D1,speekl
	rts
}

/* gesamten internen Speicher eines Laufwerks freigeben */

void free_drv(drive)
register int drive; /* Laufwerks-Kennung 0..15 */
{	long dummy[4];	/* Dfree-Puffer */
	long old_bpb;	/* Zwischenspeicher	für alte Vektoren */

	long old_rw; 
	long old_med;
	extern free_bpb(),free_rw(),free_med();

	old_bpb = peekl(hdv_bpb);	/* alte Vektoren merken */

	old_rw = peekl(hdv_rw); 
	old_med = peekl(hdv_mediach); 
	pokel(hdv_bpb,free_bpb); /* auf Spezial-Routinen setzen */
	pokel(hdv_rw,free_rw); 
	pokel(hdv_mediach,free_med);
	Dfree(dummy, drive+1);	/* Disk-Zugriff gibt Speicher frei */
	pokel(hdv_bpb,old_bpb);	/* Vektoren wieder zurückholen */
	pokel(hdv_rw,old_rw); 
	pokel(hdv_mediach,old_med); 
	return;

	/*	folgende hdv-Routinen sorgen indirekt 
		für Speicher-Freigabe */
	asm
	{ free_med:
		moveq #2,D0	;"sicherer Medium-Wechsel"
		rts 
	free_rw:
		moveq #-14,D0 ;"Medium-Wechsel" 
		rts 
	free_bpb:
		moveq #0,D0	;Medium	nicht vorhanden
		rts
	}
}

	/* internen Speicher aller Laufwerke freigeben */ 
void free_all()
{	register int drive;

	/* ohne Rücksicht auf Existenz */ 
	for (drive = 0; drive < 16; drive++) 
		free_drv(drive);
}

Alex Esser
Aus: ST-Computer 04 / 1988, Seite 33

Links

Copyright-Bestimmungen: siehe Über diese Seite