因为 −x = not(x)+1 意味着 ab = a+not(b)+1,所以
sub rax, rcx
相当于
mov temp, rcx
not temp
add rax, temp
add rax, 1
某些寄存器在哪里 temp 被认为是易失性的?
换句话说,后者是否以完全相同的方式影响 EFLAGS?如果不是,又怎么能强迫呢?
不,它们不相等。例如 ifrax = 1
和rcx = 3
, thensub rax, rcx
将设置进位标志,因为您正在从较小的数字中减去较大的数字。但是在您的第二个指令序列中,后面的add rax, temp
,rax
将包含-3
(即0xfffffffffffffffd
),并且添加1
到-3
不会导致进位。所以在你的第二个指令序列之后,进位标志将被清除。
我不知道有任何简单的方法可以准确地模拟sub
包含其对标志的影响的行为(除了使用cmp
,但这是作弊,因为它实际上只是sub
在幕后)。原则上,您可以编写一长串指令,手动执行与sub
内部执行的所有相同测试(请参阅指令集手册中的精确描述),并在最后使用sahf
or等设置标志popf
。
这将是很多工作,特别是如果您不打算使用cmp
,并且我不会为这个答案而经历它。尤其是因为我也想不出任何理由需要这样做,除了作为一项相当无意义的练习。
是的,这会在 RAX 中得到相同的整数结果。
换句话说,后者是否以完全相同的方式影响 EFLAGS?
当然不是。ZF、SF 和 PF 仅取决于整数结果,但CF 和 OF 1取决于您如何到达那里。x86 的 CF 进位标志是减法的借位输出。(与某些 ISA 不同,例如 ARM,如果没有借位,减法会设置进位标志。)
您可以在脑海中检查一个简单的反例:
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
. 设置标志与fromneg
相同。x86 历史的一个奇怪怪癖 。https: //www.felixcloutier.com/x86/not#flags-affected )sub
0
脚注 1:AF 也是如此,它是低字节中从低半字节到高半字节的半进位标志(辅助)。您不能直接对其进行分支,并且 x86-64 删除了aaa
读取它的 BCD 指令,但它仍然存在于 RFLAGS 中,您可以使用pushf
/pop rax
例如读取它。
如果不是,又怎么能强迫呢?
使用不同的指令。在 EFLAGS 上获得所需效果的最简单和最有效的方法是将其优化回sub rax, rcx
. 这就是x86有sub
和sbb
指令的原因。如果那是您想要的,请使用它。
如果你想要一个替代方案,你肯定需要避免像add rax,1
最后一步这样的事情。仅当最终结果为零时才会设置 CF,从 ULONG_MAX = -1 开始。
在大多数情况下,为 OFx -= y
工作。x += -y
(但不是最负数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
or是相同的x += -y
。但是如果-y
溢出仍然是负数(最负的 2 的补数没有逆数),它是加一个负数而不是减去一个负数。
例如-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 经常使用二进制加法器-减法器之类的东西,它以进位/借位感知方式进行多路复用A
或~A
作为全加器的输入来实现或使用正确的借位输出进行减法。A + B
A - B
应该可以在 asm 中使用stc
/adc dst, inverted_src
来复制类似硬件的实际功能:将逆运算与进位相加 1。不单独加1。
(TODO :重写这个答案的更多内容以显示使用not
//而不是可能需要通过数字传播进位的多个操作)。stc
adc
有关的: