為了徹底覆蓋 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 在不同比較場景下的含義。
掌握這些條件跳轉指令后,你可以編寫出高效、復雜的匯編程序邏輯。下次我們將探討子程序調用與棧幀管理,進一步深入底層編程!
如果有任何疑問,歡迎留言討論! 😊