《匯編語言:基于X86處理器》第6章 條件處理(1)

本章向程序員的匯編語言工具箱中引入一個重要的內容,使得編寫出來的程序具備作決策的功能。幾乎所有的程序都需要這種能力。首先,介紹布爾操作,由于能影響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有時間處理事件消息。如果省略這個延遲,那么按鍵可能被忽略。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/86989.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/86989.shtml
英文地址,請注明出處:http://en.pswp.cn/web/86989.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【分治思想】歸并排序 與 逆序對

歸并排序 歸并排序是一種分治算法&#xff0c;怎么分&#xff0c;怎么治&#xff1f; 分&#xff1a;通過遞歸不斷把數組分成兩半&#xff0c;直到每個子數組只剩 1 個元素&#xff08;天然有序&#xff09;治&#xff1a;把兩個已經排好序的子數組合并成一個有序數組。 把問…

SQL參數化查詢:防注入與計劃緩存的雙重優勢

在數據庫操作中&#xff0c;SQL參數化查詢&#xff08;Parameterized Queries&#xff09;是一種非常有效的技術&#xff0c;它不僅可以防止SQL注入攻擊&#xff0c;還可以提高數據庫查詢的效率&#xff0c;尤其是在與計劃緩存&#xff08;Query Plan Caching&#xff09;結合使…

【你怕一E1】- 孰輕孰重如何斷-組合問題的多種情形

摘要 本視頻講解了組合問題的多種情形,包括多選一、多選二、多選三以及分隊問題的解題方法。首先介紹了從不同人數中選人的不同選擇方式,如一百人中選一人有一百種選擇。隨后,詳細講解了有序思考方法在多選二問題中的應用,通過選隊長的方式列舉不同組合情況,并歸納出選擇規…

nginx反向代理的bug

nginx反向代理的bug 問題呈現 當我們配置反向代理的時候查詢error.log的時候我們發現以下的問題 2025/06/29 08:38:47 [error] 7#7: *2 open() “/usr/share/nginx/html/payed/notify” failed (2: No such file or directory), client: 192.168.98.1, server: localhost, r…

MyBatis 動態 SQL 與緩存機制深度解析

在Java持久層技術體系中&#xff0c;MyBatis憑借其靈活的SQL映射和強大的動態SQL能力&#xff0c;成為企業級應用開發的首選框架。本文從動態SQL核心語法、緩存實現原理、性能優化及面試高頻問題四個維度&#xff0c;結合源碼與工程實踐&#xff0c;系統解析MyBatis的核心特性與…

Nuxt 3 中實現跨組件通信方式總結:使用 Pinia、Provide/Inject 或 Props

在開發復雜的 Web 應用時&#xff0c;跨組件通信是一個常見的需求。Nuxt 3 提供了多種方式來實現這一點&#xff0c;包括使用狀態管理工具&#xff08;如 Pinia&#xff09;、Vue 的 provide/inject 機制以及傳統的 props 傳遞。本文將詳細介紹這三種方法&#xff0c;并通過一個…

Java ArrayList 擴容機制

一、ArrayList 簡介 ArrayList 是 Java 集合框架中基于數組實現的可變長度列表&#xff0c;其核心特性是&#xff1a; 支持隨機訪問&#xff08;通過索引&#xff09;支持動態擴容插入/刪除效率較低&#xff08;非尾部操作&#xff09; 二、底層數據結構 // JDK 11 transien…

C++面試題精講系列之數組排序

數組排序是我們經常遇到的筆試題目&#xff0c;給大家盤一下這題到底想考察什么&#xff1f; // 考題如下 void main() {int arr[4] {26,28,24,11};// 請實現一個sortArray函數&#xff0c;對數組arr進行從小到大排序 }考點1&#xff1a;數組做函數參數如何傳遞參&#xff1f;…

Windows10/11 輕度優化 純凈版,12個版本!

系統介紹 鏡像包均基于微軟官方原版系統精心制作&#xff0c;確保系統的原汁原味與穩定性。Windows 10/11&#xff0c;都集成了最新的補丁。版本選對&#xff0c;一鍵安裝到位&#xff0c;全自動無人值守安裝模式。 系統特點 系統進行優化提供了12個系統版本集成了運行庫、…

開發工具IDEA

