是的,在 RAX 中得到相同的整数结果。
换句话说,后者是否以完全相同的方式影响 EFLAGS?
当然不是。 ZF、SF 和 PF 仅取决于整数结果,但 CF 和 OF1 取决于您如何到达那里。 x86 的 CF 进位标志是减法的借位输出。 (与 ARM 等一些 ISA 不同,如果没有借位,减法会设置进位标志。)
您可以在脑海中检查的琐碎反例:
0 - 1 与 sub 设置 CF=1。但你的方式清除了 CF。
mov temp, rcx # no effect on FLAGS
not temp # no effect on FLAGS, unlike most other x86 ALU instructions
add rax, ~1 = 0xFF..FE # 0 + anything clears CF
add rax, 1 # 0xFE + 1 = 0xFF..FF = -1. clears CF
(有趣的事实:not 不会影响 FLAGS,与大多数其他 ALU 指令(包括 neg)不同。neg 设置的标志与来自 0 的 sub 相同。x86 历史的一个奇怪怪癖。@ 987654321@)
脚注 1:AF 也是如此,它是低字节中从低半字节到高半字节的半进位标志(辅助)。您不能直接对其进行分支,x86-64 删除了读取它的 BCD 指令,例如 aaa,但它仍然存在于 RFLAGS 中,您可以使用 pushf / pop rax 读取它。
如果不是,怎么能强迫呢?
使用不同的说明。在 EFLAGS 上获得所需效果的最简单和最有效的方法是将其优化回sub rax, rcx。这就是为什么 x86 有 sub 和 sbb 指令的原因。如果这是您想要的,请使用它。
如果你想要一个替代方案,你肯定需要避免像add rax,1 这样的东西作为最后一步。仅当最终结果为零时才会设置 CF,从 ULONG_MAX = -1 开始。
在大多数情况下,将 x -= y 用作 x += -y 对 OF 有效。 (但不是最负数 y=LONG_MIN (1UL<<63),neg rcx 会溢出)。
但是 CF 告诉你 64 + 64 位加法或减法的 65 位完整结果。 64 位否定是不够的:x += -y 并不总是将 CF 设置为与 x -= y 的相反。
可能涉及neg / sbb 的东西可能有用吗?但是不,这将否定的执行视为 -0 / -1,而不是 -(1<<64)。
# Broken attempt that fails for CF when rcx=0 at least, probably many more cases.
# Also fails for OF for rcx=0x8000000000000000 = LONG_MIN
mov temp, rcx # no effect on FLAGS
neg temp # or NOT + INC if you insist on avoiding sub-like operations
add rax, temp # x += -y
cmc # complement carry. CF = !CF
请注意,我们在一个步骤中组合了 x 和 y。您最后的add rax, 1 会在较早的 CF 结果上进行操作,从而使 CF 成为您想要的结果的可能性/可能性更低。
有符号溢出 (OF) 有一个极端情况。大多数输入都是相同的,其中x -= y 或x += -y 的带符号算术运算相同。但是如果-y 溢出仍然是负数(the most-negative 2's complement number 没有反转),它是加一个负数而不是减去一个负数。
例如-LONG_MIN == LONG_MIN 因为签名溢出。 (C 表示法;有符号溢出是 ISO C 中的 UB,但在 asm 中它会换行)。
CF 尝试的反例:
-1 - 0 不借,所以 CF=0。
-1 + -0 = -1 + 0 也不进位,然后CMC会将CF翻转为1
但-1 (0xff...ff) 加上任何其他数字会执行,而-1 减去任何数字不会。
所以准确模拟sub的借用输出并不容易,而且可能不是很有趣。
请注意,硬件 ALU 经常使用类似于二进制 Adder–subtractor 的东西,它将 A 或 ~A 复用为全加器的输入以进位/借位感知方式来实现 @987654369 @ 或 A - B 带有正确的借位输出以进行减法。
应该可以在 asm 中使用 stc / adc dst, inverted_src 来复制类似硬件的实际作用:添加逆进位为 1。不单独加1。
(TODO:重写此答案的更多内容以显示使用 not / stc / adc 而不是可能需要将进位一直传播到数字的多个操作)。
相关: