《汇编语言:基于X86处理器》第6章 条件处理(2)

本章向程序员的汇编语言工具箱中引入一个重要的内容,使得编写出来的程序具备作决策的功能。几乎所有的程序都需要这种能力。首先,介绍布尔操作,由于能影响CPU状态标志,它们是所有条件指令的核心。然后,说明怎样使用演绎CPU状态标志的条件跳转和循环指令。接着演示如何用本章介绍的工具来实现理论计算机科学中最根本的结构之一:有限状态机。本章最后展示的是MASM内置的32位编程的逻辑结构。

6.4 条件循环指令

6.4.1 LOOPZ和 LOOPE 指令

LOOPZ(为零跳转)指令的工作和LOOP指令相同,只是有一个附加条件:为零控制转向目的标号,零标志位必须置1。指令语法如下:

LO0PZ destination

LOOPE(相等跳转)指令相当于LOOPZ,它们有相同的操作码。这两条指令执行如下任务:

ECX=ECX-1

if ECX > 0 and F =1, jump to destination

否则,不发生跳转,并将控制传递到下一条指令。LOOPZ和LOOPE不影响任何状态标志位。32位模式下,ECX是循环计数器;64位模式下,RCX是循环计数器。

完整代码测试笔记

;6.4.1.asm  LOOPNZ指令 和LOOPNE指令用法学习
;找出数组中第1个小写字母.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
array WORD 'A', 'C', 'S',  'P', 'a', 'y', 'h', 'x'
value WORD 0.code
main PROCmov ecx, LENGTHOF arraymov esi, 0
L1:test array[esi], 00100000b					;无符号数比较,影响零标志和进位标志pushfdadd esi, TYPE arraypopfdloopz L1sub esi, TYPE arraymov ax,  WORD PTR array[esi]mov value, axINVOKE ExitProcess,0
main ENDP
END main

运行调试

6.4.2 LOOPNZ和 LOOPNE 指令

LOOPNZ(非零跳转)指令与LOOPZ相对应。当ECX中无符号数值大于零(减1操作之后)且零标志位等于零时,继续循环。指令语法如下:

LOOPNZ destination

LOOPNE(不等跳转)指令相当于LOOPNZ,它们有相同的操作码。这两条指令执行如下任务:

ECX=ECX-1

if ECX > 0 and F =0, jump to destination

否则,不发生跳转,并将控制传递到下一条指令。

完整代码测试笔记:

;6.4.2.asm  LOOPNZ指令 和LOOPNE指令学习
;与Loopnz.asm功能一样.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
array SWORD -1, -6, -1, -10, 10, 30, 40, 4
sentinel SWORD 0.code 
main PROCmov esi, OFFSET arraymov ecx, LENGTHOF array
L1:	test WORD PTR [esi], 8000h			;测试符号位,该指令影响符号标志,零标志,奇偶标志jns quit												;无符号跳转(表示非负数)add esi,TYPE array							;移动到下一个位置loopnz L1												;继续循环jnz notFind											;没有发现非负数
quit:movzx eax, WORD PTR[esi]mov sentinel, ax
notFind:nopINVOKE ExitProcess,0main ENDP 
END main

运行调试:

如果找到一个非负数,ESI会指向该数值。如果没有找到一个正数,则只有当ECX=0时才终止循环。在这种情况下,JNZ指令跳转到标号quit,同时ESI指向标记值(0),其在内存中的位置正好紧接着该数组。

6.4.3 本节回顾

1.(真/假):当(且仅当)零标志位被清除时,LOOPE指令跳转到标号

答:假。当ZF=1并且ECX > 0时才跳转

2.(真/假):32位模式下,当ECX大于零且零标志位被清除时,LOOPNZ指令跳转到标号。

答:真。

3.(真/假):LOOPZ指令的目的标号必须处在距离其后指令的-128到+127字节范围之内。

答:真

4.修改6.4.2节中的LOOPNZ示例,使之扫描数组并搜索其中的第一个负数。改变数组的初始化,用正数作为其起始值。

答:修改后的代码如下

