| Inhaltsverzeichnis
|  | Übersicht Kapitel 3
3.2 Befehlssätze |  | 4. Steuer- und Schnittstellenbausteine

## 3.3 Adressierungsarten

- 3.3.1 Voraussetzungen und Begriffe
- 3.3.2 Beschreibung der Adressierungsarten

### 3.3.1 Voraussetzungen und Begriffe |

In der einfachsten Form, die in den Anfangsjahren der Digitalrechner als einzige benutzt wurde, werden alle Adressen der Operanden und Sprungziele als absolute (physikalische) Adressen im Befehl angegeben. Als Nachteil ergibt sich dabei, daß Programme und Daten vollständig lageabhängig sind, d.h. schon zur Programmierzeit wird festgelegt, wo sie zur Ausführungszeit der Programme im Speicher liegen müssen. Durch die absoluten Adressen ist zum Beispiel die Programmierung von Tabellenzugriffen in einer Schleife nicht möglich, ohne daß die Adresse im Befehl selbst inkrementiert bzw. dekrementiert wird.
Deshalb bieten heute alle Mikroprozessoren die Möglichkeit, die Adresse eines Operanden oder eines Sprungzieles erst zur Laufzeit aus den Inhalten von Registern, Speicherzellen und Konstanten, die im Befehl angegeben sind, zu berechnen (dynamische Adreßberechnung).
Die verschiedenen Möglichkeiten eines Mikroprozessors, die Adresse eines Operanden (im Registersatz, im Speicher oder in einem Peripheriebaustein) oder eines Sprungzieles im Speicher zu berechnen, nennt man die Adressierungsarten des Prozessors. Durch den wohlüberlegten Einsatz der jeweils günstigsten Adressierungsart ist ein (Assembler-)Programmierer in der Lage, viel Speicherplatz und Rechenzeit für sein Programm einzusparen und insbesondere strukturierte Daten, wie Tabellen und Listen, geschickt zu verarbeiten.
Als Ergebnis einer Adreßberechnung erhält man die effektive Adresse (EA) des Operanden. Berücksichtigt wird dabei nur die Adreßberechnung, die explizit durch den Befehl verlangt wird, denn die berechnete Adresse ist zunächst eine logische Adresse. Nicht betrachtet wird in diesem Abschnitt die Möglichkeit, daß die gewonnene logische Adresse durch das Adreßwerk "automatisch" in eine andere physikalische Adresse umgesetzt wird.
Tabelle 3.3-1 gibt einen Überblick über die gebräuchlichsten Adressierungsarten. Sie werden in diesem Abschnitt ausführlich erklärt.

Tabelle 3.3-1: Übersicht über die gebräuchlichsten Adressierungsarten

In der Regel kann jede Adressierungsart zur Bestimmung eines Operanden unabhängig von der Richtung des Transports (vom Speicher zum Prozessor oder umgekehrt) angewendet werden. Erlaubt der Prozessor auch Speicher-Speicher-Transfers, so können für Quelle und Ziel des Datentransports meist beliebige und verschiedene Adressierungsarten benutzt werden. Zur Vereinfachung der Darstellung wird im weiteren nur auf den Transport eines Operanden aus dem Speicher in den Prozessor Bezug genommen. Besitzt der Prozessor spezielle Befehle zur Ein-/Ausgabe über Peripheriebausteine (vgl. Tabelle 3.2-8), so ist im allgemeinen bei diesen Befehlen nur eine Teilmenge der beschriebenen Adressierungsarten möglich.
In den Beispielen zu den einzelnen Adressierungsarten werden die Befehle durch ihre Mnemocodes angegeben, wie sie im letzten Abschnitt eingeführt wurden. Begriffe in spitzen Klammern '< >' stehen für Variablen, die Werte aus bestimmten Bereichen annehmen können. Insbesondere gilt für n = Anzahl der Adreßbits, k = Anzahl der Bits des Offsets:

<Adresse> | Î {0,..,2n-1}, | Operandenadresse
<Offset> | Î {0,..,2k-1}, | vorzeichenloser Offset bzw.
<Offset> | Î {-2k-1,..,2k-1-1}, | Offset im Zweierkomplement.

Wird eine Registerbezeichnung in runde Klammern '( )' gesetzt, so soll das bedeuten, daß nicht die Adresse, d.h. die Nummer des Registers, in die Berechnung der effektiven Adresse (EA) des Operanden eingeht, sondern der Inhalt des Registers. Dasselbe gilt sinngemäß für die Adresse eines Speicherwortes.

Beispiele:

EA = R0 | das Register R0 wird angesprochen;
EA = (R0) | die effektive Adresse ist gleich dem Inhalt von R0;
EA = $A4E0 | die Speicherzelle $A4E0 wird angesprochen;
EA = ($A4E0) | die effektive Adresse ist gleich dem Inhalt der Speicherzelle $A4E0.

Stimmen im letzten Beispiel Adreß- und Speicherzellen-Breite nicht überein, so steht die effektive Adresse in mehreren hintereinander folgenden Speicherzellen. An dieser Stelle sei wiederholt, daß sich Mikroprozessoren nicht zuletzt darin unterscheiden, wie sie Adressen und Daten im Speicher ablegen:

- Im sogenannten Little-Endian-Format belegen die niederwertigeren Adreß- oder Datenteile die Speicherzellen mit den niederwertigen Adressen. Der Assemblerbefehl LDX #$0A00 eines 8-bit-Prozessors kann z.B. in den (hexadezimalen) Maschinenbefehl 8E 00 0A übersetzt und in dieser Reihenfolge in drei Bytes [(n): $8E, (n+1): $00, (n+2): $A0] des Speichers abgelegt werden.
- Bei Big-Endian-Format stehen höherwertige Adreß- oder Datenteile unter höherwertigen Speicheradressen. LDX #$0A00 führt dann zur Übersetzung 8E A0 00 und zur Speicherablage in dieser Reihenfolge.

Bei den 32- und 64-bit-RISC-Prozessoren reicht die Breite eines Datenbusses und der internen Register in der Regel aus, einen vollständigen Befehl (mit z.B. 32 bit Länge) mit einem einzigen Speicherzugriff in den Prozessor zu laden. In diesem Befehl sind dann - wie im Abschnitt 3.2.2 gezeigt - neben dem OpCode alle Informationen zur Selektion des/der Operanden, insbesondere Offsets zu einer Basisadresse, oder der 'unmittelbare' Operand (immediate) selbst enthalten. Offsets und unmittelbare Daten werden vom Steuerwerk abgezweigt und in internen Speicherzellen oder Hilfsregistern abgelegt. Nachdem der OpCode vom Decoder des Steuerwerks interpretiert wurde, können diese abgezweigten Befehlsteile prozessorintern angesprochen und verarbeitet werden.
CISC-Mikroprozessoren, aber auch die x86-kompatiblen (hybriden) Prozessoren und viele Mikrocontroller, müssen jedoch z.T. mehrfach auf den Speicher zugreifen, um einen kompletten Befehl daraus zu laden. Damit verbunden ist eine mehrfache Erhöhung des Programmzählers (PC) zur Selektion der folgenden Befehlsteile. Der Erhöhung des PCs zur Auswahl der nächsten Speicherzellen entspricht bei den oben erwähnten 32/64-bit-Prozessoren die Selektion der bereits geladenen Befehlsteile in internen Speicherzellen. Um die Darstellung der Adressierungsarten möglichst allgemein zu halten, wollen wir den Fall des mehrfachen Zugriffs im weiteren zugrunde legen, wie er im Bild 3.3-1 skizziert ist. Der Programmzähler zeigt zunächst auf das Speicherwort, das den OpCode enthält. Von dort wird der OpCode in das Befehlsregister des Mikroprozessor-Steuerwerks geladen (OpCode Fetch). Der genaue Aufbau des OpCodes wurde im Abschnitt 3.2.2 beschrieben. Hier reicht es zu wiederholen, daß er aus verschiedenen Bitfeldern aufgebaut ist. In den folgenden Bildern dieses Abschnitts kennzeichnet das mit 'Op' bezeichnete Feld die Art der Operation, das Feld 'Reg' ein für die Adreßberechnung benötigtes Register. Durch den Buchstaben 'A', 'B', 'I' wird - falls erforderlich - angegeben, ob es sich um ein allgemeines Adressen-, ein Basis- oder ein Indexregister handelt.
Nach dem Laden des OpCodes wird der Programmzähler erhöht. Er zeigt dann auf das Speicherwort, das den Operanden, seine Adresse oder seine Adreßdistanz (Offset) zu einem bestimmten Registerinhalt enthält. Dieser Wert wird in ein geeignetes Register des Prozessors übertragen (z.B. in den Datenbuspuffer). Über die Länge der Register werden im weiteren keine Aussagen gemacht.

Bild 3.3-1: Laden eines OpCodes und eines Operanden bzw. seiner Adresse in den Prozessor

Register und Speicherwörter können unterschiedliche Längen besitzen. Vereinfachend wird jedoch angenommen, daß OpCodes, Adressen, Operanden und Offsets stets in ein Speicherwort passen, der Programmzähler also stets um 1 erhöht werden muß. Ein Prozessor, bei dem das nicht der Fall ist, muß gegebenenfalls mehrfach auf konsekutive Speicherzellen zugreifen. In diesem Fall ergibt sich die effektive Adresse durch Hintereinanderhängen (Konkatenation) der gelesenen Teiladressen. Zu jeder Adressierungsart wird im folgenden eine symbolische Darstellung gebracht. Zusätzlich zu den beschriebenen Adressierungsarten existiert noch eine große Menge von Mischformen, auf die hier aber nicht eingegangen werden kann.

### 3.3.2 Beschreibung der Adressierungsarten |

A. Register-Adressierung (Register Direct Addressing, Register Operand Mode)

Bei dieser Adressierungsart steht der Operand bereits in einem Register des Prozessors zur Verfügung. Ein Zugriff auf den Speicher zur Selektion des Operanden ist daher nicht nötig. Deshalb benötigt diese Adressierungsart die geringste Ausführungszeit. Man kann hier drei Fälle unterscheiden:

A1. Implizite Adressierung (inhärente Adressierung, Implied Addressing, Inherent Addressing)

Die Nummer, d.h. die effektive Adresse des angesprochenen Registers ist codiert im Operationsfeld des OpCodes enthalten (s. Bild 3.3-2). Diese Adressierungsart wird nur dann realisiert, wenn durch einen Befehl lediglich ein oder wenige bestimmte Register angesprochen werden können. In diesem Fall gibt es für den Assembler spezielle Mnemocodes, die gegebenenfalls durch Anhängen des Registernamens gebildet werden.

