程序控制转移
对于编程来说控制程序走向是非常重要的事情,它是你的程序根据条件
作出判断,跳转到相应的位值。
ORG 100h
MOV AX, 5 ; 将 AX 设置为 5.
MOV BX, 2 ; 将 BX 设置为 2.
JMP calc ; 跳转到 'calc'.
back: JMP stop ; 跳转到 'stop'.
calc:
ADD AX, BX ; 将 BX 加到 AX.
JMP back ; 返回 'back'.
stop:
RET ; 返回操作系统
END
当然有更简单的计算这两个数字之和的方法,但是
上面是一个JMP指令的很好的例子。
从例子中可以看出,JMP可以控制向前和向后。它可以
转移到当前代码段的任意位置(65535字节)。
短条件转移
与JMP这一无条件转移指令不同,还有一些有条件跳转
指令(只有在条件成立的时侯才跳转)。这些指令分为
三组,第一组是只检测单独标记位,第二组比较有符
号数,第三组比较无符号数。
检测单独标记位的转移指令
指令 |
说明 |
条件 |
相反指令 |
JZ , JE |
如果为0(相等),转移 . |
ZF =1 |
JNZ, JNE |
JC , JB, JNAE |
如果进位 (小于, 不大于等于),转移 |
CF = 1 |
JNC, JNB, JAE |
JS |
如果是负数,转移 |
SF = 1 |
JNS |
JO |
如果溢出,转移 |
OF = 1 |
JNO |
JPE, JP |
如果是偶数,转移 |
PF = 1 |
JPO |
JNZ , JNE |
如果不为0(不相等),转移 |
ZF = 0 |
JZ, JE |
JNC , JNB, JAE |
如果没有进位(大于, 大于等于),转移 |
CF = 0 |
JC, JB, JNAE |
JNS |
如果不是负数,转移 |
SF = 0 |
JS |
JNO |
如果没有溢出,转移 |
OF = 0 |
JO |
JPO, JNP |
如果不是偶数,转移 |
PF = 0 |
JPE, JP |
可以看到一些指令功能相同,对,他们编译之后生成
相同机器码所以很容易理解为什么你编译 JE 指令
而反编译得到的却是JZ.使用不同的名称是为了使程序更容易理解。
比较有符号数的转移指令
指令 |
说明 |
条件 |
相反指令 |
JE , JZ |
如果等于 (=),如果为0,跳转 |
ZF = 1 |
JNE, JNZ |
JNE , JNZ |
如果不等于 (<>),如果不等于0,跳转 |
ZF = 0 |
JE, JZ |
JG , JNLE |
如果大于 (>) 如果不小于等于 (not <=),跳转 |
ZF = 0 和 SF = OF |
JNG, JLE |
JL , JNGE |
如果小与Jump if Less (<) 如果不大于等于 (not >=),跳转 |
SF <> OF |
JNL, JGE |
JGE , JNL |
如果大于等于 (>=),如果不小于 (not <),跳转 |
SF = OF |
JNGE, JL |
JLE , JNG |
如果小于等于 (<=),如果不大于 (not >),跳转 |
ZF = 1 或者 SF <> OF |
JNLE, JG |
<> - 符号表示不等于.
比较无符号数转移指令
指令 |
说明 |
条件 |
相反指令 |
JE , JZ |
如果等于 (=).,如果为0,跳转 |
ZF = 1 |
JNE, JNZ |
JNE , JNZ |
如果不等于(<>),如果不为0,跳转 |
ZF = 0 |
JE, JZ |
JA , JNBE |
如果大于 (>),如果不小于等于(not <=),跳转 |
CF = 0 and ZF = 0 |
JNA, JBE |
JB , JNAE, JC |
如果小于 (<),如果不大于等于(not >=),如果进位,跳转 |
CF = 1 |
JNB, JAE, JNC |
JAE , JNB, JNC |
如果大于等于(>=),如果不小于 (not <),如果没有进位,跳转 |
CF = 0 |
JNAE, JB |
JBE , JNA |
如果小于或者等于(<=),如果不大于 (not >),跳转 |
CF = 1 or ZF = 1 |
JNBE, JA |
一般来说,需要使用CMP指令来比较数值(该指令与 SUB(减法)
指令相近,只不过不保存结果,而只修改标值位)
上面说法的意思是,例如:
需要比较5 和2, 5-2 =3
结果不是0(0标值位设置为 0)
另一个例子
比较 7和7
7 - 7 = 0
结果为0! (0标值位设置为1。 JZ 或者 JE 会转移).
下面是一个 CMP 指令和条件转移指令的例子:
include emu8086.inc
include emu8086.inc
ORG 100h
MOV AL, 25 ; 设置AL为 25.
MOV BL, 10 ; 设置BL为10.
CMP AL, BL ; 比较 AL - BL.
JE equal ; 如果 AL = BL (ZF = 1) 跳转
PUTC 'N' ; 如果到这里,说明 AL <> BL,
JMP stop ; 打印'N', 跳转到结束
equal: ; 如果到这里
PUTC 'Y' ; 则 AL = BL,打印'Y'.
stop:
RET
END
请用用不同的数字试验取代上述 AL 和 BL,点击[FLAGS]键
打开标志,使用[Single Step]观察发生了什么,不要忘记
每一次修改之后重新编译运行(快捷键F5)。
全部的条件转移指令都有一个很大的限制,就是与 JMP 指令
不同,他们只能向前跳转127字节或者向后跳转128字节(注意
大多数指令编译之后是3个或者更多字节)
我们可以用如下小技巧解决这一问题:
label_x: - 可以是任意合法标号.
下面是一个例子:
include emu8086.inc
ORG 100h
MOV AL, 25 ; 设置 AL 为 25.
MOV BL, 10 ; 设置 BL 为 10.
CMP AL, BL ; 比较 AL - BL.
JNE not_equal ; 如果 AL <> BL (ZF = 0),转移
JMP equal
not_equal:
; 假定这里还有编译之后超过127字节的程序
PUTC 'N' ; 如果执行到这里,说明 AL <> BL,
JMP stop ; 打印 'N', 转移到程序结束。
equal: ; 如果执行到这里,
PUTC 'Y' ; 说明 AL = BL, 打印 'Y'.
stop:
RET ; 上述都要执行这一条
END
另外,可以使用立即数来代替标号。立即数前使用“$”
编译器将直接得到偏移。例如:
ORG 100h
; 无条件向前转移
; 跳过后面2字节
JMP $2
a DB 3 ; 1 byte.
b DB 4 ; 1 byte.
; JCC 跳过 7 字节:
; (JMP 本身占用 2 字节)
MOV BL,9
DEC BL ; 2 bytes.
CMP BL, 0 ; 3 bytes.
JNE $-7
RET
END