Sprungbefehle
Es gibt 4 Sprungbefehle, welche immer springen. Alle anderen Sprungbefehle verzeigen das Programm abhängig von einer Bedingung, d.h. sie springen
nur, wenn eine gewisse Bedingung erfüllt ist, ansonsten wird das Programm mit dem nächsten Befehl fortgesetzt.
Unbedingte Sprünge
Die Befehle sjmp rel
, ajmp adr11
und ljmp adr16
springen immer an die angegebene Adresse. Der Unterschied
zwischen diesen Befehlen ist die Sprungdistanz und somit vor allem bei längeren Programmen relevant.
Die Adressangabe rel
stellt eine relative 8-Bit Adresse dar. Dies bedeutet, dass der Parameter eine vorzeichenbehaftete 8-Bit Zahl ist
(-128 bis 127). Bei einem Sprung wird PC+rel
berechnet und das Programm an dieser Stelle vortgesetzt. Somit kann mit diesem Befehl
maximal 128 Byte zurück und 127 Byte nach vor gesprungen werden.
Eine Endlosschleife mit sjmp
ergibt sich mit einer Sprungdistanz von -2 (=0FEh), da vor dem Sprung das sjmp
Befehlsbyte
und der Operand eingelesen wurden. Somit stellt sjmp 0FEh
eine Endlosschleife dar.
Die Adressangabe adr11
ist eine 11-Bit Adresse. Bei einem Sprung werden die letzten 11 Bit des PC
durch die angegebene
Adresse ersetzt. Das Sprungziel von ajmp
kann somit überall im aktuellen 2k-Block liegen - allerdings nicht außerhalb, da die ersten
5 Bit des Programm-Counters nicht verändert werden.
Der Befehl ljmp adr16
hat insgesammt eine Länge von 3 Bytes. Die letzten 2 Bytes stellen das Sprungziel als 16-Bit Adresse dar.
Bei einem Sprung wird hier der Wert des PC
durch die angegebene Adresse ersetzt und somit das Programm an dieser Stelle weiter
ausgeführt.
Der letzte unbedingte Sprung ist
jmp @A+DPTR
. Das Sprungziel liegt hier nicht fest im ROM des Controllers, sondern wird als
Summe der in
A
und
DPTR
gespeicherten Werten berechnet. Diese Summe stellt eine absolute 16-Bit Adresse dar, an
welcher das Programm fortgesetzt wird.
Nur mit diesem Befehl kann ein Sprungziel zur Laufzeit berechnet werden.
Beispiel zu jmp @A+DPTR
:
In diesem Programm wird über die Schalter an P0.1 und P0.0 die Zahl
fall zwischen 0 und 3 eingegeben.
Anhand dieses Wertes wird das Programm verzweigt. Hierzu berechnet das Programm im Accu 2*
fall.
In der Sprungtabelle
case steht für jeden Fall ein 2 Byte langer Sprungbefehl zur jeweiligen Bearbeitungsroutine (fall0 bis fall3).
Mittels
jmp @A+DPTR
wird in die richtige Zeile der Sprungtabelle gesprungen, da im
dptr
die Anfangsadresse
der Tabelle steht mit dem Akku 2*
fall hinzuaddiert werden.
Dies ist eine Möglichkeit, wie eine
switch-case
Anweisung aus einer Hochsprache (z.B. Java, C++, Delphi) in Assembler umgesetzen werden kann.
Vor allem bei einer großen Anzahl von Fällen (z.B. 256) kann hierdurch der Code deutlich besser strukturiert und lesbarer gehalten werden,
als mit einer
Reihe von Vergleichen (z.B.
cjne
). Außerdem ist diese Variante in vielen Fällen erheblich schneller, da man mit 2 Sprüngen (4 MZ)
am Ziel ankommt. Bei einer Kette von
cjne
bräuchte man im Mittel 128 Sprünge und im schlimmsten Fall sogar 256 (512 MZ).
include reg8051.inc
main:
mov A, P0
anl A, #11b ; A in {0,1,2,3}
rl A ; A=A*2 somit A in {0,2,4,6}
mov DPTR, #case ; DPTR = Start der Sprungtabelle
jmp @A+DPTR ; springt zu case+A
; Sprungtabelle mit je 2 Byte, jmp @A+DPTR
; erreicht ...
case: sjmp fall0 ; ... diese Zeile bei A=0
sjmp fall1 ; ... diese Zeile bei A=2
sjmp fall2 ; ... diese Zeile bei A=4
sjmp fall3 ; ... diese Zeile bei A=6
fall0: mov P1, #00001111b ;Bearbeitung von Fall 0
jmp main
fall1: mov P1, #00011111b ;Bearbeitung von Fall 1
jmp main
fall2: mov P1, #00111111b ;Bearbeitung von Fall 2
jmp main
fall3: mov P1, #01111111b ;Bearbeitung von Fall 3
jmp main
end
Bebedingte Sprünge - Akkumulor (un)gleich 0
Der Befehl jz rel
(jump if zero) springt genau dann, wenn der Akkumulator den Wert 0 enthält.
Hingegen springt jnz rel
(jump if not zero) wenn der Akkumulator nicht den Wert 0 enthält.
Bebedingte Sprünge - Vergleiche
Der cjne op1, op2, rel
Befehl springt genau dann, wenn op1
und op2
nicht die gleichen Werte enthalten.
Der erste Operand (op1
) kann entweder der Akkumulator, ein Register oder eine @R0 bzw. @R1 sein.
Der zweite Operand (op2
) ist eine Konstante. Nur wenn op1
der Akku ist kann op2
auch eine
direkt adressierbare Speicherzelle sein.
Der cjne
Befehl setzt das Carry Flag, wenn op1<op2
und löscht es wenn op1>=op2
. Hierbei werden
op1
und op2
als vorzeichenlose Zahlen betrachtet.
Bebedingte Sprünge - DJNZ
Der djnz op1, rel
Befehl (decrement and jump if not zero) vermindert den Wert
von op1
und springt, wenn der Wert danach nicht 0 ist.
Der erste Operand (op1
) kann entweder eine Datenadresse oder ein Register sein. Hierbei ist zu beachten, dass der
djnz A, rel
kein gültiger Befehl ist. Um den Akku hier verwenden zu können, muss seine Adresse 0E0h
verwendet werden:
djnz 0E0h, rel
ist gültig.
Dass zuerst vermindert und dann überprüft wird ist wichtig. Steht in der Speichezelle
20h
der Wert 0, so wird
die Schleife
label: djnz 020h, label
256 mal ausgeführt. Dies liegt daran, dass
0-1 = 255 (=0FFh)
ergibt und somit
nach dem ersten Druchlauf in 020h der Wert 255 steht.
Bebedingte Sprünge für Bits
Mit jc rel
und jnc rel
kann anhand des Carrys verzweigt werden. Jc rel
springt, wenn das Carry gesetzt ist
und jnc rel
wenn das Carry gelöscht ist.
Die Aquivalente zu jc rel
und jnc rel
für andere Bits sind jb badr, rel
und jnb badr, rel
.
Diese Befehle springen abhängig vom Inhalt des Bits in badr
: jb
springt falls das Bit 1 ist und jnb
falls das Bit 0 ist.
Der Befehljbc badr, rel
springt wenn das Bit in badr
gesetzt ist und löscht es gleichzeitig. Somit ist das Bit an
badr
nach diesem Befehl immer 0, da entweder das Bit 0 war (kein Sprung) oder aber es war 1 (Sprung)
und wurde von jbc
gelöscht.
Hier die Sprungbefehle in der Übersicht:
Mnemonic | Funktion | Bytes | MZ | Flags |
AJMP adr11 | Setze das Programm bei adr11 innerhalb der 2 kByte-Seite fort. | 2 | 2 | - |
LJMP adr16 | Setze das Programm bei adr16 fort. | 3 | 2 | - |
SJMP rel | Setze das Programm bei rel, relativ zum Programm-Counter, fort. | 2 | 2 | - |
JMP @A+DPTR | Setze das Programm an der Stelle fort, die sich aus der Summe von Akkumulator und DPTR ergibt. | 1 | 2 | - |
JZ rel | Springe relativ um die Adresse rel, wenn der Inhalt des Akkus gleich null ist. | 2 | 2 | - |
JNZ rel | Springe relativ um die Adresse rel, wenn der Inhalt des Akkus ungleich null ist. | 2 | 2 | - |
JC rel | Springe relativ um die Adresse rel, wenn der Inhalt des Carry-Flag gesetzt ist. | 2 | 2 | - |
JNC rel | Springe relativ um die Adresse rel, wenn der Inhalt des Carry-Flag nicht gesetzt ist . | 2 | 2 | - |
JB badr, rel | Springe relativ um die Adresse rel, wenn der Inhalt von badr gleich eins ist. | 3 | 2 | - |
JNB badr,rel | Springe relativ um die Adresse rel, wenn der Inhalt von badr gleich null ist. | 3 | 2 | - |
JBC badr,rel | Springe relativ um die Adresse rel, wenn der Inhalt von badr gleich eins ist und lösche den Inhalt von badr. | 3 | 2 | - |
CJNE A,dadr,rel | Springe relativ um die Adresse rel, wenn die Inhalte von Akkumulator und dadr ungleich sind. | 3 | 2 | CY |
CJNE A,#konst8,rel | Springe relativ um die Adresse rel, wenn der Inhalt des Akkus ungleich der 8-Bit-Konstanten ist. | 3 | 2 | CY |
CJNE Rr,#konst8,rel | Springe relativ um die Adresse rel, wenn der Inhalt des Registers Rr ungleich der 8-Bit-Konstanten ist. | 3 | 2 | CY |
CJNE @Ri,#konst8,rel | Springe relativ um die Adresse rel, wenn der Inhalt der internen Datenspeicherzelle, die durch Ri adressiert wird, ungleich der 8-Bit-Konstanten ist. | 3 | 2 | CY |
DJNZ Rr,rel | Der Inhalt von Register Rr wird um eins erniedrigt. Ist dann der Inhalt ungleich null, springe relativ um die Adresse rel. | 3 | 2 | - |
DJNZ dadr,rel | Der Inhalt von dadr wird um eins erniedrigt. Ist dann der Inhalt ungleich null, springe relativ um die Adresse rel. | 3 | 2 | - |