Assemblerschreibweise: | <Mnemo>A |  | (A Akkumulator).
Effektive Adresse: | &EA ist codiert im OpCode enthalten. |
Beispiel: | LSRA | (Logical Shift Right Accumulator)
Verschiebe den Inhalt des Akkumulators A um eine Bitposition nach rechts.

Bild 3.3-2: Implizite Adressierung

A2. Flag-Adressierung (Bild 3.3-3)

Sie ist ein Spezialfall der impliziten Adressierung. Bei ihr wird nicht ein ganzes Register angesprochen, sondern nur ein einzelnes Bit (Flag) in einem Register. Dabei handelt es sich z.B. um das Statusregister, das Steuerregister oder das Maschinenstatuswort (MSW). Befehle mit dieser Adressierungsart dienen dazu, bestimmte (Status-)Bits abzufragen oder den Prozessor in einen anderen Betriebszustand zu versetzen. Für den Assembler existiert im letztgenannten Fall in der Regel ein Paar von Mnemocodes, die das Setzen bzw. Rücksetzen des Flags ermöglichen.

Assemblerschreibweise: | SE<flag> |  | (Flag setzen),
| CL<flag> | (Flag zurücksetzen).
Effektive Adresse: | EA ist codiert im OpCode enthalten.
Beispiele: | SEI/CLI | SEC/CLC | (Set/Clear ...F lag)
Setzen/Zurücksetzen des Interrupt Enable Flags bzw. des Carry Flags.

Bild 3.3-3:

A3. Explizite Register-Adressierung (Register Operand Addressing, s. Bild 3.3-4)

Kann der Operand eines Befehles in allen oder wenigstens mehreren Registern stehen, so wird die Nummer (Adresse) des angesprochenen Registers im Registerfeld 'REG' des OpCodes angegeben.

Assemblerschreibweise: | Ri | (Register Ri).
Effektive Adresse: | EA = i | (steht im REG-Feld des OpCodes).
Beispiel: | DEC R0 | (Decrement R0)
Dekrementiere den Inhalt des Registers R0.

Bild 3.3-4: Explizite Register-Adressierung

B. Einstufige Speicher-Adressierung

Bei den einstufigen Adressierungsarten ist zur Gewinnung der effektiven Adresse (EA) nur eine Adreßberechnung notwendig. Das schließt nicht aus, daß mehr als zwei Größen (Register- oder Speicherinhalte) miteinander verknüpft werden. Wesentlich ist, daß nicht mit einem Teilergebnis der Adreßberechnung als Zwischenadresse erneut auf den Speicher zugegriffen wird und von dort weitere Daten zur Adreßberechnung geholt werden. Wie im Abschnitt 2.5 beschrieben, wird die effektive Adresse zur Anwahl einer Speicherzelle stets im Adreßpuffer(register) oder im Programmzähler gehalten. In den folgenden Bildern wurde zur Vereinfachung der Darstellung der Adreßpuffer nur dann gezeichnet, wenn er der einzige Ort im Prozessor ist, wo die Adresse zur Verfügung steht, also nicht dann, wenn dieses Register lediglich zum Zwischenspeichern des Inhalts eines anderen Registers oder der Summe anderer Registerinhalte dient.

Hinweis: Leider sind die Bezeichnungen der hier betrachteten Adressierungsarten in der Literatur sehr unterschiedlich. Die einstufigen Adressierungsarten werden häufig auch als 'direkte Adressierungsarten' bezeichnet, die folgenden mehrstufigen dementsprechend als 'indirekte Adressierungsarten'. Da dies jedoch zu Begriffsverwirrungen führt, haben wir uns nicht an diese Bezeichnungen gehalten. Leider sind auch die Darstellungen der Adressierungsarten auf Assemblerebene äußerst unterschiedlich, so daß wir uns mehr oder weniger willkürlich auf eine Form beschränken mußten.
B1. Unmittelbare Adressierung (Immediate Addressing, s. Bild 3.3-5)

Hier enthält der Befehl nicht die Adresse des Operanden oder einen Zeiger darauf, sondern diesen selbst. Unter den am Anfang des Abschnitts gemachten Voraussetzungen belegen OpCode und Operand im Speicher hintereinander folgende Speicherworte. Nachdem der OpCode ins Befehlsregister geladen wurde, stellt der Befehlsdecoder anhand des Operationsfeldes fest, daß es sich um einen Befehl mit unmittelbarer Adressierung handelt. Durch den um 1 erhöhten Programmzähler (PC) wird dann der Operand adressiert. Als Operanden werden in der Regel alle oder ein Großteil der vom Prozessor unterstützten Datentypen und ihre Formate zugelassen. Dies bedeutet natürlich, daß der Prozessor (entgegen der vereinfachenden Darstellung im Bild 3.3-5) gegebenenfalls mehrfach zum Lesen des Operanden auf den Speicher zugreifen muß. In der Assemblerprogrammierung ist der Operand eine dezimale, hexadezimale, oktale, binäre Zahl oder eine ASCII-Zeichenkette. Unmittelbar im Assemblerbefehl angegebene Daten werden üblicherweise durch das Symbol '#' gekennzeichnet.

