Sprungbefehle
Die Sprungbefehle werden in bedingte und unbedingte Sprünge unterschieden.
Beiden gemeinsam ist, daß sie ein Sprungziel benötigen. Dieses ist hinter dem eigentlichen Sprungbefehl anzugeben und sieht folgendermaßen aus:
Sprungzielname:
Man beachte den Doppelpunkt hinter dem Namen.
Das Sprungziel wird auch als Sprungmarke bezeichnet.
Man steht in jedem längeren Programm vor dem Fall, daß man abhängig von einer bestimmten Situation eine bestimmte Handlung auszuführen hat.
Egal in welcher Programmiersprache wir uns bewegen, Entscheidungen werden erst nach einem vorherigen Vergleich getroffen. Egal, ob wir mit IF..THEN..ELSE, WHILE oder FOR arbeiten, immer wird etwas verglichen und abhängig von diesem Vergleich eine Handlung ausgeführt.
Programmcode wird normalerweise linear, so wie er im Hauptspeicher steht, ausgeführt. Bei einer Entscheidung gabelt sich dieser Weg jedoch in mindestens zwei Wege. Um einen dieser Wege zu erreichen, ist ein Sprung zu diesem nötig. Um herauszufinden, zu welchem Weg das Programm springen muß, muß ein Vergleich durchgeführt werden. In fast allen Fällen wird zur Auswertung des Vergleiches das Flagregister verwendet, in zwei anderen Fällen das Register CX.
Bedingte Sprünge
Den bedingten Sprungbefehlen geht in jedem Falle ein Befehl voraus, der die Flags bzw. CX verändert. Die bedingten Sprungbefehle werten nun die Register aus und springen dann an eine bestimmte Stelle im Code (Sprungmarke).
In der folgenden Tabelle sind einige Sprungbefehle aufgeführt.
Eine Auswahl bedingter Sprungbefehle:
Befehl |
Bedeutung |
ja |
jump if above (ohne Vorzeichen) |
jae |
jump if above or equal |
jb |
jump if below (ohne Vorzeichen) |
jbe |
jump if below or equal |
jc |
jump if Carry Flag = 1 |
jcxz |
jump if CX = 0 |
je |
jump if equal |
jg |
jump if greater (mit Vorzeichen) |
jge |
jump if greater or equal (mit Vorzeichen) |
jl |
jump if lower (mit Vorzeichen) |
jle |
jump if lower or equal (mit Vorzeichen) |
jna |
jump if not above |
jnae |
jump if not above or equal |
jnb |
jump if not below |
jnbe |
jump if not below or equal |
jnc |
jump if not Carry = 1 |
jne |
jump if not equal |
jng |
jump if not greater |
jnge |
jump if not greater or equal |
jnl |
jump if not lower |
jnle |
jump if not lower or equal |
jno |
jump if not Overflow = 1 |
jnp |
jump if not Parity = 1 |
jns |
jump if not Sign = 1 |
jnz |
jump if not Zero = 1 |
jo |
jump if Overflow Flag = 1 |
jp |
jump if Parity Flag = 1 |
jpe |
jump if parity even (Parity = 1) |
jpo |
jump if paritiy odd (Parity = 0) |
js |
jump if Sign = 1 |
jz |
jump if Zero Flag = 1 |
In einem Programm würde sich ein bedingter Sprung zum Beispiel folgendermaßen realisieren lassen:
... mov cx,1 dec cx jz CXIstNull ... CXIstNull: ... ;Die Einrückung dient nur der ;Lesbarkeit ... ... |
Dieses zugegebenermaßen recht
sinnlose Programmfragment lädt in das Register CX den Wert 1 und dekrementiert
dieses dann. Da dann in CX der Wert 0 (Null) steht, wird durch den Befehl DEC das Zero-Flag im
Flag-Register auf 1 gesetzt. Der nachfolgende Befehl JZ überprüft nun, ob denn im
Flag-Register das Zero-Flag auf 1 gesetzt ist und führt, sollte dies der Fall
sein, einen Sprung an die durch die Sprungmarke CXIstNull gekennzeichnete
Stelle durch.
Wenn Sie die obige Tabelle eingehend durchgeschaut haben, dann wird Ihnen vielleicht der Befehl JCXZ aufgefallen sein. Diesen hätten Sie in diesem speziellen Fall anstelle des Befehls JZ verwenden können. Hätten wir allerdings mit dem Register BX statt CX gearbeitet, dann bliebe Ihnen nur ein Code wie der obige.
Mit den bedingten Sprungbefehlen lassen sich Konstrukte im Stile einer FOR-Schleife aufbauen:
mov cx,400 LoopStart: ... ;Code, der eine Handlung ;ausführt dec cx jnz LoopStart ... |
In das Register CX wird erst der Wert 400 geladen. Dann führt der Prozessor den Code aus, der hinter der Sprungmarke steht. Gehen wir einfach davon aus, daß dieser Code die Zahl, die in Register CX steht, auf dem Bildschirm ausgibt, im ersten Durchlauf also 400.
Nachdem dies erledigt ist, wird das Register CX um 1 dekrementiert. Beim ersten Durchlauf steht jetzt also 399 in CX. Danach wird geprüft, ob das Zero-Flag gesetzt ist. Da das vorherige Dekrementieren von CX nicht Null ergab, das Zero-Flag also nicht auf 1 gesetzt wurde, bringt uns JNZ (Jump if Not Zero) wieder zur Sprungmarke LoopStart. Ab da wird dann wieder der Inhalt von CX ausgegeben (noch immer 399), dekrementiert (jetzt ist CX=398), das Zero-Flag ausgewertet und gesprungen. Das geht solange, bis in CX nur noch eine 1 steht, diese dekrementiert wird, dadurch das Zero-Flag gesetzt wird, die Sprungbedingung nicht mehr zutrifft und schlußendlich die Schleife verlassen wird.
Unbedingter Sprung
Ein unbedingter Sprung wertet keine Flags oder andere Register aus, sondern springt sofort an die durch die Sprungmarke bezeichnete Stelle:
... mov cx,1 jmp EinfachSo ... EinfachSo: ... ... ... |
Dem JMP-Befehl ist es total egal, was in CX oder im Flag-Register steht, er springt einfach nur an die Sprungmarke EinfachSo.
Die bedingten Sprünge haben eine Reichweite von 128 Byte, es kann also maximal 128 Bytes vor- oder zurückgesprungen werden. Diese Einschränkung gilt jedoch nur bei Prozessoren, die älter als der 80386 sind. Ab dem 80386 haben die bedingten Sprünge eine Reichweite von 32 KByte. Sollten Sie für einen Prozessor ab dem 80386 programmieren, dann müssen Sie allerdings mittels der geeigneten Direktive die Codeerzeugung für diesen Prozessor einschalten, also beispielsweise mit P386N.
Im Gegensatz dazu kann der unbedingte Sprung mit jmp auf jedem Prozessor auch Segmentgrenzen überwinden, kurz gesagt also weiter als 64 KByte springen.
Unterhalb des 80386 müssen Sie sich eines kleinen Umweges bedienen, um größere (bedingte) Sprünge zu realisieren.
Das folgende Beispiel zeigt zuerst eine Schleifenkonstruktion, die einen Fehler hervorruft und danach die korrigierte Version.
;es gilt die Annahme, daß kein 80386 oder höher ;eingestellt ist ... EntferntesZiel: ... ;Code mit einer Größe von ... ;mehr als 128 Byte dec cx jnz EntferntesZiel ;falls CX <> 0 dann Sprung zum ;EntferntesZiel
... |
|
|
Und jetzt die korrigierte Version
;gleiche Annahme wie oben ... EntferntesZiel: ... ;Code mit einer Größe von ... ;mehr als 128 Byte dec cx jz Ende ;falls CX=0 dann Sprung zum ;Schleifenende jmp EntferntesZiel ;ansonsten neuer ;Durchlauf Ende: ... |
Zwischen der Sprungmarke EntferntesZiel und dem bedingten Sprungbefehl liegt Code, der mehr als 128 Bytes beansprucht. Der Sprung soll durchgeführt werden, wenn nach dem dekrementieren von CX ein anderer Wert als Null in diesem Register steht. Wenn TASM beim Kompilieren zu diesem Befehl kommt, dann gibt er den Fehler "Relative jump out of range by .. bytes", wobei die beiden Punkte durch einen entsprechenden Zahlenwert ersetzt werden.
Bei der korrigierten Variante fallen zwei Dinge auf: es gibt einen weiteren Befehl und es gibt eine weitere Sprungmarke.
Wieder wird das Register CX dekrementiert. Der nachfolgende Befehl prüft aber diesmal, ob nach dem Dekrementieren der Wert Null in CX steht. Sollte dies der Fall sein, dann wird die Schleife mit einem Sprung zur Sprungmarke Ende verlassen. Sollte dies nicht der Fall sein, dann wird einfach beim nächsten Befehl weitergemacht, der ohne Umschweife zum Schleifenanfang (EntferntesZiel) springt.