Auf der Schwelle zum Licht (3): Die Fehlermeldung des TOS (Teil 2)

Der Critical-Error-Handler

Zur Benutzerfreundlichkeit einer grafischen Oberfläche wie GEM gehört auch, dass Fehlermeldungen des Betriebssystems nicht von jedem Programm in einer anderen Art und Weise - wenn überhaupt - ausgegeben werden, sondern vom Betriebssystem selbst in einheitlicher Form präsentiert werden.

Der Critical Error Handler des GEM ist verantwortlich für die Anzeige der BIOS-Fehler mittels Alert-Boxen. Er ist Ihnen durch Fehlermeldungen wie “Floppy A: antwortet nicht...” sicherlich bestens bekannt und wird vom TOS dazu benutzt, Fehler beim Diskzugriff bzw. bei der Druckeransteuerung (bei “Datei zeigen” des Desktop) anzuzeigen. Da die BIOS-Floppy-Routinen in einer sehr tiefen Software-Ebene des TOS hegen, geschieht dies automatisch und ohne Wissen der Anwenderprogramme.

Die Adresse des aktuellen Critical Error Handlers steht in der Systemvariablen ‘etv_critic’ ($404).

Der Critical Error Handler kann jedoch auch aus eigenen Programmen heraus aufgerufen werden. Hier bieten sich vor allem eigene Treiberprogramme für Harddisks oder RAM-Disks an, die somit die von der Floppy her bekannten Fehlermeldungen benutzen können. Er muß jedoch nicht unbedingt aus Exception-Routinen (wie dem BIOS-TRAP) heraus aufgerufen werden, sondern kann auch von “normalen” Programmen aus benutzt werden. Der Prozessor muß sich dabei allerdings im Supervisor-Modus befinden. Es ist nicht erforderlich, das Programm deswegen mit ‘appl_init’ beim GEM anzumelden, da die Ausgabe der Alert-Boxen nur über das GEM-Resource des AES läuft.

long alrt_crit (err, drv) 
int err;	/* BIOS-Fehlernummer */
int drv;	/* Laufwerkskennung 0,1,... */
{ long ret;
	supon();	/* supon () /supoff () aus letzter Folge */
	ret = (*{long (*} {))peekl(etv_critic)} (err,drv); 
	supoff(); 
	return ret;
}

Listing 1 - Aufruf des Critical Error Handlers in C

Der Critical Error Handler kann recht einfach von C oder Assembler heraus aufgerufen werden (Listing 1 und 2). Der C-Aufruf funktioniert so zumindest bei MEGAMAX C, bei anderen Compilern müssen eventuell die wilden “Casts” geändert werden.

Dem Critical Error Handler wird die BIOS-Fehlernummer und die Laufwerkskennung (0,1,... für A:,B:,„.) übergeben. E_OK gilt hierbei nicht als ‘OK’, sondern wird als Fehler behandelt, daher muß man E_OK vorher selbst abfangen. Er liefert entweder einen Fehlercode zurück, mit dem abgebrochen werden soll, oder $10000, um anzuzeigen, daß die Funktion, die zu dem Fehler geführt hat, wiederholt werden soll. Er kann sogar E_OK zurückgeben, um den Fehler zu ignorieren. Man sollte im Fehlerfall also nicht die übergebene Fehlernummer, sondern die zurückgelieferte weiterverarbeiten. Die Register D3-D7 und A3-A6 werden vom Critical Error Handler selbst gerettet. Der Aufruf sollte so ähnlich wie in Listing 2 angegeben durchgeführt werden. Das BIOS setzt DO vorher auf -1, obwohl der Critical Error Handler dies nicht beachtet. Da dies eventuell für zukünftige Erweiterungen gedacht ist, wird es im Beispiel-Aufruf auch so gemacht.

Da das BIOS also somit über die Möglichkeit verfügt, bei Fehlem mehrere Versuche zu machen, die gewünschte Operation doch noch auszuführen, ist es also unsinnig, selbst auch noch Wiederholungen zu programmieren, wie es bei einigen Programmen leider der Fall ist.

etv_critic = $404	;Vektor	Critical Error Handler
do:
	;hier steht der Programmteil, bei dem ein Fehler auftreten kann
	;D0 enthält BIOS-Fehlercode 
	;D1 enthält Laufwerkskennung (0 für A: usw.)
	;Achtung: CPU mu_ sich im Supervisor-Modus befinden error:
	tst.1 D0	;überhaupt Fehler aufgetreten ?
	beq.s	end	;-> nein: alles ok
	movem.l	A0-A2/D1-D2,-(SP) ;Register retten
	move.w	D1,-(SP)	;Laufwerkskennung
	move.w	D0,-(SP)	;Fehlercode
	moveq #-1,D0	;Kompatibilität zum BIOS
	move.l	etv_critic,A0
	jsr	(A0)	;Critical Error Handler aufrufen
	addq.w	#4,SP
	movem.l (SP)+,A0-A2/D1-D2	;Register wieder zurück
	cmp.l	#$10000,D0	;Retry ?
	beq.s	do	;-> ja: das Ganze nochmal
end:
	;D0 enthält jetzt die Fehlermeldung für den Aufrufer

Listing 2 - Aufruf des Critical Error Handlers in Assembler

Critical Error Handler des TOS

Das TOS selbst hat zwei Critical Error Handler-Routinen. Eine ist für TOS/TTP-Programme vorgesehen und gibt immer ERROR zurück. Deswegen ist hier keine vernünftige Fehlerbehandlung möglich. Besser wäre es hier gewesen, den übergebenen Fehler unverändert zurückzugeben.

Der andere Händler ist bei GEM-Applikationen aktiv und sorgt für die Anzeige der bekannten Alert-Boxen. Beantwortet der Benutzer diese mit "WEITER”, so gibt der Critical Error Handler den Retry-Code S10000 zurück, bei “ABBRUCH” wird die Fehlermeldung unverändert zurückgegeben. Bei Meldungen mit nur einem Button wirkt dieser immer wie ein ABBRUCH-Button. Mit einem dritten Button hätte man eine Ignorier-Option (Rückgabe von E_OK) realisieren können; dies wurde vermutlich unterlassen, da das Nicht-Beachten von Diskettenfehlern gefährliche Folgen haben kann.

Zur Anzeige von Fehlermeldungen wird der Critical Error Handler nur von ‘Rwabs’ und ‘Getbpb’ benutzt, die Disk-Funktionen der unteren Ebene liefern sofort BIOS-Fehlercodes. Einzig die Verwaltung der zwei logischen Floppy-Laufwerke mittels EOTHER (s.u.) geschieht auch auf der untersten Ebene über den Critical Error Handler.

Das AES nimmt die Umschaltung zwischen GEM- und TOS-Routine vor, wenn Programme mittels AES-’shel_write’ gestartet werden, wie es z.B. beim Start vom Desktop aus der Fall ist. Shell-Programme wie z.B. die C- oder Pascal-Shells laden Programme dagegen über GEMDOS-'Pexec’ sozusagen “am GEM vorbei”. Bei TOS-Programmen wird zwar vielleicht der Bildschirm gelöscht, doch die GEM-internen Umstellungen bleiben aus. Dasselbe Problem tritt auch auf, wenn als GEM-Programme angemeldete Programme (‘.PRG’) gar keine sind, wie es vor allem bei

Spielen häufig der Fall ist. Daher darf man sich nicht wundern, wenn plötzlich Alert-Boxen erscheinen, aber vielleicht der Maus-Cursor weit und breit nicht zu sehen ist.

Beim Schreiben eines eigenen Critical Error Handlers muß man wissen, daß das AES darauf keine Rücksicht nimmt. Bei seiner Initialisierung merkt es sich den Critical Error Handler Vektor und setzt ihn jedesmal darauf zurück, wenn ein TOS-Programm gestartet wird. Vor der Rückkehr auf den Desktop wird wieder der Händler des AES eingehängt, egal was inzwischen mit dem Vektor passiert ist. Beim Start und Beenden eines GEM-Programms dagegen wird ‘etv_critic’ überhaupt nicht verändert. Programme im AUTO-Ordner können daher zwar einen eigenen Handler installieren, doch wird dieser nur bei TOS-Programmen aktiviert. Doch nun zurück zum Critical Error Handler des GEM. Das dokumentierte Listing zum TOS vom 6.2.1986 findet sich als Listing 3. Es kann als Anregung und Hilfe für das Schreiben eines eigenen Händlers dienen.