Assemblerschreibweise: | #<Operand>. |
Effektive Adresse: | EA = (PC) + 1 . |
Beispiel: | LDA #$A3 | (Load Accumulator)
Lade den Akkumulator A mit dem Hexadezimalwert $A3.

('$' ist ein übliches Kennzeichen (Präfix) für hexadezimale Zahlen, das wir schon häufiger in diesem Sinne benutzt haben.)

Bild 3.3-5: Unmittelbare Adressierung

B2. Direkte Adressierung (Direct Addressing)

Hier enthält der Befehl im Speicherwort nach dem OpCode die (logische) Adresse des Operanden, aber keine weiteren Vorschriften zu dessen Manipulation, z.B. durch die Addition mit einem Registerinhalt.

Assemblerschreibweise: | <Adresse> |
Effektive Adresse: | EA = ((PC) + 1) |

Es können weiter die folgenden Fälle unterschieden werden.

B2.1 Absolute Adressierung (Extended Direct Addressing, s. Bild 3.3-6)

Der Befehl enthält im Speicherwort, das dem OpCode folgt, die absolute, d.h. vollständige Adresse des Operanden im (logischen) Adreßraum.

Bild 3.3-6: Absolute Adressierung

Meist wird diese Adressierungsart durch das Operationsfeld des OpCodes bestimmt. Jedoch gibt es auch Mikroprozessoren, bei denen statt dessen eine Belegung des Registerfeldes dafür reserviert wurde (z.B. der Intel 8080).

Beispiel: | LDA $07FE | (Load Accu A)
Lade den Inhalt der Speicherzelle $07FE in den Accu A.

B2.2 Seiten-Adressierung (kurze absolute Adressierung, Direct Page Addressing)

Hier steht im Befehl als Kurzadresse nur der niederwertige Teil der Operandenadresse (L-Adr.). Diese Adressierungsart wird hauptsächlich von den Prozessoren zur Verfügung gestellt, bei denen Datenbus und Adreßbus verschiedene Breiten besitzen, und die somit für das Laden einer vollständigen Adresse mehrfach auf den Speicher zugreifen müssen. Der Vorteil dieser Adressierungsart besteht darin, daß Speicherplatz für den Befehl und Ausführungszeit für das Holen des höherwertigen Adreßteils aus dem Speicher eingespart werden. Für die Erzeugung des höherwertigen Adreßteils gibt es die folgenden beiden Möglichkeiten.

B2.2.1 Zero-Page-Adressierung (Bild 3.3-7)

Bei ihr wird der höherwertige Adreßteil durch die entsprechende Anzahl von '0'-Bits ersetzt. Dadurch wird im Adreßraum nur die "unterste Seite" (Zero Page) angesprochen. Der Assembler erkennt diese Adressierungsart in der Regel an der Anzahl der für die Adresse angegebenen Ziffern.

Beispiel:
Für eine Adreßwort-Länge von 16 bit ist der höherwertige Adreßteil durch das Byte $00 gegeben und die ansprechbare Seite umfaßt die untersten 28 (=256) Adressen.

Die Befehle |  | INC = Increment)
| INC $7F | (6809-Maschinenbefehl: 0C 7F
und | INC $007F | (6809-Maschinenbefehl: 7C 00 7F

"Erhöhe den Inhalt der Speicherzelle $007F um 1",
führen zu verschiedenen OpCodes im Maschinenprogramm, wie das oben für den Prozessor MC6809 von Motorola beispielhaft angegeben ist.

Bild 3.3-7: Zero-Page-Adressierung

Variante
Die im Befehl angegebene Kurzadresse wird als vorzeichenbehaftete Zahl aufgefaßt, deren höchstwertiges Bit das Vorzeichen angibt. Diese Kurzadresse wird vorzeichengerecht auf die volle Adreßlänge ergänzt (Sign Extended), d.h. der höherwertige Adreßteil wird durch eine Folge von '0'-Bits ersetzt, falls die Kurzadresse positiv ist, im anderen Fall durch eine Folge von '1'-Bits. Auf diese Weise wird durch die Kurzadresse eine Speicherseite angesprochen, die in ihrer Mitte die Adresse $00..0 enthält.

Beispiel: | 16-bit-Adreßbreite, 8-bit-Kurzadresse.
Adressen | $XY , 0£ X£ 7, werden ergänzt zu $00XY,
Adressen | $XY , 8£ X£ F, werden ergänzt zu $FFXY.
Adreßbereich: | $FF80...$FFFF, $0000...$007F | hexadezimal,
entsprechend: | 65408...65535, 0...127 | dezimal.

B2.2.2 Seiten-Register-Adressierung ((Direct) Page Register Addressing, Bild 3.3-8)

Bei ihr wird der höherwertige Adreßteil in einem Register des Prozessors (DP-Register, Direct Page Register) zur Verfügung gestellt. Dieses Register kann durch besondere Befehle gelesen und verändert werden. Diese Adressierungsart besitzt die gleichen Vorteile wie die Zero-Page-Adressierung. Setzt man das DP-Register auf den Wert 0, (was in der Regel automatisch nach einem Rücksetzen des Prozessors geschieht,) so erhält man letztere als Spezialfall der Seiten-Register-Adressierung. Ein weiterer Vorteil besteht darin, daß der Variablenbereich eines Programms auf einfache Weise durch das Ändern des DP-Registers verschoben werden kann. Da es bei den meisten Assemblern möglich ist, Adressen ohne führende Nullen einzugeben, muß der Benutzer dem Assembler durch ein Steuerzeichen (z.B. <, >) mitteilen, ob die Seiten-Register-Adressierung oder die absolute Adressierung gewählt werden soll.

Bild 3.3-8: Seiten-Register-Adressierung

Beispiele: Unterstellt wird eine 16-bit-Adresse. Das DP-Register besitze den Inhalt $A5.
| LD R0,>$7F | (absolute Adresse, LD = Load)
Lade das Register R0 mit dem Inhalt des Speicherwortes $007F.
| LD R0,<$7F | (Direct-Page-Adresse)
Lade das Register R0 mit dem Inhalt des Speicherwortes $A57F.

B3. Register-indirekte Adressierung (Register Indirect Addressing, Bild 3.3-9)

Hier enthält das durch seine Nummer im Registerfeld des OpCodes angegebene Adreßregister die Adresse des Operanden (Pointer, "Zeiger", deshalb auch: "Zeigeradressierung"). Dem Assembler werden z.B. durch runde Klammern '( )' angezeigt, daß nicht der Inhalt des Registers, sondern der Inhalt der durch das Register adressierten Speicherzelle benutzt werden soll.

Bild 3.3-9: Register-indirekte Adressierung

Assemblerschreibweise: | (Ri) | (Register Ri).
Effektive Adresse: | EA = (Ri). |

Da es sich bei dieser Adresse sehr häufig um die Anfangs- oder Endadresse eines Tabellenbereiches im Speicher handelt, stellen viele Prozessoren die Möglichkeit zur Verfügung, den Registerinhalt durch die Ausführung einer Operation automatisch zu verändern. Üblich sind die bereits im Zusammenhang mit den Indexregistern imAbschnitt 2.6 beschriebenen Modifikationen, die wir hier noch einmal kurz wiederholen:

- postinkrement (vereinfachend auch autoinkrement genannt):
  Nach der Ausführung des Befehls wird der Inhalt des Registers (um 1) erhöht und zeigt danach auf die nachfolgende Speicherzelle.

  Assemblerschreibweise: | (Ri)+
  Effektive Adresse: | EA = (Ri)

- predekrement (vereinfachend auch autodekrement genannt):
  Vor der Ausführung des Befehls wird der Inhalt des Registers (um 1) erniedrigt und zeigt somit auf die vorhergehende Speicherzelle.

  Assemblerschreibweise: | -(Ri)
  Effektive Adresse: | EA = (Ri) - 1

Beispiele

| LD R1,(A0) | (Load)
Lade das Register R1 mit dem Inhalt des durch das Adreßregister A0 adressierten Speicherwortes.
| INC (R0)+ | (Increment)
Inkrementiere zunächst den Inhalt des Speicherwortes, das durch das Register R0 adressiert wird, und danach den Inhalt von R0.
| CLR -(R0) | (Clear)
Dekrementiere zuerst den Inhalt des Registers R0 und lösche danach das Speicherwort, das durch R0 adressiert wird.

Ohne besondere Erwähnung wird im folgenden stets zugelassen, daß ein Index- oder Basisregister über die Möglichkeiten des 'autoinkrement/autodekrement' und der Skalierung verfügen kann (vgl. Abschnitt 2.6).
Ein Spezialfall dieser Adressierungsart mit 'autoinkrement/autodekrement' ist die Adressierung des Stacks mit den Befehlen PUSH und PULL, die Sie im vorhergehenden Abschnitt 3.2 kennengelernt haben. Die Basisadresse wird hier im Stackpointer (SP) gehalten. Der Stackpointer enthält also stets die effektive Adresse des zuletzt im Stack belegten Speicherwortes. Den beiden erwähnten Operationen, angewandt auf ein Register Ri, entsprechen damit:

PUSH Ri: | ST Ri,-(SP) | (Store)
Dekrementiere zuerst den Stack Pointer SP und speichere danach den Inhalt des Registers Ri in das durch SP adressierte Speicherwort.
PULL Ri: | LD Ri,(SP)+ | (Load)
Lade das Register Ri mit dem Inhalt des durch den Stack Pointer adressierten Speicherwortes und erhöhe danach den Stack Pointer.

Während jedoch die Befehle LD und ST den Zustand der Bedingungsbits im Statusregister verändern können, haben die Befehle PUSH und PULL auf sie keine Auswirkung.

B4. Indizierte Adressierung (Indexed Addressing, relative Adressierung)

Bei ihr wird die effektive Adresse durch die Addition des Inhalts eines Registers zu einem angegebenen Basiswert berechnet. Diese Addition wird im Adreßwerk des Prozessors vorgenommen und ist in den folgenden Bildern durch einen mit '+' gekennzeichneten Kreis dargestellt. Der Basiswert gibt die Anfangsadresse eines Speicherbereiches an, das Register enthält den "Index" eines Operanden in diesem Bereich, also die Adreßdistanz zum Basiswert. Auch hier ist in der Regel die Möglichkeit der automatischen Änderung des Registerinhalts (autoinkrement, autodekrement) gegeben. Je nachdem, in welcher Form der Basiswert vorgegeben wird, kann man unterscheiden:

B4.1 Speicher-relative Adressierung(Memory Relative Addressing, Bild 3.3-10)

Hier wird der Basiswert als absolute Adresse im Befehl vorgegeben. Bei einigen Prozessoren ist die Länge des Indexregisters kürzer als die absolute Adresse, so daß nur ein beschränkter Adreßbereich von der Basisadresse aus angesprochen werden kann. Das Indexregister wird durch das IReg-Feld im OpCode des Befehls bestimmt.

Assemblerschreibweise: | <Adresse>(Ii) |
Effektive Adresse: | EA = ((PC) + 1) + (Ii) |

Beispiel: | ST R1,$A704(R0) | (Store)
Speichere den Inhalt von Register R1 in das Speicherwort, dessen Adresse sich durch Addition des Inhalts von R0 zur Basis $A704 ergibt.

Bild 3.3-10: Indizierte Adressierung mit absoluter Basisadresse

B4.2 Register-relative Adressierung (Register Relative Addressing, Based Mode, Bild 3.3-11)

Der Basiswert befindet sich in einem Basisregister, auf das durch das BReg-Feld im OpCode verwiesen wird. Im Befehl wird ein Offset angegeben, der zum Inhalt des Basisregisters addiert wird. Dieser Offset ist in der Regel vorzeichenbehaftet und kann kürzer sein als die Länge des Basisregisters. Oft kann der Benutzer zwischen mehreren Offsetlängen wählen (z.B. 8- oder 16-bit-Offset). Ist der Offset gleich 0, so stimmt diese Adressierungsart mit der Register-indirekten funktional überein, benötigt jedoch durch das Lesen des Offsets eine längere Ausführungszeit.

Assemblerschreibweise: | <Offset>(Bi) | (Basisregister Bi).
Effektive Adresse: | EA = (Bi) + ((PC) + 1) . |

Beispiel: | CLR $A7(B0) | (Clear)
Lösche das Speicherwort, dessen Adresse sich durch die Addition des hexadezimalen (negativen) Offsets $A7 zum Inhalt des Basisregister B0 ergibt.

Bild 3.3-11: Indizierte Adressierung mit Basisregister

B4.3 Register-relative Adressierung mit Index (Based Indexed Mode, Bild 3.3-12)

Hier wird der Basiswert in einem Basisregister übergeben. Dazu wird der Inhalt eines Indexregisters addiert. Für dieses Indexregister können wieder die automatischen Veränderungen 'autoinkrement/autodekrement' gewählt werden. Zusätzlich kann häufig im Befehl ein Offset angegeben werden, der zur Adreßberechnung hinzugezogen wird.

Bild 3.3-12: Register-relative Adressierung mit Index

Assemblerschreibweise: | <Offset>(Bi)(Ij). |
Effektive Adresse: | EA = (Bi) + (Ij) + ((PC) + 1). |

Beispiel: | DEC $A7(B0)(I0)+ | (Decrement)
Dekrementiere das Speicherwort, dessen Adresse sich durch die Addition der Inhalte der Register I0 und B0 mit dem Offset $A7 ergibt, und erhöhe danach den Inhalt des Registers I0.

B5. Programmzähler-relative Adressierung (Program Counter Relative Addressing, Bild 3.3-13)

In diesem Fall entsteht die effektive Adresse durch die Addition eines im Befehl angegebenen Offsets zum aktuellen Programmzählerstand. Vor Ausführung der Adreßberechnung enthält der Programmzähler in der Regel bereits die Adresse des Befehles, der im Speicher dem aktuell ausgeführten Befehl folgt. Diese Adressierungsart erlaubt es, Programme so zu schreiben, daß sie an einer beliebigen Stelle im Arbeitsspeicher ablauffähig sind ("dynamisch verschiebbarer Programmcode"). Der Offset wird in der Regel als vorzeichenbehaftete Zahl im Zweierkomplement interpretiert.
Bei vielen Prozessoren ist diese Adressierungsart nur in Verbindung mit Sprung- und Verzweigungsbefehlen oder Unterprogramm-Aufrufen zulässig. Stimmt die Länge des Offsets mit der der Adresse überein, so kann zu jeder beliebigen Adresse im Speicherbereich gesprungen werden. Ist die Länge jedoch kürzer, so kann nur ein beschränkter Adreßbereich angesprungen werden. Verschiedene Prozessoren lassen beide Arten von Offsets zu. In diesem Fall muß im OpCode die Länge des folgenden Offsets codiert sein. Da die meisten Assembler die Eingabe von positiven Zahlen ohne führende Nullen und von negativen Zahlen ohne führende 'F's (in hexadezimaler Schreibweise) zulassen, muß dem Assembler die Länge des folgenden Offsets gegebenenfalls durch verschiedene mnemotechnische Abkürzungen mitgeteilt werden.

Assemblerschreibweise: | <Offset>(PC) |
oder nur: | <Offset> |
Effektive Adresse: | EA = (PC) + 2 + ((PC) + 1). |

Beispiel: | BNE $7F | (Branch not Equal).
Verzweige zur Speicherzelle, deren Adreßdistanz zum augenblicklichen Programmzähler 127 (=$7F) ist, wenn das Nullbit (Zero Flag) im Statusregister nicht gesetzt ist (also z.B. als Ergebnis eines Vergleichs nicht der Wert $00 herauskam). Im anderen Fall setze das Programm ohne Verzweigung mit dem nächsten Befehl fort.

| LBRA $7FFF | (Long Branch Always).
Verzweige 'unbedingt' zu der Speicherzelle, deren Adreßdistanz zum aktuellen Programmzähler 32767 (=$7FFF) ist.

Bild 3.3-13: Programmzähler-relative Adressierung

C. Zweistufige Speicher-Adressierung

Bei diesen Adressierungsarten sind zur Bestimmung der effektiven Adresse mehrere sequentiell auszuführende Adreßberechnungen und Speicherzugriffe notwendig. Das Ergebnis der ersten Berechnung liefert dabei die Adresse eines Speicherwortes, in dem wiederum eine Adresse für die folgende Adreßberechnung zu finden ist. In Analogie zur Register-indirekten Adressierung B3 kann man die folgenden Adressierungsarten auch als 'Speicher-indirekt' bezeichnen.
C1. Indirekte absolute Adressierung (Bild 3.3-14)

In diesem Fall enthält der Befehl eine absolute Adresse, die auf ein Speicherwort zeigt. Der Inhalt dieses Speicherwortes ist die effektive Adresse des gesuchten Operanden ("Zeigeradresse", Pointer Address).

Assemblerschreibweise: | (<Adresse>). |
Effektive Adresse: | EA = (((PC) + 1)). |

Beispiel: | LDA ($A347) | (Load Accumulator)
Lade den Akkumulator A mit dem Inhalt des Speicherwortes, dessen Adresse im Speicherwort $A347 steht.

Bild 3.3-14: Indirekte absolute Adressierung

C2. Indirekte Register-indirekte Adressierung (Bild 3.3-15)

In diesem Fall enthält das im Registerfeld des OpCodes ausgewählte (Adreß-)Register die Adresse eines Speicherwortes, das seinerseits die effektive Adresse des Operanden enthält. Diese Adressierungsart wird auch mit den Möglichkeiten der automatischen Veränderung des Registerinhalts 'autoinkrement/autodekrement' zur Verfügung gestellt.

Bild 3.3-15: Indirekte Register-indirekte Adressierung

Assemblerschreibweise: |  |
| a) ((Ri)) , |
| b) ((Ri)+) | (autoinkrement),
| c) (-(Ri)) | (autodekrement).
Effektive Adresse: |  |
| a) EA = ((Ri)) , |
| b) EA = ((Ri)) , |
| c) EA = ((Ri)-1) . |

