File Archive

 
Output of file : CHAP09.DOC contained in archive : ASMTUT3.ZIP




68

CHAPTER 9 - JUMPS

So far we have done almost exclusively sequential programming -
the order of execution has been from one instruction to the next
until the very end, where the jump instruction has brought us
back to the top. This is not the normal way programs work. In
order to have things like DO loops, FOR loops, WHILE loops, CASE
constructions and IF-THEN-ELSE constructs, we need to make
decisions and to be able to go to different blocks of code
depending on our decisions.

Intel has provided a wealth of conditional jumps to answer all
our needs. All of them are listed in a summary at the end of this
chapter.

The thing we will do most often is compare the size of two
numbers. In BASIC code:

IF A < B THEN

we need to see if A < B . If that is true, we do one thing, if it
is false, we do something else. One thing that we need to watch
out for is whether A and B are signed or unsigned numbers. Let A
= F523h (signed -2781; unsigned 62755) and B = 59E0h (signed
+23008; unsigned 23008). If A and B are signed numbers, then A <
B. However, if they are unsigned numbers, then A > B. In C and
PASCAL, the compiler takes care of whether you want signed or
unsigned numbers (BASIC assumes that it is always signed
numbers). You are now on the machine level, and must take care of
this yourself.

To compare two numbers, you subtract one from the other, and then
evaluate the result (less than 0, 0, or more than 0). To compare
A and B, you do A minus B. To compare AX and BX, you can:

sub ax, bx

and then evaluate it. Unfortunately, if you do that, you will
destroy the information in AX. It will have been changed in the
process. Intel has solved this problem with the CMP (compare)
instruction.

cmp ax, bx

subtracts BX from AX, sets the flags, and throws the answer away.
CMP is exactly the same as SUB except that AX remains unchanged.
We probably don't want to save the result anyway. If we do, we
can always use:

sub ax, bx

We have subtracted BX from AX. There are now three possibilities.
(1) AX > BX so the answer is positive, (2) AX = BX so the answer
is zero, or (3) AX < BX so the answer is negative. But are these

______________________

The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson




Chapter 9 - Jumps 69
_________________

signed or unsigned numbers? The 8086 uses the same subtract (or
CMP) instruction for both signed or unsigned numbers. You have to
tell the 8086 in the following instruction whether you were
talking about signed or unsigned numbers.

cmp ax, bx
ja some_label

asks the machine to compare AX and BX. It then says that AX and
BX were UNSIGNED numbers and the machine should jump to
"some_label" if AX was above BX.

cmp ax, bx
jg some_label

asks the machine to compare AX and BX. It then says that AX and
BX were SIGNED numbers and the machine should jump to
"some_label" if AX was greater than BX.

The 8086 makes the decision by looking at the flags. They give it
complete information about the result. (The decision making rules
are in the summary).

In our example on the previous page, we had A = -2781 (unsigned
62755) and B = +23008 (unsigned 23008). If we put A in AX and B
in BX, then the instruction JA will execute the jump, while the
instruction JG will not execute the jump. What happens if the
8086 doesn't execute the jump? It goes on to the next
instruction.

As I said before, the 8086 has LOTS of conditional jumps. I am
going to give you a list of them now, but be forewarned that the
mnemonics for these suckers are confusing. Rather than doing
something sensible like JUG for 'jump unsigned greater' and JSG
for 'jump signed greater' - things that would be easy to
distinguish, they have JA for 'jump above' (unsigned) and JG for
'jump greater' (signed).{1} Therefore, use the summary at the
end of the chapter. With enough use, you will remember which is
which.

The arithmetic jump instructions have two forms, a positive one
and a negative one. For instance:

ja ; jump above
jnbe ; jump not (below or equal)

are actually the same machine instruction. You use whichever one
makes the program clearer. We will have examples later to
illustrate both positive and negative mnemonics. Here are the
signed and unsigned instructions and their meaning. The
____________________

1 This wierd use of the words above and greater, below and
less, is so confusing that my copy of the Microsoft assembler
manual v5.1 has it reversed on one page. It calls the signed
jumps unsigned and the unsigned jumps signed. And that's the one
place where it SHOULD be right.




The PC Assembler Tutor 70
______________________

equivalent mnemonics will appear in pairs.

THESE ARE THE SIGNED JUMP INSTRUCTIONS

jg ; jump if greater
jnle ; jump if not (less or equal){2}

jl ; jump if less
jnge ; jump if not (greater or equal)

je ; jump if equal
jz ; jump if zero

jge ; jump if greater or equal
jnl ; jump if not less

jle ; jump if less or equal
jng ; jump if not greater

jne ; jump if not equal
jnz ; jump if not zero

These are self-explainatory, keeping in mind that these apply
only to signed numbers.


THESE ARE THE UNSIGNED JUMP INSTRUCTIONS

ja ; jump if above
jnbe ; jump if not (below or equal)

jb ; jump if below
jnae ; jump if not (above or equal)

je ; jump if equal
jz ; jump if zero

jae ; jump if above or equal
jnb ; jump if not below

jbe ; jump if below or equal
jna ; jump if not above

jne ; jump if not equal
jnz ; jump if not zero

These apply to unsigned numbers, and should be clear.

JZ, JNZ, JE and JNE work for both signed and unsigned numbers.
After all, zero is zero.
____________________

2 I was trying to decide whether or not to put in the
parentheses. If there are two things after the "not" the "not"
applies to both of them. By the rules of logic, not (less or
equal) == not less AND not equal. If you don't understand this,
try to find someone who can explain it to you.




Chapter 9 - Jumps 71
_________________


Before we get going, there is one more thing you need to know.
The unconditional jump:

jmp some_label

can jump anywhere in the current code segment. XXXX is the
current number in CS, then jmp can go from XXXX offset 0 to XXXX
offset 65535.

Conditional jumps are something else entirely. ALL conditional
jumps (including the loop instruction) are limited range jumps.
They are from -128 to +127. That is, they can go backward up to
128 bytes and they can go forward up to 127 bytes.{3} You will
find that you will get assembler errors because the conditional
jumps are too far away, but don't worry because we can ALWAYS fix
them. You will find out later how to deal with them.


As in the other arithmetic instructions, there are five forms of
the CMP instruction.

1. compare two registers
2. compare a register with a variable
3. compare a variable with a register
4. compare a register with a constant
5. compare a variable with a constant

These look like:

cmp ah, dl
cmp si, memory_data
cmp memory_data, ax
cmp ch, 127
cmp memory_data, 14938


Here are some decisions we might have to make in programs.

(1) We are writing a program to print hex numbers on the screen.
We have one routine for 0 - 9 and a different one for A - F.
Sound familiar?

cmp al, 10
jb decimal_routine
; here we are past the jump, so we can start the A - F
; routine.

(2) We want to fire everyone over the age of 55 (this is an
unsigned number since there are no negative ages):
____________________

3 But they don't jump from the beginning of the machine
instruction, they jump from the END of the machine instruction
(which is two bytes long). This means that they have an effective
range of from -126 bytes to +129 bytes from the BEGINNING of the
instruction.




The PC Assembler Tutor 72
______________________


cmp employee_age, 55
ja find_a_reason_for_termination
; start of routine for younger employees here.

(3) You want to know if you need to go to a loanshark:

mov di, bank_balance
cmp unpaid_bills, di
jg gotta_go_see_Vinnie
; continue normal routine here

(4) Notice that the last one could have also been written:

mov di, bank_balance
cmp di, unpaid_bills
jl gotta_go_see_Vinnie
; continue normal routine

though to my eye the first one seems clearer.

(5) You have the results from two calculations, the first one in
DI and the second one in CX. You need to go to different routines
depending on which is larger. If they are the same, you exit:

cmp di, cx
jg routine_one ; di is greater
jl routine_two ; cx is greater
jmp exit_label ; they are equal

We had two conditional jumps in a row and both of them were able
to look at the results of the CMP instruction because the first
jump (JG) did not change any of the flags. This is true of all
jumps - conditional jumps, the unconditional jump, and LOOP. They
never change any of the flags, so you can have two jumps in a row
and be certain that the flags will not be altered.


(6) Your dad has promised to buy you a Corvette if you don't get
suspended from school more than three times this semester. Here's
his decision code:

cmp number_of_suspensions, 3
jng buy_him_the_corvette
; better luck next time



JUMP OUT OF RANGE

If the code you are jumping to is more than -128 or +127 bytes
from the end of a conditional jump, you will get an assembler
error that the jump is out of range. This can always be fixed.
Take this code:





Chapter 9 - Jumps 73
_________________

cmp ax, bx
jg destination_label
further_code:
; continue the program

If 'destination_label' is out of range, what you need to do is
jump to 'further_code' with a conditional jump (you are within
range of 'further_code') and use JMP (which can go anywhere in
the segment) to go to 'destination_label'. To switch the jump,
you simply negate the jump condition:

jg -> jng
je -> jne
jne -> je
jbe -> jnbe

We use reverse logic. Originally, if the condition was met we
jumped. If the condition was not met we continued. Now, if the
condition is NOT met, we jump, and if the condition is NOT not
met (yes, there are two NOTs) which means it was met, we
continue, and this sends us to the JMP instruction. Make sure you
believe this works correctly before going on. The code then looks
like this:

cmp ax, bx
jng further_code
jmp destination_label
further_code:
; continue the program

This is the standard way of handling the situation.






























The PC Assembler Tutor 74
______________________

SUMMARY

CMP
CMP performs the same operation as subtraction, but it does
not change the registers or variables, it only sets the flags. It
is the cousin of TEST. As usual, there are five possibilities:

1. compare two registers
2. compare a register with a variable
3. compare a variable with a register
4. compare a register with a constant
5. compare a variable with a constant


THESE ARE THE SIGNED JUMP INSTRUCTIONS

jg ; jump if greater
jnle ; jump if not (less or equal)

jl ; jump if less
jnge ; jump if not (greater or equal)

je ; jump if equal
jz ; jump if zero

jge ; jump if greater or equal
jnl ; jump if not less

jle ; jump if less or equal
jng ; jump if not greater

jne ; jump if not equal
jnz ; jump if not zero



THESE ARE THE UNSIGNED JUMP INSTRUCTIONS

ja ; jump if above
jnbe ; jump if not (below or equal)


jb ; jump if below
jnae ; jump if not (above or equal)

je ; jump if equal
jz ; jump if zero

jae ; jump if above or equal
jnb ; jump if not below

jbe ; jump if below or equal
jna ; jump if not above

jne ; jump if not equal
jnz ; jump if not zero






Chapter 9 - Jumps 75
_________________

THESE JUMPS CHECK A SINGLE FLAG

These come in opposite pairs

jc ; jump if the carry flag is set
jnc ; jump if the carry flag is not set

jo ; jump if the overflow flag is set
jno ; jump if the overflow flag is not set

jp or jpe ; jump if parity flag is set (parity is even)
jnp or jpo ;jump if parity flag is not set (parity is odd)


js ; jump if the sign flag is set (negative )
jns ; jump if the sign flag is not set (positive or 0)


THIS CHECKS THE CX REGISTER

jcxz ; jump if cx is zero

Why do we have this instruction? Remember, the loop instruction
decrements CX and then checks for 0. If you enter a loop with CX
set to zero, the loop will repeat 65536 times. Therefore, if you
don't know what the value of CX will be when you enter the loop,
you use this instruction just before the loop to skip the loop if
CX is zero:

jcxz after_the_loop

loop_start:
.
.
.
.
loop loop_start
after_the_loop:


INFORMATION ABOUT JUMPS

The unconditional jump (JMP) can go anywhere in the code segment.
All other jumps, which are conditional, can only go from -128 to
+127 bytes from the END of the jump instruction (that's from -126
to +129 bytes from the beginning of the instruction).

Jumps have no effect on the 8086 flags.

How does the 8086 decide whether something is more, less, or the
same? The rules for unsigned numbers are easy. If you subtract a
larger number from a smaller, the subtraction will pass through
zero and will set the carry flag (CF). If they are the same, the
result will be zero and it will set the zero flag (ZF). If the
first number is larger, the machine will clear the carry flag and
the zero flag will be cleared (ZF = 0). Therefore, for unsigned
numbers, we have:





The PC Assembler Tutor 76
______________________


First number is:
above CF = 0 ZF = 0
equal CF = 0 ZF = 1
not equal CF = ? ZF = 0
below CF = 1 ZF = 0

All other unsigned compares are combinations of those three
things:

jae = ja OR je (CF = 0 and ZF = 0) or ZF = 1
jbe = jb OR je CF = 1 or ZF = 1

When you have a negative condition, it is much easier to look at
its equivalent positive condition to figure out what is going on:

jnae is the same as jb CF = 1
jnbe is the same as ja CF = 0 ZF = 0


SIGNED NUMBERS

This section is not for the fainthearted. It is not necessary, so
if you find yourself getting confused, just remember that if you
see documentation talking about a jump where the overflow flag
equals the sign flag or the overflow flag doesn't equal the sign
flag, it is talking about SIGNED numbers.

Zero is zero, so we won't concern ourselves with it here. It is
exactly the same.

If A and B are two signed word sized numbers and we have:

cmp A, B

then we can have four different cases:

1) If A is just a little greater than B [(A - B) <=
+32767], then the result will be a small positive number,
and there will be no overflow. SF = 0, OF = 0.

2) If A is much greater than B [+32767 < (A - B)], then the
result will be too positive and it will overflow from
positive to negative. This will set both the sign flag (it
is now negative) and the overflow flag. SF = 1, OF = 1.

3) If A is a little less than B [-32768 <= (A - B)], that is
if it is only a little negative, then the result is a small
negative number, and there is no overflow. SF = 1, OF = 0.

4) If A is much less than B [(A - B) < -32768], then the
result is a large negative number. It is too negative and
overflows into a positive number. SF = 0, OF = 1.

Recapping, for a positive result:

1) SF = 0, OF = 0




Chapter 9 - Jumps 77
_________________

2) SF = 1, OF = 1

and for a negative result:

3) SF = 1, OF = 0
4) SF = 0, OF = 1

For positive results (and zero), SF = OF. For negative results,
SF is not equal to OF. This, in fact, is how the 8086 decides a
signed jump. If SF = OF, it's positive, if SF is not equal to OF,
it's negative. If ZF = 1, then obviously they are equal. Here is
the list:

greater SF = OF ZF = 0
equal SF = OF ZF = 1
not equal ZF = 0
less SF is not equal to OF

As with the unsigned numbers, if you have a negative condition,
it is easier to change it into its equivalent positive condition
and then figure out the requirements. For instance:

jnge same as jl SF is not equal to OF
jnl same as jge ( SF = OF and ZF = 0 ) or ( ZF = 1)

If you think about it, this OF = SF stuff does make sense. We are
subtracting two numbers. If the first one is greater, then the
answer will be positive. It can either be a little positive as in
(cmp 0, -1 ) = 1 or it can be very positive, as in (cmp 32767,
-32768) = 65,535 (same as -1). If it is just a little positive,
there is no overflow and it has a positive sign (SF = 0, OF = 0).
If the difference gets large, then the number overflows from + to
-. At that point OF = 1, but it now has a negative sign, so OF =
SF. The flags MUST match.

In the opposite case where the second number is greater, The
answer is negative. It can either be a little negative as in (cmp
12, 13)= -1, or it can be very negative (cmp -32768, 32767) =
-65535 =1. If it is a small difference, the sign is negative, but
there is no overflow (SF = 1, OF = 0). As the difference gets
larger, the number overflows from negative to positive so the
sign flag is now positive, but the overflow flag is set (SF = 0,
OF = 1). Those flags CAN'T match.