mysql 剑法_汇编语言加法和减法详解

本文详细介绍了汇编语言中的加法、减法、取反指令,如INC、DEC、ADD、SUB和NEG,以及它们如何影响CPU状态标志位。通过示例代码展示了这些指令的使用,并探讨了如何利用这些标志位来检查算术运算结果,包括零标志位、符号标志位、溢出标志位等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

算术运算是汇编语言中一个大得令人惊讶的主题!本节重点在于加法和减法的运算。

先从最简单、最有效的指令开始:INC(增加)和 DEC(减少)指令,即加 1 和减 1。然后是能提供更多操作的 ADD、SUB 和 NEG(非)指令。最后,将讨论算术运算指令如何影响 CPU 状态标志位(进位位、符号位、零标志位等)。请记住,汇编语言的细节很重要。

INC 和 DEC 指令

INC(增加)和DEC(减少)指令分别表示寄存器或内存操作数加 1 和减 1。语法如下所示:

INC reg/mem

DEC reg/mem

下面是一些例子:

.data

myWord WORD 1000h

.code

inc myWord ; myWord = 1001h

mov bx,myWord

dec bx ; BX = 1000h

根据目标操作数的值,溢岀标志位、符号标志位、零标志位、辅助进位标志位、进位标志位和奇偶标志位会发生变化。INC 和 DEC 指令不会影响进位标志位(这还真让人吃惊)。

ADD 指令

ADD 指令将长度相同的源操作数和目的操作数进行相加操作。语法如下:

ADD dest,source

在操作中,源操作数不能改变,相加之和存放在目的操作数中。该指令可以使用的操作数与 MOV 指令相同。下面是两个 32 位整数相加的短代码示例:

.data

var1 DWORD 10000h

var2 DWORD 20000h

.code

mov eax,var1 ; EAX = 10000h

add eax,var2 ; EAX = 30000h

标志位:进位标志位、零标志位、符号标志位、溢出标志位、辅助进位标志位和奇偶标 志位根据存入目标操作数的数值进行变化。

SUB 指令

SUB 指令从目的操作数中减去源操作数。该指令对操作数的要求与 ADD 和 MOV 指令相同。指令语法如下:

SUB dest, source

下面是两个 32 位整数相减的短代码示例:

.data

var1 DWORD 30000h

var2 DWORD 10000h

.code

mov eax,var1 ;EAX = 30000h

sub eax,var2 ;EAX = 20000h

标志位:进位标志位、零标志位、符号标志位、溢出标志位、辅助进位标志位和奇偶标 志位根据存入目标操作数的数值进行变化。

NEG 指令

NEG(非)指令通过把操作数转换为其二进制补码,将操作数的符号取反。下述操作数可以用于该指令:

NEG reg

NEG mem

提示:将目标操作数按位取反再加 1,就可以得到这个数的二进制补码。

标志位:进位标志位、零标志位、符号标志位、溢出标志位、辅助进位标志位和奇偶标志位根据存入目标操作数的数值进行变化。

执行算术表达式

使用 ADD、SUB 和 NEG 指令,就有办法来执行汇编语言中的算术表达式,包括加法、减法和取反。换句话说,当有下述表达式时,就可以模拟 C++ 编译器的行为:

Rval = -Xval + (Yval - Zval);

现在来看看,使用如下有符号 32 位变量,汇编语言是如何执行上述表达式的。

Rval SDWORD ?

Xval SDWORD 26

Yval SDWORD 30

Zval SDWORD 40

转换表达式时,先计算每个项,最后再将所有项结合起来。首先,对 Xval 的副本进行取反,并存入寄存器:

; first term: -Xval

mov eax,Xval

neg eax ; EAX = -26

然后,将 Yval 复制到寄存器中,再减去 Zval:

; second term: (Yval - Zval)

mov ebx,Yval

sub ebx,Zval ; EBX = -10

最后,将两个项(EAX 和 EBX 的内容)相加:

; add the terms and store:

add eax,ebx

mov Rval,eax ; -36

加减法影响的标志位

