x86-Programmierung und -Betriebsarten (Teil 3)

27.01.2004 von Hans-Peter Messmer und Klaus Dembowski
Die sechsteilige Artikelserie behandelt die Speicheradressierung und die x86-Betriebsarten. Im dritten Teil der Serie widmen wir uns speziell den Software- und Hardware-Interrupts sowie den Exceptions.

Aus dem Zusammenwirken von Busschnittstelle, Prefetch-Queue, Ausführungseinheit et cetera des Prozessors ist unschwer zu erkennen, dass die CPU unaufhörlich Befehle ausführt. Hat sie einen Befehl abgearbeitet, so wird der nächste geladen und ausgeführt. Auch wenn Ihr PC scheinbar darauf wartet, dass Sie einen Befehl oder Text eingeben, bedeutet das nicht, dass der Prozessor auch wirklich angehalten wird, um erst mit Ihrer Eingabe die Arbeit wieder aufzunehmen. Vielmehr läuft im Hintergrund eine Routine ab, die ständig die Tastatur überprüft, um zu ermitteln, ob Sie bereits ein Zeichen eingetippt haben. Es wird also eine Programmschleife ausgeführt.

In Abhängigkeit von der Quelle einer Unterbrechung (der deutsche Ausdruck für Interrupt) unterscheidet man drei Kategorien von Interrupts:

Serie: x86-Programmierung und -Betriebsarten

Teil 1

Code-/Datensegment, Befehlszähler und Stack

Teil 2

Adressierungsarten und Befehlsdekodierung sowie Real Mode

Teil 3

Interrupts und Exceptions

Teil 4

Der Protected Mode

Teil 5

Paging und die MMU

Teil 6

Der Virtual-8086-Modus

Die Artikelserie basiert auf dem Kapitel 6 des "PC Hardwarebuch" von Addison-Wesley. In unserem Buch-Shop können Sie das über 1200 Seiten starke Kompendium bestellen oder als eBook downloaden.

Software-Interrupts

Ein Software-Interrupt wird gezielt durch einen INT-Befehl ausgelöst. Zum Beispiel ruft der Befehl INT 10h den Interrupt mit der Hex-Nummer 10h auf. Das Bild "Prozedur" zeigt den Ablauf, der bei einem Interrupt im Real Mode ausgeführt wird. Ein Interrupt-Vektor ist eine Adresse im Format Segment:Offset, die auf den Einsprungpunkt des zugehörigen Handlers zeigt. Der 80386 lädt EIP und CS mit den Werten in der Tabelle.

Im Real-Mode-Adressraum sind die ersten 1024 Byte (1 KByte) für die Interrupt-Vektortabelle reserviert. Diese Tabelle weist für jeden der 256 möglichen Interrupts einen so genannten Interrupt-Vektor auf. Von allen Speicherverwaltungsregistern besitzt das Interrupt-Deskriptor-Table-Register IDTR bereits im Real Mode eine Bedeutung. Es speichert nämlich die Basisadresse und das Limit der Real-Mode-Deskriptortabelle.

Nach einem Prozessor-Reset wird das IDTR standardmäßig mit den Werten 00000000h für die Basis und 03ffh für das Limit geladen. Das entspricht exakt einer 1-KByte-Tabelle bei Segment 0000h, Offset 0000h. Durch die Befehle LIDT (Load IDTR) und SIDT (Store IDTR) können die beiden Werte aber verändert und die Tabelle dadurch mit anderer Größe an eine beliebige Stelle des Real-Mode-Adressraums verschoben werden. Achten Sie aber darauf, dass die Tabelle auch alle Vektoren für die möglicherweise auftretenden Interrupts aufnehmen kann. Sonst ist eine Exception 8 die Folge.

Interrupt-Vektoren

Jeder Interrupt-Vektor ist 4 Byte lang und gibt im Intel-Format die Adresse Segment:Offset des Interrupt-Handlers an, der den Interrupt bedient. Da ein Interrupt meist eine bestimmte Ursache hat, wie die Anforderung einer Betriebssystemfunktion, der Empfang eines Zeichens über die serielle Schnittstelle et cetera, behandelt der Interrupt-Handler diese Ursachen in geeigneter Weise. Er führt also beispielsweise die Betriebssystemfunktion aus oder nimmt das empfangene Zeichen entgegen. Durch Ersetzen des Handlers kann einem Interrupt auf einfache Weise eine andere Funktion zugeordnet werden.