Beispiel: | LD R0,(-(R1)) | (Load)
Dekrementiere zunächst den Inhalt des Registers R1. Lade dann den Inhalt des durch R1 adressierten Speicherwortes in den Adreßpuffer und bringe danach den Inhalt des durch den Adreßpuffer angesprochenen Speicherwortes in das Register R0.

C3. Indirekte indizierte Adressierung (Preindexed Indirect Addressing, Bild 3.3-16)

Hier wird zunächst nach einem der Indizierungsverfahren unter B4 eine effektive (Zwischen-)Adresse gebildet. Diese zeigt auf ein Speicherwort, das die effektive Adresse des gesuchten Operanden enthält. Im Bild 3.3-16 ist nur der komplexeste Fall dargestellt.

Bild 3.3-16: Indirekte indizierte Adressierung

Assemblerschreibweise: |  |
| a) (<Adresse>(Ii)) | indirekt Speicher-relativ,
| b) (<Offset>(Bi)) | indirekt Register-relativ,
| c) (<Offset>(Bi)(Ii)) | indirekt Register-relativ mit Index.
Effektive Adresse: |  |
| a) EA = ((Ii) + ((PC) +1)) , |
| b) EA = ((Bi) + ((PC) +1)) , |
| c) EA = ((Bi) + (Ii) + ((PC) +1)) . |