;6.4.3_4.asm   6.4.3 本节回顾      4.修改6.4.2节中的LOOPNZ示例,  
;使之扫描数组并搜索其中的第一个负数。改变数组的初始化,用正数作为其起始值。.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
array SWORD 10, 30, 40, 4, -56, -17, -98
sentinel SWORD 0.code 
main PROCmov esi, OFFSET arraymov ecx, LENGTHOF array
L1:	test WORD PTR [esi], 8000h			;测试符号位,该指令影响符号标志,零标志,奇偶标志pushfd							;标志位入栈add esi,TYPE array				;移动到下一个位置popfd						    ;标志位出栈loopz L1						;继续循环jz quit							;没有发现负数sub esi, TYPE array				;ESI指向数值
quit:movzx eax, WORD PTR[esi]mov sentinel, axINVOKE ExitProcess,0main ENDP 
END main

运行调试:

5.挑战:6.4.2节的LOOPNZ示例依靠一个标记值来处理没有发现正数的可能性。如果把这个标记值去掉,会发生什么?

答:如果没有发现匹配值,ESI将以指向数组末层之外作为结束。若指向了一个未定义的内存位置,那么程序运行就可能导致运行时错误。

6.5 条件结构

条件结构被定义为,能够在不同的逻辑分支中触发选择的一个或多个条件表达式。每一个分支都执行不同的指令序列。毫无疑问,在高级编程语言中已经使用了条件结构,但是你可能并不了解语言编译器是如何将条件结构转换为低级机器代码的。现在就来讨论这个转换过程.

6.5.1 块结构的 IF 语句

IF结构包含一个布尔表达式,其后有两个语句列表:一个是当表达式为真时执行,另一个是当表达式为假时执行:

if(boolean - expression)statement - list - 1
elsestatement - list - 2

结构中的else部分是可选的。在汇编语言中,则是用多个步骤来实现这种结构的。首先,对布尔表达式求值,这样一来某个CPU状态标志位会受到影响。然后,根据相关CPU状态标志位的值,构建一系列跳转把控制传递给两个语句列表。

示例1下面的C++代码中,如果op1等于op2,则执行两条赋值语句:

if(op1 == op2) 
{X = 1;Y = 2;
}

在汇编语言中,这种正语句转换为条件跳转和CMP指令。由于op1和op2都是内存操作数(变量),因此,在执行CMP之前,要将其中的一个操作数送人寄存器。下面实现IF语句的程序是高效的,当逻辑表达式为真时,它允许代码“通过”直达两条期望被执行的MOV 指令:

;6.5.1_1.asm      块结构的 IF 语句       示例1 用汇编语言实现下面c++语句
;if(op1 == op2)  {
;		X = 1;
;		Y = 2;
;}.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
op1 DWORD 45
op2 DWORD 45
X DWORD ?
Y DWORD ?.code 
main PROC;1.jne的方式。它允许代码“通过”直达两条期望被执行的MOV 指令:
;		mov eax, op1
;		cmp eax, op2								;op1 == op2 ?
;			jne L1									;否:跳过后续指令
;			mov X, 1								;是:X,Y赋值
;			mov Y, 2
;			jmp quit
;L1:	mov X, 10
;			mov Y, 20;2.je的方式。用正来实现==运算符,生成的代码就没有那么紧凑了(6条指令,而非5条指令):mov eax, op1cmp eax, op2								; op1 == op2 ?je L1										;是:跳转到L1jmp L2										;否:跳过赋值语句
L1:	mov X, 1										;X,Y赋值mov Y, 2									jmp quit
L2:	mov X, 10									mov Y, 20									
quit:nopINVOKE ExitProcess,0main ENDP 
END main

方式1调试:

方式2调试:

从上面的例子可以看出,相同的条件结构在汇编语言中有多种实现方法。本章给出的编译代码示例只代表一种假想的编译器可能产生的结果

示例2  NTFS文件存储系统中,磁盘簇的大小取决于磁盘卷的总容量。如下面的伪代码所示,如果卷大小(用变量terrabytes存放)不超过16TB,则簇大小设置为4096。否则簇大小设置为8192

clustersize=8192
if terrabytes <16clusterSize = 4096:

用汇编语言实现该伪代码:

;6.5.1_2.asm      块结构的 IF 语句       示例2  用汇编语言实现下面语句
;clustersize = 8192
;if terrabytes < 16
;  clusterSize = 4096:.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
clusterSize WORD ?								;簇大小
terrabytes WORD 1								;磁盘卷大小,单位TB.code 
main PROCmov clusterSize, 8192					;假设较大的磁盘簇cmp terrabytes, 16						;小于16TB ?jae next								;大于或等于跳转								mov clusterSize, 4096					;切换到较小的磁盘簇
next:	nopINVOKE ExitProcess,0main ENDP 
END main

运行调试:

示例3 下面的伪代码有两个分支:

if op1 > op2call Routine1
elsecall Routine2
end if

用汇编语言翻译这段伪代码,设op1和op2是有符号双字变量。对这两个变量比较时其中一个必须送入寄存器:

;6.5.1_3.asm      块结构的 IF 语句       示例3  用汇编语言实现下面语句
;if op1 > op2
;	call Routine1
;else
;	call Routine2
;end if.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
op1 DWORD 55
op2 DWORD 51.code 
main PROCmov eax, op1									;op1送入寄存器cmp eax, op2									;op1 >op2?jg A1											;是:调用 Routine1call Routine2									;否:调用 Routine2jmp A2											;退出IE语句
A1:	call Routine1									
A2:	nopINVOKE ExitProcess,0
main ENDP Routine1 PROCmov edx, op1ret
Routine1 ENDPRoutine2 PROCmov edx, op2ret
Routine2 ENDP
END main

运行调试:

白盒测试

复杂条件语句可能有多个执行路径,这使得它们难以进行调试检查(查看代码)。程序员经常使用的技术称为白盒测试,用来验证子程序的输入和相应的输出。白盒测试需要源代码,并对输人变量进行不同的赋值。对每个输入组合,要手动跟踪源代码,验证其执行路径和子程序产生的输出。下面,通过嵌套正语句的汇编程序来看看这个测试过程:

if op1 == op2if X > Ycall Routine1elsecall Routine2
elsecall Routine3
end if

下面是可能的汇编语言翻译,加上了参考行号。程序改变了初始条件(op1=-op2),并立即跳转到 ELSE部分。剩下要翻译的内容是内层IF-ELSE 语句:

    mov eax, op1				cmp eax, op2				;op1 == op2 ?jne L2						;否:调用Routine3,  不等于跳转到L2mov eax, X					;处理内层IF-ELSE 语句cmp eax, Y					;X > Y ?jg L1						;是:调用 Routine1,   大于跳转到L1  call Routine2				;否:调用 Routine2jmp L3						;退出
L1:	call Routine1				;调用 Routine1jmp L3						;退出
L2:	call Routine3
L3:	

表6-6给出了示例代码的白盒测试结果。前四列对op1、op2、X和Y进行测试赋值。

第5列和第6列对生成的执行路径进行了验证

6.5.2 复合表达式

汇编语言很容易实现包含AND运算符的复合布尔表达式。考虑下面的伪代码,假设其中进行比较的是无符号整数:

if (al > bl) AND (bl > cl)X = 1
else if

短路求值 下面的例子是短路求值的简单实现,如果第一个表达式为假,则不需计算第二个表达式。高级语言的规范如下:

;6.5.2_1.asm      复合表达式       1.逻辑 AND 运算符
;if (al > bl) AND (bl > cl)  
;		X = 1;
;end if.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
X BYTE 23.code 
main PROCmov al, 5mov bl, 4mov cl, 3;方式1cmp al, bl							;第一个表达式…ja L1								;大于跳转到L1jmp next							;否则退出if
L1:	cmp bl, cl								;第二个表达式…ja L2								;大于跳转到L2jmp next							;否则退出if
L2:	mov X, 1								;全为真:将X置1
next:jmp quit;方式2,    如果把第一条 JA指令替换为JBE,就可以把代码减少到5条:cmp al, bl							;第一个表达式…jbe quit							;如果假,则退出    小于或等于跳转到quit,退出if, jbe无符号比较cmp bl, cl							第二个表达式…jbe quit							;如果假,则退出    小于或等于跳转到quit,退出if, jle有符号比较mov X, 1							;全为真
quit: nopINVOKE ExitProcess,0main ENDP 
END main

运行调试

方式1:

方式2:

若第一个JBE不执行,CPU可以直接执行第二个CMP指令,这样就能够减少29%的代码量(指令数从7条减少到5条)。

2.逻辑 OR 运算符

当复合表达式包含的子表达式是用OR运算符连接的,那么只要一个子表达式为真,则整个复合表达式就为真。以如下伪代码为例:

if (al > bl) OR (bl > cl)X = 1

在下面的实现过程中,如果第一个表达式为真,则代码分支到L1;否则代码直接执行第二个CMP指令。第二个表达式翻转了>运算符,并使用了JBE指令:

【代码】

对于一个给定的复合表达式而言,汇编语句有多种实现方法。

2.逻辑 OR 运算符

