雖然jmp指令提供了控制轉移,但是它不允許進行任何複雜的判斷。80x86條件跳轉指令提供了這種判斷。條件跳轉指令是創建循環和實現其他條件執行語句,如if…endif的基本要素。
條件跳轉指令檢查一個或多個標誌位,判斷它們是否匹配某個特殊條件(就像setcc指令):如果標誌匹配成功,該指令就將控制轉移到目標位置;如果匹配失敗,CPU忽略該條件跳轉指令而繼續執行下一條指令。一些條件跳轉指令只是簡單測試符號位(sign)、進位位(carry)、溢出位(overflow)、零標誌(zero)位的設置。例如,在執行一條sh1指令後,您需要測試進位標誌,來判斷sh1是否從操作數的高地址位移出一位。類似地,也可以在一條test指令後測試零標誌位,來判斷指定的位是否爲1。大多數情況,在cmp指令之後執行條件跳轉指令。cmp指令設置標誌位,以便判斷小於、大於、等於等情況。
條件跳轉指令形式如下:
Jcc label;
其中,Jcc中的“cc”,必須用表示測試條件類型的字符序列替換。這些字符和setcc指令使用的一樣。例如,“js”表示根據符號(sign)標誌是否被置位來決定是否跳轉。一個典型的js指令如下:
js ValueIsNegative ;
在這個示例中,如果符號(sign)標誌被置位,則js指令將控制轉移到ValueIsNegative語句標號處;如果符號標誌清零,則將控制直接轉移給js指令後的指令。
與無條件jmp指令不同,條件跳轉指令不提供間接跳轉的形式。惟一允許的形式是跳轉到程序中某一標號處。條件跳轉指令有一個限制:目標標號的位置必須在跳轉指令本身附近32768字節範圍內,這通常對應着8000~32000條機器指令。一般情況下不會超過這種限制。
注意:Intel文檔爲許多條件跳轉指令定義了多種替代名或指令別名。表7-1、7-2和7-3列出了每個指令所有的別名。這些表格也列出了表示相反分支的指令。很快您將明白這些相反分支指令的作用。
表7-1 測試標誌位的JCC指令
指 令 |
描 述 |
條 件 |
別 名 |
相 反 指 令 |
JC |
如果進位位被置位則跳轉 |
進位標誌=1 |
JB,JNAE |
JNC |
JNC |
如果進位位沒有置位則跳轉 |
進位標誌=0 |
JNB,JAE |
JC |
JZ |
如果0標誌被置位則跳轉 |
0標誌=1 |
JE |
JNZ |
JNZ |
如果0標誌沒有置位則跳轉 |
0標誌=0 |
JNE |
JZ |
(續表)
指 令 |
描 述 |
條 件 |
別 名 |
相反指令 |
JS |
如果符號位被置位則跳轉 |
符號標誌=1 |
JNS | |
JNS |
如果符號位沒有被置位則跳轉 |
符號標誌=0 |
JS | |
JO |
如果溢出標誌置位則跳轉 |
溢出標誌=1 |
JNO | |
JNO |
如果溢出標誌沒有置位則跳轉 |
溢出標誌=0 |
JO |
|
JP |
如果奇偶校驗位被置位則跳轉 |
奇偶校驗標誌=1 |
JPE |
JNP |
JPE |
如果奇偶校驗位爲偶校驗則跳轉 |
奇偶校驗標誌=1 |
JP |
JPO |
JNP |
如果奇偶校驗位沒有被置位則跳轉 |
奇偶校驗標誌=0 |
JPO |
JP |
JPO |
如果奇偶校驗位爲奇校驗則跳轉 |
奇偶校驗標誌=0 |
JNP |
JPE |
表7-2 使用無符號數比較的JCC指令
指 令 |
描 述 |
條 件 |
別 名 |
相反指令 |
JA |
如果超過(>)則跳轉 |
進位標誌=0,0標誌=0 |
JNBE |
JNA |
JNBE |
如果不低於或等於(不 <=)則跳轉 |
進位標誌=0,0標誌=0 |
JA |
JBE |
JAE |
如果超過或等於(>=)則跳轉 |
進位標誌=0 |
JNC,JNB |
JNAE |
JNB |
如果不低於則跳轉(不 <) |
進位標誌=0 |
JNC,JAE |
JB |
JB |
如果低於(<)則跳轉 |
進位標誌=1 |
JC,JNAE |
JNB |
JNAE |
如果不超過或等於(不>=)則跳轉 |
進位標誌=1 |
JC,JB |
JAE |
JBE |
如果低於或等於(<=)則跳轉 |
進位標誌=1或0標誌=1 |
JNA |
JNBE |
JNA |
如果不超過(不>)則跳轉 |
進位標誌=1或0標誌=1 |
JBE |
JA |
JE |
如果相等(=)則跳轉 |
0標誌=1 |
JZ |
JNE |
JNE |
如果不相等(<>)則跳轉 |
0標誌=0 |
JNZ |
JE |
表7-3 使用有符號數比較的JCC指令
指 令 |
描 述 |
條 件 |
別 名 |
相反指令 |
JG |
如果大於(>)則跳轉 |
符號標誌=溢出標誌或0標誌=0 |
JNLE |
JNG |
JNLE |
如果小於或等於(<=)則跳轉 |
符號標誌=溢出標誌或0標誌=0 |
JG |
JLE |
JGE |
如果大於或等於(>=)則跳轉 |
符號標誌=溢出標誌 |
JNL |
JGE |
JNL |
如果不小於(不<)則跳轉 |
符號標誌=溢出標誌 |
JGE |
JL |
JL |
如果小於(<)則跳轉 |
符號標誌<>溢出標誌 |
JNGE |
JNL |
JNGE |
如果大於或等於(>=)跳轉 |
符號標誌<>溢出標誌 |
JL |
JGE |
JLE |
如果小於或等於(<=)跳轉 |
符號標誌<>溢出標誌或0標誌=1 |
JNG |
JNLE |
JNG |
如果不大於(不>)則跳轉 |
符號標誌<>溢出標誌或0標誌=1 |
JLE |
JG |
JE |
如果等於(=)則跳轉 |
0標誌=1 |
JZ |
JNE |
JNE |
如果不等於(<>)則跳轉 |
0標誌=0 |
JNZ |
JE |
接下來將對“相反指令”一列進行簡單的說明。在許多情況下,需要產生與某條分支指令條件相反的分支(在本章後面會給出示例),即相反分支。除了兩個例外,都可以按下面的簡單規則(後面統稱爲N/No N規則)產生相反分支:
● 如果Jcc的第二個字母不是“n”,則在“j”後面插入一個“n”。例如:je對應爲jne,jl對應爲jnl。
● 如果Jcc的第二個字母是“n”,則去掉指令中的“n”。例如:jng對應爲jg,jne對應爲je。
不遵循這兩條規則的兩個例外是jpe(奇偶位爲偶跳轉)和jpo(奇偶位爲奇跳轉)。這兩個例外並不會導致什麼問題,因爲:(a)很少需要測試奇偶標誌;(b)可以使用別名jp和jnp替代jpe和jpo。而“N/No N”規則對jp和jnp是適用的。
雖然jge是jl的相反指令,但是建議使用jnl作爲jl的相反指令。因爲很容易誤認爲“大於是小於的相反”,從而把jg當作jl的相反指令。您可以堅持使用“N/No N”規則以避免這種混淆。
80x86條件跳轉指令提供了這樣的能力:根據判斷條件將程序流分支到兩條路徑中的某一條。例如,要實現:如果BX等於CX,則寄存器AX的值加1。可以使用下面的代碼來完成該功能:
cmp(bx,cx );
jne SkipStmts;
inc(ax );
SkipStmts:
其中的訣竅是使用相反分支指令來跳過在條件滿足的情況下需要執行的指令。請堅持使用前面介紹的“N/no N”規則來選擇相反分支指令。
使用條件跳轉指令還可以實現循環。例如,下面的代碼序列實現了從用戶輸入讀入一串字符,並將字符存儲到一組連續的單元中,直到用戶輸入回車鍵。
mov(0,edi );
RdLnLoop:
stdin.getc(); //Read a character into the AL register.
mov(al,Input [edi])); //Store away the character.
inc(edi ); //Move on to the next character.
cmp(al,stdio.cr ); //See if the user pressed Enter.
jne RdLnLoop;
與setcc指令類似,條件跳轉指令分爲兩類—— 測試特殊處理器標誌位的條件跳轉指令(例如jz、jc、jno)和測試某些條件(小於、大於等)的條件跳轉指令。當測試某個條件時,條件跳轉指令通常緊跟在一個cmp指令之後。cmp指令設置標誌位後,如果是無符號數比較,使用ja、jae、jb、jbe、je或jne等指令測試這些標誌來判斷是否小於、小於等於、等於、不等於、大於或大於等於;如果是有符號數比較,則使用jl、jle、je、jne、jg、jge指令。
條件跳轉指令測試標誌位,但不影響標誌位。