Auf die Angabe von Beispielen wird hier verzichtet.

C4. Indizierte indirekte Adressierung (Postindexed Indirect Addressing, Bild 3.3-17)

In diesem Fall wird zunächst nach dem Verfahren C3 mit Basisregister und Offset (1. Offset) eine effektive (Zwischen-)Adresse ermittelt. Der Inhalt des dadurch adressierten Speicherwortes wird dann nach dem Verfahren B4.1 indiziert. Dabei kann zusätzlich ein weiterer Offset (2. Offset, Outer Displacement) herangezogen werden.

Assemblerschreibweise: | <2. Offset>(<1. Offset>(Bi))(Ij). |
Effektive Adresse: | EA = ((Bi) + ((PC)+1)) + (Ij) + ((PC)+2) . |

Erklärung:

(<Offset>(Bi)) liefert mit dem Basisregister Bi nach C3b eine Adresse, die nach B4.1 durch das Indexregister Ij indiziert wird. Bei dieser Berechnung wird gegebenenfalls der zweite Offset berücksichtigt.

Bild 3.3-17: Indizierte indirekte Adressierung

Beispiel: | INC $A7($10(B0))(I2) |
Addiere zunächst den Offset $10 zum Inhalt des Basisregisters B0. Entnehme dem durch diese Summe adressierten Speicherwort die Basisadresse des Operanden. Berechne die effektive Adresse EA des Operanden durch Addition des Inhalt des Indexregisters I2 sowie des Offsets $A7 zu dieserBasisadresse. Erhöhe den Wert des durch EA adressierten Speicherwortes.

C5. Indirekte Programmzähler-relative Adressierung (Indirect Program Counter Relative Addressing, s. Bild 3.3-18)

