x86-Programmierung und -Betriebsarten (Teil 2)

21.01.2004 von Hans-Peter Messmer und Klaus Dembowski
Die sechsteilige Artikelserie behandelt die Speicheradressierung und die x86-Betriebsarten. Im zweiten Teil geht es um die verschiedenen Adressierungsarten und die Befehlsdekodierung sowie den Real Mode.

Im ersten Teil dieser Artikelserie wurden bereits Befehle wie JNZ, MOV oder CALL genannt. Wenn Sie ein Programm mit einem geeigneten Editor oder dem DOS-Befehl TYPE betrachten, werden Sie allerdings vergeblich nach solchen Befehlen suchen. JNZ, MOV et cetera werden als mnemonische Codes oder kurz Mnemonics bezeichnet. Sie dienen nur dazu, dem Programmierer eine Gedächtnisstütze zu liefern, da die Mnemonics die Operation des entsprechenden Befehls in verkürzter Form angeben (wie schön ist da eine selbstgestrickte Routine in C mit der Bezeichnung _nun_wollen_wir_Daten_einlesen).

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.

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

Programmierung auf Prozessorebene: Mnemonics und der Assembler

Ein Assembler versteht diesen mnemonischen Code und führt eine entsprechende Kodierung in einen Maschinenbefehl aus. Maschinenbefehle sind - wie könnte es anders sein - eine Folge von Nullen und Einsen mit einem oder mehreren Byte Länge. Mit einem Assembler lassen sich aber viele Dinge erleichtern. Beispielsweise können die üblichen Assembler Makros verdauen (daher auch die Bezeichnung Makroassembler), unterschiedlichste Adressierungen ausführen und Variablen, Sprungstellen (JNZ hier_gehts_weiter) sowie Prozeduren (CALL subroutine) symbolisch ansprechen. Der Assembler setzt diese Anweisungen dann in entsprechende Maschinenbefehle um. Ein Assembler ist die maschinennaheste Möglichkeit, Programme zu erstellen. Damit können Sie auf Registerebene unmittelbar auf die Hardware einwirken. In Assembler geschriebene Programme sind im Allgemeinen sehr kompakt und äußerst schnell (wichtig bei Echtzeitanwendungen zur Maschinensteuerung). Reine Programmierung in Maschinensprache würde keine weitere Kontrolle über den Mikroprozessor bringen, sondern die Angelegenheit nur verkomplizieren und fehleranfälliger machen.

In der weiteren Beschreibung von Maschinenbefehlen werden wir daher stets die mnemonischen Codes und Elemente der Assembler-Programmierung (wie beispielsweise symbolische Sprungstellen oder Bezeichnungen von Prozeduren) benutzen.

Adressierungsarten

Soll ein Register (hier beispielsweise der Akkumulator EAX) über

MOV EAX, wert

mit einem Wert geladen werden, dann stehen drei Möglichkeiten zur Verfügung:

Als effektive Adresse bezeichnet man den Offset des Operanden innerhalb des ausgewählten Segments (hier: DS). Die effektive Adresse setzt sich aus bis zu vier Elementen zusammen:

Beispiel

Displacement, Basisregister, Indexregister und Skalierungsfaktor können Sie in beliebiger Kombination verwenden. Damit können Sie eine sehr ausgeklügelte und mehrdimensionale Adressierung beispielsweise von Feldelementen definieren.

Beispiel: Gegeben sei ein Feld mit zehn Elementen, das zehn verschiedene Körper bezeichnet; jedes Element besitzt den Aufbau Höhe:Breite:Tiefe:Querschnitt. Die Teilelemente Höhe et cetera umfassen jeweils 1 Byte. Das Feld beginnt bei 0c224h. Durch das folgende Programmfragment können die Tiefen der Elemente in den Akkumulator EAX geladen werden:

MOV EBX, 0c224h ;Laden der Basisadresse in EBX
MOV ESI, nr ;Laden der Elementnummer in ESI
MOV EAX, [EBX+ESI*4+2] ;Tiefe (Displacement 2 gegenüber dem Beginn) des Elementes
;nr (Elementgröße=4 Byte, daher Skalierung 4)
;des Feldes (beginnend bei der Basisadresse in EBX)
;in den Akkumulator EAX laden

