Fehlerbehandlung
-
Hallo,
gibt es eine möglichkeit Fehler abzufangen die dem Prozessor passieren? So etwas wie Division durch 0. Das System befindet sich im 16 BIT RealMode ohne OS.
-
Solche Fehler werden über Interrupts abgewickelt, so wird z.B. eine Division durch Null über interrupt 0 abgewickelt, ein ungültiger Opcode üebr Interrupt 6 etc.
-
Nachtrag: hab was ausm Buch abgepinselt
Nummer Typ Bedeutung 0 f Division durch Null 1 f/t Debug 2 - Nicht maskierbarer Interrupt 3 t Breakpoint 4 t Overflow 5 f Bound Range überschritten 6 f Ungültiger Opcode 7 f Gerät nicht verfügbar (z.B. FPU) 8 a Double-Fault (Fault im Fault-Handler) 9 a reserviert (früher: FPU Segmentüberlauf) 10 f Ungültiges Task State Segment (nur PM) 11 f Segment nicht vorhanden (nur PM) 12 f Stacksegment-Fault (nur PM) 13 f Allgemeine Schutzverletzung (ach wie schön ;)) 14 f Page Fault (nur PM) 15 - reserviert 16 f FPU-Exception 17 f Alignment-Fehler 18 a Hardware-Fehler 19 f SIMD-Exception 20-31 - reserviert
Typ: a = abort, f = fault, t = trap
Nachtrag 2:
Auf dem Stack liegen nach dem Aufruf des Interrupthandlers im Real-Mode:--- top of stack --- IP vor dem Aufruf <-- SP CS vor dem Aufruf <-- SP + 2 FLAGS vor dem Aufruf <-- SP + 4
[ Dieser Beitrag wurde am 18.08.2002 um 14:04 Uhr von TriPhoenix editiert. ]
-
Kannst du dazu bitte ein kleines Beispiel posten, ich hab das iegendwie nicht hinbekommen.
-
Beispiel...hab ich selber noch nie gemacht, aber kann dir ja mal die Theorie dazu schreiben
:
Zuerst mal musst du einen Interrupthandler reinhauen. Die Interrupthandleradressen liegen wie im RM üblich ab der Adresse $0000:0000 in der IVT (Interrupt vector table), nämlich 256 Stück an der Zahl. Die IVT hat folgendes Layout:
+--------+--------------------------------+ | Offset | Inhalt | +--------+--------------------------------+ | 0000 | Offset von Interrupthandler 0 | | 0002 | Segment von Interrupthandler 0 | +--------+--------------------------------+ | 0004 | Offset von Interrupthandler 1 | | 0006 | Segment von Interrupthandler 1 | +--------+--------------------------------+ | ... | ... |
Das heißt nun im Klartext, wenn z.B. ein Interrupt 0 auftritt (Division durch Null), dann holt der Prozessor die Werte die im Speicher bei 0000:0000 und 0000:0002 liegen, bildet daraus eine Adresse (im 0002 das Segment, in 0000 der Offset) und springt dorthin. Vorher hat er natürlich schon altes CS/IP gesichert, genauso wie die FLAGS. Die Adresse woird angesprungen und der dort vorhandene Code ausgeführt. Interrupthandler-Code muss mit "iret" enden, ein Return-Befehl speziell für Interrupts. Dann wird der Code normal fortgesetzt. Zu beachten ist weiter noch, dass man zwischen Faults und Traps (und aborts) unterscheiden muss. Welche Interrupts welche sind habe ich mal oben in der Tabelle hinzugefügt.
Faults sind Interrupts, die VOR der Ausführung einer Instruktion ausgelöst werden. Nach dem Interrupthandler wird die Instruktion erneut ausgeführt. Ein Beispiel dafür ist int 0. Der Prozessor merkt es frühzeitig, wenn man versucht durch Null zu teilen. Dann soll der Interrupthandler dieses Problem beseitigen und danach wird die Division erneut versucht. Wenn man dies vermeiden wollte, muss man halt die Rücksprungadresse aufm Stack schnappen und verändern (was aber auchnicht trivial ist, weil Instruktionen ja verschieden lang sind, deswegen machen int 0 wohl meistens einen Programmabbruch ;).
Traps sind Interrupts, die NACH der Ausführung einer Instruktion aufgerufen werden.
Aborts schließlich sind Interrupts, die einen fatalen Fehler bedeuten. Aborts bekommen keine Rücksprungadresse und bilden damit meistens einen Endzustand für alles, weil man den ursprünglichen Programmfluss nihct wieder herstellen kann.So, genug geredet was heißt das nun genau für uns?
Als erstes brauchen wir eine Interrupthandler-Prozedur, die irgendwo halt im Speicher liegt, halt irgedsowas wie:interrupt_handler_7: blabla bla programmtext iret
Deren Adresse brauchen wir natürlich auch. Dann berechnen wir, wo wir in der IVT die Adresse lassen müssen. Das berechnet sich über:
Offset Speicherposition = 4 * Interruptnummer; Segment Speicherposition = 4 * Interruptnummer + 2;
Wenn also z.B. unser Handler im Speicher fest auf $1234:abcd liegt und er den Interrupt 7 handeln soll, brauchen wir etwas wie
mov es, $0000 mov [es:001c], $abcd mov [es:001e], $1234
Und schon wird bei jedem Interrupt 7 die schöne Prozedur aufgerufen. Halt nur aufpassen, wenn man z.B. Interrupt 0 handelt, dass die Division nochmal vom Prozessor probiert werden wird, also muss man sich irgendwas einfallen lassen
So, genug geredet, ich hoffe das hat geholfen, ich werds ehrlichgesagt auch mal versuchen
Greetz
TriPhoenix
-
cli
push es
xor ax,ax
mov es,ax
mov bx,13h
mov cx,fehler
shl bx,2
mov [es:bx],cx
add bx,2
mov [es:bx],cs
pop es
sti
ret...
fehler:
;Allgemeine Schutzverletzung
iretoder wie ?