【问题标题】:When are the carry flags set by x86 negation (NEG) / subtraction (SUB)?x86 否定 (NEG) / 减法 (SUB) 何时设置进位标志?
【发布时间】:2013-07-20 12:04:51
【问题描述】:

“将 NEG 指令应用于非零值”是什么意思 操作数总是设置进位标志。”

为什么1减2会设置进位标志?

           00000001 (1)
 +         11111110 (-2) [in 2-complement form]
 ---------------------
CF:1       11111111 (-1) [ why is the carry flag set here???]

【问题讨论】:

  • 因为规范说确实如此。 (您所做的是从零中减去 1。这会导致“借”到高位。借和进位是相同的,只是不同。)
  • 当运算结果太大而无法放入您正在使用的寄存器时,会设置进位标志。
  • 我不明白为什么在这种情况下它太大了。
  • "你正在做的是从零中减去 1。"我将 1 加到零以得到八个 1 ;)
  • 对于减法,进位标志充当借位标志。由于1 - 2 为负数,因此会导致借位 - 因此设置了进位标志。

标签: assembly x86 carryflag eflags


【解决方案1】:

在阅读 CF 标志是如何定义的那一刻之前还不清楚。英特尔 CPU 文档手册说:

If the result of an arithmetic operation is treated as an unsigned integer, 
the CF flag indicates an out-of-range condition

否定任何被视为无符号的东西显然是无效的,因为没有无符号值可以翻转其符号并且仍然保持无符号(除非它为零)。 因此,尽管使用铅笔和纸的方法,您会得到完全相反的结果:否定零时的“铅笔进位 = 1”和否定任何其他位模式时的“铅笔进位 = 0”。

可能这就是为什么我们在标志寄存器中有“carry FLAG”而不是“carry BIT”。 再次 - 进位标志表明操作数(视为无符号)的操作结果超出特定位数的允许“无符号范围”。

如果您否定的值被视为已签名 - 您应该查看溢出标志。

这里的重点是:'carry FLAG' 不是 'carry BIT'。它有时像一个人,但并非总是如此。

顺便说一句:并不是只有 x86/64 可以做到这一点。例如在 Atmel 的 AVR 中也是如此。

ARM 可能会这样做,因为他们的手册说:

For a subtraction, including the comparison instruction CMP and the negate 
instructions NEGS and NGCS, C is set to 0 if the subtraction produced a 
borrow (that is, an unsigned underflow), and to 1 otherwise.

一般来说,ARM 的文档调用进位标志 - 进位/借位标志,因此不应将其视为进位 BIT

【讨论】:

  • 在 x86 上,CF 是 CMP/SUB 的借用输出。在 ARM 上,它是反转的:它设置为无借位,在从高位传播出借位时清除。所以 ARM 绝对与 x86 设置减法进位标志的方式相同。
【解决方案2】:
  • 首先,我们知道a < ba - b 什么时候总能产生carry(Borrow)

  • 其次,让我们了解一下为什么 x86 在减法时会反转进位标志。
    第一点对于人类是如此明显,但对于计算机而言并非如此。 (假设计算机用a + (~b+1) 代替a - b 进行计算。) 计算机如何算出有借?我们可以取2的补码作为时钟。(顺时针表示原数--a,逆时针表示倒数--~b+1

    所以对于计算机来说,它可以告诉(减法)a > ba + ~b + 1 是否有进位(图中重叠);如果a < ba + ~b + 1没有有进位(图片中的空白)。

  • 结论:
    因此对于减法,如果没有进位,则表示有借位; 如果有进位表示没有借位,即取反进位位。

顺便说一句,据我尝试,没有溢出(即,1 - 2 时未设置OF。)我不认为@Oliver 是正确的这一点。

【讨论】:

  • 请注意,x86 sub 标志设置完全等同于 add-src。例如,将sub a,b 模拟为neg b / add a,b / cmc 不适用于b=0sub a,0 总是清除 CF,但 add a,0 也是如此,因此反转 CF 结果是错误的。 (即 ~0 + 1 回绕到 0 是特殊情况,您必须处理该结转。)请参阅 Arithmetic identities and EFLAGS
【解决方案3】:

当结果不能以无符号二进制数表示时,在减法中设置进位标志。无符号数始终被视为正数。

1 - 2 给出的结果将是 -1-1 不能以无符号 8 位格式表示,因此设置了进位标志。

即使减法算法的结果适合 8 位,即使该结果可以解释为具有正确结果的 2 补码二进制数,也会发生这种情况。

NEG 指令旨在与将被解释为 2 补码数字的数字一起使用,因为该指令所做的正是对该数字进行 2 补码,即更改其符号。符号是 2 补数的属性,而不是无符号数的属性。

NEG n 与计算0-n 相同,这里唯一适合无符号8 位数的结果是00000000。任何其他结果都不是正确的无符号数。

【讨论】:

    【解决方案4】:

    您可以将NEG a 视为等同于SUB 0, a。如果a 不为零,那么这将设置进位标志(因为这将总是导致无符号溢出)。

    【讨论】:

    • 为什么有意义?
    • @user2453180:您熟悉进位标志的用途吗?您是否熟悉何时设置减法?
    • 我不明白为什么 '11111111'(8 个数字)不适合 8 位寄存器。
    • 是根据定义反转的进位标志。在 x86 上使用减法时? arm 和 mips 怎么样?
    • @user2453180:在您的 1-2 示例中,可以将其建模为 1+(-2) 的假设是有效的,只要您调整进位标志的计算方式即可。请记住,如果您进行了长减法(即 0001 - 0010),您将产生借位。所以从这个意义上说,是的,它是倒置的。
    猜你喜欢
    • 1970-01-01
    • 2017-12-03
    • 1970-01-01
    • 1970-01-01
    • 2016-11-05
    • 1970-01-01
    • 2016-08-03
    • 2011-03-09
    • 1970-01-01
    相关资源
    最近更新 更多