;Critical Error Handler des GEM 
;Aufruf nur im Supervisor-Mode erlaubt 
;Register D0,D1,D2,A0,A1,A2 werden verändert

FE3226 MOVE.W	$0004 (A7) ,D0	;Fehlercode
FE322A MOVE.W	$0006(A7),D1	;Drive
FE322E JSR	$FE2C06	; IPL merken und auf 7 setzen
FE3234 MOVE.L	A7,$00643A	;SP merken
FE323A MOVE.L #$00006C3E,A7	;eigenen Stack aufsetzen
FE3240 JSR	$FE2C12	;IPL zurück
FE3246 MOVEM.L D3-D7/A3-A6,-(A7)
FE324A MOVE.L #$00FEF598,A1	;Tabelle für Zuordnung Fehler -> Alert 
FE3250 MOVE.W D0,-(A7)	;übergebenen Fehlercode merken
FE3252 BNE	$FE325A
FE3254 MOVE.W #$0000,A0	;0 übergeben: Default-Fehler erzeugen
FE3258 BRA	$FE3268
FE325A NOT.W	D0	;umrechnen: -1,-2,... -> 0,1,...
FE325C MOVE.W	D0,A0
FE325E CMP.W	#$0011,D0
FE3262 BLE	$FE3268
FE3264 MOVE.W #$0000,A0	;Fehlernummer <= -19:Default-Fehler 
						;Achtung: Abfrage für Codes > 0 fehlt ! !
FE3268 MOVE.B $00(A0,A1.L) , D0	;Nummer des Alerts auf Tabelle holen
FE326C MOVE.W	D1,-(A7)	;Drive-Nummer
FE326E MOVE.W	D0,-(A7)	;Alert-Nummer
FE3270 JSR	$FE6CA6	;Alert erzeugen und ausgeben
FE3276 ADDQ.L #4,A7
FE3278 MOVE.W	(A7)+,D1	;übergebener Fehlercode
FE327A EXT.L	D1
FE327C CMP.W	#$0000,D0
FE3280 BEQ	$FE3288	;Abbruch-Button: übergebenen Code zurück 
FE3282 MOVE.L	#$00010000,D1	;OK-Button: Retry-Code zurück
FE3288 MOVE.L D1,D0 
FE328A MOVEM.L (A7)+, D3-D7/A3-A6 
FE328E MOVE.L $00643A,A7	;SP zurück
FE3294 RTS

;interne Alert-Nummern für Fehlercodes -1,..,-18
FEF598 de.b 04,01,01,02,01,01,02,02,04,02,02,02,00,03,04,
			02,06,00

Listing 3 - Dokumentiertes Listing des Critical Error Handlers

Den Fehlermeldungen -1...-18 wird über eine Tabelle einer von 7 Alert-Strings zugeordnet. Die nachfolgend aufgerufenen Routinen ermitteln zu dieser “Alert-Nummer” zuerst den Objekt-Index des Strings im AES-Resource, sowie die Nummer des vorgesehenen Default-Buttons. Als Platzhalter für die Laufwerks-Kennung enthalten manche dieser Strings ein ‘%S’. In solch einem Fall wird der String ins RAM (GEM-Variablen-Bereich) kopiert und der Platzhalter ersetzt. Die eigentliche Ausgabe auf dem Bildschirm erfolgt dann mit der normalen AES - 'form_alert ’ -Funktion.

Bei E_OK oder Fehlercodes kleiner als -18 wird der gleiche Fehler wie bei -1 erzeugt. Auf Grund eines Programmierfehlers werden jedoch positive Fehlermeldungen nicht abgefangen, was zu sehr merkwürdigen Effekten (gestreifte, gerasterte oder verschobene Alert-Boxen) oder Bomben führen kann.

Übrigens benutzt der Critical Error Handler einen eigenen Stack, so daß der Stack des Aufrufers nicht noch durch den großen Verbrauch des AES belastet wird. Kommen wir nun zu den Alert-Boxen. Die Alerts sind intern von 0 bis 6 durchnummeriert (vgl. Listing)

