Befehlsaufbau und Adressierungsarten
Befehlsaufbau
Einzelne Programmanweisungen
oder Operationen (=Maschinenbefehle) werden im Mikrocomputer als
Binärwerte im Programmspeicher abgelegt. Man unterscheidet
zwischen Operationen zum Datentransport, Arithmetikoperationen,
logischen Operationen und Programmsteueroperationen. Je nach Art
und Verwendung benötigt ein vollständiger Maschinenbefehl
1, 2 oder 3 Byte Speicherplatz. Das erste Byte enthält dabei
immer den Operationscode (kurz: Opcode). Im Befehlsumfang
eines 8-Bit µC gibt es maximal 256 Opcodes. Jeder Befehlszyklus
beginnt immer mit dem Lesen und dekodieren des Opcodes, anhand dessen
das Steuerwerk die weiteren Befehlsschritte steuert. Bei 2- oder
3-Byte-Befehlen veranlasst das Steuerwerk, dass weitere Bytes aus
dem Programmspeicher gelesen werden. Diese werden als Operanden
bezeichnet und enthalten bei einem Transportbefehl zum Beispiel
Ziel- und Quelladresse für die zu transferierenden Daten.
Beispiel: Ein 8-Bit Eingangsbyte an
Port 1 (Adresse 90h) des Controllers soll in die Speicherstelle
mit der Adresse 6Eh im internen RAM geschrieben werden. Der Assembler-Befehl
hierzu lautet:
- mov 6Eh,90h
;
6Eh = Zieladresse, 90h = Quelladresse
Übersetzt in
den binären Maschinencode lautet dieser Befehl:
Hier steht zunächst der Quell-
und anschließend der Zieloperand. In der mnemonischen Darstellung
gilt die umgekehrte Reihenfolge. Zur Anzeige werden die binären
Maschinencodes in einen Hexcode umgewandelt. Für das Beispiel
ergibt sich folgender Hexcode: 85 90 6E
Aus
dem Programmspeicher wird zunächst der Opcode und anschließend
Quell- und Zieladresse der Daten gelesen. Das Speichermapping zeigt
die Anordnung der drei Befehlsbytes im Programmspeicher. Der Programmzähler
(PC) zeigt nach der Befehlsausführung auf den Opcode des nachfolgenden
Befehls.
Adressierungsarten
Zum Zugriff auf Speicherstellen, Register und On-Chip-Peripherie
kennt der 8051-Controller fünf verschiedene Adressierungsarten.
Sie unterscheiden sich in der Anzahl benötigter Befehls-Bytes,
der Befehlsausführungszeit und den möglichen Zugriffszielen:
1. Direkte Adressierung
Der Operand besteht aus einer Speicheradresse im internen
RAM. Diese Adresse wird als Hex-Code oder symbolisch angegeben (siehe
oben).
Beispiel: mov 48h,0A0h ;
Inhalt von Port2 ins int. RAM mov
48h,P2 ;
dito. Mit symbolischer Adresse
Für die direkte Adressierung wird je Operand ein
Byte im Programmspeicher benötigt.
2. Register-Adressierung
Bei der Register-Adressierung besteht der Operand aus
einem der besonderen Register: R0 ... R7, A, B, DPTR (16 Bit), CY
(1 Bit).
Beispiel: mov A,R4 ;
Kopiert den Inhalt des Registers R0 in den ;
Akku
Da diese speziellen Register im 8051 sehr häufig
benötigt werden, sind Sie direkt in den Opcode integriert.
Für den obigen Befehl gibt es daher insgesamt 8 Opcodes, je
nachdem welches der Register R0 ... R7 als Operand verwendet wird:
Befehl |
Opcode |
mov A,R0 |
E8h |
mov A,R1 |
E9h |
mov A,R2 |
EAh |
mov A,R3 |
EBh |
mov A,R4 |
ECh |
mov A,R5 |
EDh |
mov A,R6 |
EEh |
mov A,R7 |
EFh |
Da sich alle Register des 8051 organisatorisch im SFR
oder internen RAM wiederfinden, kann jedes dieser Register natürlich
auch mit seiner Speicheradresse (direkt) angesprochen werden. Obiger
Befehl lässt sich also auch so formulieren:
mov
0E0h,04h ;
E0 = Akkumulator, 04 = Register R4
Der Unterschied der beiden Adressierungsarten zeigt sich,
wenn man den Speicherbedarf und die Ausführungszeit der Befehle
vergleicht. Das erste Beispiel belegt nur 1 Byte Programmspeicher
und ist in einem Maschinenzyklus bearbeitet. Mit direkter Adressierung
werden drei Bytes und zwei Maschinenzyklen benötigt.
3. Unmittelbare (Immediate) Adressierung
Bei dieser Adressierungsart besteht der Operand aus einer
Konstanten im Programmspeicher. Da der Programmspeicher nur bei
der Programmierung, nicht aber während der Laufzeit geändert
werden kann, ist eine solche Konstante natürlich nur als Quelloperand
zulässig. Der Assembler erlaubt verschiedene Darstellungsarten
für Konstanten. Diese werden aber immer durch das #-Symbol
(Raute, Doppelkreuz oder Gartenzaun) gekennzeichnet. Die
Zahl 65 kann somit in folgenden äquivalenten Formen dargestellt
werden:
Konstante |
Codierung |
Grösse |
#65 |
Dezimal |
Byte |
#65d |
Dezimal |
Byte |
#41h |
Hex |
Byte |
#01000001b |
Binär |
Byte |
#101o |
Oktal |
Byte |
#'A' |
ASCII |
Byte |
Zur
Adressierung können weiterhin Adresskonstanten definiert werden:
Konstante |
Codierung |
Grösse |
#1F00h |
Adresskonstante |
Word |
Beispiel: mov A,#0 ;
Löscht den Akku! mov
DPTR,#1F00h ;
setzt den Datenzeiger auf die Adresse 1F00h ;
im Programmspeicher mov
P1,#01110111b ;
Die Bits von Port 1 werden entsprechend ;
der Bitmaske belegt
4. Registerindirekte Adressierung
Hier wird die Adresse des Operanden nicht direkt im Befehl
angegeben, sondern indirekt aus einem Hilfsregister gelesen. Als
Hilfsregister dienen dabei R0
und R1 für Zugriffe ins interne
RAM, sowie der Datenzeiger DPTR
für Zugriffe ins eXterne RAM.
Beispiel: mov R1,#2Eh ;
R1 mit Adresskonstanten 2Eh laden mov
A,@R1 ;
Kopiert den Inhalt von 2Eh in den Akku
Die Registerindirekte Adressierung ermöglicht, dass
der selbe Code mit unterschiedlichen Ziel- oder Quelloperanden ausgeführt
werden kann.
5. Indizierte Adressierung (Basisadresse
+ Index)
Durch indizierte Adressierung kann sehr bequem auf Datentabellen,
Datenfelder oder Textkonstanten im Programmspeicher zugegriffen
werden. Auch hier ist natürlich nur ein lesender Zugriff möglich.
Als Basisadresse wird der Datenzeiger (DPTR) oder der Programmzähler
(PC) verwendet. Als Index (oder Offset) dient jeweils der Akkumulator.
Das Holen eines Tabellenwertes aus dem Programmspeicher zeigt folgendes
Beispiel:
Beispiel: mov DPTR,#02B3h ;
Lädt die Adresskonstante 02B3h ;
in den Datenzeiger (Basisadresse) mov
A,#02h ;
Index 02h in den Akku movc
A,@A+DPTR ;
Tabellenwert aus 02B5h holen
Übung zu Befehlsaufbau und Adressierungsarten: |