当复合表达式包含的子表达式是用OR运算符连接的,那么只要一个子表达式为真,则整个复合表达式就为真。以如下伪代码为例:

if (al > bl) OR (bl > cl)X = 1

在下面的实现过程中,如果第一个表达式为真,则代码分支到L1;否则代码直接执行第二个CMP指令。第二个表达式翻转了>运算符,并使用了JBE指令:

;6.5.2_2.asm      复合表达式       2.逻辑 OR 运算符
;if (al > bl) OR (bl > cl)  
;		X = 1;.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
X BYTE 23.code 
main PROCmov al, 2mov bl, 4mov cl, 3cmp al, bl							;1:比较AL和 BLja L1										;如果真,跳过第二个表达式cmp bl, cl							;2:比较BL和CLjbe next								;假:跳过下一条语句
L1:	mov X, 1								;真:将X置1
next: nopINVOKE ExitProcess,0main ENDP 
END main

运行调试:

对于一个给定的复合表达式而言,汇编语句有多种实现方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/89466.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/89466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深度剖析NumPy核心函数reshape()

深度剖析NumPy核心函数reshape reshape()函数基础概念reshape()函数语法与参数详解reshape()函数使用示例基本的形状重塑使用-1自动计算维度多维数组的形状重塑不同order参数的效果 reshape()函数的应用场景数据预处理机器学习模型输入算法实现 当我们使用np.array()创建好数组…

Linux平台MinGW32/MinGW64交叉编译完全指南:原理、部署与组件详解

一、MinGW是什么&#xff1f;为什么需要交叉编译&#xff1f; MinGW&#xff08;Minimalist GNU for Windows&#xff09;是一套在Linux上构建Windows应用程序的完整工具链。它允许开发者&#xff1a; 在Linux环境下编译Windows可执行文件&#xff08;.exe/.dll&#xff09;避…

为什么我画的频谱图和audacity、audition不一样?

文章目录 系列文章目录 目录 文章目录 前言 一、问题引入 二、使用步骤 三、分析和改进 总结 前言 我们知道audacity和audition都有频谱分析这个窗口&#xff0c;一般过程肯定是分帧加窗&#xff0c;fft变换然后呈现&#xff0c; 大体这个过程是没问题的&#xff0c;但为什…

责任链模式 Go 语言实战

责任链模式&#xff08;Chain of Responsibility&#xff09; 责任链模式是一种行为设计模式&#xff0c;它允许将请求沿着处理者链进行传递&#xff0c;直到有一个处理者能够处理它。这个模式的主要目的是解耦请求的发送者和接收者&#xff0c;使得多个对象都有机会处理这个请…

使用开源项目youlai_boot 导入到ecplise 中出现很多错误

我是使用ecplise 导入得youlai_boot 这个项目&#xff0c;但是导入到ecplise 中一直出现报错&#xff0c;然后各种maven clean 和maven install 以及update Maven 都没有效果不知道怎么办才好&#xff0c;怎么样解决这个问题&#xff0c;原来是我本地的环境中没有安装 lombok.…

06_Americanas精益管理项目_数据分析

文章目录 Americanas精益管理项目_数据分析(一)思维方法1、数据分析思维2、零售行业-万能「人货场」分析框架(二)商品分析1、品类销量分析2、销量趋势分析3、帕累托法则分析4、商品TopN分析(三)用户分析(四)场景分析Americanas精益管理项目_数据分析 数据分析与数据开…

ES6从入门到精通:类与继承

ES6 类的基本概念 ES6 引入了基于类的面向对象编程语法&#xff0c;通过 class 关键字定义类。类可以包含构造函数、方法和属性。 class Person {constructor(name) {this.name name;}greet() {console.log(Hello, ${this.name}!);} }const person new Person(Alice); pers…

【经验】新版Chrome中Proxy SwitchyOmega2已实效,改为ZeroOmega

1、问题描述 手欠更新了 Chrome 导致无法“上网”&#xff0c;原因是 Proxy SwitchyOmega2 已实效。 2、解决方法 2.1 下载 新版Chrome中Proxy SwitchyOmega2已实效&#xff0c;改为ZeroOmega&#xff1b; 想方设法去下载 ZeroOmega 的crx包&#xff0c;最新的为&#xff1…

在windows上设置python的环境

安装好了python,再具体说下python语言的相关环境。 #01 关于Python Python 是一个高级别的、边运行边解释的、动态类型的编程语言,以简洁的语法、强大的功能和丰富的资源库而闻名。广泛应用于 Web 开发、数据分析、人工智能、自动化脚本等多个领域。 目前 Python 语言有两…

