Assembler (80x86 Prozessor)-Programmierung: Der Prozessor

Aus Wikibooks

Wechseln zu: Navigation, Suche
Nuvola apps bookcase.svg Assembler (80x86 Prozessor)-Programmierung


Inhaltsverzeichnis

[Bearbeiten] Die Von-Neumann-Architektur

Fast alle heute entwickelten Rechner basieren auf der Von-Neumann-Architektur. Sie stammt von John von Neumann, der in einem Konzept 1946 skizzierte, wie ein Universalrechner aussehen sollte ([Sie04] ):

Von Neumann Architektur

1.) Jeder Rechner besteht demnach mindestens aus den folgenden Komponenten:

  • Der Zentraleinheit bzw. der CPU (Central Processing Unit). Die CPU wiederum besteht aus dem Steuerwerk und dem Rechenwerk.
  • Dem Speicher
  • Den Ein-/Ausgabeeinheiten

Alle Einheiten sind über den Systembus miteinander verbunden.

2.) Der Aufbau des Rechners ist unabhängig von der Problemstellung (daher auch die Bezeichnung Universalrechner). Durch die Programmierung wird er in die Lage versetzt, wechselnde Aufgaben zu lösen.

3.) Programme und Daten werden im selben Speicher abgelegt. Der Speicher ist dabei fortlaufend durchnummeriert, so dass jede Speicherzelle über eine Nummer (man sagt auch: ihre Adresse) angesprochen werden kann.

Die Von Neumannsche Architektur hat vor allem einen Nachteil: Der Bus ist der Flaschenhals des Systems. Moderne Prozessoren sind in der Regel wesentlich schneller als die Daten, die über den Bus vom Speicher in den Prozessor geladen werden können. Als Folge davon wird die Programmausführung gebremst. Man spricht in diesem Zusammenhang deshalb auch vom "Von-Neumannschen-Flaschenhals" (engl. The von Neumann Bottleneck). So versuchen heutige Prozessoren, die Bearbeitung von Programmen zu parallelisieren, anstatt sie sequenziell abzuarbeiten, sowie Daten und Code teilweise in unterschiedlichen Speichern zu halten (Cache). Damit weichen sie immer mehr vom Von-Neumann-Konzept ab. Da diese Abweichungen das Erlernen der Assemblerprogrammierung aber nur in geringem Maße beeinflussen, werden wir im folgenden nur am Rande auf diese Änderungen eingehen.

[Bearbeiten] Die 80x86-Prozessorfamilie

Bevor wir uns eingehender mit der CPU (Central Processor Unit) beschäftigen, wollen wir uns einen Überblick über die Entwicklung der Intelprozessoren verschaffen. Interessant ist, dass alle aktuellen Prozessoren abwärtskompatibel sind. Das heißt, selbst ein moderner Prozessor wie der Pentium 4 kann sich verhalten wie ein 8086-Prozessor – freilich nutzt er dann nur einen kleinen Teil seiner Fähigkeiten. Der Nachteil daran ist, dass alle 80x86-CPUs heute alte Technik enthalten. Es lohnt ein Blick auf die Anfangszeiten, um die Zusammenhänge zu verstehen.

8088,8086

Der 8086 ist eine 16-Bit-CPU und gehörte im Jahr seines Erscheinens 1978 zu den leistungsfähigsten Prozessoren für Personal Computer. Er konnte ein Megabyte Speicher adressieren (20 Bit breiter Adressbus) und arbeitete im sogenannten Real Mode. In diesem Modus konnte ein Programm auf jede verfügbare Adresse im Speicher zugreifen - eingeschlossen den Code und die Daten anderer Programme. Der Speicher von Programmen wurde in Segmente aufgeteilt, die höchstens 64 Kb einnehmen konnten.

