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.