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 damit ein Eingriff in die Autonomie der Task geschieht, unterliegt diese Art der Fehlerzustellung folgenden Regeln:
Jede privilegierte Task (SYSUR und Nachfolger) darf jeder anderen Task ein halt zustellen und jede Task darf ihren Nachkommen ein halt zustellen.
Da halt ein Auftrag an die Task SUPERVISOR ist (siehe auch Supervisor Protokoll), kann die Einhaltung dieser Regeln sicher geprüft werden.
Trotz Auftreten eines solchen Fehlers kann es sinnvoll sein, ein laufendes Programm nicht abzubrechen, sondern den Abbruch zu unterdrücken, z.B. wenn
Die Reaktion auf Fehler und die Möglichkeiten zum Aufbau von Fängerebenen sollen anhand einiger kleiner Beispiele entwickelt werden:
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.)
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.
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.
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.
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.
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
PROC error stop (TEXT CONST message)
PROC error stop (INT CONST code, TEXT CONST message)
Die Kapazität des Fehlerstacks beträgt 1000 Einträge.
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.
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.
PROC trace dump (INT CONST addr, length)
PROC trace dump (TEXT CONST string)
PROC trace line (TEXT CONST event)
PROC tracer timeout (INT CONST new timeout)
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
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)
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.
In der Kopfzeile des ESD-Fensters wird der Grund für den Aufruf angezeigt. Folgende Anzeigen sind möglich:
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:
Beim ESD Aufruf werden folgende Tasten angeboten:
Eingabe symbolischer Adressen
Wenn ESD die Eingabe einer Adresse erwartet, erscheint:
Address:
Die Eingabe wird über die ELAN-Prozedur editget gelesen, d.h.
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: spund
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:
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.
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.
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.
Address: P [4*21]
Wie oben, die Basisadresse wird auf den Anfang der INT 21 Routine gesetzt.
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
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:
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 ).
Mit Hilfe der Dump-Funktion kann der Inhalt eines Speicherplatzes gesucht, angezeigt und verändert werden.
Eine Dump-Zeile besteht aus
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:
Wirkung der Kommandos:
Der Cursor kann beliebig positioniert werden. Eine Adresse unterhalb von 0 oder oberhalb von pages(ds) * 4096 kann nicht erreicht werden.
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.
Liest eine neue Basisadresse. Die aktuelle Adresse entspricht nun der Basisadresse.
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
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).
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:
Mit den Cursortasten kann beliebig positioniert werden. Die aktuelle Adresse wird entsprechend verändert.
Hinweis: Befehle rückwärts zu dekodieren liefert unsichere Ergebnisse.
Liest eine neue Basisadresse. Die Disassemblierung beginnt mit dieser Adresse.
Wenn F2 gedrückt wird, wird der Modus umgeschaltet. Der Code wird erneut disassembliert, beginnend mit der aktuellen Adresse.
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.
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.
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ü.
Das Programm wird schrittweise ausgeführt.
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.
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
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.
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:
Der ESD überprüft dann, ob die Datei existiert. Jetzt können View oder Trace benutzt werden.
Hier kann die Einstellung globaler Parameter des ESD verändert werden. Es lassen sich einstellen:
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:
Beispiel:
push [a030010] lea eax, ebp-20 push eax call [8003f0]
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 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.