next up previous contents index
Next: L3DOS Up: L3 Referenzhandbuch Previous: Systeminfo/Systemsteuerung

Fehlerbehandlung

Einführung

Fehler zur Laufzeit treten auf, wenn Operationen (Prozeduren, Programmteile) geforderte Leistungen nicht erbringen können. Dabei sind Ursache und »Tiefe« des aufgetretenen Fehler zunächst unerheblich. Es ist gleichgültig, ob versucht wurde, eine nicht vorhandene Datei zu drucken, ob ein Überlauf bei Subskription auftrat oder ob eine Division durch Null bemerkt wurde. Wenn nachfolgende Programmanweisungen davon ausgehen, daß die gewünschten Leistungen erbracht wurden, ist es nicht sinnvoll, diese Anweisungen dennoch auszuführen. Ein Abbruch einer Operation liegt vor, wenn nach einem Fehler keine Anweisungen mehr ausgeführt werden, sondern die Operation verlassen wird.

Gerade bei Konstruktion komplexer Programmsysteme ist es erforderlich, Fehlerbehandlung innerhalb der Programme vornehmen zu können. Mögliche Fehlersituationen sollen innerhalb der Anwendung behandelt werden, nicht einfach vom Betriebssystem abgefangen werden.

In ELAN Programmen kann durch folgende Maßnahmen ein Abbruch eingeleitet werden:

Da alle drei Maßnahmen zum Abbruch führen können und somit eine anormale (vorzeitige) Beendigung eines Programms bewirken, werden sie im L3 System als Fehler bezeichnet. Im Prozeßkontrollblock der Task wird nach Aufruf der Prozeduren errorstop, stop oder halt ein Fehlerzustand vermerkt.

Trotz Auftreten eines solchen Fehlers kann es sinnvoll sein, ein laufendes Programm nicht abzubrechen, sondern den Abbruch zu unterdrücken, z.B. wenn

Die Prozeduren enable stop und disable stop bestimmen, ob Operationen im Fehlerzustand weiter bearbeitet oder abgebrochen werden. Enable stop legt fest, daß Programmabbrüche durch Fehler zulässig sind, disable stop besagt, daß das Programm trotz eines aufgetretenen Fehlers weiterlaufen soll.

Fehlerbehandlung innerhalb einer Task

Die Reaktion auf Fehler und die Möglichkeiten zum Aufbau von Fängerebenen sollen anhand einiger kleiner Beispiele entwickelt werden:

Beispiel:

INT VAR x; 
get (x); 
... 
disable stop; 
x := x * x; 
...

Hier wird mit disable stop verhindert, daß ein Abbruch beispielsweise durch INT-Ueberlauf auftreten kann. Die Anweisungen nach 'x * x' werden also weiter bearbeitet.

Welchen Wert hat aber nun die Variable 'x', nachdem ein Fehler aufgetreten ist? Wenn das Ergebnis der Operation '*' außerhalb des gültigen Wertebereichs liegt, wird die Operation abgebrochen und liefert in der Regel keinen definierten Wert. Der Wert von 'x' in unserem Beispiel ist also nach einem Fehler bei '*' undefiniert und kann zu Folgefehlern führen, wenn mit diesem Wert weitergerechnet wird.

An diesem Beispiel ist bereits ersichtlich, daß mit der Anwendung der Prozedur disable stop äußerst vorsichtig zu verfahren ist, weil u.U. Werte verloren gehen können bzw. mit unerwarteten Werten weitergerechnet wird.

Um zu testen, ob ein Fehler aufgetreten ist, gibt es die Informations-Prozedur is error über den Fehlerzustand. Die Prozedur liefert den Wert TRUE, wenn ein Fehler vorliegt, andernfalls FALSE. Die Prozedur clear error »löscht« den Fehlerzustand, d.h. anschließende Abfragen mit is error liefern FALSE. (Die »richtige« Reaktion auf den Fehler muß ein Programmierer natürlich selbst bestimmen.)

Beispiel:

INT VAR x; 
get (x); 
... 
disable stop; 
x := x * x; 
IF is error 
  THEN put ("'x'-Wert unzulässig"); 
       x := 0; 
       clear error 
FI; 
...

Da die Anweisung disable stop in einem Programm so lange gültig bleibt, bis sie aufgehoben wird, würden auch alle folgenden Anweisungen bei eventuellen Fehlern nicht abgebrochen, also auch in Situationen, in denen ein Abbruch erwünscht ist, um Programmierfehler zu erkennen. Deshalb können durch enable stop Abbrüche wieder zugelassen werden.

Beispiel:

INT VAR x; 
get (x); 
... 
disable stop; 
x := x * x; 
IF is error 
  THEN put ("'x'-wert unzulässig"); 
       x := 0; 
       clear error 
FI; 
enable stop; 
...

Durch diese Formulierung würden - wie gewünscht - eventuelle Fehler in den Anweisungen nach enable stop zu einem Abbruch führen.

Nicht mit clear error gelöschte Fehler führen bei enable stop ebenfalls zu einem Abbruch. In dem Programmteil

... 
disable stop; 
x := x * x; 
enable stop; 
...

würde der eventuell auftretende Fehler INT-Ueberlauf nicht abgefangen, sondern nur verzögert (nach Ausführung der Prozedur enable stop) wirksam, weil er nicht mit clear error gelöscht wurde.

Für die Behandlung von Fehlern durch Benutzer gibt es Prozeduren, die eine adäquate Reaktion auf den Fehler erlauben. Mit error message können Sie auf die erste Fehlermeldung (eines error stop) nach dem letzen clear error zugreifen (d.h. Folgefehler verändern nicht die Originalmeldung).

Die Prozedur error code liefert den Fehlercode, der bei der Prozedur errorstop zusätzlich zum Fehlertext angegeben werden kann (zur Festlegung von Fehlercodes vergl. errorcode).

error line liefert die Zeilennummer des zuletzt aufgetretenen Fehlers. Mit put error kann eine noch anstehende Fehlermeldung ausgegeben werden.

Beispiel:

INT VAR x; 
get (x); 
... 
disable stop; 
x := x * x; 
IF is error 
  THEN IF error message = "INT-Ueberlauf" 
         THEN put ("'x'-wert zu gross"); 
         ELSE put error 
       FI; 
       clear error 
FI; 
enable stop; 
...

Eine Fänger-Ebene ist eine Prozedur, die disable stop setzt und dann andere Operationen aufruft. Nach jedem dieser Aufrufe kann eine Fehlerbehandlung mit clear error durchgeführt werden. Damit ist gewährleistet, daß Fehler immer von der Fänger-Ebene »aufgefangen« und entsprechend behandelt werden.

Tritt ein Fehler auf, so wird die den Fehler auslösende Operation entweder abgebrochen oder »normal« weiter bearbeitet, je nachdem, ob enable stop oder disable stop gesetzt ist. Auf jeden Fall wird der Fehlerzustand an die aufrufende Operation weitergemeldet, die wiederum abgebrochen oder weiterbearbeitet werden kann usw. Die Weitermeldung eines Fehlers kann auch über mehrere Stufen erfolgen, solange bis der Fehler gelöscht wird. Das Setzen von enable stop / disable stop gilt nicht nur für die aktuelle Operation, sondern auch für aufgerufene Operationen (»Vererbung«). Die aufgerufenen Operationen können allerdings enable stop / disable stop neu festlegen.

Beispiel:

PROC a:             PROC b:          PROC c: 
...                 ...              ROW 10 INT VAR x; 
disable stop;       enable stop;     ... 
b;                  ...              INT VAR i :: 4711; 
IF is error         c;               x [i] := ...; 
  THEN ...          ...              ... 
     clear error    END PROC b       END PROC c 
FI; 
enable stop 
END PROC a;

In der Prozedur »a« wird die Prozedur »b« aufgerufen. Diese ruft wiederum eine Prozedur »c« auf. Für die Prozedur »c« gilt nun der Zustand enable stop der Prozedur »b' (Vererbung von enable stop). Tritt jetzt in »c« der Subskriptions-Fehler auf, wird »c' abgebrochen. Die Wirkung der fehlerauslösenden Operation ist nicht definiert.

Da aber auch die Prozedur »b« im enable stop Zustand ist, wird auch die Prozedur »b' abgebrochen. Der Fehler bleibt jedoch erhalten, wird also weitergemeldet. Dies wirkt sich so aus, daß die Anweisung »c« nicht ausgeführt wird. Da die Prozedur »a« disable stop gesetzt hat, werden die auf den Aufruf von »b« folgenden Anweisungen durchlaufen und somit durch clear error der Fehler gelöscht. In diesem Beispiel "fängt" die Prozedur »a« Fehler auf, die in den Prozeduren »b« und »c« entstehen können.

Ein solcher Fänger wird durch zwei Prozeduren konstruiert. Der eigentliche Fänger (hier: Prozedur »a') ruft eine ausführende Prozedur (hier: »b') im disable stop- Zustand auf. Die aufgerufene Prozedur setzt sofort enable stop und führt dann die eigentlichen Aktionen aus. So wird die aufgerufene Prozedur abgebrochen (kann also im Fehlerfall nicht zuviel Schaden anrichten). Der Abbruch führt bis zur Fängerprozedur ('a') hinter den Aufruf der gerufenen Prozedur ('b'). Nach Löschung eventuell auftretender Fehler ist somit sichergestellt, daß der Fänger immer weiterarbeiten kann.

Falls ein Programm kritische Bereiche enthält, in denen mehrere Fehler auftreten können, ohne daß eine sofortige Fehlerbehandlung möglich oder gewünscht ist, so können aufgetretene Fehler durch push error auf dem Fehlerstack der Task abgelegt werden und zum Zeitpunkt einer Fehlerauswertung durch pop error vom Fehlerstack geholt werden.

Wichtiger Hinweis:

Da im disable stop-Zustand kein Fehler zum Abbruch führt, kann eine Operation in diesem Zustand auch nicht durch halt abgebrochen werden. Einerseits ist das für manche Systemteile wünschenswert, andererseits können Operationen, die auf Grund von Programmierfehlern nicht terminieren (Endlosschleifen), nicht unter Kontrolle gebracht werden. Also Vorsicht! (Letztes Mittel: Task löschen)

Es ist nicht (!) garantiert, daß im Fehlerzustand aufgerufene Prozeduren ihre normale Wirkung haben. Garantiert ist dies jedoch für alle Prozeduren und Operatoren, die in diesem Abschnitt aufgeführt werden.

Prozeduren zur Fehlerbehandlung

tabular9840

clear error

PROC clear error

Wirkung: Löscht den Fehlerzustand. is error liefert anschließend wieder FALSE. error message, error code und error line werden nicht gelöscht.

disable stop

PROC disable stop

