x86汇编:INC和DEC指令和溢出标志
在x86汇编中,当有符号整数上的add
或sub
操作溢出时,溢出标志置位;当无符号整数上的操作溢出时,置载标志置位。
但是,当涉及到inc
和dec
指令时,情况似乎有些不同。 根据该网站 , inc
指令根本不影响进位标志。
但是我找不到有关inc
和dec
如何影响溢出标志的任何信息,如果有的话。
发生整数溢出时,是否设置inc
或dec
设置溢出标志? 对于有符号整数和无符号整数,这种行为是否相同?
============================= 编辑 ==================== =========
好的,基本上这里的共识是,在设置标志方面,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
但是当我们使用无符号值执行相同操作时,行为并不像我预期的那样:
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
。
咦? 为什么在这种情况下没有设置溢出标志?
当操作导致符号更改时,将设置溢出标志。 你的代码非常接近。 我能够使用以下(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。
英特尔®64和IA-32架构软件开发人员手册
查看相应的手动指令集参考,AM 。 每条指令都是精确记录的。
以下是受影响标志的INC部分:
CF标志不受影响。 根据结果设置OF,SZ,ZF,AZ和PF标志。
尝试更改您的测试以传递数字而不是硬编码,然后有一个循环尝试所有256个数字来找到影响该标志的那个。 或者让asm执行循环并在它到达标志时退出,或者当它包围它开始的数字时(以0x00,0x7f,0x80或0xFF以外的其他内容开始)。
编辑
.globl inc INC: mov $ 33,%eax 最佳: inc%al 做完了 jmp顶部 完成: RET .globl dec 12月: mov $ 33,%eax topx: dec%al jo donex jmp topx donex: RET
Inc从0x7F变为0x80时溢出。 当它从0x80变为0x7F时,dec溢出,我怀疑问题是你使用内联汇编程序的方式。
正如许多其他答案所指出的那样, INC
和DEC
不会影响CF
,而ADD
和SUB
会影响CF
然而,尚未说过的是,这可能会带来性能差异。 并不是说你通常会为此烦恼,除非你试图优化地狱的例程,但基本上没有设置CF
意味着INC
/ DEC
只写入部分标志寄存器,这可能导致部分标志寄存器停止 ,请参阅Intel 64和IA-32架构优化参考手册或Agner Fog的优化手册 。
除了进位标志inc之外,设置标志的方式与添加操作数1的方式相同。
inc不影响进位标志的事实非常重要。
http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter_6/CH06-2.html#HEADING2-117
处理器所做的是为有符号和无符号情况设置这些指令(add,adc,dec,inc,sbb,sub)的结果的适当标志,即每个op的两个不同的标志结果。 替代方案是具有两组指令,其中一组设置与签名相关的标志,另一组设置与无符号相关的标志。 如果发布编译器在操作中使用无符号变量,它将测试进位和零(jc,jnc,jb,jbe等),如果签名则测试溢出,符号和零(jo,jno,jg,jng,jl,jle等) )。
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指令使用。 所以我们确实有二进制数0x80
和0xFF
。 它们都将符号位设置为1.结果是0x7F
,这是一个符号位设置为0的数字。我们得到2个具有相同符号的初始数字,并且结果具有不同的符号,因此我们指示溢出。 -128-1
导致127
,因此溢出标志被设置为表示错误的签名结果。
对于第二个问题,您使用255
作为第一个数字。 第二个数字隐含为1
,由INC指令使用。 所以我们确实有二进制数0xFF
和0x01
。 它们都有一个不同的符号位,因此不可能得到溢出(只有在基本上添加2个相同符号的数字时才可能溢出,但它永远不可能溢出2个不同符号的数字,因为它们永远不会超越可能的签名价值)。 结果是0x00
,并且它没有设置溢出标志,因为255+1
,或更确切地说, -1+1
给出0,这对于带符号算术显然是正确的。
请记住,对于要设置的溢出标志,要添加/减去的2个数字需要使符号位具有相同的值,然后结果必须具有值与其不同的符号位。