Allerdings war das Mainboard für die 8086-CPU sehr teuer, weshalb Intel sich entschloss, eine Billigvariante zu entwickeln: die 8088-CPU. Sie war zwar ebenfalls eine 16-Bit-CPU, besaß allerdings nur einen Datenbus von 8 Bit Breite. Für den Programmierer war das aber nicht relevant, da dieser Prozessor 16-Bit-Zugriffe selbstständig auf jeweils zwei 8-Bit-Zugriffe verteilt, was eben dementsprechend langsamer ist.

80186,80188

Der Vollständigkeit wegen sollen hier noch kurz die Nachfolger dieser beiden Prozessoren erwähnt werden. Deren Befehlsumfang wurde erweitert und stellenweise optimiert, so dass ein 80186 etwa ein Viertel schneller arbeitet als ein gleich getakteter 8086. Der 80186 ist eher ein integrierter Mikrocontroller als ein Mikroprozessor, denn er enthält auch einen Interrupt Controller, einen DMA-Chip und einen Timer. Diese waren allerdings inkompatibel zu den vorher verwendeten externen Logiken des PC/XT, was eine Verwendung dort sehr schwierig machte. Darum wurde er im PC selten verwendet, war jedoch auf Adapterkarten als selbstständiger Mikroprozessor relativ häufig zu finden, beispielsweise auf ISDN-Karten.

Der 80188 ist wiederum eine abgespeckte Version mit 8-Bit-Datenbus, der 16-Bit-Zugriffe selbsttätig aufteilt. Wie beim 80188 sind 8-Bit-Zugriffe genauso schnell wie beim 80186, 16-Bit-Zugriffe benötigen die doppelte Zeit.

80286

Man kann den 80286 als Nachfolger des 8086 sehen, da der 80186/80188 durch die Inkompatiblitäten nicht weit verbreitet war. Das interessanteste neue Feature war der 16-bit-Protected-Mode. Damit ist es möglich, Speicher, der von anderen Programmen reserviert ist, zu schützen. Das ist eine Voraussetzung für Multitasking-Betriebssysteme, die mehrere Programme zugleich in den Arbeitsspeicher laden können.

80386

Mit dem 80386 entwickelte Intel erstmals eine 32-Bit-CPU. Damit konnte der Prozessor 4 GB Speicher linear ansprechen. Da sich der Protected-Mode anfänglich nicht durchsetzen konnte, enthielt die 80386-CPU einen virtuellen 8086-Modus. Er kann aus dem Protected Mode heraus gestartet werden und simuliert einen oder mehrere 8086-Prozessoren.

Wie schon beim 8086 brachte Intel mit dem 80386SX eine Low-Cost Variante in den Handel, der statt des 32 Bit breiten nur einen 16 Bit breiten Datenbus hatte.

80486

Mit dem 80486-Prozessor brachte Intel eine überarbeitete 80386-CPU auf den Markt, die um einen Cache und den mathematischen Coprozessor erweitert wurde, der b-Chip. Er ist teurer, aber viel schneller als der Hauptspeicher. Der schnelle Cache kann die Ausführung der Programme beschleunigen, da die CPU nur noch selten auf den langsamen Hauptspeicher zugreifen muss. Das ist dem Lokalitätsprinzip geschuldet. Es besagt, dass die Ausführung sich im Wesentlichen auf einen kleinen Bereich des Codes und der Daten beschränkt. So findet der Prozessor meist im Cache, was er gerade braucht.

Der mathematische Coprozessor dient dazu, komplexere mathematische Operationen auszuführen. Auch für den anfänglich noch teuren 80486-Prozessor gab es mit dem 80486SX eine "Sparvariante", die ohne den mathematischen Coprozessor arbeitete. Der Datenbus wurde dabei nicht beschränkt. Mit seinem Cache war er eigentlich nur eine schnellere 80386-CPU.

[Bearbeiten] Die Register der 8086/8088-CPU