Wirkung: Unterbindet den Abbruch in aufgerufenen Operationen. disable stop gilt für die Prozedur, in der sie aufgerufen wird und in allen nachfolgend aufgerufenen Prozeduren, es sei denn, sie wird durch enable stop außer Kraft gesetzt. Wird die Operation verlassen, in der disable stop aufgerufen wurde, wird der »alte« Zustand wiederhergestellt, der vor dem Aufruf der Operation galt. disable stop kann weiterhin in einer aufgerufenen Operation durch den Aufruf von enable stop in dieser und den folgenden Operationen außer Kraft gesetzt werden.
Bemerkung: INLINE Prozeduren sollten nie disable stop aufrufen, da in diesem Falle die Eigenschaft, im disable stop zu laufen, für den Aufrufer gilt. Die Fehlerunterdrückung gilt dann wahrscheinlich für eine Prozedur, in der dieser Zustand nicht erwartet ist.

enable stop

PROC enable stop

Wirkung: Setzt die Wirkung eines Aufrufs von disable stop zurück. Fehler (errorstop, stop oder halt) in der aktuellen Operation oder den folgenden aufgerufenen Operationen führen zum Abbruch. Bisher nicht gelöschte Fehler (siehe clear error) führen sofort zum Abbruch.

error code

INT PROC error code

Wirkung: Liefert den durch errorstop gesetzten Fehlercode.

Beispiel:
PROC test: 
enable stop; 
error stop (110, "Dies ist mein Abbruch!"); 
END PROC test; 
... 
disable stop; 
test; 
put (error code);   (* liefert 110 *) 
clear error; 
enable stop
Von L3 vorbelegte Fehlercodes:

tabular9918

error line

INT PROC error line

Wirkung: Liefert die Zeilennummmer des Fehlers.
Bemerkung: Voraussetzung für die Möglichkeit, die Zeilennummer auszugeben ist, daß die Übersetzung der Prozedur, die den Fehler erzeugt, im »checkon-Modus« erfolgte.

error message

TEXT PROC error message

Wirkung: Liefert die Fehlermeldung als Text. Anhand dieser Meldung kann entschieden werden, welcher Fehler vorliegt.
Bemerkung: Eine Fehlermeldung "" (also: error stop ("")) führt zum Fehlerabbruch mit der Bedeutung "Fehlermeldung wurde bereits ausgegeben". Dementsprechend erfolgt bei der Fehlermeldung 'niltext' keine Reaktion bei put error.

error stop

PROC error stop (TEXT CONST message)

Wirkung: Bricht ab und setzt die Zeilennummer (wenn man sich im »checkon-Modus« befindet), in der der Fehler aufgetreten ist, sowie den Text message. Der Abbruch kann mit disable stop unterbunden werden. errorstop hat keine Wirkung, wenn ein noch nicht gelöschter Fehler vorliegt. Zu einer Fehlermeldung "" siehe auch die Prozedur error message. Als errorcode wird 0 gesetzt.

PROC error stop (INT CONST code, TEXT CONST message)

Wirkung: Analog obiger errorstop-Prozedur, aber mit Angabe des Fehlercodes, der durch die Prozedur error code in einer Fängerebene erfragt werden kann.

is error

BOOL PROC is error

Wirkung: Informationsprozedur auf das Vorhandensein eines Fehlers.

pop error

PROC pop error

Wirkung: Ein mit push error auf dem Fehlerstack abgelegter Fehler wird vom Stack geholt.
Bemerkung: Jeder mit push error auf dem Stack abgelegte Fehler muß explizit vom Stack geholt und mit clear error gelöscht werden.

push error

PROC push error

Wirkung: Ein Fehlereintrag wird auf dem Fehlerstack abgelegt.
Bemerkung: Ein Fehlereintrag umfaßt Fehlercode, Zeilennummer, falls im »checkon-Modus« übersetzt wurde, und Fehlermeldung. Diese Einträge können nach pop error mit den entsprechenden Prozeduren benutzt werden.

Die Kapazität des Fehlerstacks beträgt 1000 Einträge.

put error

PROC put error

Wirkung: Gibt die durch errorstop gesetzte Fehlermeldung aus, falls ein Fehler noch nicht gelöscht ist (error message).

stop

PROC stop

Wirkung: Abbruch mit Fehlermeldung 'stop'.

halt

PROC halt (TASK CONST task)

Wirkung: Im Prozeßkontollblock der Task 'task' wird das Auftreten eines Fehlers signalisiert. In der Task läuft nun die Fehlerbehandlung an.
Fehler: 'halt' unzulässig Die aufrufende Task ist weder vom Privileg, noch vom Verwandtschaftsgrad her, berechtigt, 'task' anzuhalten.

Fehlersuche

Die Fehlersuche in ELAN (und möglicherweise auch anderen) Programmen in einem L3 System kennt zwei Schwerpunktthemen. Erstes ist die Überprüfung der Kommunikation zwischen Tasks und damit natürlich zwischen Programmen, also die Protokollierung von Botschaften in Aktion. Das Zweite ist die interne Überprüfung eines Programms, das klassische Debugging. Die Beschreibung des Debuggers ist Thema von Abschnitt 11.6.

Trace

Die Prüfung von Botschaften, aber auch anderer Datenobjekte, in einer Task, die keinen Bildschirmdialog erlaubt, ist mit Hilfe der trace Routinen möglich. Damit kann in einer Datei in der Task, oder aber auch in einer zu diesem Behufe angelegten Sohntask der zu prüfenden Task, auf einfache Weise beliebiges protokolliert werden. Insbesondere kann in der Sohntask die Protokollierung von Kontrollereignissen direkt am Bildschirm verfolgt werden.

