当前位置: 首页 > news >正文

《汇编语言:基于X86处理器》第7章 整数运算(3)

本章将介绍汇编语言最大的优势之一:基本的二进制移位和循环移位技术。实际上,位操作是计算机图形学、数据加密和硬件控制的固有部分。实现位操作的指令是功能强大的工具,但是高级语言只能实现其中的一部分,并且由于高级语言要求与平台无关,所以这些指令在一定程度上被弱化了。本章将展示一些对移位操作的应用,包括乘除法的优化。

并非所有的高级编程语言都支持任意长度整数的运算。但是汇编语言指令使得它能够加减几乎任何长度的整数。本章还将介绍执行压缩十进制整数和整数字符串运算的专用指令。

7.5 ASCI和非压缩十进制运算

(7.5节讨论的指令只能用于32位模式编程。)到目前为止,本书讨论的整数运算处理的都是二进制数。虽然CPU用二进制运算,但是也可以执行ASCI十进制串的运算。使用后者进行运算,对用户而言既便于输入也便于在控制台窗口显示,因为不用进行二进制转换假设程序需要用户输人两个数,并将它们相加。若用户输人3402和1256,则程序输出如下所示:

输入第一个数:3402

输入第二个数:1256

和 数: 4658

有两种方法可以计算并显示和数:

1)将两个操作数都转换为二进制,进行二进制加法,再将和数从二进制转换为ASCII数字串。

2)直接进行数字串的加法,按序相加每对ASCI数字(2+6、0+5、4+2、3+1)。和数为ASCII数字串,因此可以直接显示在屏幕上。

第二种方法需要在执行每对ASCI数字相加后,用特殊指令来调整和数。有四类指令用于处理 ASCII加法、减法、乘法和除法,如下所示:

AAA

(执行加法后进行 ASCI 调整)

AAM

(执行乘法后进行 ASCI 调整)

AAS

(执行减法后进行 ASCII 调整)

AAD

(执行除法前进行 ASCII 调整)

ASCII 十进制数和非压缩十进制数 非压缩十进制整数的高4位总是为零,而ASCII十进制数的高4位则应该等于0011b。在任何情况下,这两种类型的每个数字都占用一个字节。下面的例子展示了3402用这两种类型存放的格式:

尽管ASCI运算执行速度比二进制运算要慢很多,但是它有两个明显的优点:

●不必在执行运算之前转换串格式。

●使用假设的十进制小数点,使得实数操作不会出现浮点运算的舍入误差的危险。

ASCII 加减法运行操作数为ASCI格式或非压缩十进制格式,但是乘除法只能使用非压缩十进制数。

7.5.1 AAA 指令

在 32位模式下,AAA(加法后的ASCI调整)指令调整ADD或ADC指令的二进制运算结果。设两个ASCI数字相加,其二进制结果存放在AL中,则AAA将AL转换为两个非压缩十进制数字存人AH和AL。一旦成为非压缩格式,通过将AH和AL与30h进OR运算,很容易就能把它们转换为 ASCII码。

下例展示了如何用AAA指令正确地实现ASCI数字8加2。在执行加法之前,必须把AH清零,否则它将影响AAA执行的结果。最后一条指令将AH和AL转换为ASCI数字:

mov ah, 0
mov al, '8'							;AX = 0038h
add al, '2'							;AX = 006Ah
aaa									;AX = 0100h(结果进行ASCII调整)
or ax, 3030h						;AX = 3130h ='10'(转换为ASCII码)

使用 AAA 实现多字节加法

现在来查看一个过程,其功能为实现包含了隐含小数点的ASCII十进制数值相加。由于每次数字相加的进位标志位都要传递到更高位,因此,过程的实现要比想象的更复杂一些。下面的伪代码中,acc代表的是一个8位的累加寄存器:

esi (index)=length of first number - 1
edi (index)=length of first number
ecx =lengthoffirst number
set carry value to 0
Loopacc = first number[esi]add previous carry to accsave carry in carrylacc += second_number[esi]OR the carry with carry1sum[edi] = accdec edi
Until ecx == 0
Store last carry digit in sum

进位值必须总是被转换为ASCI码。将进位值与第一个操作数相加时,就需要用AAA来调整结果。程序清单如下:

;ASCII_add.asm   ASCII加法
;对有隐含固定小数点的串执行ASCII运算。INCLUDE Irvine32.incDECIMAL_OFFSET = 5								;距离串右侧的偏移量
.data
decimal_one BYTE '100123456789765'		        ;1001234567.89765
decimal_two BYTE '900402076502015'		        ;9004020765.02015
sum BYTE (SIZEOF decimal_one + 1) DUP(0), 0.code
main PROC;从最后一个数字位开始mov esi, SIZEOF decimal_one - 1mov edi, SIZEOF decimal_onemov ecx, SIZEOF decimal_onemov bh, 0										;进位值清零
L1:	mov ah, 0											;执行加法前清除AHmov al, decimal_one[esi]						;取第一个数字add al, bh										;加上之前的进位值aaa												;调整和数AH=进位值mov bh, ah										;将进位保存到carry1or bh, 30h										;将其转换为ASCII码add al, decimal_two[esi]						;加第二个数字aaa												;调整和数AH=进位值or bh, ah										;进位值与 carry1进行 OR运算or bh, 30h										;将其转换为ASCII 码or al, 30h										;将AL转换为ASCII码mov sum[edi], al								;将AL保存到sumdec esi											;后退一个数字dec edi												loop L1mov sum[edi], bh								;保存最后的进位值;显示和数字符串。mov edx, OFFSET sumcall WriteStringcall CrlfINVOKE ExitProcess,0
main ENDP
END main

程序输出如下所示,和数没有显示十进制小数点:

7.5.2 AAS 指令

32位模式下,AAS(减法后的ASCII调整)指令紧随SUB或SBB指令之后,这两条指令执行两个非压缩十进制数的减法,并将结果保存到AL中。AAS指令将AL转换为ASCII码的数字形式。只有减法结果为负时,调整才是必需的。比如,下面的语句实现ASCI码数字8减去9:

;7.5.2.asm  7.5.2 AAS 指令
;下面的语句实现ASCI码数字8减去9:.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
val1 BYTE '8'
val2 BYTE '9'.code
main PROCmov ah, 0mov al, val1					;AX = 0038hsub  al, val2					;AX = 00FFhaas								;AX = 0FF09hpushf							;保存进位标志位or al, 30h						;AX = 0FF39hpopf							;恢复进位标志位INVOKE ExitProcess,0
main ENDP
END main

执行SUB指令后,AX等于00FFh。AAS指令将AL转换为09h,AH减1等于FFh并且把进位标志位置1。

7.5.3 AAM 指令

32位模式下,MUL执行非压缩十进制乘法,AAM(乘法后的ASCII调整)指令转换由其产生的二进制乘积。乘法只能使用非压缩十进制数。下面的例子实现5乘以6,并调整AX中的结果。调整后,AX=0300h,非压缩十进制表示为30:

;7.5.3.asm  7.5.3 AAM指令
;下面的例子实现5乘以6,并调整AX中的结果。
;调整后,AX=0300h,非压缩十进制表示为30:.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
ascVal BYTE 05h, 06h.code
main PROCmov bl, ascVal						;第1个操作数mov al, [ascVal+1]					;第2个操作数mul bl								;AX=001Eh aam									;AX=0300h INVOKE ExitProcess,0
main ENDP
END main

7.5.4 AAD 指令

32位模式下,AAD(除法之前的ASCII调整)指令将AX中的非压缩十进制被除数转换为二进制,为执行DIV指令做准备。下面的例子把非压缩0307h转换为二进制数,然后除以5。DIV指令在AL中生成商07h,在AH中生成余数02h:

;7.5.4.asm  7.5.4 AAD指令
;下面的例子把非压缩0307h转换为二进制数,然后除以5。
;DIV指令在AL中生成商07h,在AH中生成余数02h:.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
quotient BYTE ?
remainder BYTE ?.code
main PROCmov ax, 0307h					;被除数aad								;AX = 0025hmov bl, 5						;除数div bl							;AX=0207hmov quotient, almov remainder, ahINVOKE ExitProcess,0
main ENDP
END main

7.5.5 本节回顾

1.编写一条指令,将 AX中的一个两位非压缩十进制整数转换为十进制的 ASCII码。

答:or ax, 3030h

2.编写一条指令,将 AX中的一个两位 ASCII码十进制整数转换为非压缩十进制形式

答:and ax, 0F0Fh

3.编写有两条指令的序列,将 AX中的一个两位 ASCII 码十进制整数转换为二进制。

答:and ax, 0F0Fh ;转换为非压缩形式

aad

4.编写一条指令,将 AX中的一个无符号二进制整数转换为非压缩十进制数。

答:aam

7.6 压缩十进制运算

(7.6节讨论的指令仅用于32位编程模式。)压缩十进制数的每个字节存放两个十进制数字,每个数字用4位表示。如果数字个数为奇数,则最高的半字节用零填充。存储大小可变:

bcd1 QWORD 2345673928737285h		;十进制数 2345673928737285
bcd2 DWORD 12345678h				;十进制数12345678
bcd3 DWORD 08723654h				;十进制数8723654
bcd4 WORD 9345h						;十进制数9345
bcd5 WORD 0237h						;十进制数237
bcd6 BYTE 34h						;十进制数34

压缩十进制存储至少有两个优势:

●数据几乎可以包含任何个数的有效数字。这使得以很高的精度执行计算成为可能

●实现压缩十进制数与 ASCII码之间的相互转换相对简单。

DAA(加法后的十进制调整)和DAS(减法后的十进制调整)这两条指令调整压缩十进制数加减法的结果。可惜的是,目前还没有与乘除法有关的相似指令。在这些情况下,相乘或相除的数必须是非压缩的,执行后再压缩。

7.6.1 DAA 指令

32位模式下,ADD或ADC指令在AL中生成二进制和数,DAA(加法后的十进制调整)指令将和数转换为压缩十进制格式。比如,下述指令执行压缩十进制数35加48。二进制和数(7Dh)被调整为83h,即35和48的压缩进制和数。

mov al, 35h			
add al, 48h			;AL=7Dh
daa					;AL=83h(调整后的结果)

DAA的内部逻辑请参阅Intel指令集参考手册。示例 下面的程序执行两个16位压缩十进制整数加法,并将和数保存在一个压缩双字中。加法要求和数变量的存储大小比操作数多一个数字:

;AddPacked.asm   7.6.1  DAA指令   压缩十进制示例
;下面的程序执行两个16位压缩十进制整数加法,并将和数保存在一个压缩双字中。
;加法要求和数变量的存储大小比操作数多一个数字:INCLUDE Irvine32.inc.data
packed_1 WORD 4536h
packed_2 WORD 7207h
sum DWORD ?.code
main PROC;初始化和数与索引:mov sum, 0mov esi, 0;低字节相加。mov al, BYTE PTR packed_1[esi]add  al, BYTE PTR packed_2[esi]daamov BYTE PTR sum[esi], al;高字节相加,包括进位标志位。inc esimov al, BYTE PTR packed_1[esi]adc  al, BYTE PTR packed_2[esi]daamov BYTE PTR sum[esi], al;若还有进位,则加上该进位值。inc esimov al, 0adc  al, 0mov BYTE PTR sum[esi], al;用十六进制显示和数,mov eax, sumcall WriteHexcall Crlfexit;INVOKE ExitProcess,0
main ENDP
END main

显然,这个程序包含重复代码,因此建议使用循环结构。本章的一道习题将会要求编写一个过程,实现任意大小的压缩十进制整数加法。

7.6.2 DAS指令

32位模式下,SUB或SBB指令在AL中生成二进制结果,DAS(减法后的十进制调整)指令将其转换为压缩十进制格式。比如,下面的语句计算压缩十进制数85减48,并调整结果:

mov bl, 48h							
mov al, 85h							
sub al, bl						;AL = 3Dh
das								;AL = 37h (调整后)

DAS的内部逻辑请参阅Intel指令集参考手册.

7.6.3 本节回顾

1.举例说明,什么情况下DAA指令会把进位标志位置1?

答:当压缩十进制加法的和数大于99时,DAA将进位标志位置1,例如:

;7.6.3_1.asm  7.6.3   本节回顾
;1.举例说明,什么情况下DAA指令会把进位标志位置1?.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.code
main PROCmov al, 56hadd  al, 92h				;AL = E8hdaa							;AL = 48h, CF = 1INVOKE ExitProcess,0
main ENDP
END main

2.举例说明,什么情况下DAS指令会把进位标志位置1?

答:若从小的压缩十进制整数中减去大的压缩十进制整数,则DAS将进位标志位置1.例如:

;7.6.3_2.asm  7.6.3   本节回顾
;2.举例说明,什么情况下DAS指令会把进位标志位置1?.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.code
main PROCmov al, 56hsub  al, 92h				;AL = C4hdas							;AL = 64h, CF = 1INVOKE ExitProcess,0
main ENDP
END main

3.两个长度为"字节的压缩十进制整数相加时,和数应该保留多少字节?

答:和数应该保留n+1个字节。

7.7 本章小结

与前面章节介绍的位元指令一样,移位指令也是汇编语言最显著的特点之一。一个数移位就意味着把它的位元进行右移或左移。

SHL(左移)指令把目标操作数的每一位都向左移动,最低位用0填充。SHL最大的作用之一是快速实现与2的幂相乘。任何操作数左移位即为乘以2"。SHR(右移)指令则把每一位都向右移动,最高位用0填充。任何操作数右移位即为除以2"。

