• 汇编语言加法和减法详解

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

    先从最简单、最有效的指令开始: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 最高有效位的进位复制到进位 标志位。

    在 0FFh 上加 1 时,操作数的位是如何变化的

更多...

加载中...