Bei diesem Verfahren wird zunächst aus dem aktuellen Inhalt des Programmzählers und einem Offset nach B5 eine (Zwischen-)Adresse gebildet. Diese zeigt auf ein Speicherwort, das die effektive Adresse des Operanden enthält.

Assemblerschreibweise: | (<Offset>(PC)) . |
Effektive Adresse: | EA = ((PC) + 2 + ((PC) + 1)) . |

(Hinweis: Der Summand '2' berücksichtigt die bereits unter B5 erwähnte Tatsache, daß bei der Programmzähler-relativen Adressierung in der Regel von der Adresse des dem Befehl folgenden OpCodes ausgegangen wird.)

Beispiel: | JMP ($A7(PC)) |
Addiere zum Inhalt des Programmzählers den Offset $A7. Entnehme dem durch diese Summe adressierten Speicherwort die Adresse, an der das Programm fortgesetzt werden soll.

Bild 3.3-18: Indirekte Programmzähler-relative Adressierung

Selbsttestaufgabe S3.3-1: (–>Lösungsvorschlag)
2. Der 8-bit-Mikroprozessor 6502 der Firma MOS Technologies aus der Mitte der 70er Jahre besaß einen 8-bit-Datenbus und einen 16-bit-Adreßbus. 16-bit-Adressen wurden byteweise unter zwei hintereinander folgenden Adressen im Speicher abgelegt, wobei das höherwertige Adreßbyte unter der höherwertigen Adresse zu finden war (Little-Endian Format). Der Prozessor enthielt unter anderem einen 8-bit-Akkumulator A und ein gleich langes Indexregister Y. Geben Sie für die Adressierungsart: "postindizierte indirekte Zero-Page-Adressierung" mit der Assemblerschreibweise: (<L-Adresse>),Y die Berechnungsvorschrift für die effektive Adresse an und stellen Sie sie symbolisch (wie in den Bildern dieses Abschnittes) dar. In Maschinensprache umfaßt jeder Befehl mit dieser Adressierungsart zwei Bytes: <OpCode> <L-Adresse> . Der Befehlssatz dieses Prozessors enthält u.a. die Befehle:
   CLC | ösche das Übertragsbit | (Clear Carry Flag)
   LDA | Lade den Akkumulator A | (Load Accu A)
   STA | Speichere den Akkumulator A | (Store Accu A)
   ADC | Addiere den Speicheroperanden zum Akkumulator A unter Berücksichtigung des Übertrags | (Add with Carry)
   LDY | Lade das Y-Register | (Load Y)
   DEY | Dekrementiere den Inhalt des Y-Registers um 1 | (Decrement Y)
   BNE | Verzweige, falls Zero Flag = 0, (das Zero Flag wird als Ergebnis einer Operation genau dann gesetzt, wenn das A - bzw. Y-Register danach den Wert $00 hat) | Branch not Equal)
   JSR | Unterprogrammaufruf | (Jump to Subroutine)
   RTS | Rücksprung aus Unterprogramm | (Return from Subroutine)
Schreiben Sie ein Unterprogramm SUM, das im Festwertspeicher abgelegt werden kann und die 16-bit-Summe über einen maximal 255 byte langen Datenbereich berechnet. Die Anfangsadresse von SUM sei $E000. Die um 1 dekrementierte Anfangsadresse des Datenbereiches werde dem Unterprogramm in den Speicherzellen $00A0 und $00A1, die Länge des Datenbereiches (ungleich 0) im Y-Register übergeben. Das Endergebnis werde in den Speicherzellen $00A2 und $00A3 zum Hauptprogramm übertragen. Stellen Sie den Unterprogrammaufruf dar, falls die Summe der 128 Bytes ab der Basisadresse $1000 berechnet werden soll.

Zum Abschluß dieses Abschnitts wollen wir mit dem bereits mehrfach erwähnten RISC II-Prozessor der Universität Berkley einen Mikroprozessor mit einem äußerst einfachen Satz von Adressierungsarten vorstellen (Tabelle 3.3-2). Dieser Prozessor kannte nur sechs verschiedene Adressierungsarten, von denen für arithmetische, logische und Verschiebebefehle nur die beiden ersten benutzt werden konnten. Für diese Befehle mußten alle Operanden in den Registern des Prozessors vorliegen. Die letzten vier Adressierungsarten dienten nur in den Befehlen LOAD und STORE zur Selektion einer Speicherzelle sowie in Sprung- und Verzweigungsbefehlen zur Angabe des Sprungzieles. (EA: effektive Adresse):

Tabelle 3.3-2: Adressierungsarten des RISC II

Bezeichnung | Notation | Effektive Adresse
| Arithmetische, logische, Verschiebebefehle
Unmittelbare Adressierung | #<Operand> | EA = (PC)
Explizite Register-Adressierung | Ri | EA = i
| LOAD/STORE-Befehle
Register indirekte Adressierung | (Ri), | EA = (Ri) (Inhalt von Ri)
Register-relative Adressierung: | <Offset>(Ri) | EA = (Ri) + <Offset>
Register-relative Adressierung mit Index | (Ri)(Rj) | EA = (Ri) + (Rj).
Programmzähler-relative Adressierung | <Offset>(PC) | EA = (PC) + <Offset>

| Inhaltsverzeichnis
|  | Übersicht Kapitel 3
3.2 Befehlssätze |  | 4. Steuer- und Schnittstellenbausteine