sbb edx, edx
您对该指令的分析是正确的。SBB
意思是“借位减法”。它以一种考虑进位标志 ( CF
) 的方式从目标中减去源。
因此,它等价于dst = dst - (src + CF)
,所以这是edx = edx - (edx + CF)
,或者简单地说edx = -CF
。
不要让它欺骗你源和目标操作数都在edx
这里!SBB same, same
在编译器生成的代码中隔离进位标志CF
(有其他方法可以做到这一点,即SETC
指令,这在大多数 x86 架构上可能更快(请参阅评论以获得更彻底的剖析),但数量不多。来自不同供应商(甚至可能是不同版本)的编译器倾向于偏爱其中一种,并且在您不进行特定于架构的调整时到处使用它。
neg edx
同样,您对该指令的分析是正确的。这是一个非常简单的。NEG
对其操作数执行二进制补码否定。因此,这只是edx = -edx
.
在这种情况下,我们知道edx
最初包含-CF
,这意味着它的初始值是0
或-1
(因为CF
始终是 0 或 1,开或关)。否定它意味着edx
现在包含0
or 1
。
也就是说,如果CF
最初设置,edx
现在将包含1
; 否则,它将包含0
. 这确实是上面讨论的成语的完成;您需要NEG
完全隔离 的值CF
。
test edx, edx
该TEST
指令与AND
指令相同,只是它不影响目标操作数——它只设置标志。
但这是另一个特例。TEST same, same
是优化代码中的一个标准习惯用法,用于有效地确定寄存器中的值是否为 0。您可以编写CMP edx, 0
,这是人类程序员天真地做的事情,但test
速度更快。(为什么会这样?因为按位与的真值表。唯一的情况value & value == 0
是何时value
为 0。)
所以这具有设置标志的效果。具体来说,ZF
如果为 0,则设置零标志 ( ) ,如果为非零,edx
则将其清除edx
。
因此,如果CF
原来是这样设置的,ZF
现在就清楚了;否则,它将被设置。也许更简单的看待它的方式是这三个指令设置ZF
为原始值的相反值CF
。
以下是两种可能的数据流:
CF
== 0 → edx
= 0 → edx = 0
→ZF = 1
CF
== 1 → edx
= -1 → edx = 1
→ZF = 0
jz 0x810f31c
最后,这是一个基于 值的条件跳转ZF
。如果ZF
已设置,则跳转到0x810f31c
; 否则,它会进入下一条指令。
然后,将所有内容放在一起,此代码CF
通过涉及零标志 ( ) 的间接路径测试进位标志 ( ) 的补码ZF
。如果进位标志最初被清除,它会分支,如果进位标志最初被设置,则它会失败。
这就是它的工作原理。也就是说,我无法解释为什么编译器选择以这种方式生成代码。它似乎在许多层面上都不是最理想的。最明显的是,编译器可以简单地发出一条JNC
指令(如果没有进位则跳转)。尽管 Peter Cordes 和我在评论中提出了各种其他观察和推测,但我认为将所有这些都纳入答案是没有意义的,除非可以提供有关此代码来源的更多信息。