Die CPU besitzt eigene, interne Speicher, die sogenannten Register. Auf die Daten in den Registern kann die CPU viel schneller zugreifen, als auf die Daten im Hauptspeicher oder im Cache. Allerdings bieten die Register noch weniger Speicherplatz als der Cache: Nur 14 Register, die je 16 Bit aufnehmen können, finden sich auf dem Chip der 8086-CPU. Daher muss der Assembler-Programmierer sehr darauf achten, dass sich nur die nötigsten Daten in den Registern befinden.

Die Register können in Typen unterteilt werden:

  • Datenregister sind in der Regel universell einsetzbar und nehmen Operanden auf.
  • Adressregister enthalten keine Daten, sondern die Adresse einer Speicherzelle. Sie sind nötig, um Adressen zu berechnen (man spricht hier auch von Adressarithmetik).
  • Stackregister: Es dient zur Verwaltung des Stacks. Der Stack ist eine Datenstruktur, auf der der Inhalt einzelner Register wie auf einem Stapel vorübergehend abgelegt werden kann.
  • Spezialregister: Die benötigt der Prozessor für sich. Der Programmierer kann sie meist nur indirekt ändern.

Die Register im Einzelnen:

Registersatz der 8088/86-CPU

Datenregister

Das AX-Register

Das AX-Register (Akkumulator) dient hauptsächlich der Durchführung von Rechenoperationen. Einige Arithmetikbefehle laufen ausschließlich über dieses Register.

Das BX-Register

Dieses Register wird häufig bei indirekten Speicheroperationen genutzt.

Das CX-Register

Das CX-Register (Count Register) dient einigen Maschinenbefehlen als Zählregister. Z.B. legt der Schleifenbefehl die Anzahl der Durchgänge im CX-Register ab.

Das DX-Register

Das DX-Register unterstützt das AX-Register bei der Berechnung von Zahlen, deren Ergebnis länger als 16 Bit ist.

Diese Register fungieren auch als allgemeine Register, solange sie nicht für besondere Aufgaben gebraucht werden.

Die unteren und die oberen 8 Bit der Register AX, BX, CX und DX lassen sich getrennt ansprechen. Mit AL (Low) werden beispielsweise die unteren 8 Bit des AX Registers angesprochen, mit AH (High) die oberen acht.

Adressregister

CS-, DS-, ES- und SS-Register

Eine spezielle Bedeutung unter den Registern haben die Segmentregister CS (Codsegmentregister), DS (Datensegmentregister), ES (Extrasegmentregister) und SS (Stacksegmentregister). Sie bilden im so genannten Real Mode gemeinsam mit dem Offset die physikalische Adresse. Man kann sie nur dafür nutzen; sie können weder als allgemeine Register benutzt, noch direkt verändert werden.

Stackregister

BP- und SP-Register

Während das Base Pointer-Register (BP) meistens als allgemeines Register benutzt werden kann und nur zur Adressberechnung herangezogen wird, dient das Stack Pointer-Register (SP) zusammen mit dem Stacksegmentregister zur Verwaltung des Stacks. Der Stack ist ein Bereich im Arbeitsspeicher, in dem Werte der Register zwischengespeichert werden können. Der Stack funktioniert nach dem sogenannten LIFO-Prinzip (last in, first out). Das bedeutet, dass der Wert, der als letztes auf den Stack gelegt wurde, auch als erstes wieder von ihm geladen werden muss - ähnlich einem Stapel oder Keller.

Spezialregister

Das IP-Register

Das Instruction Pointer-Register (IP) enthält zusammen mit dem CS-Register immer die Adresse des Speicherplatzes mit dem als nächstes auszuführenden Befehl. Dadurch "weiß" der Prozessor immer, wo er mit der Bearbeitung des Programms fortsetzen muss. Das IP-Register kann der Programmierer nicht mit einem anderen Wert füllen. Eine Änderung ist nur indirekt über Sprungbefehle oder Prozeduraufrufe möglich, wodurch das Programm an einer anderen Stelle fortgesetzt wird.

Das Flag-Register