SAL(算术左移)和SAR(算术右移)是特别为有符号数移位设计的指令。

ROL(循环左移)指令把每一位向左移动,并将最高位复制到进位标志位和最低位。ROR(循环右移)指令把每一位向右移动,并将最低位复制到进位标志位和最高位。

RCL(带进位循环左移)指令把每一位都左移,并先将进位标志位复制到移位结果的最低位,再将最高位复制到进位标志位。RCR(带进位循环右移)指令把每一位都右移,并将最低位复制到进位标志位,而进位标志位则复制到结果的最高位。

x86处理器可使用的SHLD(双精度左移)和SHRD(双精度右移)指令对大数的移位非常有用。

32位模式下,MUL指令实现一个8位、16位或32位的操作数与AL、AX或EAX相乘64位模式下,一个数还可以实现与RAX寄存器相乘。IMUL指令执行有符号数乘法,它有三种格式:单操作数、双操作数和三操作数。

32位模式下,DIV指令实现8位、16位或32位操作数的除法。64位模式下,还可以实现 64位除法。IDIV指令执行有符号数乘法,其格式与DIV指令相同。

CBW(字节转字)指令把AL的符号位扩展到AH寄存器。CDO(双字转四字)指令把EAX的符号位扩展到EDX寄存器。CWD(字转双字)指令把AX的符号位扩展到DX寄存器。

扩展加减法是指加减任意大小的数,ADC和SBB指令可以用于实现这种加减运算ADC(带进位加法)指令实现源操作数与进位标志位的内容和目的操作数相加。SBB(带借位减法)指令实现目的操作数减去源操作数和进位标志位的值。

ASCII十进制数每个字节存放一个数字,并编码为ASCI形式。AAA(加法后的ASCII调整)指令将ADD或ADC指令的二进制结果转换为ASCII十进制。AAS(减法后的ASCII调整)指令将SUB或SBB指令的二进制结果转换为ASCII十进制。所有这些指令都只能用于32位模式。

非压缩十进制数每个字节存放一个十进制数字,表现为二进制数值。AAM(乘法后的ASCII 调整)指令转换的是MUL指令执行非压缩十进制数乘法所生成的二进制结果。AAD(除法前的 ASCI 调整)指令在执行 DIV指令之前,将非压缩十进制被除数转换为二进制。所有这些指令都只能用于32位模式。

压缩十进制数每个字节存放两个十进制数字。DAA(加法后的十进制调整)指令转换的是 ADD或 ADC指令执行压缩十进制加法所生成的二进制结果。DAS(减法后的十进制调整)指令转换的是SUB或SBB指令执行压缩十进制减法所生成的二进制结果。所有这些指令都只能用于 32 位模式,

7.8 关键术语

7.8.1 术语

7.8.2 指令、运算符和伪指令


http://www.dtcms.com/a/276710.html

相关文章:

  • Noting
  • L1正则化 VS L2正则化
  • 全连接网络 和卷积神经网络
  • 《Java Web程序设计》实验报告一 Java Web环境配置
  • Cypress与多语言后端集成指南
  • C++——类和对象的相关知识点
  • 复习笔记 31
  • RHCSA(2)
  • STM32--USART串口通信的应用(第一节串口通信的概念)
  • docker网络与数据持久化
  • SolidWorks并发不足频出,浮动许可还能怎么优化?
  • Python 中 enumerate(s) 和 range() 的对比
  • 博途多重背景、参数实例--(二)
  • 分布式系统高可用性设计 - 缓存策略与数据同步机制
  • 飞算JavaAI:重新定义Java开发效率的智能引擎
  • Cell2location maps fine-grained cell types in spatial transcriptomics 文章解析
  • 基于Python Keras 实践大全
  • STP生成树协议
  • 堆内存的详细结构以及java中内存溢出和排查方式
  • Jinja2模板引擎技术在dify中的应用方法
  • Python基于Django的WEB服务统一身份认证协议的设计与实现【附源码、文档说明】
  • 3 c++提高——STL常用容器(一)
  • 【理念●体系】路径治理篇:打造可控、可迁移、可复现的 AI 开发路径结构
  • 【一起来学AI大模型】RAG系统组件:检索器(LangChain)
  • 深度学习-LeNet5-AlexNet
  • ZeroNews 版本升级预告!
  • 【PMP备考】敏捷专题 - 敏捷概述
  • CPU 与存储器连接方式的深入理解
  • Java使用Langchai4j接入AI大模型的简单使用(三)--输入文字生成图片
  • C++结构体数组应用