Falls beispielsweise in einer Managertask die Aufträge protokolliert werden sollen, kann einleitend vor Aufruf der Prozedur manager eine Tracetask gestartet werden (start tracer). In der eigentlichen Managerprozedur werden dann empfangene Botschaften etc. durch traceline (hex(msg)) direkt in der Tracetask angezeigt.

Prozeduren

tabular10066

trace

PROC trace (BOOL CONST flag)

Wirkung: Allgemeine Kontrolle der trace-Operationen. Falls trace auf TRUE gesetzt ist, werden folgende traces in eine Protokolldatei "\TRACE1" geschrieben. Falls eine Kontrolltask eingerichtet wurde, wird auch diese Protokolldatei dort geführt. Die Protokolldatei wird beim ersten Aufruf von trace erzeugt. Falls die Datei voll ist (9996 Zeilen), wird eine Datei "\TRACE2" erzeugt. Falls trace auf FALSE gesetzt ist, sind die folgenden trace Prozeduren deaktiviert.

BOOL PROC trace

Wirkung: liefert Wert des trace Flag.

trace on

PROC trace on

Wirkung: trace (TRUE).

trace off

PROC trace off

Wirkung: trace (FALSE).

trace

PROC trace (INT CONST value)

Wirkung: Protokolliert 'value', gefolgt von einem Leerzeichen. Der Botschaftsvektor wird vor trace gesichert und anschließend wiederhergestellt.

PROC trace (TEXT CONST text)

Wirkung: Protokolliert 'text', gefolgt von einem Leerzeichen. Der Botschaftsvektor wird vor trace gesichert und anschließend wiederhergestellt.

trace dump

PROC trace dump (INT CONST addr, length)

Wirkung: Protokolliert ab Adresse 'addr' 'length' Bytes. Der Botschaftsvektor wird vor trace dump gesichert und anschließend wiederhergestellt.

PROC trace dump (TEXT CONST string)

Wirkung: Protokolliert 'string' als Textdenoter. Steuerzeichen in 'string' werden decodiert. Der Botschaftsvektor wird vor trace dump gesichert und anschließend wiederhergestellt.

trace line

PROC trace line

Wirkung: Zeilenvorschub in Protokolldatei/Bildschirmausgabe.

PROC trace line (TEXT CONST event)

Wirkung: Protokolliert 'event' und macht einen Zeilenvorschub. Falls mehrere Zeilen weniger als 4 Zeichen enthalten, werden sie zusammengezogen.

show trace

PROC show trace

Wirkung: Zeigt die aktuelle Protokolldatei am Bildschirm an.

start tracer

PROC start tracer

Wirkung: Eine Protokolltask namens name(myself) + ".trace" wird erzeugt und alle traces werden an diese Task geschickt. Die Protokolldatei befindet sich in dieser Task. Wenn die Protokolltask an einen Bildschirm gekoppelt wird, werden die trace Aufträge direkt am Bildschirm angezeigt. Folgende Aktionen sind im Monitordialog mit der Protokolltask zulässig:

p:
pause. Das Programm wird gestoppt, bis irgendeine Taste gedrückt wird.
s:
show trace (s.o.)
m:
monitor. Startet den normalen Monitordialog in der Protokolltask. Durch exit wird der Protokollmonitor wieder gestartet.
u:
unlink
CursorUp/Down
verlängert/verkürzt die Verzögerung nach Ausgabe einer neuen Zeile.

stop tracer

PROC stop tracer

Wirkung: Löscht die Protokolltask. Protokollausgaben werden nun wieder in einer Datei in der Task selbst gespeichert.

tracer

TASK PROC tracer

Wirkung: Liefert den Taskbezeichner der Protokolltask.

tracer timeout

PROC tracer timeout (INT CONST new timeout)

Wirkung: Stellt die Verzögerung für Ausgaben des Protokollmonitors ein.

INT PROC tracer timeout

Wirkung: Liefert die eingestellte Verzögerung für Ausgaben des Protokollmonitors. Voreingestellt sind 200 Millisekunden.

Der ELAN Quellcode Debugger

Der ELAN-Quellcode-Debugger (ESD, ELAN Sourcecode Debugger) ist ein Debugger der in Privilegstufe 3 des Prozessors läuft. Folglich bietet er Zugriff nur auf Objekte innerhalb der Task, in der er aktiviert ist.

Der Debugger unterstützt

Der Aufruf des Debuggers erfolgt durch Exceptions.

Exkurs: Exceptions

Exceptions sind Unterbrechungen des Programmflusses, die durch den Prozessor selbst hervorgerufen werden. (Im Gegensatz dazu sind Interrupts diejenigen Unterbrechungen, die extern verursacht werden und dem Prozessor über Leitungen gemeldet werden.) Exceptions entstehen entweder durch Fehler, die vom Prozessor im Programmfluß entdeckt werden oder durch Anweisung im Programm (programmierte Exceptions). Standardbeispiele für Fehler sind die Division durch 0 und die beim 80386 allgegenwärtige, weil semantisch schwer beladene, General Protection Exception. Eine programmierte Exception ist insbesondere der Befehl INT 3 (Breakpoint, Opcode CC)

Der Aufruf des ESD