Das Flag-Register ist ein Register von 16 Bits, die in diesem Register Flags heißen, von denen jedes eine Spezialaufgabe hat und einzeln mit einem Wert gefüllt werden kann. Hat eines den Wert 1, spricht man von einem gesetzten Bit, oder hier: Flag. Ist es 0, nennt man es gelöscht.

Das Flag-Register dient der Kommunikation zwischen Programm und Prozessor. So kann z.B. das Programm richtig reagieren, wenn unerwartet ein Ergebnis nicht in ein 16-Bit-Register passt, und der Programmierer dafür gesorgt hat, dass das Programm das dafür zuständige Flag prüft. Der Prozessor und das Programm können Flags setzen oder zurücksetzen.

Die Flags im Einzelnen:

Flagregister.PNG

  • Bit 11 - Überlaufflag / Overflow Flag (OF)
  • Bit 10 - Richtungsflag / Direction Flag (DF)
  • Bit 9 - Interruptflag (IF)
  • Bit 8 - Einzelschrittflag /Trap Flag (TF)
  • Bit 7 - Vorzeichenflag / Sign Flag (SF)
  • Bit 6 - Nullflag / Zero Flag (ZF)
  • Bit 4 - Auxiliary Flag (AF)
  • Bit 2 - Paritätsflag / Parity Flag (PF)
  • Bit 0 - Übertragsflag / Carryflag (CF)

Einige der Flags haben bei der 8086-CPU noch keine Bedeutung und sind für spätere CPUs reserviert. Deshalb darf der Programmierer sie nicht verwenden.

[Bearbeiten] Bearbeitung eines Maschinenprogramms

Jedes Programm besteht aus einer Reihe von Befehlen, die der Prozessor bearbeitet. In einem vereinfachten Modell verläuft die Ausführung eines Maschinenprogramms in zwei Phasen:

  1. Der Programmzähler wird auf die Adresse des Speicherplatzes gesetzt, in dem sich der Code des nächsten Befehls befindet. Dann wird der Befehlscode aus dem Arbeitsspeicher geholt und dekodiert (Fetchphase).
  2. Der Befehl wird ausgeführt (Ausführungsphase).

Danach wird der Programmzähler erhöht und ein neuer Zyklus beginnt.

Dies ist allerdings ein idealisiertes Bild, weil eine ganze Reihe von Ausnahmen dazu führen kann, dass der Prozessor die Programmausführung unterbricht:

  • Der Maschinenbefehl braucht einen zusätzlichen Operanden aus einer Speicherzelle und benötigt dafür zusätzliche Lesezyklen.
  • Der Prozessor muss einen Sprung- oder einen Schleifenbefehl ausführen und den Programmzähler mit der Adresse des Speicherplatzes laden, der den Befehl am Sprungziel enthält. Auch beim Ausführen eines Unterprogramms muss der Programmzähler neu geladen werden.
  • Der Prozessor empfängt ein Interrupt-Signal. Es unterbricht das Programm, damit die CPU ein Spezialprogramm, eine Interruptroutine, ausführt, etwa zur Beseitigung einer Störung. (mehr unter Interrupts)

[Bearbeiten] BIU und EU

Früher musste die CPU nach jedem Befehl den nächsten Befehl aus dem Arbeitsspeicher holen. Das kostete Zeit, während der die CPU in der Abarbeitung der Befehle unterbrochen wurde. Um den Zeitverlust zu mindern, parallelisieren die Entwickler heute die Vorgänge des Holens und des Ausführens, d.h. sie erledigen beides gleichzeitig. Dazu besitzt die CPU zwei getrennte Funktionseinheiten:

  • Die Executive Unit (EU) und
  • die Bus Interface Unit (BIU)

Die BIU ist für die Verbindung von Prozessor und Bussystem verantwortlich, die EU führt die Befehle aus. Beide sind unabhängig voneinander und arbeiten parallel.