Beim oben angeführten Beispiel verwendet der Prozessor standardmäßig das Segmentregister DS (beziehungsweise SS im Fall von EBP als Basisregister). Der 80386 besitzt aber noch die drei Extrasegmentregister ES bis GS und das Codesegmentregister CS.

Anstelle dieser Segmentregister können auch sie benutzt werden, wenn man den Wert von ES oder CS beispielsweise in das Register DS lädt. Eine weitere Möglichkeit, das Register ES zu benutzen, ist die Segmentüberschreibung (Segment-Override). Der Assembler erkennt eine solche Überschreibung, indem man die Bezeichnung des zu verwendenden Segments dem Speicheroperanden, abgetrennt durch einen Doppelpunkt, voranstellt. Wenn wir im oben angeführten Beispiel ein Feld in einem Segment ansprechen wollen, das durch den Wert im Segmentregister ES definiert wird, müssen wir folgenden Befehl benutzen:

MOV EAX, ES:[EBX+ESI*4+2]

Eine Segmentüberschreibung ist vor allem dann vorteilhaft, wenn ein Segment nur selten angesprochen werden soll. Greifen dagegen viele aufeinander folgende Befehle auf das ES-Segment zu, dann ist es besser, den Wert von ES in DS zu laden. Nun wird der in DS abgelegte Wert von ES standardmäßig benutzt, und Sie können sich (und dem Prozessor) die ständigen Overrides ES: ersparen.

Befehlscodierung

Im Abschnitt Adressierungsarten über das Displacement haben wir kurz erwähnt, dass der Assembler den Befehl

MOV EAX, array[0]

im angegebenen Beispiel in MOV EAX, 0f2h umsetzt. Wie aber erkennt der Prozessor, dass 02fh ein Displacement (eine Adresse) und keinen Wert darstellt, er also den Wert an der Adresse DS:02fh und nicht den Wert 02fh selbst in das Register AX laden soll? Den Schlüssel hierzu bildet die Kodierung der Befehle.

Der Prozessor kennt keine Anweisungen wie

MOV AX, ES:[BX+SI*4+2]

(deren ASCII-Kodierung ja auch viel zu aufwendig wäre), sondern nur Bytes und Folgen von Bytes. Daher muss der Assembler (oder Compiler im Falle von Hochsprachen) die im "Klartext" geschriebenen Befehle in eine mehr oder weniger lange Folge von Bytes umsetzen.

Zentraler Bestandteil ist der so genannte Opcode (Operationscode). Er umfasst ein bis zwei Byte und kennzeichnet die Befehle in eindeutiger Weise. Lautet das erste Byte 00001111b, so besteht der Opcode aus zwei Byte. Einfache Befehle ohne Operanden wie CLC (Clear Carry = Carry-Flag löschen) bestehen nur aus dem Opcode und sind somit nur ein Byte lang. Umfangreiche Befehle mit Präfixen und mehreren Operanden/Displacement/Daten können hingegen bis zu 15 Byte umfassen. Präfix, Operand(en) und Displacement/Daten sind angegeben, wenn der Opcode anzeigt, dass der Prozessor derartige Informationen benötigt. Die zwei niederwertigen Bits des Opcode geben häufig die Richtung eines Datentransfers (Register nach Register/Speicher oder Speicher nach Register) sowie die Verwendung von 8-/16-Bit-Operanden (zum Beispiel AL, AX oder EAX) an.

Im Feld Operand(en) ist abgelegt, welche Register und gegebenenfalls welche direkten oder indirekten Speicheroperanden verwendet werden. Das Feld Displacement/Daten gibt ein zur Assemblierzeit festes Displacement oder einen unmittelbaren Operanden (Immediate) an.

Beispiel

Die Kodierung für den oben erwähnten Befehl MOV EAX, 02fh ist je nachdem, ob 02fh ein Displacement oder einen unmittelbaren Operanden darstellt, im Opcode verschieden:

Displacement: Opcode 10100011 00101111
Immediate: Opcode 10111000 00101111

Das Byte 00101111 stellt einmal das Displacement 02fh dar, das andere Mal den unmittelbaren Operanden (Wert) 02fh.

Die Befehlseinheit (IU) kann anhand des unterschiedlichen Opcodes feststellen, ob ein MOV-Befehl mit Displacement oder mit einem unmittelbaren Operanden vorliegt. Die IU steuert daher die Ausführungseinheit (EU) entsprechend an, so dass sie den MOV-Befehl mit Displacement oder unmittelbarem Operanden ausführt. Liegt ein Displacement vor, so steuert die EU die Adressierungseinheit an, um über die Busschnittstelle den Wert bei der Adresse DS:02fh aus dem Speicher in den Akkumulator zu übertragen. Bei 02fh als einem unmittelbaren Operanden lädt der Prozessor den Wert 02fh als Bestandteil des Befehlsstroms aus der Prefetch-Queue nach EAX. Im ersten Fall ist also ein zusätzlicher Speicherzugriff erforderlich, während die EU den unmittelbaren Operanden sofort verarbeitet.

Um bei einem Displacement den Operanden an der Offset-Adresse 02fh im Extrasegment ES, FS oder GS statt dem standardmäßigen Datensegment DS zu verwenden, wird bei der Befehlskodierung nicht etwa ein anderer Opcode erzeugt, sondern ein Präfix vorangestellt. Dieses Segment-Override-Präfix bewirkt eine Segmentüberschreibung, das heißt eine Überschreibung des Standarddatensegments DS durch ein Extrasegment (oder auch SS oder CS). In der Tabelle finden Sie als Beispiel alle ab dem 80386 gültigen Segmentüberschreibungspräfixe.

Segmentüberschreibungspräfixe

Segment

Präfix

CS

00101110 (2eh)

DS

00111110 (3eh)

SS

00110110 (36h)

ES

00100110 (26h)

FS

01100100 (64h)

GS

01100101 (65h)

Neben den Segmentüberschreibungspräfixen werden auch Wiederholungen von String-Operationen (durch REP), die Verriegelung des Busses (durch LOCK) und ab dem 80386 die Änderung der Operanden- (16 oder 32 Bit) und Adressgröße (16 oder 32 Bit) durch ein Präfix angegeben. Der Vorteil von Präfixen gegenüber einer Kodierung im Opcode liegt auf der Hand: Präfixe werden nur eingesetzt, wenn eine Operation dies erfordert. Eine Kodierung im Opcode würde durch das Ansteigen der Kodierungspositionen, um auch die Information der Präfixe unterbringen zu können, unweigerlich zu einem Opcode mit einer Länge von mindestens zwei Byte führen. Damit benötigt die fallweise Kodierung mit Präfixen weniger Speicherplatz, und die Programme sind kompakter. Abgesehen von einem umfangreicheren Speicherzugriff durch das Segmentüberschreibungspräfix werden Befehle mit Bezug auf ein anderes als das Standarddatensegment in derselben Zeit ausgeführt. Da die Prefetch-Queue im Hintergrund ständig nachgeladen wird, verlangsamt selbst der zusätzliche Speicherzugriff die Arbeitsgeschwindigkeit nur unter ungünstigen Umständen, beispielsweise wenn die Prefetch-Queue nach einem Sprung oder Prozeduraufruf geleert worden ist.

Assembler erkennen an Bezeichnungen wie EAX oder ES: automatisch, um welche Operandengröße beziehungsweise um welches Segment es sich handelt und führen eine entsprechende Kodierung von Opcode, Operand(en) und Displacement/Daten aus sowie das Einsetzen der erforderlichen Präfixe.

Einlesen von Befehlen und Prefetching

Die Prefetch-Queue dient der Entlastung des Bussystems, weil der Prefetcher ständig neue Befehle aus dem Speicher liest, sobald in der Prefetch-Queue ausreichend Platz ist. Ab dem 80386 wird durch sein 32-Bit-Bussystem immer dann ein Doppelwort eingelesen, wenn in der Queue vier Byte frei sind. Dieses automatische Befehls-Fetching läuft linear ab, das heißt der Prefetcher liest beginnend mit einer Startadresse immer aufeinander folgende Doppelwörter ein.

Es bleibt festzuhalten, dass der Prefetcher Sprünge und Verzweigungen ignoriert. Für bedingte Sprünge ist das auch logisch, weil der Prozessor ja zunächst die Sprungbedingung auswerten muss. Das kann aber möglicherweise eine ganze Zeit nach dem Einlesen des Sprungbefehls erfolgen. Anders sieht das bei unbedingten Verzweigungen und Subroutinenaufrufen aus: Sie werden ja immer ausgeführt. Tatsächlich haben sich die Hardware-Ingenieure für CPUs ab dem Pentium auch eine Menge einfallen lassen, um den Prefetcher dazu zu zwingen, das Befehls-Fetching entsprechend den Sprungbefehlen auszuführen. Ein magisches Stichwort wäre Branch Prediction oder Verzweigungsvorhersage.

Befehls-Prefetching

Da der Zugriff des Prefetchers üblicherweise auf Doppelwörter mit Adressen erfolgt, die ein Vielfaches von vier sind, liest die Busschnittstelle in der Regel ein ganzes Doppelwort an einer Doppelwortadresse ein. Die einzige Ausnahme betrifft den Fall, dass ein CALL- oder Sprungbefehl zu einer Speicheradresse erfolgt, die keine Doppelwortadresse darstellt. In diesem Fall liest der Prefetcher über die Buseinheit nur ein bis drei Byte ein, bis für den nächsten Zugriff wieder eine Doppelwortadresse erreicht ist. Anschließend wird stets ein ganzes 32-Bit-Doppelwort mit Doppelwortadresse eingelesen (sofern in der Prefetch-Queue vier Byte frei sind).

Speicherzugriffe durch einen Maschinenbefehl wie zum Beispiel MOV reg, mem haben natürlich Vorrang vor dem Befehls-Fetching. Führt der Prozessor einen solchen Befehl aus, dann muss der Prefetcher warten, bis der Speicherzugriff abgeschlossen ist. Während der Ausführung von Befehlen ohne Speicherzugriff, wie etwa ADD reg, reg, und der internen Abarbeitung eines Befehls in einer Phase, die keinen Speicherzugriff benötigt, kann der Prefetcher dagegen ungestört und ohne die Befehlsausführung im Prozessor zu stören, ein Doppelwort in die Prefetch-Queue einlesen. Das sichert eine maximale Auslastung des Speicherbusses und eine fast immer gefüllte Prefetch Queue.

Der Prozessor bricht das Prefetching ab, wenn die Befehlseinheit (IU) einen CALL- oder Sprungbefehl erfasst und den Aufruf beziehungsweise Sprung auch tatsächlich anweist (das heißt die Aufruf- oder Sprungbedingung ist erfüllt). In diesem Fall leert der Prozessor seine Prefetch Queue, invalidiert die bereits dekodierten Befehle in der Befehlseinheit IU und beginnt an der Sprungadresse mit dem Einlesen der neuen Befehle. Auch jetzt wird das Prefetching wieder linear ausgeführt, bis ein weiterer CALL- oder Sprungbefehl ausgeführt wird. Gleiches gilt natürlich auch für RET- oder IRET-Befehle. Das Problem ist, dass der nächste Befehl nicht nur zuerst eingelesen, sondern auch dekodiert werden muss. Beachten Sie, dass Befehle bis zu 15 Byte umfassen können, so dass zum Einlesen eines Befehls bis zu vier Buszyklen notwendig sind. Diese Zyklen kommen dann zur Dekodier- und Ausführungszeit dazu. Die in den Handbüchern angegebenen Ausführungszeiten beziehen sich stets auf die reine Ausführungszeit. Sie sind also nur dann gültig, wenn der Befehl bereits vollständig eingelesen und dekodiert worden ist. Das Prefetching sorgt dafür, dass in den meisten Fällen die Befehle rechtzeitig in dekodierter Form zur Ausführung bereitstehen. Wenn Sie sich vorstellen, dass sich ohne Prefetching für jeden Befehl noch die Befehlslese- und Befehlsdekodierzeit bemerkbar machen würden, dann erkennen Sie, welchen Performance-Gewinn man durch das relativ einfach zu realisierende Prefetching erzielt.

Bei einem Sprung oder Prozeduraufruf zu einer Adresse, die außerhalb des vom Prefetcher bereits eingelesenen Codebereichs liegt, muss die Steuereinheit recht lange auf den nächsten dekodierten Befehl warten. Liegt das Sprungziel noch innerhalb der Queue und umfasst der Befehl auch nur wenige Byte, so dass er bereits vollständig eingelesen worden ist, dann muss er nur noch dekodiert werden, der Befehlslesezyklus entfällt. Wird überhaupt kein Sprung ausgeführt (weil die Sprungbedingung für zum Beispiel JNE nicht erfüllt ist), dann macht der Prozessor ohne Verzögerung weiter.

Wenn Sie einmal mit einem Debugger ein kompiliertes Programm untersuchen, so werden Sie feststellen, dass es vor lauter JUMPs und CALLs nur so wimmelt, zum Beispiel setzen Compiler die CASE-Anweisung der Sprache C häufig in eine Vielzahl von bedingten JUMPs um. Liegt die Zieladresse außerhalb des Bereichs, der durch die Prefetch-Queue abgedeckt ist, muss der Prozessor mit dem Befehls-Fetching und der Dekodierung von vorne beginnen. Die Umsetzung des Source-Codes in eine ungünstige Folge von JUMP-Befehlen kann die Ausführungszeit eines Programms sehr nachteilig beeinflussen.

Der Real Mode, High Memory Area und HIMEM.SYS

Der 80386 und die Weiterentwicklungen können aus Kompatibilitätsgründen neben den moderneren Modi Protected und Virtual 8086 auch im Real Mode betrieben werden. Er bildet dann wie der 8086 eine lineare Adresse durch Multiplikation des Segmentregisterwerts mit 16 und Addition des Offsets. Beim 8086 ist die ausgegebene Adresse streng auf unter 1M begrenzt, weil in der Adressierungseinheit auf Grund des 20-Bit-Adressbusses nur ein 20-Bit-Addierer zur Verfügung steht. Da sowohl die Segment- als auch die Offset-Register eine Breite von 16 Bit aufweisen, ist ihr maximaler Wert jeweils gleich ffffh. Addiert man das Segment ffffh und den Offset ffffh im Real Mode, um eine physikalische Adresse zu ermitteln, so erhält man folgenden Wert:

Segment (* 24) ffff0h
+ Offset ffffh
phys. Adresse 10ffefh

Da der 8086/88 nur einen 20-Bit-Adressbus und damit in der Adressierungseinheit (AU) nur einen 20-Bit-Adressaddierer besitzt, fällt die führende 1 als Übertrag weg und wird ignoriert. Es bleibt damit ffefh als physikalische Adresse übrig. Diese fällt in das erste 64-KByte-Segment des Adressraums, es findet ein so genanntes Wrap-around statt.

Ab dem 80386 fällt die Beschränkung auf 20 Bit natürlich weg, weil der 80386 erstmalig durch seinen 32-Bit-Adressbus auch einen 32-Bit-Addierer in der Adressierungseinheit besitzt und dadurch lineare Adressen mit 32 Bit erzeugen kann. Daher wird die führende 1 hier nicht als Übertrag ignoriert, sondern es werden insgesamt 21 Adressleitungen aktiviert, so dass die CPU im Real Mode mit einem "beschränkten" 21-Bit-Adressbus arbeitet. Aus diesem Grund können die Bytes mit den Adressen 10000h bis 10ffefh im Extended Memory angesprochen werden. Die 1-MByte-Grenze im Real Mode ist also um fff0h (65.520) Bytes oder knapp 64 KByte durchbrochen worden (siehe Abbildung 6.2). Diesen Speicherbereich bezeichnet man als High Memory Area. Er kann von DOS und Real-Mode-Anwendungsprogrammen als 10-prozentige Speichererweiterung über die 640 KByte Basisspeicher hinaus verwendet werden. Typischerweise werden dort Teile von DOS und Treiber abgelegt. Die Ausgabe der Adresse 10ffefh entspricht natürlich nicht einem "echten" 21-Bit-Adressbus, da mit 21 Adressbits ja die Bytes von 00000h bis 1fffffh und nicht nur bis 10ffefh angesprochen werden können.

DOS-Relikte