開發工具IDEA 開發調試&#xff08;debug&#xff09;Maven配置三級目錄 開發調試&#xff08;debug&#xff09; 史上最全的 IDEA Debug 調試技巧&#xff08;超詳細案例&#xff09; Maven配置 idea全局Maven配置 IDEA中Maven配置詳解 有些時候不要配置maven_home這些環境…

GitHub Actions與AWS OIDC實現安全的ECR/ECS自動化部署

引言 在現代云原生應用開發中,實現安全、高效的CI/CD流程至關重要。本文將詳細介紹如何利用GitHub Actions和AWS OIDC(OpenID Connect)構建一個無需長期憑證的安全部署管道,將容器化應用自動部署到Amazon ECR和ECS服務。 架構概述 整個解決方案的架構包含三個主要部分:…

一、MongoDB安裝-二進制安裝

下載tar包 wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-7.0.21.tgz wget https://downloads.mongodb.com/compass/mongosh-2.5.3-linux-x64.tgz安裝 解壓 tar xf mongodb-linux-x86_64-rhel70-7.0.21.tgz cp mongodb-linux-x86_64-rhel70-7.0.21/bi…

學習日志03 ETF 基礎數據可視化分析與簡易管理系統

1 代碼的選擇和改進 import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from ipywidgets import (AppLayout, Dropdown, Button, Output, VBox, HBox, Label, Layout, SelectMultiple,IntSlider, FloatSlider, Checkbox, Text, Select) from IPytho…

[Python] -基礎篇7-新手常見Python語法錯誤及解決方案

Python 以其簡潔明了的語法引人入勝,但對于初學者而言,仍然容易遭遇各類語法錯誤。本文總結了 Python 語言日常編寫中最常見的語法錯誤類型,并提供解決方案和正確寫法,幫助新手快速突破編程路上的一道道埋伏。 1. 拼寫錯誤 (SyntaxError) 這是最基本也最常見的錯誤類型。…

位運算實戰:數值構造終極優化

位運算優化實戰&#xff1a;數值構造問題詳解 今天我們將深入分析一個有趣的位運算優化問題&#xff0c;這個問題展示了如何通過巧妙的預處理和貪心算法來高效解決數值構造問題。 問題背景與定義 給定一個初始值x&#xff08;0 ≤ x ≤ m&#xff09;和一系列位運算操作&…

nosql項目:基于 Redis 哨兵模式的鮮花預訂配送系統

1 鮮花預訂配送系統概述 1.1 項目背景 鮮花預訂系統是一個實時處理用戶訂單、庫存管理和配送跟蹤的平臺。系統需要處理大量并發訂單&#xff0c;實時更新鮮花庫存狀態&#xff0c;并跟蹤配送進度。傳統關系型數據庫難以應對高并發的訂單處理和實時庫存更新需求&#xff0c;因…

中心效應:多中心臨床試驗的關鍵考量

一、中心效應的來源與影響 1.1 常見來源 1.1.1 患者異質性 中心間基線特征差異(如疾病嚴重度、合并癥比例) 1.1.2 操作差異 給藥規范(如輸液速度)、隨訪依從性、數據記錄質量 1.1.3 評估偏倚 影像學判讀標準(如RECIST)、實驗室檢測方法(如中心實驗室 vs 本地實驗室) …

Redis 實現消息隊列

一、為什么選擇 Redis 作為消息隊列&#xff1f; 在分布式系統架構中&#xff0c;消息隊列是實現異步通信和解耦的核心組件。Redis 作為一個高性能的內存數據庫&#xff0c;憑借其卓越的速度和豐富的數據結構&#xff0c;成為輕量級消息隊列的理想選擇&#xff1a; 1.1 核心優…

(3)pytest的setup/teardown

1. 簡介 學過unittest的都知道里面用前置和后置setup和teardown非常好用&#xff0c;在每次用例開始前和結束后都去執行一次。 當然還有更高級一點的setupClass和teardownClass&#xff0c;需配合classmethod裝飾器一起使用&#xff0c;在做selenium自動化的時候&#xff0c;它…

Starrocks存算一體和存算分離

網上整理了一下starrocks兩種部署方式的區別差異性&#xff0c;個人感覺生產環境還是盡量存算分離部署&#xff0c;防止資源爭奪等問題影響線上生產數據&#xff0c;雖然存算一體部署起來更方便一些 &#x1f4ca; 1. 架構設計 存算一體&#xff1a; 節點類型&#xff1a;僅包含…