3D 建模与点云建模:从虚拟构建到实景复刻的数字孪生双引擎

在数字化浪潮席卷全球的当下&#xff0c;3D 建模与点云建模如同数字世界的左膀右臂&#xff0c;一个以抽象化的创意构建虚拟蓝图&#xff0c;一个以高精度的实景数据复刻现实世界。它们不仅深刻重塑了影视娱乐、工业制造、建筑设计等传统领域&#xff0c;更成为数字孪生技术蓬勃…

智能检测原理和架构

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 智能检测系统基于AI和大数据分析技术&#xff0c;通过主动感知、行为建模与实时响应构建动态防御体系。其核心在于将传统规则匹配升级为**多模态威胁认知**&#xff0c;实现对新型攻击&#xff08;如AI…

2025年6月个人工作生活总结

本文为 2025年6月工作生活总结。 研发编码 某国产操作系统curl下载sftp服务器文件问题记录 场景&#xff1a; 某国产系统curl版本信息&#xff1a; # curl --version curl 7.71.1 (x86_64-koji-linux-gnu) libcurl/7.71.1 OpenSSL/1.1.1f-fips zlib/1.2.11 brotli/1.0.7 li…

Java 导出PDF 1、内容可以插入自定义表格 2、内容插入图片

Java PDF导出工具&#xff1a;自定义表格与图片插入 下面我将实现一个Java PDF导出工具&#xff0c;支持插入自定义表格和图片的功能。这个解决方案使用iText 7库&#xff0c;提供了一个直观的用户界面&#xff0c;可以预览生成的PDF内容。 import javax.swing.*; import jav…

sklearn机器学习概述及API详细使用指南

一、机器学习与sklearn简介 机器学习是人工智能的一个分支&#xff0c;它通过算法让计算机从数据中学习规律&#xff0c;并基于这些规律做出预测或决策。scikit-learn&#xff08;简称sklearn&#xff09;是Python中最流行的机器学习库之一&#xff0c;它提供了各种监督学习和…

「日拱一码」015 机器学习常用库——scikit-learn

目录 数据预处理 数据标准化&#xff08;StandardScaler&#xff09; 数据归一化&#xff08;MinMaxScaler&#xff09; 数据离散化&#xff08;KBinsDiscretizer&#xff09; 缺失值处理&#xff08;SimpleImputer&#xff09; 特征选择 基于单变量特征选择&#xff08…

网络编程学习路线

C网络编程从零基础到精通的学习路线&#xff0c;每一步都和你的项目实际需求紧密结合&#xff0c;帮助你真正做到“学以致用”。 C网络编程学习路线&#xff08;结合FileHub项目&#xff09; 第一阶段&#xff1a;网络编程基础入门 1. 计算机网络基础 理解OSI七层模型、TCP/I…

NLP:文本张量表示方法

本文目录&#xff1a; 一、one-hot编码二、word2vec模型&#xff08;一&#xff09;概念1.CBOW(Continuous bag of words)模式2.skipgram模式:3.词向量的检索获取 &#xff08;二&#xff09;word2vec的训练和使用1. 获取训练数据2.查看原始数据3.原始数据处理&#xff0c;并查…

高阶数据结构------并查集

并查集 在一些应用问题中&#xff0c;需要将n个不同的元素划分成一些不相交的集合。开始时&#xff0c;每个元素自成一个集合&#xff0c;然后按照一定的规律将归于同一组的元素集合合并。在此过程中要反复用到查询某一个元素归属于哪一个集合的运算。适合于描述这类问题的抽象…

OWASP Top 10 是什么?

OWASP&#xff08;Open Web Application Security Project&#xff0c;开放Web应用安全项目&#xff09;是一个致力于提高软件安全性的国际非营利组织。其发布的 ​OWASP Top 10​ 是最具影响力的Web应用安全风险清单&#xff0c;每3-4年更新一次&#xff0c;帮助开发人员、安全…

如何在IIS上部署net系统(安装iis参考上一篇)

1.对后端项目打包&#xff0c;我使用的时rider 2.打包前端 npm run build 3.在iis上部署 网站-添加网站 4.选择之前打包的后端文件&#xff0c;设置端口 5.安装对应net环境插件&#xff1a;主要是runtime和sdk插件以及dotnet-hosting-2.2.0-win&#xff0c;具体版本看自己项…