执行算术运算指令时,常常想要了解结果。它是负数、正数还是零?对目的操作数来说,它是太大,还是太小?这些问题的答案有助于发现计算错误,否则可能会导致程序的错误行为。

检查算术运算结果使用的是 CPU 状态标志位的值,同时,这些值还可以触发条件分支指令,即基本的程序逻辑工具。下面是对状态标志位的简要概述:

进位标志位意味着无符号整数溢出。比如,如果指令目的操作数为 8 位,而指令产生的结果大于二进制的 1111 1111,那么进位标志位置 1。

溢出标志位意味着有符号整数溢出。比如,指令目的操作数为 16 位,但其产生的负数结果小于十进制的 -32 768,那么溢出标志位置 1。

零标志位意味着操作结果为 0。比如,如果两个值相等的操作数相减,则零标志位置 1。

符号标志位意味着操作产生的结果为负数。如果目的操作数的最高有效位(MSE)置 1,则符号标志位置 1。

奇偶标志位是指,在一条算术或布尔运算指令执行后,立即判断目的操作数最低有效字节中 1 的个数是否为偶数。

辅助进位标志位置 1,意味着目的操作数最低有效字节中位 3 有进位。

要在调试时显示 CPU 状态标志位,打开 Register 窗口,右键点击该窗口,并选择 Flags。

1) 无符号数运算:零标志位、进位标志位和辅助进位标志位

当算术运算结果等于 0 时,零标志位置 1。下面的例子展示了执行 SUB、INC 和 DEC 指令后,目的寄存器和零标志位的状态:

mov ecx,1

sub ecx,1 ;ECX = 0, ZF = 1

mov eax,0FFFFFFFFh

inc eax ;EAX = 0, ZF = 1

inc eax ;EAX = 1, ZF = 0

dec eax ;EAX = 0, ZF = 1

加法和进位标志位,如果将加法和减法分开考虑,那么进位标志位的操作是最容易解释的。两个无符号整数相加时,进位标志位是目的操作数最高有效位进位的副本。直观地说,如果和数超过了目的操作数的存储大小,就可以认为 CF = 1。在下面的例子里,ADD 指令将进位标志位置 1,原因是,相加的和数(100h)超过了 AL 的大小:

mov al,0FFh

add al,1 ; AL = 00, CF = 1

下图演示了在 0FFh 上加 1 时,操作数的位是如何变化的。AL 最高有效位的进位复制到进位 标志位。

e2eb073b5b477b9780d431c049e58982.gif

另一方面,如果 AX 的值为 00FFh,则对其进行加 1 操作后,和数不会超过 16 位,那么进位标志位清 0:

mov ax,00FFh

add ax, 1 ; AX = 0100h, CF = 0

但是,如果 AX 的值为 FFFFh,则对其进行加 1 操作后,AX 的高位就会产生进位:

mov ax,0FFFFh

add ax, 1 ; AX = 0000, CF = 1

减法和进位标志位,从较小的无符号整数中减去较大的无符号整数时,减法操作就会将进位标志位置 1。下图说明了,操作数为 8 位时,计算(1-2)会出现什么情况。下面是相应的汇编代码:

mov al, 1

sub al,2 ; AL = FFh, CF = 1

77862d3cb4d4cf3bb56f005e84d7ff81.gif

提示:INC 和 DEC 指令不会影响进位标志位。在非零操作数上应用 NEG 指令总是会将进位标志位置 1。

辅助进位标志位,辅助进位(AC)标志位意味着目的操作数位 3 有进位或借位。它主要用于二进制编码的十进制数(BCD)运算,也可以用于其他环境。现在,假设计算(1+0Fh),和数在位 4 上为 1,这是位 3 的进位:

mov al,0Fh

add al, 1 ; AC = 1

计算过程如下:

00001111

+  00000001

--------------

00010000

奇偶标志位,目的操作数最低有效字节中 1 的个数为偶数时,奇偶(PF)标志位置 1。下例中,ADD 和 SUB 指令修改了 AL 的奇偶性:

mov al,10001100b

add al,00000010b ; AL = 10001110, PF = 1

sub al,10000000b ; AL = 00001110, PF = 0