Der Gerätetreiber HIMEM.SYS wurde beginnend mit der DOS-Version 4.00 ausgeliefert, um diese knapp ersten 64 KByte über der 1-MByte-Grenze in klar definierter Weise im Real Mode ansprechen zu können. Da auch Gerätetreiber wie SMARTDRV.SYS oder RAMDRIVE.SYS diesen Speicherabschnitt benutzen, kann es bei einem Zugriff darauf ohne HIMEM.SYS zu einer Kollision der Daten kommen. Ein weiteres Problem entsteht mit bestimmten MS-DOS-Versionen: Manche internen und nicht dokumentierten Funktionen verlassen sich auf den Wrap-around des 8086/8088. Ab einem 80386 findet dieser aber niemals statt. Man kann die CPU jedoch überlisten, indem man die Adressleitung A20 mit dem 21. Adressbit durch ein externes Gatter auf low (0) zwingt. Der Speicher wird dann effektiv wie mit einem Wrap-around bei einer Adresse über 1 MByte adressiert. Die Steuerung des Gatters zum Aktivieren und Deaktivieren der Adressleitung A20 erfolgte ursprünglich über den Tastatur-Controller und wird nunmehr mit Hilfe einer Schaltung im Chipset realisiert. HIMEM.SYS ist neben anderen Aufgaben auch dafür zuständig, dass nur bei einem gewollten Zugriff auf die ersten 64 KByte des Extended Memory die Leitung A20 aktiviert und ansonsten durch eine Deaktivierung von A20 ein Wrap-around im Prozessor emuliert wird. SMARTDRV.SYS und RAMDRIVE.SYS verwenden dagegen den Protected Mode, um auf den Extended Memory zuzugreifen. Das darf aber nicht zu einer Kollision mit den Daten in den ersten 64 KByte der High Memory Area führen. Im Protected Mode stehen alle 32 Adressleitungen des Adressbusses uneingeschränkt zur Verfügung. Auch bestimmte Funktionen von HIMEM.SYS können dazu benutzt werden, auf den Extended Memory im Protected Mode zuzugreifen.

Real Mode

Die Adresserzeugung im Real Mode unterscheidet sich bei 32-Bit-CPUs aber noch in einem weiteren Punkt von der beim 8086 oder seinem Nachfolger 80286. Durch die 32-Bit-Offset-Register kann die 32-Bit-CPU mit Hilfe von Adresspräfixen eigentlich 32-Bit-Adressen ausgeben. Wird der Inhalt eines 32-Bit-Offset-Registers als Adresse verwendet und liegt der Wert der erzeugten effektiven Adresse über ffffh, so erzeugt zum Beispiel der 80386 eine so genannte Pseudo-Protection-Exception ohne Fehlercode, die zu einem Interrupt 12 oder 13 führt (siehe nächstes Kapitel). Dadurch wird die Erzeugung von linearen Adressen jenseits von 10ffefh unterbunden (schade, sonst würde bereits im Real Mode der gesamte Adressraum von 4 GByte statt der jämmerlichen 1 MByte zur Verfügung stehen).

Zu den ungültigen Opcodes zählen im Real Mode übrigens auch alle Befehle, die rein für den Protected Mode vorgesehen sind, so zum Beispiel LLDT zum Laden der lokalen Deskriptortabelle (einer Datenstruktur, die nur im Protected Mode verwendet wird).

Erwähnen möchten wir an dieser Stelle noch, dass es neben der High auch eine Upper Memory Area gibt. Der für ROMs vorgesehene Adressbereich zwischen 640 KByte und 1 MByte des Real-Mode-Adressraums ist ja nur in seltenen Fällen auch von ROMs komplett belegt. Speicher-Controller können dann einen RAM-Abschnitt in den ROM-Bereich einblenden, wenn dort kein ROM installiert ist. Somit hat der Prozessor im Real Mode die Möglichkeit, mit Hilfe des Speicher-Controllers zusätzlich ein paar KBytes anzusprechen, die möglicherweise aber wichtig sind, damit speicherresidente Programme (Maustreiber u.ä.) nicht den Hauptspeicher belegen und Windows dadurch nicht starten kann.

Ausblick

Im nächsten Teil widmen wir uns speziell den Software- und Hardware-Interrupts sowie den Exceptions.

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