8086 Assembler Tutorial for Beginners (Part 7)

Program Flow Control

Controlling the program flow is a very important thing, this is where your program can make decisions according to certain conditions.

  • Unconditional Jumps

    The basic instruction that transfers control to another point in the program is JMP.

    The basic syntax of JMP instruction:
    JMP label
    To declare a label in your program, just type its name and add ":" to the end, label can be any character combination but it cannot start with a number, for example here are 3 legal label definitions:
    label1:
    label2:
    a:
    Label can be declared on a separate line or before any other instruction, for example:
    x1:
    MOV AX, 1

    x2: MOV AX, 2
    Here is an example of JMP instruction:

    
    ORG    100h
    
    MOV    AX, 5          ; set AX to 5.
    MOV    BX, 2          ; set BX to 2.
    
    JMP    calc           ; go to 'calc'.
    
    back:  JMP stop       ; go to 'stop'.
    
    calc:
    ADD    AX, BX         ; add BX to AX.
    JMP    back           ; go 'back'.
    
    stop:
    
    RET                   ; return to operating system.
    
    END                   ; directive to stop the compiler.

    Of course there is an easier way to calculate the some of two numbers, but it's still a good example of JMP instruction.
    As you can see from this example JMP is able to transfer control both forward and backward. It can jump anywhere in current code segment (65,535 bytes).


  • Short Conditional Jumps

    Unlike JMP instruction that does an unconditional jump, there are instructions that do a conditional jumps (jump only when some conditions are in act). These instructions are divided in three groups, first group just test single flag, second compares numbers as signed, and third compares numbers as unsigned.

    Jump instructions that test single flag

    Instruction Description Condition Opposite Instruction
    JZ , JE Jump if Zero (Equal).  ZF = 1 JNZ, JNE
    JC , JB, JNAE Jump if Carry (Below, Not Above Equal).  CF = 1 JNC, JNB, JAE
    JS Jump if Sign.  SF = 1 JNS
    JO Jump if Overflow.  OF = 1 JNO
    JPE, JP Jump if Parity Even.  PF = 1 JPO
    JNZ , JNE Jump if Not Zero (Not Equal).  ZF = 0 JZ, JE
    JNC , JNB, JAE Jump if Not Carry (Not Below, Above Equal).  CF = 0 JC, JB, JNAE
    JNS Jump if Not Sign.  SF = 0 JS
    JNO Jump if Not Overflow.  OF = 0 JO
    JPO, JNP Jump if Parity Odd (No Parity).  PF = 0 JPE, JP


    As you can see there are some instructions that do that same thing, that's correct, they even are assembled into the same machine code, so it's good to remember that when you compile JE instruction - you will get it disassembled as: JZ.
    Different names are used to make programs easier to understand and code.


    Jump instructions for signed numbers

    Instruction Description Condition Opposite Instruction
    JE , JZ Jump if Equal (=).
    Jump if Zero.
    ZF = 1 JNE, JNZ
    JNE , JNZ Jump if Not Equal (<>).
    Jump if Not Zero.
    ZF = 0 JE, JZ
    JG , JNLE Jump if Greater (>).
    Jump if Not Less or Equal (not <=).
    ZF = 0
    and
    SF = OF
    JNG, JLE
    JL , JNGE Jump if Less (<).
    Jump if Not Greater or Equal (not >=).
    SF <> OF JNL, JGE
    JGE , JNL Jump if Greater or Equal (>=).
    Jump if Not Less (not <).
    SF = OF JNGE, JL
    JLE , JNG Jump if Less or Equal (<=).
    Jump if Not Greater (not >).
    ZF = 1
    or
    SF <> OF
    JNLE, JG


    <> - sign means not equal.


    Jump instructions for unsigned numbers

    Instruction Description Condition Opposite Instruction
    JE , JZ Jump if Equal (=).
    Jump if Zero.
    ZF = 1 JNE, JNZ
    JNE , JNZ Jump if Not Equal (<>).
    Jump if Not Zero.
    ZF = 0 JE, JZ
    JA , JNBE Jump if Above (>).
    Jump if Not Below or Equal (not <=).
    CF = 0
    and
    ZF = 0
    JNA, JBE
    JB , JNAE, JC Jump if Below (<).
    Jump if Not Above or Equal (not >=).
    Jump if Carry.
    CF = 1 JNB, JAE, JNC
    JAE , JNB, JNC Jump if Above or Equal (>=).
    Jump if Not Below (not <).
    Jump if Not Carry.
    CF = 0 JNAE, JB
    JBE , JNA Jump if Below or Equal (<=).
    Jump if Not Above (not >).
    CF = 1
    or
    ZF = 1
    JNBE, JA


    Generally, when it is required to compare numeric values CMP instruction is used (it does the same as SUB (subtract) instruction, but does not keep the result, just affects the flags).

    The logic is very simple, for example:
    it's required to compare 5 and 2,
    5 - 2 = 3
    the result is not zero (Zero Flag is set to 0).

    Another example:
    it's required to compare 7 and 7,
    7 - 7 = 0
    the result is zero! (Zero Flag is set to 1 and JZ or JE will do the jump).

    Here is an example of CMP instruction and conditional jump:

    
    include emu8086.inc
    
    ORG    100h
    
    MOV    AL, 25     ; set AL to 25.
    MOV    BL, 10     ; set BL to 10.
    
    CMP    AL, BL     ; compare AL - BL.
    
    JE     equal      ; jump if AL = BL (ZF = 1).
    
    PUTC   'N'        ; if it gets here, then AL <> BL,
    JMP    stop       ; so print 'N', and jump to stop.
    
    equal:            ; if gets here,
    PUTC   'Y'        ; then AL = BL, so print 'Y'.
    
    stop:
    
    RET               ; gets here no matter what.
    
    END


    Try the above example with different numbers for AL and BL, open flags by clicking on [FLAGS] button, use [Single Step] and see what happens, don't forget to recompile and reload after every change (use F5 shortcut).



    All conditional jumps have one big limitation, unlike JMP instruction they can only jump 127 bytes forward and 128 bytes backward (note that most instructions are assembled into 3 or more bytes).

    We can easily avoid this limitation using a cute trick:

    • Get a opposite conditional jump instruction from the table above, make it jump to label_x.

    • Use JMP instruction to jump to desired location.

    • Define label_x: just after the JMP instruction.

    label_x: - can be any valid label name.

    Here is an example:

    
    include emu8086.inc
    
    ORG    100h
    
    MOV    AL, 25     ; set AL to 25.
    MOV    BL, 10     ; set BL to 10.
    
    CMP    AL, BL     ; compare AL - BL.
    
    
    JNE    not_equal  ; jump if AL <> BL (ZF = 0).
    JMP    equal
    not_equal:
    
    
    ; let's assume that here we
    ; have a code that is assembled
    ; to more then 127 bytes...
    
    
    PUTC   'N'        ; if it gets here, then AL <> BL,
    JMP    stop       ; so print 'N', and jump to stop.
    
    equal:            ; if gets here,
    PUTC   'Y'        ; then AL = BL, so print 'Y'.
    
    stop:
    
    RET               ; gets here no matter what.
    
    END




Another, yet rarely used method is providing an immediate value instead of a label. When immediate value starts with a '$' character relative jump is performed, otherwise compiler calculates instruction that jumps directly to given offset. For example:

ORG    100h

; unconditional jump forward:
; skip over next 2 bytes,
JMP $2
a DB 3    ; 1 byte.
b DB 4    ; 1 byte.

; JCC jump back 7 bytes:
; (JMP takes 2 bytes itself)
MOV BL,9
DEC BL      ; 2 bytes.
CMP BL, 0   ; 3 bytes.
JNE $-7

RET

END




<<< Previous Part <<<      >>> Next Part >>>