Die Zuordnung von Interrupt und Interrupt-Vektor erfolgt auf einer Eins-zu-eins-Basis, das heißt, dem Interrupt 0 wird der Interrupt-Vektor 0 (an der Adresse 0000:0000), dem Interrupt 1 der Interrupt-Vektor 1 (an der Adresse 0000:0004) und so weiter zugeordnet. Damit muss nur die Nummer des Interrupt mit 4 multipliziert werden, und man erhält den Offset des Interrupt-Vektors im Segment 0000h. Der Prozessor führt genau diese Multiplikation aus, um aus der Interrupt-Nummer dessen Adresse zu ermitteln. Neben dem Bereich f000:fff0 bis f000:ffff für den Prozessor-RESET existiert damit ein zweiter für den Prozessor reservierter Adressbereich. Diese Adressbereiche stehen für Anwendungsprogramme nicht zur Verfügung. Ein Überschreiben der Interrupt-Vektortabelle mit ungültigen Werten hat katastrophale Folgen: Beim nächsten Interrupt stürzt der Rechner ab.

Interrupt-Prozedur

Beim Aufruf eines Interrupt läuft nun folgende Prozedur (siehe Bild "Prozedur" im Abschnitt "Software-Interrupts") ab, die ab dem 80386 automatisch und ohne weiteres Eingreifen eines Programms ausgeführt wird:

CS:EIP des Interrupt-Vektors gibt den Einsprungpunkt des Interrupt-Handlers an. Damit setzt der Prozessor seine Arbeit mit dem Interrupt-Handler fort. Die Rückkehr zum aufrufenden Programm erfolgt über ein IRET. Beispiel:

INT 10h

Der Prozessor schiebt die aktuellen Flags, CS und EIP auf den Stack, löscht das Interrupt- und Trap-Flag und liest den Interrupt-Vektor an der Adresse 0000:0040. Die zwei Bytes bei 0000:0040 werden in EIP, die beiden Bytes bei 0000:0042 in CS geladen (beachten Sie das INTEL-Format low-high).

Die CPU lädt den Offset-Wert des Handlers im 32-Bit-Modus zwar in EIP, aber der Offset in der Interrupt-Vektortabelle umfasst aus Kompatibilitätsgründen mit den 16-Bit-Vorgängern 8086 und 80286 des 80386 nur 16 Bit. Die zwei höher wertigen Bytes in EIP werden dann einfach mit dem Wert 0000h aufgefüllt.

Von Interesse ist auch die Kodierung des INT-Befehls: 11001100 beziehungsweise 11001101 stellt den Opcode dar, und vvvvvvvv gibt die Nummer des Interrupt an: Der Befehl INT 3 besteht also aus nur einem Byte. Im Abschnitt Exceptions erfahren Sie Näheres zu diesem Interrupt. Für alle anderen INT-Befehle ist nur eine unmittelbare Adressierung möglich, das heißt, die Nummer des Interrupt-Vektors ist Teil des Befehlsstroms. Damit kann die Nummer des Interrupt nicht in einem Register oder Speicheroperanden bereitgehalten werden.

Software-Interrupts treten synchron zur Programmausführung auf, das heißt, jedes Mal, wenn das Programm den Punkt mit dem INT-Befehl erreicht, wird ein Interrupt ausgelöst. Das unterscheidet sie wesentlich von den Hardware-Interrupts und Exceptions.

Hardware-Interrupts

Wie schon der Name sagt, werden diese Unterbrechungen durch einen Hardware-Baustein oder ein Peripheriegerät wie beispielsweise eine Schnittstellenkarte ausgelöst. Man unterscheidet zwei grundsätzlich verschiedene Arten von Hardware-Interrupts: den nicht maskierbaren Interrupt NMI sowie den (maskierbaren) Interrupt IRQ. Für die Bedienung eines solchen IRQs spielt der Interrupt-Controller eine große Rolle, denn er verwaltet mehrere Interrupt-Anforderungen und gibt sie geordnet nach ihrer Priorität gezielt an den Prozessor weiter.

Löst der Computer einen NMI aus, so wird dem NMI-Anschluss des Prozessors ein aktives Signal zugeführt. Die CPU arbeitet den gerade ausgeführten Befehl ab und führt unmittelbar anschließend in gleicher Weise wie oben einen Interrupt 2 aus. Beim PC wird ein NMI ausgelöst, wenn beim Lesen von Daten aus dem Speicher ein Paritätsfehler oder ein anderes ernstes Hardware-Problem, wie zum Beispiel eine fehlerhafte Busarbitrierung, auftritt. Der Computer meldet sich dann bei einem Paritätsfehler mit der folgenden (oder einer ähnlichen) Nachricht:

Paritätsfehler bei xxxx:xxxx

