x86 汇编中的【条件跳转指令】:从基础到扩展的全面解析(查表版)
为了彻底覆盖 x86 架构中所有条件跳转指令,包括 8086 到现代 x86-64 的全部变体,我重新整理了分类体系,并补充了鲜为人知的指令变体、操作数大小前缀和历史演进。
本文需要运用的知识(需要详细了解可点击对应的点):
- flags寄存器
一、条件跳转指令总表(不用背只要用的时候查表就可以)
分类 | 指令 | 跳转条件 | 英文全称 | 操作数 | 机器码 | 64 位兼容性 | 描述 |
---|---|---|---|---|---|---|---|
零标志(ZF) | JE/JZ | ZF=1 | Jump if Equal/Zero | 短跳转 | 74 cb | 兼容 | 结果为零或相等时跳转 |
JNE/JNZ | ZF=0 | Jump if Not Equal/Not Zero | 短跳转 | 75 cb | 兼容 | 结果不为零或不相等时跳转 | |
进位标志(CF) | JB/JNAE/JC | CF=1 | Jump if Below/Not Above or Equal/Carry | 短跳转 | 72 cb | 兼容 | 无符号数低于或有进位时跳转 |
JNB/JAE/JNC | CF=0 | Jump if Not Below/Above or Equal/No Carry | 短跳转 | 73 cb | 兼容 | 无符号数高于等于或无进位时跳转 | |
溢出标志(OF) | JO | OF=1 | Jump if Overflow | 短跳转 | 70 cb | 兼容 | 有符号数溢出时跳转 |
JNO | OF=0 | Jump if Not Overflow | 短跳转 | 71 cb | 兼容 | 有符号数未溢出时跳转 | |
符号标志(SF) | JS | SF=1 | Jump if Sign | 短跳转 | 78 cb | 兼容 | 结果为负时跳转 |
JNS | SF=0 | Jump if Not Sign | 短跳转 | 79 cb | 兼容 | 结果为正时跳转 | |
奇偶标志(PF) | JP/JPE | PF=1 | Jump if Parity/Parity Even | 短跳转 | 7A cb | 兼容 | 结果低 8 位中 1 的个数为偶数时跳转 |
JNP/JPO | PF=0 | Jump if Not Parity/Parity Odd | 短跳转 | 7B cb | 兼容 | 结果低 8 位中 1 的个数为奇数时跳转 | |
有符号数比较 | JG/JNLE | SF=OF 且 ZF=0 | Jump if Greater/Not Less or Equal | 短跳转 | 7F cb | 兼容 | 有符号数大于时跳转 |
JGE/JNL | SF=OF | Jump if Greater or Equal/Not Less | 短跳转 | 7D cb | 兼容 | 有符号数大于等于时跳转 | |
JL/JNGE | SF≠OF 且 ZF=0 | Jump if Less/Not Greater or Equal | 短跳转 | 7C cb | 兼容 | 有符号数小于时跳转 | |
JLE/JNG | SF≠OF 或 ZF=1 | Jump if Less or Equal/Not Greater | 短跳转 | 7E cb | 兼容 | 有符号数小于等于时跳转 | |
无符号数比较 | JA/JNBE | CF=0 且 ZF=0 | Jump if Above/Not Below or Equal | 短跳转 | 77 cb | 兼容 | 无符号数高于时跳转 |
JAE/JNB | CF=0 | Jump if Above or Equal/Not Below | 短跳转 | 73 cb | 兼容 | 无符号数高于等于时跳转 | |
JB/JNAE | CF=1 | Jump if Below/Not Above or Equal | 短跳转 | 72 cb | 兼容 | 无符号数低于时跳转 | |
JBE/JNA | CF=1 或 ZF=1 | Jump if Below or Equal/Not Above | 短跳转 | 76 cb | 兼容 | 无符号数低于等于时跳转 | |
循环控制 | LOOP | ECX≠0(执行后 ECX 减 1) | Loop | 短跳转 | E2 cb | 兼容 | 循环执行,ECX 为 0 时终止 |
LOOPE/LOOPZ | ECX≠0 且 ZF=1 | Loop if Equal/Zero | 短跳转 | E1 cb | 兼容 | 循环且结果为零 | |
LOOPNE/LOOPNZ | ECX≠0 且 ZF=0 | Loop if Not Equal/Not Zero | 短跳转 | E0 cb | 兼容 | 循环且结果不为零 | |
JECXZ | ECX=0(不改变 ECX) | Jump if ECX is Zero | 短跳转 | E3 cb | 兼容 | ECX 为零时跳转(常用于循环前检查) | |
JCXZ | CX=0 | Jump if CX is Zero | 短跳转 | E3 cb | 兼容(64 位下检查 ECX 低 16 位) | 16 位 CX 寄存器为零时跳转(不改变 CX) | |
64 位扩展 | JRCXZ | RCX=0(不改变 RCX) | Jump if RCX is Zero | 短跳转 | F3 /9 | 仅 64 位 | 64 位模式下 RCX 为零时跳转 |
分支预测提示 | JMPX | 基于 CPU 分支预测提示 | Jump with Prediction Hint | 近跳转 | 0F 1F /0 | P6 及以后 | 向 CPU 提供分支预测建议(Intel P6 微架构及以后) |
二、条件跳转指令详解
1. 基础条件跳转指令(基于标志位)
这类指令直接基于 FLAGS 寄存器中的单个标志位进行判断,是最基本的条件跳转形式。
典型指令示例:
; 判断AL是否为0(ZF标志)
TEST AL, AL
JE zero_value ; ZF=1时跳转; 判断加法是否溢出(OF标志)
ADD AX, BX
JO overflow ; OF=1时跳转; 判断结果是否为负(SF标志)
SUB CX, DX
JS negative ; SF=1时跳转
机器码分析:
- 所有基础条件跳转指令的机器码均为 2 字节:
第 1 字节为操作码(如74
表示 JE),第 2 字节为 8 位相对偏移量。 - 偏移量采用补码表示,范围为 - 128~+127 字节。
2. 比较类条件跳转指令
这类指令基于比较操作(如 CMP)后的多个标志位组合进行判断,分为无符号数和有符号数两套体系。
无符号数比较示例:
; 判断AX是否大于BX(无符号数)
CMP AX, BX
JA greater ; 高于则跳转(CF=0且ZF=0)
JBE less_equal ; 低于等于则跳转(CF=1或ZF=1)
有符号数比较示例:
; 判断AX是否大于BX(有符号数)
CMP AX, BX
JG greater ; 大于则跳转(SF=OF且ZF=0)
JLE less_equal ; 小于等于则跳转(SF≠OF或ZF=1)
标志位组合逻辑:
- 无符号数比较依赖 CF 和 ZF:
- 进位标志(CF)表示 “低于”,零标志(ZF)表示 “等于”。
- 有符号数比较依赖 SF、OF 和 ZF:
- 符号标志(SF)与溢出标志(OF)的异或表示 “小于”。
3. 循环控制指令
循环指令隐式使用 ECX/RCX 寄存器作为计数器,简化了固定次数循环的实现。
经典循环模式:
; 循环10次
MOV ECX, 10
loop_start:; 循环体代码LOOP loop_start ; ECX减1,若不为0则跳转
高效替代方案:
由于LOOP
指令在现代 CPU 上性能较差,推荐使用手动递减方式:
MOV ECX, 10
manual_loop:; 循环体代码DEC ECXJNE manual_loop ; 性能更优的循环实现
4. 64 位扩展指令
在 64 位模式下,新增了针对 64 位寄存器的条件跳转指令:
JRCXZ 指令:
; 检查RCX是否为0(64位)
MOV RCX, 0
JRCXZ zero_rcx ; 若RCX=0则跳转
注意事项:
- JRCXZ 仅在 64 位模式下可用,操作 64 位寄存器 RCX。
- 与 JECXZ 不同,JRCXZ 检查完整的 64 位值,而非低 32 位。
5. 特殊条件跳转指令
JMPX(分支预测提示指令)
; 向CPU提示该分支大概率会跳转
JMPX target [, RAX] ; 预测跳转
NOP ; 预测不跳转时执行的指令
功能说明:
- JMPX 是 Intel P6 微架构引入的特殊指令,用于优化分支预测。
- 第二个操作数(可选)用于指定目标地址寄存器。
三、条件跳转指令的高级应用
1. 多条件组合判断
通过连续使用条件跳转指令,可以实现复杂的逻辑判断:
; 判断AL是否在10到20之间(有符号数)
CMP AL, 10
JL out_of_range ; AL < 10,跳转
CMP AL, 20
JG out_of_range ; AL > 20,跳转
; 否则AL在范围内
JMP in_rangeout_of_range:; 处理超出范围的情况JMP donein_range:; 处理范围内的情况done:; 程序结束
2. 条件跳转与标志位的关系
不同指令对标志位的影响不同,需谨慎选择前置指令:
指令 | ZF(零标志) | CF(进位标志) | SF(符号标志) | OF(溢出标志) |
---|---|---|---|---|
CMP AX, BX | 反映 AX-BX 的结果 | 反映借位 / 进位 | 反映符号 | 反映溢出 |
TEST AX, BX | 反映 AX AND BX 的结果 | 清零 | 反映符号 | 清零 |
ADD AX, BX | 反映加法结果 | 反映进位 | 反映符号 | 反映溢出 |
SUB AX, BX | 反映减法结果 | 反映借位 | 反映符号 | 反映溢出 |
3. 条件跳转的性能优化
现代 CPU 对条件跳转的优化策略:
-
分支预测:
CPU 会根据历史执行情况预测分支走向,若预测错误会导致流水线清空。 -
跳转目标缓存(BTB):
记录最近的跳转指令及其目标地址,加速跳转执行。 -
优化建议:
- 减少条件跳转的使用,优先使用条件移动指令(如 CMOVcc)。
- 对高频执行的分支,通过代码顺序暗示 CPU 预测方向。
四、历史演进与兼容性
1. 从 8086 到 x86-64 的演变
处理器型号 | 新增条件跳转指令 | 备注 |
---|---|---|
8086 | JE/JZ, JNE/JNZ, JB/JC 等基础指令 | 仅支持 16 位操作,跳转范围有限 |
80386 | JECXZ, 32 位操作数支持 | 引入 32 位寄存器,扩展跳转范围 |
AMD64 | JRCXZ | 64 位模式下新增,支持 64 位寄存器检查 |
2. 操作数大小前缀
通过添加66h
前缀,可以强制使用 16 位操作数:
66h JE short target ; 强制使用16位操作数(现代编译器自动处理)
兼容性注意:
- 在 64 位模式下,默认使用 32 位操作数,除非显式指定 64 位操作数(REX 前缀)。
五、总结与实用技巧
-
记忆技巧:
- 无符号数比较用
A/B
(Above/Below),有符号数比较用G/L
(Greater/Less)。 - 带
E
的指令表示 “或等于”(如 JAE=Above or Equal)。
- 无符号数比较用
-
常见错误:
- 混淆无符号数和有符号数比较指令(如误用 JA 代替 JG)。
- 跳转前未执行影响标志位的指令(如 CMP、TEST)。
-
调试建议:
- 使用调试器观察 FLAGS 寄存器的值,验证条件跳转的触发逻辑。
- 注意区分 ZF 和 CF 在不同比较场景下的含义。
掌握这些条件跳转指令后,你可以编写出高效、复杂的汇编程序逻辑。下次我们将探讨子程序调用与栈帧管理,进一步深入底层编程!
如果有任何疑问,欢迎留言讨论! 😊