Nr.0: “Disk in Floppy %S: ist schreibgeschützt...”

Diese Meldung kommt nicht nur bei EWRPRO sondern auch bei dem nicht verwendeten Fehlercode -18.

Nr.1: “Floppy %S: antwortet nicht...”

Hiermit wird angezeigt, daß der FDC nicht ordentlich auf die Diskette selbst zugreifen konnte (EDRVNR, EUNCMD, EBADRQ, E_SEEK).

Nr.2: “Daten auf Floppy %S: defekt..."

Die Daten konnten nicht korrekt geschrieben oder gelesen werden, bzw. ein Formatierfehler trat auf (E_CRC, EMEDIA. ESECNF, EWRITF, EREADF, EBADSF oder -12).

Nr.3: “Anwendung kann Disk %S: nicht lesen...”

Diese Fehlermeldung ist Ihnen vermutlich noch nicht über den Weg gelaufen. Sie würde nämlich nur bei E_CHNG erscheinen, aber der BIOS-Disk-Treiber behandelt Diskettenwechsel separat und ruft den Critical Error Handler dabei nicht auf. Diese Fehlermeldung läßt sich daher hervorragend für eigene Zwecke verwenden.

Nr.4: “Ausgabegerät antwortet nicht...”

Dies tritt bei Disketten-Operationen ebenfalls nicht auf, aber der Desktop erzeugt diese Meldung, wenn beim Ausdrucken einer Datei der Drucker nicht ansprechbar ist. Dies ist die Default-Meldung und erfolgt bei E_OK, ERROR, EP APER, EUNDEV und den nicht benutzten Fehlercodes kleiner als -18.

Nr.5: “GEM Fehler"

Diese Meldung dürfte Ihnen ebenfalls unbekannt sein. Sie ist zwar für den Critical Error Handler vorgesehen, ist aber überhaupt keiner BIOS-Fehlermeldung zugeordnet und kann daher auch nicht erscheinen.

Falls Sie sich den Spaß machen wollen, so können Sie z.B. ihr altes RAM-TOS booten, mit einem Maschinensprache-Monitor die Zuordnungs-Tabelle (s. Listing 3) heraussuchen und an einer Stelle eine 5 eintragen.

Nr.6: “Bitte Disk einlegen”

Hierbei handelt es sich nicht um eine Fehlermeldung, sondern um eine Aufforderung, die das BIOS bei EOTHER ausgeben läßt.

Die 'form_error'-Funktion

GEM unterstützt nicht nur die Anzeige von BIOS-Fehlern, sondern auch die von GEMDOS-Fehlern. Dies geschieht jedoch nicht automatisch über den Critical Error Handler, sondern mittels der vom Programmierer selbst aufzurufenden AES-Funktion ‘formerror’.

Doch wer 'form_error’ so wie in den Dokumentationen angegeben ausprobiert, wird eine Enttäuschung erleben, da z.B. bei ‘form_error(EFILNF)’ nur ein ‘TOS Fehler #65503’ erscheint.

Doch nicht gleich verzweifeln. ‘form_error’ ist keineswegs kaputt, sondern nur schlecht an den ST angepaßt. Dazu muß man wissen, daß die GEMDOS-Fehlercodes aus denen des PC-DOS abgeleitet wurden. Die ST-Codes von -32 an abwärts entsprechen denen des PC-DOS von 1 an aufwärts, mit einigen Abweichungen: Einige PC-DOS-Fehler gibt es beim GEMDOS nicht, und einige Fehlermeldungen sind beim ST (Codes kleiner als -64) hinzugekommen. Bei der Implementierung hat man anscheinend ‘nur’ vergessen, Torm_error’ für die GEMDOS-Fehlermeldungen anzupassen.

Die Funktion ‘alrt_tos’ (Listing 4) rechnet die GEMDOS-Fehlernummern auf die zugehörigen PC-DOS-Nummern um, so daß bei den häufigsten Fehlem eine vernünftige Fehlermeldung erscheint, mit den Einschränkungen, die generell bei der Bedeutung der GEMDOS-Fehlermeldungen zu machen sind. Man braucht sich somit für den “Kleinkram” keine eigenen Alerts mehr zu definieren.

