x86汇编:INC和DEC指令和溢出标志

时间:2010-10-13 15:50:16

标签: c assembly x86 x86-64 inline-assembly

在x86汇编中,当有符号整数上的addsub运算溢出时,溢出标志置位;当无符号整数上的运算溢出时,置位标志置位。

但是,当谈到incdec指令时,情况似乎有所不同。根据此websiteinc指令根本不会影响进位标志。

但我找不到有关incdec如何影响溢出标志的任何信息,如果有的话。

发生整数溢出时,incdec是否设置溢出标志?对于有符号整数和无符号整数,这种行为是否相同?

============================= 编辑 ========== ===================

好的,基本上这里的共识是,就设置标志而言,INC和DEC应该与ADD和SUB的行为相同,但进位标志除外。这也是英特尔手册中的内容。

问题是,当涉及无符号整数时,我实际上无法在实践中重现这种行为。

考虑以下汇编代码(使用GCC内联汇编以便更容易打印结果。)

int8_t ovf = 0;

__asm__
(
    "movb $-128, %%bh;"
    "decb %%bh;"
    "seto %b0;"
    : "=g"(ovf)
    :
    : "%bh"
);

printf("Overflow flag: %d\n", ovf);

这里我们递减一个带符号的8位值-128。由于-128是可能的最小值,溢出是不可避免的。正如所料,这打印出来:Overflow flag: 1

但是当我们使用 unsigned 值执行相同操作时,行为并不像我预期的那样:

int8_t ovf = 0;

__asm__
(
    "movb $255, %%bh;"
    "incb %%bh;"
    "seto %b0;"
    : "=g"(ovf)
    :
    : "%bh"
);

printf("Overflow flag: %d\n", ovf);

这里我将无符号8位值255递增。由于255是最大可能值,因此溢出是不可避免的。但是,这打印出来:Overflow flag: 0

咦?为什么在这种情况下没有设置溢出标志?

7 个答案:

答案 0 :(得分:16)

当操作导致符号更改时,将设置溢出标志。你的代码非常接近。我能够使用以下(VC ++)代码设置OF标志:

char ovf = 0;

_asm {
    mov bh, 127
    inc bh
    seto ovf
}
cout << "ovf: " << int(ovf) << endl;

当BH增加时,MSB从0变为1,导致OF被设置。

这也设置了OF:

char ovf = 0;

_asm {
    mov bh, 128
    dec bh
    seto ovf
}
cout << "ovf: " << int(ovf) << endl;

请记住,处理器不区分有符号和无符号数字。使用2的补码算法时,可以使用一组指令来处理两者。如果要测试无符号溢出,则需要使用进位标志。由于INC / DEC不影响进位标志,因此在这种情况下需要使用ADD / SUB。

答案 1 :(得分:3)

Intel® 64 and IA-32 Architectures Software Developer's Manuals

查看相应的手册Instruction Set Reference, A-M。每条指令都有精确记录。

以下是受影响标志的INC部分:

  

CF标志不受影响。 OF,SZ,ZF,AZ和PF标志根据结果设置。

答案 2 :(得分:3)

尝试更改您的测试以传递数字而不是硬编码,然后有一个循环尝试所有256个数字来找到影响该标志的那个。或者让asm执行循环并在它到达标志时退出,或者当它包围它开始的数字时(从0x00,0x7f,0x80或0xFF以外的其他东西开始)。

修改

.globl inc
inc:
    mov $33, %eax

top:
    inc %al
    jo done
    jmp top

done:
    ret

.globl dec
dec:
    mov $33, %eax

topx:
    dec %al
    jo donex
    jmp topx

donex:
    ret

Inc从0x7F变为0x80时溢出。当从0x80到0x7F时,dec溢出,我怀疑问题是你在使用内联汇编程序的方式。

答案 3 :(得分:3)

正如许多其他答案所指出的那样,INCDEC不会影响CF,而ADDSUB会影响CF

然而,尚未说过的是,这可能会带来性能差异。并不是说你通常会为此烦恼,除非你试图从例行程序中优化地狱,但基本上没有设置INC意味着DEC / {{1}}只写入部分标志寄存器,可能导致部分标志寄存器停止,请参阅Intel 64 and IA-32 Architectures Optimization Reference ManualAgner Fog's optimisation manuals

答案 4 :(得分:2)

  

除了进位标志inc外,设置标志的方式与添加操作数1的方式相同。

     

inc不会影响进位标志的事实非常重要。

http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-2.html#HEADING2-117

答案 5 :(得分:0)

处理器所做的是为这些指令的结果设置适当的标志(add,adc,dec,inc,sbb,sub),对于有符号和无符号的情况,每个操作都有两个不同的标志结果。替代方案是具有两组指令,其中一组设置与签名相关的标志,另一组设置与无符号相关的标志。如果发布编译器在操作中使用无符号变量,它将测试进位和零(jc,jnc,jb,jbe等),如果签名则测试溢出,符号和零(jo,jno,jg,jng,jl,jle等) )。

答案 6 :(得分:0)

CPU / ALU只能处理无符号二进制数,然后使用OF,CF,AF,SF,ZF等,以便您决定是否将其用作有符号数(OF),无符号数(CF)或BCD数(AF)。


关于您的问题,请记住将二进制数字本身视为无符号。

**此外,溢出和OF需要3个数字:输入数字,算术中使用的第二个数字以及结果数字。

仅当第一个和第二个数字对于符号位(最高有效位)具有相同值且结果具有不同符号时,才会激活溢出。 同样如此,添加2个负数会产生正数,或者添加2个正数就会产生负数:

if( (Sign_Num1==Sign_Num2) && (Sign_Result!=Sign_Num1) ) OF=1;
else OF=0;

对于您的第一个问题,您使用-128作为第一个号码。第二个数字是隐式-1,由DEC指令使用。所以我们确实有二进制数0x800xFF。它们都将符号位设置为1.结果为0x7F,这是一个符号位设置为0的数字。我们得到2个具有相同符号的初始数字,并且结果具有不同的符号,所以我们指出溢出。 -128-1导致127,因此溢出标志设置为表示错误的签名结果。


对于您的第二个问题,您使用255作为第一个号码。第二个数字是隐式1,由INC指令使用。所以我们确实有二进制数0xFF0x01。它们都有一个不同的符号位,因此不可能得到溢出(只有在基本上添加2个相同符号的数字时才可能溢出,但它永远不可能溢出2个不同符号的数字,因为它们永远不会超越可能的签名价值)。 结果为0x00,并且它没有设置溢出标志,因为255+1或更确切地说,-1+1给出0,这对于带符号的算术来说显然是正确的。

请记住,要设置溢出标志,要添加/减去的2个数字需要使符号位具有相同的值,然后结果必须有一个符号位,其值与它们不同。