执行了 ADD 指令后,AL 的值为 1000 1110 (4 个 0, 4 个 1), PF=1。执行了 SUB 指令后,AL 的值包含了奇数个 1,因此奇偶标志位等于 0。

2) 有符号数运算:符号标志位和溢出标志位

符号标志位,有符号数算术操作结果为负数,则符号标志位置 1。下面的例子展示的是小数(4)减去大数(5):

mov eax, 4

sub eax,5 ; EAX = -1, SP = 1

从机器的角度来看,符号标志位是目的操作数高位的副本。下面的例子表示产生了负数结果后,BL 中的十六进制的值:

mov bl,1 ; BL = 01h

sub bl,2 ; BL = FFh(-1), SF = 1

溢出标志位,有符号数算术操作结果与目的操作数相比,如果发生上溢或下溢,则溢出标志位置 1。例如,8 位有符号整数的最大值为 +127,再加 1 就会溢出:

mov al,+127

add al, 1 ; OF = 1

同样,最小的负数为-128,再减1就发生下溢。如果目的操作数不能容纳一个有效算 术运算结果,那么溢出标志位置 1:

mov al,-128

sub al,1 ;OF = 1

加法测试,两数相加时,有个很简单的方法可以判断是否发生溢出。溢出发生的情况有:

两个正数相加,结果为负数

两个负数相加,结果为正数

如果两个加数的符号相反,则不会发生溢出。

硬件如何检测溢出,加法或减法操作后,CPU 用一种有趣的机制来检测溢出标志位的状态。计算结果的最高有效位产生的进位与结果的最高位进行 异或操作,异或的结果存入溢岀标志位。如下图所示,两个 8 位二进制数 1000 0000 和 1111 1110 相加,产生进位 CF=1,和数最高位(位 7)= 0,即 1 XOR 0=1,则 OF=1。

0ef45f01dbac068ba9bfd3d991afa866.gif

NEG 指令,如果 NEG 指令的目的操作数不能正确存储,则该结果是无效的。例如, AL 中存放的是 -128,对其求反,正确的结果为 +128,但是这个值无法存入 AL。则溢出标志位置 1 就表示 AL 中存放的是一个无效的结果:

mov al,-128 ;AL = 10000000b

neg al ;AL = 10000000b, OF = 1

反之,如果对 +127 求反,结果是有效的,则溢出标志位清 0:

mov al,+127 ;AL = 01111111b

neg al ;AL = 10000001b, OF = 0

CPU 如何知道一个算术运算是有符号的还是无符号的?答案看上去似乎有点愚蠢:它不知道!在算术运算之后,不论标志位是否与之相关,CPU 都会根据一组布尔规则来设置所有的状态标志位。程序员要根据执行操作的类型,来决定哪些标志位需要分析,哪些可以忽略。

示例程序(AddSubTest)

AddSubTest 程序利用 ADD、SUB、INC、DEC 和 NEG 指令执行各种算术运算表达式,并展示了相关状态标志位是如何受到影响的:

;加法和减法 (AddSubTest.asm)

.386

.model flat,stdcall

.stack 4096

ExitProcess proto,dwExitCode:dword

.data

Rval SDWORD ?

Xval SDWORD 26

Yval SDWORD 30

Zval SDWORD 40

.code

main PROC

;INC和DEC

mov ax,1000h

inc ax ;1001h

dec ax ;1000h

;表达式:Rval=-Xval+(Yval-Zval)

mov eax,Xval

neg eax ;-26

mov ebx,Yval

sub ebx,Zval ;-10

add eax,ebx

mov Rval,eax;36

;零标志位示例

mov cx,1

sub cx,1 ;ZF = 1

mov ax,0FFFFh

inc ax ;ZF = 1

;符号标志位示例

mov cx,0

sub cx,1 ;SF = 1

mov ax,7FFFh

add ax,2 ;SF = 1

;进位标志位示例

mov al,0FFh

add al,1 ;CF = 1,AL = 00

;溢出标志位示例

mov al,+127

add al,1 ;OF = 1

mov al,-128

sub al,1 ;OF = 1

INVOKE ExitProcess,0

main ENDP

END main

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值