《汇编语言:基于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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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;仅包含…