跳转至

分支

求值表达式后,程序可以根据结果决定接下来执行什么,通常使用 if() 语句或 switch() 语句等结构。但这些控制流语句在汇编层面不存在。相反,汇编代码用 EFLAGS 寄存器做决定,用跳转操作执行不同的块;这个过程叫做分支。

为了让 EFLAGS 有正确的值,汇编代码使用 TEST 或 CMP 其中一条指令。两者都比较两个操作数,设置 EFLAGS 的状态位,然后丢弃任何结果。TEST 用逻辑 AND 比较操作数,而 CMP 用有符号减法,用后者减去前者。

为了正确分支,比较后紧接着跳转命令。每种跳转指令接受单个操作数,指定要跳转到的代码地址。特定跳转指令的行为取决于 EFLAGS 的状态位。表 4-11 描述了一些 x86 跳转指令。

指令名称行为无条件跳转跳转到 dest(把 EIP 设为 dest)。相等则跳转如果 ZF(零标志)为 1 则跳转。不等则跳转如果 ZF 为 0 则跳转。大于则跳转如果 ZF 为 0 且 SF(符号标志)等于 OF(溢出标志)则跳转。大于或等于则跳转如果 SF 等于 OF 则跳转。无符号 JG如果 CF(进位标志)为 0 且 ZF 为 0 则跳转。指令名称行为无符号 JGE如果 CF 为 0 则跳转。小于则跳转如果 SF 不等于 OF 则跳转。小于或等于则跳转如果 ZF 为 1 或 SF 不等于 OF 则跳转。无符号 JL如果 CF 为 1 则跳转。无符号 JLE如果 CF 为 1 或 ZF 为 1 则跳转。溢出则跳转如果 OF 为 1 则跳转。未溢出则跳转如果 OF 为 0 则跳转。为零则跳转如果 ZF 为 1 则跳转(与 JE 相同)。非零则跳转如果 ZF 为 0 则跳转(与 JNE 相同)。

记住哪些标志控制哪些跳转指令可能很麻烦,但它们的用途在名称中表达得很清楚。一个很好的经验法则是,CMP 后面跟着的跳转与它对应的操作符相同。例如,表 4-11 把 JE 列为「相等则跳转」,所以当 JE 跟在 CMP 操作后面时,它等同于== 操作符。类似地,JGE 是 >=,JLE 是 <=,等等。

例如,考虑清单 4-8 中显示的高级代码。

--snip--
if (EBX > EAX)
    ECX = EDX;
else
    ECX = 0;
--snip--

清单 4-8:一个简单的条件语句

这个 if() 语句只检查 EBX 是否大于 EAX,并根据结果设置 ECX。在汇编中,同样的语句可能看起来像这样:

--snip--
CMP EBX, EAX ; if (EBX > EAX)
JG label1 ; 如果 EBX > EAX 则跳转到 label1
MOV ECX, 0 ; ECX = 0else 
JMP label2 ; 跳过 if 
label1:

ECX = EDX(if 块)

label2:
--snip--

清单 4-8 中 if() 语句的汇编以 CMP 指令开始,如果 EBX 大于 EAX 就分支。如果采用分支,EIP 在①处借助 JG 指令被设为 if 块。如果没有采用分支,代码继续线性执行,在 JG 指令之后立即遇到 else 块。else 块执行完时,无条件 JMP 把 EIP 设为 0x7,跳过 if 块。