Während der Ausführung eines Maschinenprogramms lädt die BIU den nächsten Befehl aus dem Arbeitsspeicher in eine interne Befehlswarteschlange, den sogenannten Prefetch Buffer. Von dort holt ihn sich die EU und bearbeitet ihn. Zugleich lädt die BIU den darauf folgenden Befehl in den Prefetch Buffer.

Moderne Prozessoren parallelisieren sogar die Arbeit der Executive Unit. Diese Technik bezeichnet man als "superskalar". Ziel ist abermals die schnellere Programmausführung.

[Bearbeiten] Der Bus

Wie wir gesehen haben, werden im Von-Neumann-Rechner alle Komponenten mit dem Systembus verbunden. Der Systembus lässt sich in drei Teile gliedern:

Der Datenbus

Der Datenbus ist für die Übertragung von Daten und Programmen zwischen den Komponenten des Rechners zuständig. Der Prozessor kann auf den Datenbus in zwei Richtungen zugreifen: lesend und schreibend. So eine Verbindung, die in beide Richtungen möglich ist, bezeichnet man auch als bidirektional.

Der Adressbus

Der Adressbus dient dem Prozessor zum Ansprechen des Arbeitsspeichers. Durch ihn gelangt der Prozessor an den Inhalt einer Speicherzelle, indem er die Adresse über den Adressbus schickt, man sagt auch: auf den Adressbus legt. Anders als der Datenbus leitet der Adressbus Informationen nur in eine Richtung, nämlich vom Steuerwerk des Prozessors zum Arbeitsspeicher. Er wird daher als unidirektional bezeichnet.

Die Breite des Adressbusses, d.h. die Anzahl seiner Datenleitungen, bestimmt, wie viele Speicherzellen des Arbeitsspeichers er höchstens adressieren kann. Die 8086/8088-CPU beispielsweise besitzt einen 20 Bit breiten Adressbus. Damit kann sie auf 220 = 1 MB Speicher zugreifen.

Der Kontroll- bzw. Steuerbus

Der Kontrollbus koordiniert den Adress- und den Datenbus. Er sorgt dafür, dass nicht mehrere Komponenten gleichzeitig darauf zugreifen.

[Bearbeiten] Interrupts

Während die CPU ein Programm bearbeitet, können verschiedene Ereignisse wie diese eintreten: Der Anwender drückt eine Taste, dem Drucker geht das Papier aus oder die Festplatte ist mit dem Schreiben der Daten fertig.

Um darauf reagieren zu können, hat der Prozessor zwei Möglichkeiten:

  • Die CPU kann ständig nachfragen, ob so ein Ereignis eingetreten ist. Dieses Verfahren bezeichnet man als programmierte I/O oder auch Polling. Nachteil der Methode ist, dass die ständige Prüfung den Prozessor arg aufhält.
  • Das Gerät erzeugt eine Nachricht, sobald ein Ereignis eintritt, und schickt diese über einen Interrupt-Controller an den Interrupteingang des Prozessors. Die CPU unterbricht das gerade laufende Programm und reagiert auf das Ereignis. Anschließend kann er mit der unterbrochenen Arbeit fortfahren.

Ein solches Interrupt-Signal, das von einem Gerät zur CPU geschickt wird, bezeichnet man im Unterschied zu Software-Interrupts als Hardware-Interrupt. Hardware-Interrupts können asynchron auftreten, d.h. es ist nicht vorhersagbar, wann einer aufritt. Einen Softwareinterrupt hingegen löst die Anwendungssoftware aus, bevor sie auf Systemfunktionen des Betriebssystems zugreifen kann. Software-Interrupts treten im Unterschied zu Hardware-Interrupts synchron auf, d.h. vorhersagbar, weil das im Programm so festgelegt ist.

Manchmal ist es nötig, die Unterbrechung des Programms durch Interrupts zu verhindern, zum Beispiel bei der Ausnahmebehandlung. Dazu löscht man das Interruptflag (IF = 0) mit dem Befehl cli (Clear Interruptflag). sti (Set Interruptflag) setzt es (IF = 1). Sind die Interrupts auf diese Weise gesperrt, kann das System auf keine Ein- oder Ausgabe mehr reagieren. Der Anwender merkt das daran, dass das System seine Eingaben über die Tastatur oder die Maus nicht mehr bearbeitet.

Die 80x86-CPU hat zwei Interrupt-Eingänge, den INTR (Interrupt Request) und den NMI (Non Maskable Interrupt). Der Interrupt INTR ist für normale Anfragen an den Prozessor zuständig, während der NMI nur besonders wichtigen Interrupts vorbehalten ist. Das Besondere am Non Maskable Interrupt ("nicht maskierbar") ist, dass er nicht durch Löschen des Interruptflags unterdrückt werden kann.

[Bearbeiten] Adressierung des Arbeitsspeichers im Real Mode

Der Adressbus dient der CPU dazu, Speicherplätze im Arbeitsspeicher anzusprechen, indem sie die Adresse eines Speicherplatzes über die Leitungen des Busses sendet. Die 8086/88-CPU hat dafür 20 Adressleitungen, die den Adressbus bilden. Damit könnte der Prozessor unmittelbar 220 = 1 MByte adressieren, wenn auch das zuständige Register in der CPU 20 Bit breit wäre. In dem Register muss sich die Adresse befinden, bevor sie auf den Adressbus geht. Mit ihren 16-Bit-Registern kann die 8086/88-CPU jedoch höchstens 216 = 65.536 Byte direkt adressieren.

Segmentierung in Real Mode

Um mit dieser CPU ein Megabyte Speicher anzusprechen, benötigt man zwei Register. Mit zwei 16-Bit-Registern können wir 232 = 4 Gigabyte Arbeitsspeicher ansprechen - mehr als wir benötigen. Eine 20-Bit-Adresse könnte mit Hilfe der zwei Register gebildet werden, indem man auf das erste Register die ersten 16 Bit der Adresse legt und auf das zweite die restlichen 4 Bit. Ein Vorteil dieser Methode ist, dass der gesamte Adressraum linear angesprochen werden kann. "Linear" bedeutet, dass die logische Adresse – also die, die sich in den Registern befindet – mit der physikalischen Adresse der Speicherzelle im Arbeitsspeicher übereinstimmt. Das erspart uns die Umrechnung zwischen logischer und physikalischer Adresse.

Das Verfahren hat allerdings auch Nachteile: Die CPU muss beim Adressieren immer zwei Register laden. Zudem bleiben zwölf der sechzehn Bits im zweiten Register ungenutzt.

Deshalb ist im Real Mode ein anderes Adressierungsverfahren üblich: Mit einem 16-Bit-Register können wir 65.536 Byte an einem Stück ansprechen. Bleiben wir innerhalb dieser Grenze, brauchen wir für die Adressierung einer Speicherzelle kein zweites Register.

Um den ganzen Speicherbereich von 1 MByte zu nutzen, können wir mehrere 65.536-Byte-Blöcke über den gesamten Speicher von 1 MByte verteilen und jeden von ihnen mit dem zweiten Register adressieren. So ein Block heißt Segment. Das zweite Register legt fest, an welcher Speicheradresse ein Segment anfängt, und mit dem ersten Register bewegen wir uns innerhalb des Segments.

Der Speicher von 1 MByte, der mit den 20 Leitungen des Adressbusses adressiert werden kann, ist allerdings nicht in ein paar säuberlich getrennte Segmente unterteilt. Vielmehr überlappen sich eine Menge Segmente, weil jedes 16. Byte, verteilt über 1 MByte, als möglicher Beginn eines 64-KByte-Segments festgelegt wurde. Der Abstand von 16 Byte kommt zu Stande, weil die erlaubten Anfänge der Segmente gleichmäßig auf die 1 MByte verteilt wurden (Division 1.048.576 Byte durch 65.536).

Die Überlappung der Segmente ist eine Stolperfalle für Programmierer: Ein und dieselbe Speicherzelle im Arbeitsspeicher kann durch viele Kombinationen von Adressen des ersten und des zweiten Registers adressiert werden. Passt man nicht auf, dann überschreibt ein Programm unerlaubt Daten und Programmcode und stürzt ab - eine so genannte Schutzverletzung ist aufgetreten.

Die Adresse im zweiten Register, die den Anfang eines Segments darstellt, heißt Segmentadresse. Die physikalische Adresse im Arbeitsspeicher errechnet sich aus der Segmentadresse und der Adresse des ersten Registers, der so genannten Offsetadresse. Erst wird die Segmentadresse mit 16, dem Abstand der Segmente, multipliziert. Zur Multiplikation mit 16 hängt man rechts einfach vier Nullen an. Hinzu wird die Offsetadresse addiert:

Physikalische Adresse = Segmentadresse * 16 + Offsetadresse


Die Offsetadresse im ersten Register erlaubt, auf ein 65.536 Byte großes zusammen hängendes Speicherfeld zuzugreifen. In Verbindung mit der Segmentadresse im zweiten Register kann man den gesamten Speicher erreichen. Beschränkt sich ein Programm auf die Speicherplätze innerhalb eines Segments, braucht die Segmentadresse gar nicht geändert werden.

Sehen wir uns dazu ein Beispiel an: Nehmen wir an, dass das Betriebssystem ein Programm lädt, das 40 KByte Daten und 50 KByte Code umfasst. Daten und Code wandern in den Arbeitsspeicher, wo sie säuberlich getrennt eigene Speicherbereiche einnehmen sollen. Um separate Speicherbereiche für die Daten und den Code zu schaffen, initialisiert das Betriebssystem zuvor das Datensegmentregister (DS) und das Codesegmentregister (CS) mit den Adressen, an denen beginnend die Daten und der Code im Arbeitsspeicher gelagert werden.

Wollen wir z.B. Text ausgeben, müssen wir eine Betriebssystemfunktion aufrufen, der wir die Adresse des Textes übergeben, den sie ausgeben soll. Die Segmentadresse des Textes steht schon im Datensegmentregister. Wir müssen der Betriebssystemfunktion nur noch die Offsetadresse nennen. Man sieht: Das Betriebssystem erfährt die vollständige Adressinformation, obwohl wir der Funktion nur den Offsetanteil übergeben haben.

Kommt ein kleines Programm mit weniger als 64 KByte inklusive aller Daten und des Codes aus, braucht man das Segmentregister beim Programmstart nur einmal zu initialisieren. Die Daten erreicht man alle allein mit dem Offset - eine ökonomische Sache.

Fassen wir die Eigenschaften von Segmenten und Offsets zusammen:

  • Jedes Segment fasst maximal 64 KByte, weil die Register der CPU nur 16 Bit breit sind.
  • Speicheradressen innerhalb eines Segments erreicht man mit Hilfe des Offsets.
  • Es gibt 65.536 Segmente (216).
  • Die Segmentanfänge liegen immer 16 Byte weit auseinander.
  • Segmente können sich gegenseitig überlappen.
  • Die Adressierung mit Segmenten und Offset erfolgt nur im Real Mode so wie beschrieben.

Neuere Prozessoren und Betriebssysteme besitzen die genannte Adressregister-Beschränkung nicht mehr. Ab dem 80386 können bis zu 4 GB linear adressiert werden. Deshalb setzen wir uns nicht weiter im Detail mit Segmenten und Offset auseinander. Die Programme jedoch, die wir im Folgenden entwickeln, sind dennoch höchstens 64 KByte groß und kommen deshalb mit einem Segment aus.


Wikibooks buchseite.svg Zurück zu Grundlagen | One wikibook.svg Hoch zu Assembler (80x86 Prozessor)-Programmierung | Wikibooks buchseite.svg Vor zu Das erste Assemblerprogramm


Persönliche Werkzeuge