Im einzelnen stellt ‘alrt_tos’ mittels ‘form_error’ die folgenden Alert-Boxen zur Verfügung. Angegeben sind die GEMDOS-Codes und in Klammem der für‘form_error’ benötigte Wert.

“Diese Anwendung kann das angesprochene Objekt nicht finden.”
EFILNF (2), EPTHNF (3), ENMFIL (18)

“Die Anwendung benötigt mehr Platz zum Öffnen einer neuen Datei. Schließen Sie eine nicht benötigte Datei.”
ENHNDL (4)

“Objekt mit gleichem Namen bereits vorhanden bzw. hat den Nur-Lesen-Status.”
EACCDN (5)

“Der Arbeitsspeicher reicht nicht für diese Anwendung.”
ENSMEM (8), auch bei nicht benutzten -41 (10) und -42(11)

“Floppy mit dieser Kennung unbekannt.”
EDRIVE (15)

Bei allen anderen Fehlercodes zwischen -32768 und 63 (einschließlich der Null) kommt die bekannte allgemeine Meldung ‘TOS Fehler #’, wobei negative Nummern als positive 16-Bit-Integer aufgefaßt werden. Fehlercodes von 64 bis 32767 werden ignoriert. Dies sollte wohl eher so funktionieren, daß auch die negativen Werte ignoriert werden, aber hier wurde wieder nicht berücksichtigt, daß ‘ints’ in C normalerweise vorzeichenbehaftete Zahlen sind.

Bei den ignorierten Fehlercodes gibt ‘form_error’ immer 0 zurück. Bei den anderen ist ‘form_error’ dafür ausgelegt, auch Alerts mit zwei Buttons auszugeben. Bei OK erhielte man dann eine 1, bei ABBRUCH eine 0. Da in dieser GEM-Version aber alle Alerts nur einen Button haben, bekommt man immer 0 zurück.

Im AES Resource findet man noch den Alert “Falsche Funktionsnummer”, der eigentlich recht gut zu EINVFN passen würde. Bei EINVFN gibt’s jedoch nur den üblichen ‘TOS Fehler #65504’. Es gibt auch keinen anderen Fehlercode, der diese Meldung über ‘form_error‘ erzeugt. In einigen Fällen tritt diese Fehlermeldung aber auf, so daß ich vermute, daß GEM diesen Fehler unter bestimmten Bedingungen selbst ausgibt.

'form_error' auf dem Desktop

In Anwenderprogrammen finden sich diese Fehlermeldungen recht selten, da sich die richtige Benutzung von ‘form_error‘ anscheinend noch nicht herumgesprochen hat. Der Desktop verwendet sie jedoch, d.h. dort werden die Codes korrekt umgerechnet.

Wie schon bei der “Programmverwaltung” erläutert, können Prozesse mit ‘Pterm’ Fehlermeldungen an die sie aufrufenden Prozesse zurückgeben.

Beim Starten eines Programms “vom Desktop aus” wird es in Wirklichkeit vom AES geladen, nachdem der Desktop sich beendet hat. Nach der Terminierung des Programms bekommt zuerst das AES wieder die Kontrolle über das System, bevor es den Desktop erneut startet. Handelt es sich dabei um ein GEM-Programm, das einen Fehlercode zurückgegeben hat, so wird dieser über ‘form_error’ ausgegeben, wobei hier die Codes ebenfalls korrekt umgerechnet werden! Beendet man also ein GEM-Programm durch Rückgabe einer GEMDOS-Fehlermeldung, so zeigt das AES den Fehler dem Anwender an.

Bei TOS-Programmen erfolgt diese Anzeige jedoch nicht.

Ausblick

Nächsten Monat geht es endlich an den wichtigsten Teil des GEMDOS - das eigentliche Disketten-Betriebssystem. Begonnen wird mit der internen Verwaltung von Dateien und der File Allocation Table (FAT).


Alex Esser
Aus: ST-Computer 02 / 1988, Seite 126

Links

Copyright-Bestimmungen: siehe Über diese Seite