本章向程序員的匯編語言工具箱中引入一個重要的內容,使得編寫出來的程序具備作決策的功能。幾乎所有的程序都需要這種能力。首先,介紹布爾操作,由于能影響CPU狀態標志,它們是所有條件指令的核心。然后,說明怎樣使用演繹CPU狀態標志的條件跳轉和循環指令。接著演示如何用本章介紹的工具來實現理論計算機科學中最根本的結構之一:有限狀態機。本章最后展示的是MASM內置的32位編程的邏輯結構。
6.1 條件分支
允許作決策的編程語言使得程序員可以改變控制流,使用的技術被稱為條件分支。高級語言中的每一個IF 語句、switch 語句和條件循環都內置有分支邏輯。匯編語言,雖然是低級語言,但提供了決策邏輯所需的所有工具。本章將了解如何實現這種從高級條件語句到低級實現代碼的轉化。
處理硬件設備的程序必須要能夠控制數字的單個位。每一個位都要被測試、清除和置位。數據加密和壓縮也要依靠位操作。本章將展示如何在匯編語言中實現這些操作。
本章將回答一些基本問題:
●怎樣使用第1章介紹的布爾操作(AND、OR、NOT)?
●怎樣用匯編語言寫IF語句?
●編譯器如何將嵌套 IF 語句翻譯為機器語言?
●如何清除和置位二進制數中的單個位?
●怎樣實現簡單的二進制數據加密?
●在布爾表達式中,如何區分有符號數和無符號數?
本章遵循自底而上的方法,以編程邏輯的二進制基礎為開端。然后,說明 CPU 怎樣用CMP 指令和處理器狀態標志來比較指令操作數。最后,將這些內容綜合起來,展示如何用匯編語言實現高級語言的邏輯結構特征。
6.2 布爾和比較指令
第1章介紹了四種基本的布爾代數操作:AND、OR、XOR 和 NOT。用匯編語言指令,這些操作可以在二進制位上實現。同樣,這些操作在布爾表達式層次上也很重要,比如 IF語句。首先了解按位指令,這里使用的技術也可以用于操作硬件設備控制位,實現通信協議以及加密數據,這里只列舉了幾種應用。Intel指令集包含了AND、OR、XOR和NOT指令,它們能直接在二進制位上實現布爾操作,如表 6-1 所示。此外,TEST 指令是一種非破壞性的 AND 操作。
表6-1 部分布爾指令 | |
操作 | 說明 |
AND | 源操作數和目的操作數進行邏輯與操作 |
OR | 源操作數和目的操作數進行邏輯或操作 |
XOR | 源操作數和目的操作數進行邏輯異或操作 |
NOT | 對目標操作數進行邏輯非操作 |
TEST | 源操作數和目的操作數進行邏輯與操作,并適當地設置CPU標志位 |
6.2.1 CPU狀態標志
布爾指令影響零標志位、進位標志位、符號標志位、溢出標志位和奇偶標志位。下面簡單回顧一下這此標志位的含義:
●操作結果等于0時,零標志位置 1。
●操作使得目標操作數的最高位有進位時,進位標志位置1。
●符號標志位是目標操作數高位的副本,如果標志位置 1,表示是負數;標志位清 0,表示是正數。(假設0為正。)
●指令產生的結果超出了有符號目的操作數范圍時,溢出標志位置1。
●指令使得目標操作數低字節中有偶數個1時,奇偶標志位置 1。
6.2.2 AND指令
AND 指令在兩個操作數的對應位之間進行(按位)邏輯與(AND)操作,并將結果存放在目標操作數中:
AND destination, source
下列是被允許的操作數組合,但是立即操作數不能超過32位:
AND reg, reg
AND reg, mem
AND reg,imm
AND mem, reg
AND mem, imm
操作數可以是8 位、16 位、32 位和 64 位,但是兩個操作數必須是同樣大小。兩個操作數的每一對對應位都遵循如下操作原則:如果兩個位都是1,則結果位等于1;否則結果位等于 0。下表是出自第1章的真值表,有兩個輸入位x和 y。表的第三列是表達式x^y的值:
X | Y | X^Y |
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
AND指令可以清除一個操作數中的1個位或多個位,同時又不影響其他位。這個技術就稱為位屏蔽,就像在粉刷房子時,用遮蓋膠帶把不用粉刷的地方(如窗戶)蓋起來。例如,假設要將一個控制字節從AL寄存器復制到硬件設備。并且當控制字節的位0和位3等于 0時,該設備復位。那么,如果想要在不修改 AL 其他位的條件下,復位設備,可以用下面的指令:
and AL, 11110110b ;清除位0和位3,其他位不變
如,設 AL 初始化為二進制數10101110,將其與1111 0110 進行AND 操作后,AL 等于1010 0110:
mov al, 10101110b
and al, 11110110b ;AL 中的結果=1010 0110
標志位 AND指令總是清除溢出和進位標志位,并根據目標操作數的值來修改符號標志位、零標志位和奇偶標志位。比如,下面指令的結果存放在 EAX 寄存器,假設其值為 0。在這種情況下,零標志位就會置1:
and eax, 1Fh
將字符轉換為大寫
AND指令提供了一種簡單的方法將字符從小寫轉換為大寫。如果對比大寫A和小寫a的 ASCII 碼,就會發現只有位 5不同:
0 1 1 0 0 0 0 1 = 61h {'a'}
0 1 0 0 0 0 0 1 = 41h {'A'}
其他的字母字符也是同樣的關系。把任何一個字符與二進制數11011111 進行 AND,則除位 5 外的所有位都保持不變,而位 5清 0。下例中,數組中所有字符都轉換為大寫:
;6.2.2.asm AND指令 數組中所有字符都轉換為大寫.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
array BYTE 50 DUP(97).code
main PROCmov ecx, LENGTHOF arraymov esi, OFFSET array
L1:and BYTE PTR [esi], 11011111b ;清除位5inc esiloop L1INVOKE ExitProcess,0
main ENDP
END main
運行調試:
轉換后:
6.2.3 OR 指令
OR 指令在兩個操作數的對應位之間進行(按位)邏輯或(OR)操作,并將結果存放在目標操作數中:
OR destination, source
OR 指令操作數組合與AND指令相同:
OR reg, reg
OR reg, mem
OR reg,imm
OR mem, reg
OR mem, imm
操作數可以是8 位、16 位、32 位和 64 位,但是兩個操作數必須是同樣大小。對兩個操作數的每一對對應位而言,只要有一個輸入位是1,則輸出位就是1。下面的真值表(出自第1章)展示了布爾運算X∨Y:
X | Y | X∨Y |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
當需要在不影響其他位的情況下,將操作數中的1個位或多個位置為1時,OR 指令就非常有用了。比如,計算機與伺服電機相連,通過將控制字節的位2 置1來啟動電機。假設該控制字節存放在AL 寄存器中,每一個位都含有重要信息,那么,下面的指令就只設置了位2:
or AL, 00000100b ;位2置1,其他位不變
如果 AL初始化為二進制數11100011,把它與00000100進行OR操作,其結果等于1110 0111:
mov al.11100011b
or a1, 00000100b ;AL中的結果=11100111
標志位 OR指令總是清除進位和溢出標志位,并根據目標操作數的值來修改符號標志位、零標志位和奇偶標志位。比如,可以將一個數與它自身(或0)進行OR運算,來獲取該數值的某些信息:
or al, al
下表給出了零標志位和符號標志位對AL內容的說明:
零標志位 | 符號標志位 | AL 中的值 |
清0 | 清0 | 大于0 |
置1 | 清0 | 等于0 |
清0 | 置1 | 小于0 |
6.2.4 位映射集
有些應用控制的對象是從一個有限全集中選出來的一組項目。就像公司里的雇員,或者氣象監測站的環境讀數。在這些情景中,二進制位可以代表集合成員。與 Java HashSet 用指針或引用指向容器內對象不同,應用可以用位向量(或位映射)把一個二進制數中的位映射為數組中的對象。
如下例所示,二進制數的位從左邊0號開始,到右邊31號為止,該數表示了數組元素0、1、2 和 31 是名為 SetX 的集合成員:
SetX=1000 0000 0000 0000 0000 0000 0000 0111
(為了提供可讀性,字節已經分開。)通過在特定位置與1進行 AND 運算,就可以方便地檢測出該位是否為集合成員:
mov eax, SetX
and eax, 10000b ;元素[4]是Setx的成員嗎?
如果本例中的 AND指令清除了零標志位,那么就可以知道元素[4]是 SetX的成員。
1.補集
補集可以用 NOT 指令生成,NOT 指令將所有位都取反。因此,可以用下面的指令生成上例中 SetX 的補集,并存放在EAX中:
mov eax, SetX
not eax ;SetX 的補集
2.交集
AND指令可以生成位向量來表示兩個集合的交集。下面的代碼生成集合SetX和SetY的交集,并將其存放在 EAX 中:
mov eax,SetX
and eax,Sety
SetX和SetY交集生成過程如下所示:
很難想象還有更快捷的方法生成交集。對于更大的集合來說,它所需要的位超過了單個寄存器的容量,因此,需要用循環來實現所有位的AND 運算。
3.并集
OR 指令生成位圖表示兩個集合的并集。下面的代碼產生集合 SetX 和 SetY 的并集,并將其存放在EAX 中:
mov eax, Set
xor eax, Sety
OR指令生成SetX和Sety并集的過程如下所示:
6.2.5 XOR指令
XOR 指令在兩個操作數的對應位之間進行(按位)邏輯異或(XOR)操作,并將結果存放在目標操作數中:
XOR destination, source
XOR 指令操作數組合和大小與 AND 指令及 OR 指令相同。兩個操作數的每一對對應位都應用如下操作原則:如果兩個位的值相同(同為 0 或同為1),則結果位等于0;否則結果位等于1。下表描述的是布爾運算x⊕y:
X | Y | X⊕Y |
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
與0異或值保持不變,與1異或則被觸發(求補)。對相同操作數進行兩次 XOR 運算,則結果逆轉為其本身。如下表所示,位x與位y進行了兩次異或,結果逆轉為x的初始值:
X | Y | X⊕Y | (X⊕Y)⊕Y |
0 | 0 | 0 | 0 |
0 | 1 | 1 | 0 |
1 | 0 | 1 | 1 |
1 | 1 | 1 | 1 |
在 6.3.4 節中會發現,異或運算這種“可逆的”屬性使其成為簡單對稱加密的理想工具。
標志位 XOR指令總是清除溢出和進位標志位,并根據目標操作數的值來修改符號標志位、零標志位和奇偶標志位。
檢查奇偶標志 奇偶檢查是在一個二進制數上實現的功能,計算該數中1的個數;如果計算結果為偶數,則說該數是偶校驗;如果結果為奇數,則該數為奇校驗。x86 處理器中,當按位操作或算術操作的目標操作數最低字節為偶校驗時,奇偶標志位置1。反之,如果操作數為奇校驗,則奇偶標志位清 0。一個既能檢查數的奇偶性,又不會修改其數值的有效方法是,將該數與0進行異或運算:
mov al, 10110101b ;5個 1,奇校驗
xor al, 0 ;奇偶標志位清0(奇)
mov al, 11001100b ;4 個 1,偶校驗
xor al, 0 ;奇偶標志位置1(偶)
Visual Studio 用PE=1 表示偶校驗,PE=0 表示奇校驗。
16 位奇偶性 對16位整數來說,可以通過將其高字節和低字節進行異或運算來檢測數的奇偶性:
mov ax, 64C1h ;0110 0100 1100 0001
xor ah, al ;奇偶標志位置1(偶)
將每個寄存器中的置1位(等于1的位)想象為一個8 位集合中的成員。XOR 指令把兩個集合交集中的成員清 0,并形成了其余位的并集。這個并集的奇偶性與整個 16 位整數的奇偶性相同。
那么32位數值呢?如果將數值的字節進行編號,從B0到B3那么計算奇偶性的表達式為:B0 XOR B1 XOR B2 XOR B3
6.2.6 NOT 指令
NOT 指令觸發(翻轉)操作數中的所有位。其結果被稱為反碼。該指令允許的操作數類型如下所示:
NOT reg
NOT mem
例如,F0h 的反碼是 0Fh:
mov al, 11110000b
not al ;AL=00001111b
標志位 NOT指令不影響標志位。
6.2.7 TEST指令
TEST 指令在兩個操作數的對應位之間進行AND操作,并根據運算結果設置符號標志位、零標志位和奇偶標志位。TEST 指令與AND 指令唯一不同的地方是,TEST 指令不修改目標操作數。TEST 指令允許的操作數組合與 AND 指令相同。在發現操作數中單個位是否置位時,TEST 指令非常有用。
示例: 多位測試 TEST指令同時能夠檢查幾個位。假設想要知道 AL 寄存器的位0和位3 是否置1,可以使用如下指令:
test al, 00001001b ;測試位0 和位 3
(本例中的 0000 1001 稱為位掩碼。)從下面的數據集例子中,可以推斷只有當所有測試位都清 0時,零標志位才置1:
0 0 1 0 0 1 0 1 <- 輸入值
0 0 0 0 1 0 0 1 <- 測試值
0 0 0 0 0 0 0 1 <- 結果: ZF=0
0 0 1 0 0 1 0 0 <- 輸入值
0 0 0 0 1 0 0 1 <- 測試值
0 0 0 0 0 0 0 0 <- 結果: ZF=1
標志位 TEST指令總是清除溢出和進位標志位,其修改符號標志位、零標志位和奇偶標志位的方法與 AND指令相同。
6.2.8 CMP指令
了解了所有按位操作指令后,現在來討論邏輯(布爾)表達式中的指令。最常見的布爾表達式涉及一些比較操作,下面的偽碼片段展示了這種情況:
if A > B...
while x > 0 and x < 200 ...
if check forerror(N)=true
x86 匯編語言用 CMP指令比較整數。字符代碼也是整數,因此可以用 CMP 指令。浮點數需要特殊的比較指令,相關內容將在第 12 章介紹。
CMP(比較)指令執行從目的操作數中減去源操作數的隱含減法操作,并且不修改任何操作數:
CMP destination, source
標志位 當實際的減法發生時,CMP指令按照計算結果修改溢出、符號、零、進位、輔助進位和奇偶標志位。如果比較的是兩個無符號數,則零標志位和進位標志位表示的兩個操作數之間的關系如右表所示:
如果比較的是兩個有符號數,則符號標志位、零標志位和溢出標志位表示的兩個操作數之間的關系如下表所示:
CMP指令是創建條件邏輯結構的重要工具。當在條件跳轉指令中使用MP時,匯編語言的執行結果就和語句一樣。
示例 下面用三段代碼來說明標志位是如何受到CMP影響的。設AX=5,并與10進行比較,則進位標志位將置 1,原因是(5-10)需要借位:
mov ax, 5
cmp ax, 10 ;ZF = 0 and CF = 1
1000與1000比較會將零標志位置1,因為目標操作數減去源操作數等于0:
mov ax, 1000
mov cx, 1000
cmp cx, ax ;ZF = 1 and CF = 0
105與0進行比較會清除零和進位標志位,因為(105-0)的結果是一個非零的正整數。
mov si, 105
cmp si, 0 ;ZF = 0 and CF = 0
6.2.9置位和清除單個CPU標志位
怎樣能方便地置位和清除零標志位、符號標志位、進位標志位和溢出標志位?有幾種方法,其中的一些需要修改目標操作數。要將零標志位置1,就把操作數與0進行TEST或AND操作;要將零標志位清零,就把操作數與1進行 OR 操作:
test al, 0 ;零標志位置1
and al, 0 ;零標志位置1
or al, 1 ;零標志位清零
TEST指令不修改目的操作數,而AND指令則會修改目的操作數。若要符號標志位置1,將操作數的最高位和1進行OR操作;若要清除符號標志位,則將操作數最高位和0進行 AND 操作:
or al, 80h ;符號標志位置1
and al, 7Fh ;符號標志位清零
若要進位標志位置1,用STC指令;清除進位標志位,用CLC指令:
stc ;進位標志位置1
clc ;進位標志位清零
若要溢出標志位置1,就把兩個正數相加使之產生負的和數;若要清除溢出標志位,則將操作數和0進行 OR操作:
mov al, 7Fh ;AL= +127
inc al ;AL=80h(-128),OF=1
or eax, 0 ;溢出標志位清零
6.2.10 64位模式下的布爾指令
大多數情況下,64 位模式中的64 位指令與32位模式中的操作是一樣的。比如,如果源操作數是常數,長度小于32 位,而目的操作數是一個64 位寄存器或內存操作數,那么,目的操作數中所有的位都會受到影響:
.data
allones QWORD 0FFFFFFFFFFFFFFFFh
.code
mov rax, allones ;RAX = 0FFFFFFFFFFFFFFFFh
and rax, 80h ;RAX = 0000000000000080h
mov rax, allones ;RAX = 0FFFFFFFFFFFFFFFFh
and rax, 8080h ;RAX = 0000000000008080h
mov rax, allones ;RAX = 0FFFFFFFFFFFFFFFFh
and rax, 808080h ;RAX = 0000000000808080h
但是,如果源操作數是 32 位常數或寄存器,那么目的操作數中,只有低32 位會受到影響。如下例所示,只有RAX 的低 32 位被修改了:
mov rax,allones ???????????????;RAX = FFFFFFFFFFFFFFFF
and rax,80808080h ????????;RAX = FFFFFFFF80808080
當目的操作數是內存操作數時,得到的結果是一樣的。顯然,32位操作數是一個特殊的情況,需要與其他大小操作數的情況分開考慮。
6.2.11 本節回顧
1.編寫一條指令,用 16 位操作數清除 AX 的高 8 位,而 AX 的低 8位不變。
答:and ax, 00FFh
2.編寫一條指令,用16位操作數使AX的高8位置1,而AX的低8位不變。
答:or ax, 0FF00h
3.編寫一條指令(不使用NOT),使EAX 中所有位取反。
答:xor eax, 0FFFFFFFFh
4.編寫指令實現如下功能,當EAX 的32 位值為偶數時,將零標志位置1;當 EAX 的值為奇數時,將零標志位清零。
答:test eax, 1 ;若eax為奇數則低位置1
5.編寫一條指令,將 AL 中的大寫字母轉換為小寫字母;如果AL 中已包含小寫字母,則不修改 AL。
答:or AL,00100000b
6.3 條件跳轉
6.3.1 條件結構
x86 指令集中沒有明確的高級邏輯結構,但是可以通過比較和跳轉的組合來實現它們。執行一個條件語句需要兩個步驟:第一步,用CMP、AND 或SUB 操作來修改CPU 狀態標志位;第二步,用條件跳轉指令來測試標志位,并產生一個到新地址的分支。下面是一些例子。
示例1 本例中的CMP指令把EAX的值與0進行比較,如果該指令將零標志位置1,則JZ(為零跳轉)指令就跳轉到標號L1:
????????cmp eax, 0
????????jz L1 ????????????????;如果ZF=1則跳轉
L1:
示例2 本例中的AND指令對DL寄存器進行按位與操作,并影響零標志位。如果零標志位清零,則JNZ(非零跳轉)指令跳轉:
????????and dl, 10110000b
????????jnz L2 ????????????????;如果ZF=0則跳轉
L2:
6.3.2 Jcond指令
當狀態標志條件為真時,條件跳轉指令就分支到目標標號。否則,當標志位條件為假時,立即執行條件跳轉后面的指令。語法如下所示:
Jcond destination
cond 是指確定一個或多個標志位狀態的標志位條件。下面是基于進位和零標志位的例子:
CPU 狀態標志位最常見的設置方法是通過算術運算、比較和布爾運算指令。條件跳轉指令評估標志位狀態,利用它們來決定是否發生跳轉。
用CMP指令 假設當EAX=5時,跳轉到標號L1。在下面的例子中,如果EAX=5,CMP指令就將零標志位置1;之后,由于零標志位為1,JE指令就跳轉到L1:
cmp eax, 5
je L1 ;如果相等則跳轉
(JE指令總是按照零標志位的值進行跳轉。)如果EAX 不等于 5,CMP就會清除零標志位,那么,JE 指令將不跳轉。
下例中,由于 AX 小于 6,所以JL 指令跳轉到標號 L1:
mov ax, 5
cmp ax, 6
jl L1 ;小于則跳轉
例中,由于 AX 大于 4,所以發生跳轉:
mov ax, 5
cmp ax, 4
jg L1 ;大于則跳轉
完整代碼測試筆記
;6.3.2.asm Jcond指令 當狀態標志條件為真時,條件跳轉指令就分支到目標標號。
;語法所示: Jcond destinationINCLUDE Irvine32.inc.data
L1str BYTE "leftOp == rightOp",0
L2str BYTE "leftOp < rightOp",0
L3str BYTE "leftOp > rightOp",0.code
main PROCmov eax, 5cmp eax, 5je L1 ;如果相等則跳轉
next1:mov ax, 5cmp ax, 6jl L2 ;小于則跳轉
next2:mov ax, 5cmp ax, 4jg L3 ;大于則跳轉jmp quit
L1:mov edx, OFFSET L1str ;顯示跳轉提示call WriteStringcall Crlf jmp next1
L2:mov edx, OFFSET L2str ;顯示跳轉提示call WriteStringcall Crlf jmp next2
L3:mov edx, OFFSET L1str ;顯示跳轉提示call WriteStringcall Crlf ;換行
quit:nopINVOKE ExitProcess,0
main ENDP
END main
運行調試:
6.3.3 條件跳轉指令類型
x86 指令集包含大量的條件跳轉指令。它們能比較有符號和無符號整數,并根據單個CPU 標志位的值來執行操作。條件跳轉指令可以分為四個類型:
●基于特定標志位的值跳轉。
●基于兩數是否相等,或是否等于(E)CX的值跳轉
●基于無符號操作數的比較跳轉
●基于有符號操作數的比較跳轉
表 6-2展示了基于零標志位、進位標志位、溢出標志位、奇偶標志位和符號標志位的跳轉。
1.相等性的比較
表6-3列出了基于相等性評估的跳轉指令。有些情況下,進行比較的是兩個操作數;其他情況下,則是基于CX、ECX或RCX的值進行跳轉。表中符號1efOp和rightOp分別指的是CMP指令中的左(目的)操作數和右(源)操作數:
CMP leftOp, rightOp
操作數名字反映了代數中關系運算符的操作數順序。比如,表達式X<Y中,X被稱為1eftOp, Y 被稱為 rightOp。
盡管正指令相當于JZ(為零跳轉),INE指令相當于INZ(非零跳轉),但是,最好是選擇最能表明編程意圖的助記符(JE或JZ),以便說明是比較兩個操作數還是檢查特定的狀態標志位。
下述示例使用了JE、INE、JCXZ和JECXZ指令。仔細閱讀注釋,以保證理解為什么條件跳轉得以實現(或不實現)。
;6.3.3_1.asm 相等性的比較INCLUDE Irvine32.inc.data
L1str BYTE "leftOp == rightOp",0
L2str BYTE "cx == 0",0
L5str BYTE "leftOp != rightOp",0.code
main PROC;示例 1:mov edx, 0A523hcmp edx, 0A523hjne L5 ;不發生跳轉je L1 ;相等跳轉;示例 2:mov bx, 1234hsub bx, 1234hjne L5 ;不發生跳轉je L1 ;結果等0,即標志位ZF=1, 跳轉;示例 3:mov cx, 0FFFFhinc cx ;cx = cx+1jcxz L2 ;cx==0就跳轉;示例 4:xor ecx, ecxjcxz L2 ;cx==0就跳轉L1: mov edx, OFFSET L1strcall WriteStringjmp quit
L2:mov edx, OFFSET L2strcall WriteStringjmp quit
L5:mov edx, OFFSET L5strcall WriteStringjmp quit
quit:call CrlfINVOKE ExitProcess,0
main ENDP
END main
運行調試:
示例 1:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 示例 2:
示例 3:???????????????????????????????????????????????????????????????? 示例 4:
2.無符號數比較
基于無符號數比較的跳轉如表6-4所示。操作數的名稱反映了表達式中操作數的順序(比如1eftOp<rightOp)。表6-4中的跳轉僅在比較無符號數值時才有意義。有符號操作數使用不同的跳轉指令。
對下面的代碼示例,閱讀注釋,以保證理解為什么跳轉得以實現(或不實現):
示例 1
mov edx,-1
cmp edx, 0
jnl L5 ;不發生跳轉(-1>0為假)
jnle L5 ;不發生跳轉(-1>0為假)
jl L1 ;跳轉(-1<0為真)
示例 2
mov bx, +32
cmp bx, -35
jng L5 ;不發生跳轉(+32 <= -35為假)
jnge L5 ;不發生跳轉(+32 < -35為假)
jge L1 ;跳轉(+32 >= -35為真)
示例 3
mov ecx, 0
cmp ecx, 0
jg L5 ;不發生跳轉(0>0為假)
jnl L1 ;跳轉(0>=0為真)
示例 4
mov ecx, 0
cmp ecx, 0
jl L5 ;不發生跳轉(0<0為假)
jng L1 ;跳轉(0<=0為真)
6.3.4 條件跳轉應用
測試狀態位 匯編語言做得最好的事情之一就是位測試。通常,不希望改變進行位測試的數值,但是卻希望能修改CPU狀態標志位的值。條件跳轉指令常常用這些狀態標志位來決定是否將控制轉向代碼標號。例如,假設有一個名為status的8位內存操作數,它包含了與計算機連接的一個外設的狀態信息。如果該操作數的位5等于1,表示外設離線,則下面的指令就跳轉到標號:
mov al, status
test a1, 00100000b ;測試位5
jnz Deviceoffline
如果位0、1或4中任一位置1,則下面的語句跳轉到標號:
mov al, status
test al, 00010011b ;測試位0、1、4
jnz InputDataByte
如果是位 2、3和7都置1使得跳轉發生,則還需要AND和CMP指令:
mov al, status
and a1, 10001100b ;屏蔽位2、3和 7
cmp a110001100b ;所有位都置1?
je ResetMachine ;是:跳轉
兩個數中的較大數 下面的代碼比較了EAX 和EBX 中的兩個無符號整數,并且把其中較大的數送人EDX:
;6.3.4_1.asm 6.3.4 條件跳轉應用 兩個數中的較大數
;下面的代碼比較了EAX 和EBX 中的兩個無符號整數,并且把其中較大的數送人EDX:INCLUDE Irvine32.inc.data
L1str BYTE "The maximum value is: ",0.code
main PROCmov eax, 55mov ebx, 33mov edx, eax ;假設 EAX 存放較大的數cmp eax, ebx ;若EAX≥EBXjae L1 ;跳轉到 L1mov edx, ebx ;否則,將 EBX 的值送入 EDX
L1: ;EDX 中存放的是較大的數mov eax, edxmov edx, offset L1strcall WriteStringcall WriteIntmov edx, eax
quit:call CrlfINVOKE ExitProcess,0
main ENDP
END main
運行調試:
三個數中的最小數 下面的代碼比較了分別存放于三個變量V1、V2和 V3 的無符號 16位數值,并且把其中最小的數送人 AX:
;6.3.4_2.asm 6.3.4 條件跳轉應用 三個數中的最小數
;下面的代碼比較了分別存放于三個變量V1、V2和 V3 的無符號 16位數值,并且把其中最小的數送人 AX: INCLUDE Irvine32.inc.data
L2str BYTE "The minimum value is: ",0
V1 WORD 66
V2 WORD 55
V3 WORD 77.code
main PROCmov ax, V1 ;假設 V1是最小值cmp ax, V2 ;如果AX≤ V2jbe L1 ;跳轉到 L1mov ax, V2 ;否則,將 V2 送入AX
L1: cmp ax, V3 ;如果 AX≤V3jbe L2 ;跳轉到L2mov ax, V3 ;否則,將V3送入AXL2: and eax, 0000FFFFhmov edx, offset L2strcall WriteStringcall WriteIntquit:call CrlfINVOKE ExitProcess,0
main ENDP
END main
運行調試:
循環直到按下按鍵 下面的32位代碼會持續循環,直到用戶按下任意一個標準的字母數字鍵。如果輸入緩沖區中當前沒有按鍵,那么Irvine32庫中的ReadKey函數就會將零標志位置1:
;6.3.4_3.asm 6.3.4 條件跳轉應用
;環直到按下按鍵 下面的32位代碼會持續循環,直到用戶按下任意一個標準的字母數字鍵。
;如果輸入緩沖區中當前沒有按鍵,那么Irvine32庫中的ReadKey函數就會將零標志位置1:INCLUDE Irvine32.inc.data
char BYTE ?
KeyStr BYTE "The pressed key is: ",0.code
main PROC
L1: mov eax, 10call Delaycall ReadKeyjz L1mov char, ALmov edx, offset KeyStrcall WriteString ;顯示字符串call WriteChar ;顯示ALcall CrlfINVOKE ExitProcess,0
main ENDP
END main
運行調試:
上述代碼在循環中插人了一個10毫秒的延遲,以便MS-Windows有時間處理事件消息。如果省略這個延遲,那么按鍵可能被忽略。