bedingte Sprünge

  JB, JNB und JBC   JC und JNC   JZ und JNZ   CJNE   DJNZ   JMP
Die zwei grundlegendsten Befehle sind nun bekannt - etwas wirklich sinnvolles läßt sich damit aber noch nicht machen. Um etwas Interaktivität in das eigene Programm zu bringen, werden nun die bedingten Sprünge vorgestellt. Bedingte Sprünge führen in Abhängigkeit bestimmter Zustände entweder einen Sprung zu einem angegebenen Label durch oder nicht.

JB, JNB und JBC

JB [Bit],[Sprungziel], JNB [Bit],[Sprungziel] und JBC [Bit],[Sprungziel] führen einen Sprung in Abhängigkeit von [Bit] nach [Sprungziel] aus. JB (Jump if bit is set) springt nach [Sprungziel], wenn [Bit] auf 1 gesetzt ist. JNB (Jump if bit is not set) macht genau das Gegenteil, es springt, wenn [Bit] gelöscht bzw. 0 ist. Die dritte Variante, JBC (Jump if bit is set and clear bit) verbindet das Verzweigen bei gesetztem [Bit] mit dem Löschen ebendieses Bits. Dieser Befehl kann eingesetzt werden, wenn es auf das letzte bisschen Rechnerleistung ankommt und beste Optimierung gefragt ist, da JBC um 1/3 schneller ist als eine Kombination aus JB und einem Löschbefehl, und 2/5 weniger Speicher benötigt als diese Befehlskombination.

JB [Bit],[Sprungziel]
JNB [Bit],[Sprungziel]
JBC [Bit],[Sprungziel]

Um die Anwendung bedingter Sprünge zu demonstrieren, wird das Beispiel eines kleinen Zahlenschlosses gewählt. An P1.0 bis P1.3 sind die Tasten 0 bis 3 angeschlossen. Nur wenn die Ziffernfolge '13' in der richtigen Reihenfolge eingegeben wird, soll ein an P3.0 angeschlossenes Relais anziehen und die Tür freigeben. Im Falle eines Einbruchsversuchs soll an P3.1 ein Alarmsignal ausgegeben werden, dass sich nicht abstellen lassen darf. Alle Tasten werden mit invertierten Pegeln betrieben, dabei bedeutet eine 0 eine gedrückte Taste.

	$NOMOD51
	$INCLUDE (89C1051.MCU)

		mov P1,#0FFh		; Quell-Pins auf Input stellen
		mov P3,#11111100b	; Ausgabepins auf Anfangswert setzen

	wartenAufT1:
		jnb P1.0,alarm		; falsche Taste, Alarm auslösen
		jnb P1.1,wartenAufEndeT1; richtige Taste
		jnb P1.2,alarm		; falsche Taste, Alarm auslösen
		jnb P1.3,alarm		; falsche Taste, Alarm auslösen
		jmp wartenAufT1		; keine Taste, zurück zum Anfang

	wartenAufEndeT1:		; warten, bis Anwender Taste losläßt
		jnb P1.1,wartenAufEndeT1

	wartenAufT3:
		jnb P1.0,alarm		; falsche Taste, Alarm auslösen
		jnb P1.1,alarm		; falsche Taste, Alarm auslösen
		jnb P1.2,alarm		; falsche Taste, Alarm auslösen
		jnb P1.3,aktion		; richtige Taste
		jmp wartenAufT3		; keine Taste, zurück zum Anfang

	aktion:				; Tür freigeben
		mov P3,#11111101n
		jmp ende

	alarm:				; Alarm auslösen
		mov P3,#11111110b		

	ende:
		jmp ende		; Programm in Endlosschleife beenden

	END			; Dem Assembler das Programmende mitteilen
		

Ganz wichtig ist es, jedes Programm, das seine Aufgabe erledigt hat, in einer Endlosschleife abzuschließen. Niemals darf ein Programm einfach aus dem geschriebenen Code 'herauslaufen'!!! Im gelöschten Flash-Speicher, also dem nicht mit einem Programm gefüllten Programmspeicher, sind alle Bytes auf 0FFh gesetzt. In Maschinensprache steht dies für mov R7,A. Und nachdem der Controller also einige Male diesen Befehl ausgeführt hat, wird er wieder am Anfang des Programmspeichers beginnen - und im Falle dieses Beispiels den Alarm sofort wieder abstellen und die Tür verriegeln. Das geht so schnell, dass man durchaus den Eindruck bekommt, das Programm würde überhaupt nicht reagieren.
zurück zum Anfang

JC und JNC

JC [Sprungziel] und JNC [Sprungziel] führen einen Sprung in Abhängigkeit vom Carry-Flag durch. JC (Jump if Carry is set) verzweigt, wenn bei der Ausführung des Befehls das C-Flag den Wert 1 enthält. JNC (Jump if Carry not set) macht genau das Gegenteil davon, es verzweigt wenn C=0 ist.

Bei den Mathematikbefehlen werden einige Befehle auftauchen, die einen Teil des Ergebnisses wie z.B. das 'borgen' bei einer Subtraktion in das C-Flag schreiben. Im Zusammenhang mit diesen Mathe-Befehlen kommt JC und JNC eine große Bedeutung zu.

JC [Sprungziel]
JNC [Sprungziel]

zurück zum Anfang

JZ und JNZ

JZ (Jump if A is zero) führt einen Sprung nach [Sprungziel] aus, wenn das ganze A-Register 0, also kein einziges Bit im A-Register gesetzt ist. JNZ (Jump if A is not zero) macht genau das Gegenteil, es springt, wenn A nicht 0 ist. Diese Befehle machen den meisten Sinn im Zusammenhang mit dem Testen auf bestimmte Bitkombinationen im Zusammenhang mit den logischen Operationen und Bitmanipulationen, die einem der nächsten Abschnitte beschrieben werden.

JZ [Sprungziel]
JNZ [Sprungziel]

zurück zum Anfang

CJNE

Wer schon einmal mit 80x86-Assembler gearbeitet hat, wird sich wundern: CJNE ist ein Assemblerbefehl mit satten drei Argumenten - und diese haben es in sich! CJNE (Compare and Jump if not equal) vergleicht [Operator1] mit [Operator2] und verzweigt nach [Sprungziel], falls die beiden Operatoren nicht übereinstimmen. Diese komplexe Operation verbraucht dabei nur genausoviel Platz und Rechenzeit wie JB oder JNB. Daher ist CJNE im Allgemeinen sehr gut benutzbar und wird häufig eingesetzt.

Folgende Kombinationen für die Operatoren sind möglich:

CJNE [Op1],[Op2],[Label] Operator2
Byte im RAM #Konstante
Operator1 A X X
Rn   X
@Rn   X

Die wenigen möglichen Kombinationen lassen leider einige Wünsche offen. Aber mit dem Wissen, dass bereits vermittelt wurde, lassen sich einige der Punkte geeignet formulieren:

	; Ein Sprung-wenn-nicht-gleich
		cjne a,#162,weiter
		jmp sprungziel
	weiter:

	; Register als zweiten Operator 
		cjne a,3,sprungziel	; 3 ist die Speicheradresse von R3

	; ...
	sprungziel:
		

zurück zum Anfang

DJNZ

Der DJNZ-Befehl (Decrement and jump if not zero) ist ein weiterer Befehl mit einer komplexen Funktion. Zuerst verringert er den Wert von [Operator]. Ist der Operator nach dem decrementieren nicht 0, so wird nach [Sprungziel] gesprungen. Damit ist dieser Befehl die erste Wahl, wenn es um Zählschleifen geht.

DJNZ [Op],[Label]
Operator Rn
Byte im RAM

Eine Anwendung für DJNZ liegt in exakt berechneten Warteschleifen. Der 89C2051 läuft mit 24MHz. Ein Takt benötigt dabei 41,666 Nanosekunden, ein Maschinenzyklus aus je 12 Takten 0.5 Microsekunden. Um eine genau definierte Zeitspanne von beispielsweise 100 Microsekunden zu warten, müssen 200 Maschinenzyklen irgendwie verbraucht werden. Der DJNZ-Befehl benötigt zwei Maschinenzyklen, ein Mov-Befehl von einer Konstante nach A einen Maschinenzyklus. Folgender Code verzögert das Programm also um genau 100us:

		mov a,#0		; 1 Maschinenzyklus
		mov a,#99		; 1 Maschinenzyklus
	warteschleife:
		djnz Acc,warteschleife	; 2 Zyklen 99 mal verbrauchen
		

Hinweis: Das Symbol Acc steht für die RAM-Adresse 0E0h, auf der der Inhalt des A-Registers vom Controller abgelegt wird. So ist es möglich, DJNZ auch mit dem A-Register zu benutzen, obwohl als möglicher Operator nur 'Byte im RAM' bzw 'Rn' erlaubt ist.

Solche hochpräzisen Timings sind eine Stärke von Assembler. In C beispielsweise könnte so eine Schleife von einem guten Compiler einfach wegoptimiert werden, da sie nichts bewirkt - oder (was leider weitaus wahrscheinlicher ist) durch irgendwelche sinnfreien Befehle zusätzlich unnötig aufgebläht werden, da der Compiler den Sinn eines solchen Konstrukts nicht semantisch erfassen kann.
zurück zum Anfang

JMP

Der JMP-Befehl ist einer der wenigen Befehle, die von der basisrelativen Adressierung gebrauch machen. Er verzweigt an eine Adresse, aus der Summe von DPTR und A-Register ermittelt wird. Sinnvoll ist dieser Befehl, um Tabellen anzuspringen.

JMP @A+DPTR

Auch dieses Beispiel zeigt eine der ganz großen Stärken von Assembler. Es ermöglicht eine hocheffiziente Verzweigung zu verschiedenen Programmteilen mit einer Zahl als Operator (in C oder Java als Switch-case Anweisung realisiert). Die gewünschte Adressnummer muss in A vorliegen und jeweils mit 2 multipliziert werden, da ein AJMP-Befehl genau zwei Byte lang ist. A=0 springt also zu adresse1, A=2 zu adresse2, A=4 zu adresse3 und so weiter.

	; Sprung in Abhängigkeit von A
		mov DPTR,#sprungtabelle
		jmp  @A+DPTR	

	sprungtabelle:
		ajmp adresse1
		ajmp adresse2
		ajmp adresse3
		ajmp adresse4
		ajmp adresse5

	adresse1:
	; ... Code ...

	adresse2:
	; ... und so weiter ...
		

zurück zum Anfang
Seite zurück Startseite Seite vor
Erik Buchmann
EMail an: Owner@ErikBuchmann.de