Sobald der Prozessor eine Exception entdeckt, wird eine entsprechende Routine (exception handler) aufgerufen. Einige dieser Exceptions haben für den Benutzer keine Bedeutung (z.B. der Zugriff auf den Bildschirmspeicher), andere führen zum Aufruf des ESD. Eine spezielle Exception wird von der ELAN-Prozedur debug ausgelöst, die in den INT 3 Befehl übersetzt wird.

Wem das Wort debug zu lang ist oder unpassend erscheint, der kann auch ke benutzen. »ke« bedeutet »Kontrollereignis« und ist eine Referenz der L3 Entwickler an die Telefunken TR 440, den Rechner, mit dem für sie alles begann. Zugleich ist es nach Meinung des Autors der schlagende Beweis dafür, daß auch ein modernes, durchdesigntes Betriebssystem stets einige bizarre Details enthält, die keinesfalls auf Nachlässigkeit oder dergleichen beruhen.

Bei Aufruf des ESD wird der aktuelle Bildschirminhalt gesichert. Der ESD schaltet explizit in den alphanumerischen Darstellungsmodus um und eröffnet ein Bildschirmfenster.

Aufbau und Handhabung des ESD

In der Kopfzeile des ESD-Fensters wird der Grund für den Aufruf angezeigt. Folgende Anzeigen sind möglich:

Ein errorstop (siehe Kap.12) führt z.Z. nicht zum Aufruf des ESD. Daher können Fehler wie "Text nicht initialisiert" nicht abgefangen werden.

Im Fenster ist fernerhin angezeigt, in welcher Umgebung unterbrochen wurde:

Interrupted within DOS 
 
Interrupted within RTS 
 
Interrupted within ELAN-Compiler 
 
Interrupted at  LINE  xxxx 
            in PACKET nnnn

In der unteren Zeile des ESD-Fensters sind die an dieser Stelle möglichen Kommandos aufgeführt. Einige Funktionen sind immer aufrufbar, auch wenn sie aus Platzmangel nicht explizit aufgeführt sind:

F8:
Quittieren (verlassen) Zurück zum übergeordneten Menü. Falls diese Taste im Hauptmenü gedrückt wird, wird der ESD verlassen.

F9:
Wiederholtes Quittieren Rücksprung zum Hauptmenü. Wird diese Taste im Hauptmenü gedrückt, hat sie keine Auswirkung.

F10:
Monitor Der L3 Monitor wird aufgerufen. Mit dem exit-Kommando kommt man zurück in den ESD. Zur Erinnerung, daß der Monitor vom ESD aufgerufen wurde, wird die Schachtelungstiefe des Aufrufs angezeigt.

Shift F5:
Der ursprüngliche Bildschirminhalt wird wiederhergestellt. Auf diese Weise lassen sich Ausgaben des unterbrochenen Programms besichtigen. Ein beliebiger Tastendruck läßt ESD weiterlaufen.

TAB:
Zweites ESD Fenster Ein zweites ESD Fenster wird eröffnet. Falls es eröffnet ist, kann so zwischen den Fenstern gewechselt werden.

?:
ESD Hilfe

Beim ESD Aufruf werden folgende Tasten angeboten:

F1:
Dump; siehe Seite gif

F2:
Disass; siehe Seite gif

F3:
Regs; siehe Seite gif

F4:
Trace; siehe Seite gif

F5:
View; siehe Seite gif

F6:
File; siehe Seite gif

F7:
Config; siehe Seite gif

Eingabe symbolischer Adressen

Wenn ESD die Eingabe einer Adresse erwartet, erscheint:

Address:

Die Eingabe wird über die ELAN-Prozedur editget gelesen, d.h.

Eine zulässige Adreßangabe kann bestehen aus:

und daraus zusammengesetzten Ausdrücken.

Leerzeichen haben keine Bedeutung und können außer in den Datenraumnamen überall eingefügt werden. Auf die V86 Segment Register kann nur dann zugegriffen werden, wenn der Interrupt im V86 Modus ausgelöst wurde.

Hinweis: Im 386 Modus sind die Ausdrücke

Address: sp
und
Address: W esp

gleichbedeutend. Aus technischen Gründen wird im V86 Modus mit esp der 386- Stackpointer angesprochen. Daher werden bei Address: W esp die unteren 16 Bit des 80386-Stackpointers als Adresse angesprochen und bei Address: sp wird der V86- Stackpointer als Adresse betrachtet.

Beispiele:

Direkte Adressierung

Address: 100

Die Basisadresse wird auf 0x100 gesetzt.

Address: eip

Defaultwert. Die Basisadresse wird auf den Wert des EIP-Registers gesetzt.

Address: eip + 100

Die Basisadresse wird auf den Wert des EIP-Registers plus 0x100 gesetzt.

Indirekte Adressierung

Address: [ebp]

In ELAN enthält das EBP-Register einen Pointer auf den Prozedur-Stack. Mit indirekter Adressierung wird über das EBP Register die Basisadresse auf den Prozedur-Stack der Vorgängerprozedur gesetzt.

Address: [80 0a 00]

Die Basisadresse wird auf den Anfang einer ELAN-Prozedur gesetzt. Hier wird sie auf den Anfang des letzten Paketes gesetzt.

Address: [ebp+10]

In ELAN steht der letzte Parameter auf Adresse EBP + 0x10. Die Basisadresse wird nun auf den Parameter selbst gesetzt.

Address: [ebp] + 10

Jetzt wird die Basisadresse auf die Adresse des letzten Parameters des Vorgängers gesetzt.

Beschränkung von Werten

Address: B 128

Der Wert wird auf 8 Bit Länge begrenzt. Die Basisadresse wird auf 128 gesetzt.

Address: W [4*21]

Die Basisadresse wird auf den Offset-Teil (nur 16 Bits) der Adresse 4*21 gesetzt.

Address: W [4*21]*10 + W [4*21+2]

Die Basisadresse wird auf den Anfang der INT 21 Routine gesetzt.

V86 Pointer

Address: P [4*21]

Wie oben, die Basisadresse wird auf den Anfang der INT 21 Routine gesetzt.

OFF SEG Adressierung

Address: cs:100

Die Basis wird nach V86 Art berechnet.

Address: P [ss:sp]

Angenommen, ein INT xx Befehl wird ausgeführt. Die Basisadresse wird dann auf die Return-Adresse gesetzt.

Um dieses Beispiel noch etwas auszuweiten, nehmen wir an, daß die Interrupt- Routine auf ihren eigenen Stack zeigt. Die alten Werte von SS und SP werden dann in CS:400 und CS:402 gespeichert.

Address: cs:400 zeigt diese Werte an.

Address: P [cs:400] zeigt den Inhalt des alten Stacks, also die Return-Adresse.

Address: P [ P[cs:400] ] - 2

Ansprechen von Datenräumen

Mit Address: "table":1000 greift man auf einen benannten Datenraum zu.

Die Basisadresse wird auf 0x1000 relativ zum Anfang des Datenraumes "table" gesetzt. Solange kein neuer Datenraum spezifiziert wird, wird jede Adresse relativ zum Anfang von "table" interpretiert.

Address: [800]

Hier wird der Wert auf Adresse 0x800 im Datenraum "table" als neue Basisadresse genommen.

Wenn der Datenraum keinen Namen hat, wie z.B. der Standarddatenraum, kann über die Datenraumnummer auf den Datenraum zugegriffen werden:

Address: DS [1]:200 000

Jetzt weisen die Adressen auf den Standarddatenraum.

Eine Adresse kann auch einfach aus einem Datenraumnamen bestehen:

Address: "table1":

Der Wert der aktuellen Adresse bleibt dann unverändert, adressiert aber jetzt einen Speicherplatz im Datenraum "table1". Diese Möglichkeit kann sehr nützlich sein, wenn eine Menge von Datenräumen auf eine spezielle Zeichenfolge an einer festen Adresse untersucht wird.

Fehler:

invalid:
Syntaxfehler. Ein vertikater Strich zeigt

DS does not exist:
Ein Datenraum mit dem angegebenen Namen existiert nicht.

DSID invalid:
Ein Datenraum mit der angegebenen Datenraumnummer existiert nicht.

out of bounds:
Der Wert der Adresse überschreitet die Grenzen des Datenraumes.

Bemerkung:

Der Lernmodus des Editors kann genutzt werden, um die Eingabe von Adreßausdrücken zu vereinfachen.

<ESC> <HOP> P [ ] <C> LEFT> <RUBIN> <ESC> <HOP> P

Mit <ESC> P wird der Zeigerausdruck vorbereitet. Eingaben werden sofort zwischen den Klammern eingefügt.

Adreßdarstellung

Intern führt der ESD eine Basisadresse und eine aktuelle Adresse (Offset). Die Basisadresse ist der Wert der symbolischen Adresse, die zuletzt berechnet wurde. Die aktuelle Adresse besteht aus der Basisadresse und einem Offset. Die Dump-Routine beginnt z.B. mit der Basisadresse. Beim Aufwärtsscrollen im Fenster bleibt die Basisadresse unverändert, während der Offset erhöht wird. Beim Abwärtsscrollen wird der Adreßoffset vermindert. Dadurch können negative Offsets auftreten.

Eine Adresse wird, falls der Offset ungleich Null ist, wie folgt angezeigt:

Address: aaaa          Offset: oooo

Falls zwei Fenster benutzt werden, hat jedes eigene Basis- und Offsetadresse (siehe auch Seite gif).

Dump

Mit Hilfe der Dump-Funktion kann der Inhalt eines Speicherplatzes gesucht, angezeigt und verändert werden.

Eine Dump-Zeile besteht aus

Der Cursor steht auf der aktuellen Adresse.

Beispiel für einen Speicherauszug (Dump):

 
002c1e00 ff 15 cc 03 80 00 cc-68-  ec d5 ac 00 ff 15 b4 19 .......h........ 
002c1e10 80 00 68 f4 d5 ac 00 ff   15 20 33 80 00 68 f8 d5 ..h...... 3..h.. 
002c1e20 ac 00 ff 15 b4 19 80 00   68 fc d5 ac 00 ff 15 b4 ........h....... 
002c1e30 19 80 00 68 00 d6 ac 00   ff 15 b4 19 80 00 68 04 ...h..........h. 
002c1e40 d6 ac 00 ff 15 b4 19 80   00 ff 35 44 26 80 00 ff ..........5D&... 
002c1e50 15 cc 03 80 00 c9 8d 64   24 08 c3 ff 15 b4 19 80 .......d$....... 
002c1e60 00 ff 35 44 26 80 00 ff   15 cc 03 80 00 c9 8d 64 ..5D&..........d

Die Kommandozeile bietet die folgenden Befehle an:

F1:
Addr
F2:
Disass
F3:
BeginSearch
F4:
ContSearch
F5:
Stack

Wirkung der Kommandos:

Der Cursor kann beliebig positioniert werden. Eine Adresse unterhalb von 0 oder oberhalb von pages(ds) * 4096 kann nicht erreicht werden.

HOP:
Schreiben

Um versehentliches Schreiben zu verhindern ist die Tastatur gesperrt. Durch diese Taste wird die Sperre aufgehoben, es sind nun Eingaben möglich. Die Eingabe wird durch CR abgeschlossen.

F1:
Addr

Liest eine neue Basisadresse. Die aktuelle Adresse entspricht nun der Basisadresse.

F3:
Search

Eine Zeichenfolge kann beginnend bei der aktuellen Adresse gesucht werden. Bei:

Pattern (X:hex, C:char):

kann eine Zeichenkette eingegeben werden. Das erste Zeichen muß entweder ein X sein, um eine hexadezimale Eingabe zu kennzeichnen, oder ein C für die Eingabe von ASCII-Zeichen. Die zuletzt eingegebene Zeichenfolge wird angezeigt und kann ediert werden. Die Eingabe wird beendet mit der Taste CR . Der Cursor wird während der Suche auf das Feld ContSearch in der Kommandozeile gesetzt. Zum Lesen der "Zeichenfolge wird die ELAN Prozedur editget benutzt.

Die Suche wird beendet, wenn

F4:
ContSearch

Die Suche wird mit der zuletzt eingegebenen Zeichenfolge fortgesetzt. Die aktuelle Adresse kann sich inzwischen geändert haben und es können in der Zwischenzeit beliebige andere Aktionen stattgefunden haben (incl. Beenden und Aufrufen des ESD).

Disassembler

Der Disassembler kann sowohl 80386 als auch V86 Befehle decodieren. 80387 Instruktionen werden nicht korrekt decodiert. Die Ausgabe des Disassemblers kann nicht als Eingabe für einen Assembler benutzt werden, da sie eine eigene Syntax hat.

Beispiel für eine Disassemblierung:

00201e07  68 ec d5 ac 00      push    '00acd5ec' 
002c1e0c  ff 15 b4 19 80 00   call    [008019b4] 
002c1e12  68 f4 d5 ac 00      push    '00acd5f4' 
002c1e17  ff 15 20 33 80 00   call    [00803320] 
002c1e1d  68 f8 d5 ac 00      push    '00acd5f8' 
002c1e22  ff 15 b4 19 80 00   call    [008019b4] 
002c1e28  68 fc d5 ac 00      push    '00acd5fc' 
 Address: eip Offset: 26

Die aktuelle Instruktion (EIP) ist invers dargestellt.

Die Kommandozeile bietet die folgenden Befehle an:

F1:
Addr
F2:
V86 oder 386
F3:
Regs
F4:
GoTo
F5:
GoSub
F6:
Single Step
F7:
Breakpoint

Mit den Cursortasten kann beliebig positioniert werden. Die aktuelle Adresse wird entsprechend verändert.

Hinweis: Befehle rückwärts zu dekodieren liefert unsichere Ergebnisse.

F1:
Address

Liest eine neue Basisadresse. Die Disassemblierung beginnt mit dieser Adresse.

F2:
V86 oder 386

Wenn F2 gedrückt wird, wird der Modus umgeschaltet. Der Code wird erneut disassembliert, beginnend mit der aktuellen Adresse.

F3:
Register

Der Inhalt der allgemeinen Register wird in einem neuen Fenster angezeigt. Falls der Interrupt im V86 Modus ausgelöst wird, werden auch die Segmentregister angezeigt.

Beispiel für einen Registerdump, wenn der Interrupt im 80386 Modus ausgelöst wurde:

Registers 
 
EAX = 002bb544    EDI = 00ad3000    EIP = 002c1e07 
EBX = 00a01530    ESI = 00ace000    INT = 4e490003 
ECX = 000000d0    EBP = 001ff8f4 EFLAGS = 00000216 
EDX = 00ad3000    ESP = 001ff8d0     CS = 53430017

Beispiel für einen Registerdump, wenn der Interrupt im V86 Modus ausgelöst wurde:

Registers 
 
EAX = 0000ff00    EDI = 00000100    EIP = 0000dd9d I SP = 0520  SS = 0dd1 
EBX = 00000063    ESI = 00000050    INT = 4e490001 I ES = 0dc1  FS = 00b8 
ECX = 00000004    EBP = 001f0084 EFLAGS = 000233c6 I DS = 0dd1  GS = 0016 
EDX = 000000a6    ESP = 001ff9e8     CS = 53430088 I CS = 0dd1

Wiederum ist die Tastatur gesperrt, um unabsichtliches Schreiben zu verhindern. Durch HOP kann die Sperre entriegelt werden. Mit den Cursortasten läßt sich das gewünschte Register erreichen. Nur eine hexadezimale Eingabe, die mit CR abgeschlossen wurde, ändert den Registerinhalt. Der Schreibschutz wird direkt wieder aktiviert. Die Inhalte der Segmentregister im V86 Modus lassen sich nicht ändern.

F4:
GoTo

Nur, wenn das ausgewählte Statement ein jmp-, jcc- oder call-Befehl ist. Die Zieladresse des Befehls wird als neue Basisadresse genommen. Die Disassemblierung wird dann mit der neuen Basisadresse fortgesetzt.

F5:
GoSub

Nur, wenn das ausgewählte Statement ein jmp-, jcc- oder call-Befehl ist. Die Zieladresse des Befehls wird als neue Basisadresse einer rekursiv eingebetteten Disassemblierung genommen. Wenn der eingebettete disassemblierte Teil verlassen wird, wird der vorherige Wert der aktuellen Adresse wieder eingestellt.

