确定寄存器的值是否等于零的最简单方法是什么?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了确定寄存器的值是否等于零的最简单方法是什么?相关的知识,希望对你有一定的参考价值。
我正在使用与Irvine库的x86程序集。
检查寄存器值是否等于零的最简单方法是什么?
我使用cmp指令,但我正在寻找替代方法。这是我使用cmp指令的代码,寄存器是ebx
cmp ebx,0
je equ1
mov ebx,0
jmp cont
equ1:
mov ebx,1
jmp cont
cont:
exit
这个“booleanizes”一个值,产生0或1像int ebx = !!ebx
将在C中。
可能最“最简单”,或最简单,“不关心细节”的答案如何确定是:
; here ebx is some value, flags are set to anything
test ebx,ebx ; CF=0, ZF=0/1 according to ebx
jz whereToJumpWhenZero
; "non-zero ebx" will go here
; Or you can use the inverted "jnz" jump to take
; a branch when value was not zero instead of "jz".
有一个详细的answer from Peter Cordes to "testl
eax against eax?" question关于标志设置等的推理。还有一个链接到另一个类似的答案,但从性能的角度推断为什么它是最好的方式。 :)
当eax
为零时,如何将其他寄存器(我将选择ebx
)设置为1,当ebx
为非零时(ebx
本身的非破坏性方式)设置为0:
xor eax,eax ; eax = 0 (upper 24 bits needed to complete "al" later)
test ebx,ebx ; test ebx, if it is zero (ZF=0/1)
setz al ; al = 1/0 when ZF=1/0 (eax = 1/0 too)
或者当ebx
为零/非零时如何将ebx
本身转换为1/0:
neg ebx ; ZF=1/0 for zero/non-zero, CF=not(ZF)
sbb ebx,ebx ; ebx = 0/-1 for CF=0/1
inc ebx ; 1 when ebx was 0 at start, 0 otherwise
或者当ebx
为零/非零时,如何将ebx
本身转换为1/0,其他变体(“P6”更快到“Haswell”核心更快):
test ebx,ebx ; ZF=1/0 for zero/non-zero ebx
setz bl ; bl = 1/0 by ZF (SETcc can target only 8b r/m)
movzx ebx,bl ; ebx = bl extended to 32 bits by zeroes
等等...这取决于你的测试之前发生了什么,以及你真正想要的测试输出,有很多可能的方法(最适合不同的情况,最适合不同的目标CPU)。
我将添加一些非常常见的情况......一个从N到零的反向循环计数,循环N次:
mov ebx,5 ; loop 5 times
exampleLoop:
; ... doing something, preserving ebx
dec ebx
jnz exampleLoop ; loop 5 times till ebx is zero
如何处理word
(16b)数组的5个元素(在数组[0],数组[1],......顺序中访问它们):
mov ebx,-5
lea esi,[array+5*2]
exampleLoop:
mov ax,[esi+ebx*2] ; load value from array[i]
; process it ... and preserve esi and ebx
inc ebx
jnz exampleLoop ; loop 5 times till ebx is zero
还有一个例子,我在某种程度上喜欢这个:
当eax
为零/非零时,如何将目标寄存器(示例中为ebx
)设置为~0(-1)/ 0,并且在某些寄存器中已经有值1
(示例中为ecx
):
; ecx = 1, ebx = some value
cmp ebx,ecx ; cmp ebx,1 => CF=1/0 for ebx zero/non-zero
sbb eax,eax ; eax = -1 (~0) / 0 for CF=1/0 ; ebx/ecx intact
-1可能看起来像1一样实用(至少用于索引),但-1也可用作进一步and/xor/or
操作的完整位掩码,所以有时它更方便。
使用旗帜,卢克 通过检查零标志来测试寄存器是否为零。 如果寄存器通过某些影响标志的操作(或者更具体地说,零标志)获得其值,那么您不必执行任何操作,因为零标志已经反映了该寄存器中存储的值。
仅在需要时进行测试 如果您不能保证已设置标志,则必须使用测试操作。 这些操作有两种形式:破坏性和非破坏性。
你可以看到一个指令列表和它改变的标志:http://ref.x86asm.net-更具体地说,在:http://ref.x86asm.net/coder32-abc.html
mov
和lea
指令永远不会改变旗帜,因此需要帮助。大多数其他指令设置至少一个标志。
不要创建错误的依赖项
如果需要测试寄存器为零,但不想更改其值,则使用test
指令。
您不应该使用or
或and
指令来检查寄存器,因为CPU可能不知道or/and
可以非破坏性地使用,并且可能无法应用某些优化。对此的技术术语是“虚假依赖”。寄存器需要ebx并且“认为”它最近被更改,所以它等待结果最终确定。
test ebx, ebx ; <-- CPU knows ebx was not altered, no stalls on subsequent reads.
or ebx, ebx ; <-- CPU 'thinks' ebx was changed, stall on subsequent read.
如果您希望零状态反映在另一个寄存器中,您可以简单地将mov
ebx反映到另一个寄存器中。
将值减少为布尔值 如果要将寄存器减少为布尔值(如果非零则为True,否则为False),则使用以下序列之一:
; ebx holds the value to reduce to a boolean.
; eax is an unused register.
xor eax, eax ; eax = 0
sub eax, ebx ; eax = 0 - ebx; CF (carry flag) = 1 if ebx <> 0
sbb ebx, ebx ; ebx = ebx - ebx - CF
; <<-- ebx = -1 if non zero, 0 if zero
xor eax, eax ; eax = 0
sub eax, ebx ; eax = - ebx; CF = 1 if ebx <> 0
adc ebx, eax ; ebx = (ebx + -ebx) aka 0 + CF
; <<== ebx = 1 if non zero, 0 if zero
test ebx, ebx ; force ZF to be correct
setnz al ; Store 1 if non-zero, 0 otherwise in byte register AL.
注意,由于与“部分寄存器写入”相关的停顿,使用字节寄存器可能会有问题。
你可以使用:
or ebx, 0 ;This does nothing, just triggers the zero flag if ebx is zero
jnz notZero
or ebx, 1 ;ebx was zero, then ebx is 1
notZero:
以上是关于确定寄存器的值是否等于零的最简单方法是什么?的主要内容,如果未能解决你的问题,请参考以下文章