xxxx:xxxx gibt das Byte mit dem Paritätsfehler an. Die Besonderheit des NMI ist, dass er (wie auch hier bereits der Name ausdrückt) nicht unterdrückt, das heißt maskiert, werden kann. Ein NMI drängelt sich immer nach vorne. Da er normalerweise nur bei einer wirklich schwerwiegenden Fehlfunktion der Hardware ausgelöst wird, ist dies aber verständlich und auch richtig.

IRQs

Demgegenüber können Interrupt-Anforderungen via IRQ maskiert werden, indem man mit CLI das Interrupt-Flag IE löscht. Alle Interrupt-Anforderungen über den Anschluss INTR (Pin B7, beim 80386) werden ignoriert. Erst durch den umgekehrten Befehl STI werden solche Interrupts wieder zugelassen. Beachten Sie dabei, dass der Software-Befehl INT xx implizit ein CLI ausführt. Sie müssen also nach einem INT-Befehl explizit mit einem STI Interrupt-Anforderungen wieder zulassen, sonst wird der Computer taub. Interrupt-Anforderungen werden üblicherweise durch ein Peripheriegerät ausgelöst, beispielsweise, wenn die serielle Schnittstelle dem Prozessor mitteilen möchte, dass sie gerade ein Zeichen empfangen hat.

Hardware-Interrupt-Anforderungen werden durch den Interrupt-Controller (im Chipset) verwaltet. Empfängt dieser ein Signal, das anzeigt, dass ein Hardware-Interrupt ausgelöst werden soll, so führt er dem INTR-Eingang der CPU ein Anforderungssignal (Interrupt-Request) zu. Kann der Prozessor den Interrupt bedienen (ist IE gesetzt), gibt er ein Bestätigungssignal INTA (Interrupt Acknowledge) aus, indem er die Steuersignale M/IO, D/C und W/R jeweils auf einen niedrigen Pegel setzt. Die CPU erhält dann vom Interrupt-Controller die Nummer des auszuführenden Interrupt. Der Prozessor fährt nun in gleicher Weise wie oben beschrieben fort und ruft den entsprechenden Interrupt-Handler auf.

Hardware-Interrupts (NMI und IRQ) sind im Gegensatz zu Software-Interrupts völlig asynchron zur Programmausführung. Beispielsweise benötigt die Festplatte in Abhängigkeit von der Ausgangsstellung der Schreib-/Leseköpfe eine unterschiedliche Zeitspanne, bis die Daten zum Prozessor übertragen werden können. Dies macht die Erfassung von Programmfehlern sehr schwierig, wenn sie nur im Zusammenwirken mit Hardware-Interrupts auftreten.

Exceptions

Zwei Quellen für Interrupts haben wir bereits kennen gelernt, eine dritte bildet der Prozessor selbst. Interrupts, die vom Prozessor erzeugt werden, bezeichnet man als Exceptions (Ausnahmen). Die Auswirkungen einer Exception entsprechen dabei einem Software-Interrupt, das heißt, es wird ein Interrupt aufgerufen, dessen Nummer in diesem Fall vom Prozessor selbst angegeben wird. Ursache für eine Exception ist im Allgemeinen eine prozessorinterne Fehlerbedingung, die den Eingriff von System-Software erfordert und vom Prozessor nicht mehr allein behandelt werden kann.

Exceptions werden nach drei Klassen unterschieden: Faults, Traps und Aborts. Im Folgenden möchten wir kurz die Kennzeichen der drei Klassen beschreiben.

Exceptions im Real Mode

Ein großer Teil der Exceptions ist für den Betrieb im Protected Mode vorgesehen. Im Real Mode sind nur die folgenden Exceptions von Bedeutung:

Ausblick

Der nächste Teil der Artikelserie widmet sich einer besonderen Spezialität der x86-Prozessoren - dem Protected Mode. Wir werden diese Betriebsart anhand von zahlreichen Bildern ausführlich erläutern.

Serie: x86-Programmierung und -Betriebsarten

Teil 1

Code-/Datensegment, Befehlszähler und Stack

Teil 2

Adressierungsarten und Befehlsdekodierung sowie Real Mode

Teil 3

Interrupts und Exceptions

Teil 4

Der Protected Mode

Teil 5

Paging und die MMU

Teil 6

Der Virtual-8086-Modus

Diese Artikelserie basiert auf dem Kapitel 6 des "PC Hardwarebuch" von Addison Wesley. Sie können in unserem Buch-Shop das über 1200 Seiten starke Kompendium bestellen oder als eBook downloaden.

tecCHANNEL Buch-Shop

Weitere Literatur zum Thema Hardware

Titelauswahl

Titel von Pearson Education

Bücher

PDF-Titel (50% billiger als Buch)

Downloads