Hinweis: Die F9 Funktion (wiederholtes Quittieren) verläßt alle eingebetteten disassemblierten Teile und bewirkt einen Rücksprung zum Hauptmenü.

F6:
Single Step

Das Programm wird schrittweise ausgeführt.

Shift F6:
Single Step

Nur, wenn das ausgewählte Statement ein ELAN call-Befehl mit dem Opcode FF 15 ist. Wenn der Aufruf ausgeführt wird, wird die ELAN Prozedur schrittweise auf der Ebene der einzelnen Statements abgearbeitet.

F7:
Breakpoint

Hiermit wird ein Breakpoint für die Befehlsausführung gesetzt, wobei die Debug Register des 80386 benutzt werden. Der Befehl wird mit einem B markiert. Wenn bereits ein Breakpoint gesetzt ist, wird dieser gelöscht. Falls kein freies Debug Register mehr verfügbar ist, wird eine Fehlermeldung angezeigt.

Beispiel für einen Codeausschnitt mit Breakpoint:

00201e07   68 ec d5 ac 00      push    '00acd5ec' 
002c1e0c   ff 15 b4 19 80 00   call    [008019b4] 
002c1e12   68 f4 d5 ac 00      push    '00acd5f4' 
002c1e17 B ff 15 20 33 80 00   call    [00803320] 
002c1e1d   68 f8 d5 ac 00      push    '00acd5f8' 
002c1e22   ff 15 b4 19 80 00   call    [008019b4] 
002c1e28   68 fc d5 ac 00      push    '00acd5fc' 
 Address:  eip Offset: 26

Trace, View

Voraussetzung: Eine Quelldatei ist für das aktuelle Paket definiert. Das Fenster des L3 Editors wird in der unteren Hälfte des Bildschirmes geöffnet und zeigt die Quelldatei an. Der Cursor steht in der aktuellen Zeile.

File

In einigen Fällen muß der ESD den Namen der Datei kennen, die den Quellcode eine bestimmten Paketes enthält. Die Verbindung zwischen einer Datei und einem oder mehreren Paketen wird in drei Schritten hergestellt:

  1. Auswahl der Eintrittsstelle.
  2. Eingabe des Dateinamens.
  3. Das Editorfenster wird angezeigt. Dann können die Namen der Pakete eingegeben werden, einer pro Zeile. Die Eingabe wird mit ESC q abgeschlossen.

Der ESD überprüft dann, ob die Datei existiert. Jetzt können View oder Trace benutzt werden.

Config

Hier kann die Einstellung globaler Parameter des ESD verändert werden. Es lassen sich einstellen:

  1. Die Größe des ESD Fensters läßt sich auf einen Wert zwischen 7 und 12 Zeilen einstellen. Falls ein zweites Fenster eröffnet wird, erhält es den restlichen Platz des Bildschirms.
  2. Standardadreßeinstellung für oberes Fenster.
  3. Standardadreßeinstellung für unteres Fenster.
  4. Startaktion bei ESD Aufruf. Die Voreinstellung ist -.-. Ist eine der Optionen Dump, Regs oder Disass eingestellt, wird nach Aktivierung des ESD das untere Fenster geöffnet und die eingestellte Funktion ausgeführt. Bei Dump oder Disass wird die Einstellung aus Punkt 3 als Startadresse genommen.

Das Modell des ELAN-Laufzeit-Systems

Prozeduren, Operatoren, Pakete

Im ELAN System gibt es PACKETs, PROCs (Prozeduren) und OPs (Operatoren). Intern werden Operatoren wie Prozeduren mit der gleichen Anzahl von Parametern behandelt. Das Ergebnis wird als zusätzlicher Parameter betrachtet. Daher werden beide im folgenden mit Prozedur bezeichnet. Der Code für ein Paket wird generiert, als sei das Paket eine parameterlose Prozedur mit zwei Ausnahmen:

Parameter werden by reference übergeben, das Ergebnis ist der letzte Wert. Für einen Prozeduraufruf generiert der ELAN Compiler den 80386 Befehl call near, memory indirect.

Beispiel:

push [a030010] 
lea  eax, ebp-20 
push eax 
call [8003f0]

Benutzung der Register

Die Register EBP und ESP beschreiben den aktuellen Stack-Status, die anderen Register enthalten Operanden und Adressen. Beim Eintritt in eine Prozedur werden diese Register nicht gesichert.

Der Stack

Der ELAN Prozedurkopf enthält die folgenden Befehle:

push   linenr + packetlink SHL 16 
push   [ebp+4] 
enter  locoff 
mov    size,[ebp+6]

Der erste push-Befehl schreibt ein Doppelwort auf den Stack. Die oberen 16 Bit enthalten einen Verweis auf den Namen des Paketes, zu dem die Prozedur gehört. Die unteren 16 Bit enthalten eine Zeilennummer relativ zum Anfang der Quelldatei.

Der zweite push-Befehl kopiert das Statuswort des dynamischen Vorgängers. Die oberen 16 Bit enthalten die Größe des Stacks. Dieser Wert wird durch den mov-Befehl ersetzt.


next up previous contents index
Next: L3DOS Up: L3 Referenzhandbuch Previous: Systeminfo/Systemsteuerung

Michael Hohmuth
Sun Mar 31 04:49:53 MET DST 1996