|
Although the jmp instruction provides transfer of control, it does not allow you to make any serious decisions. The 80x86's conditional jump instructions handle this task. The conditional jump instructions are the basic tool for creating loops and other conditionally executable statements like the if..endif statement.
The conditional jumps test one or more flags to see if they match some particular pattern (just like the setcc instructions). If the flag settings match, the instruction transfers to the target location. If the match fails, the CPU ignores the conditional jump and execution continues with the next instruction. Some conditional jump instructions simply test the setting of the sign, carry, overflow, and zero flags. For example, after the execution of an shl instruction, you could test the carry flag to determine if the shl shifted a one out of the H.O. bit of its operand. Likewise, you could test the zero flag after a test instruction to see if any specified bits were one. Most of the time, however, you will probably execute a conditional jump after a cmp instruction. The cmp instruction sets the flags so that you can test for less than, greater than, equality, and so on.
The conditional jmp instructions take the following form:
Jcc label;
The "cc" in Jcc indicates that you must substitute some character sequence that specifies the type of condition to test. These are the same characters the setccinstruction uses. For example, "js" stands for jump if the sign flag is set. A typical js instruction looks like this:
js ValueIsNegative;
In this example, the js instruction transfers control to the ValueIsNegative statement label if the sign flag is currently set; control falls through to the next instruction following the js instruction if the sign flag is clear.
Unlike the unconditional jmp instruction, the conditional jump instructions do not provide an indirect form. The only form they allow is a branch to a statement label in your program. Conditional jump instructions have a restriction that the target label must be within 32,768 bytes of the jump instruction. However, because this generally corresponds to somewhere between 8,000 and 32,000 machine instructions, it is unlikely you will ever encounter this restriction.
Note: Intel's documentation defines various synonyms or instruction aliases for many conditional jump instructions. Tables 7-1, 7-2, and 7-3 list all the aliases for a particular instruction. These tables also list out the opposite branches. You'll soon see the purpose of the opposite branches.
Instruction | Description | Condition | Aliases | Opposite |
---|---|---|---|---|
| ||||
JC | Jump if carry | Carry = 1 | JB, JNAE | JNC |
JNC | Jump if no Carry | Carry = 0 | JNB, JAE | JC |
JZ | Jump if zero | Zero = 1 | JE | JNZ |
JNZ | Jump if not zero | Zero = 0 | JNE | JZ |
JS | Jump if sign | Sign = 1 | JNS | |
JNS | Jump if no sign | Sign = 0 | JS | |
JO | Jump if overflow | Ovrflw = 1 | JNO | |
JNO | Jump if no overflow | Ovrflw = 0 | JO | |
JP | Jump if parity | Parity = 1 | JPE | JNP |
JPE | Jump if parity even | Parity = 1 | JP | JPO |
JNP | Jump if no parity | Parity = 0 | JPO | JP |
JPO | Jump if parity odd | Parity = 0 | JNP | JPE |
Instruction | Description | Condition | Aliases | Opposites |
---|---|---|---|---|
| ||||
JA | Jump if above (>) | Carry=0, Zero=0 | JNBE | JNA |
JNBE | Jump if not below or equal (not <=) | Carry=0, Zero=0 | JA | JBE |
JAE | Jump if above or equal (>=) | Carry = 0 | JNC, JNB | JNAE |
JNB | Jump if not below (not <) | Carry = 0 | JNC, JAE | JB |
JB | Jump if below (<) | Carry = 1 | JC, JNAE | JNB |
JNAE | Jump if not above or equal (not >=) | Carry = 1 | JC, JB | JAE |
JBE | Jump if below or equal (<=) | Carry = 1 ro Zero = 1 | JNA | JNBE |
JNA | Jump if not above (not >) | Carry = 1 or Zero = 1 | JBE | JA |
JE | Jump if equal (=) | Zero = 1 | JZ | JNE |
JNE | Jump if not equal (:) | Zero = 0 | JNZ | JE |
Instruction | Description | Condition | Aliases | Opposite |
---|---|---|---|---|
| ||||
JG | Jump if greater (>) | Sign = Ovrflw or Zero=0 | JNLE | JNG |
JNLE | Jump if not less than or equal (not <=) | Sign = Ovrflw or Zero=0 | JG | JLE |
JGE | Jump if greater than or equal (>=) | Sign = Ovrflw | JNL | JGE |
JNL | Jump if not less than (not <) | Sign = Ovrflw | JGE | JL |
JL | Jump if less than (<) | Sign <> Ovrflw | JNGE | JNL |
JNGE | Jump if not greater or equal (not >=) | Sign <> Ovrflw | JL | JGE |
JLE | Jump if less than or equal (<=) | Sign <> Ovrflw or Zero = 1 | JNG | JNLE |
JNG | Jump if not greater than (not >) | Sign <> Ovrflw or Zero = 1 | JLE | JG |
JE | Jump if equal(=) | Zero = 1 | JZ | JNE |
JNE | Jump if not equal (|) | Zero = 0 | JNZ | JE |
One brief comment about the "opposites" column is in order. In many instances you will need to be able to generate the opposite of a specific branch instructions (examples appear later in this chapter). With only two exceptions, a very simple rule completely describes how to generate an opposite branch:
If the second letter of the Jcc instruction is not an "n", insert an "n" after the "j". E.g., je becomes jne and jl becomes jnl.
If the second letter of the Jcc instruction is an "n", then remove that "n" from the instruction. E.g., jng becomes jg and jne becomes je.
The two exceptions to this rule are jpe (jump if parity is even) and jpo (jump if parity is odd). These exceptions cause few problems because (a) you'll hardly ever need to test the parity flag, and (b) you can use the aliases jp and jnp synonyms for jpe and jpo. The "N/No N" rule applies to jp and jnp.
Though you know that jge is the opposite of jl, get in the habit of using jnl rather than jge as the opposite jump instruction for jl. It's too easy in an important situation to start thinking "greater is the opposite of less" and substitute jg instead. You can avoid this confusion by always using the "N/No N" rule.
The 80x86 conditional jump instructions give you the ability to split program flow into one of two paths depending upon some condition. Suppose you want to increment the AX register if BX is equal to CX. You can accomplish this with the following code:
cmp( bx, cx ); jne SkipStmts; inc( ax ); SkipStmts:
The trick is to use the opposite branch to skip over the instructions you want to execute if the condition is true. Always use the "opposite branch (N/no N)" rule given earlier to select the opposite branch.
You can also use the conditional jump instructions to synthesize loops. For example, the following code sequence reads a sequence of characters from the user and stores each character in successive elements of an array until the user presses the ENTER key (carriage return):
mov( 0, edi ); RdLnLoop: stdin.getc(); // Read a character into the AL register. mov( al, Input[ edi ] ); // Store away the character. inc( edi ); // Move on to the next character. cmp( al, stdio.cr ); // See if the user pressed Enter. jne RdLnLoop;
Like the setcc instructions, the conditional jump instructions come in two basic categories — those that test specific processor flags (e.g., jz, jc, jno) and those that test some condition ( less than, greater than, and so on). When testing a condition, the conditional jump instructions almost always follow a cmp instruction. The cmp instruction sets the flags so you can use a ja, jae, jb, jbe, je, or jne instruction to test for unsigned less than, less than or equal, equality, inequality, greater than, or greater than or equal. Simultaneously, the cmp instruction sets the flags so you can also do a signed comparison using the jl, jle, je, jne, jg, and jge instructions.
The conditional jump instructions only test flags; they do not affect any of the 80x86 flags.
|