我正在使用与Irvine库的x86程序集。
检查寄存器值是否等于零的最简单方法是什么?
我使用了cmp指令,但我正在寻找替代方法。 这是我使用cmp指令的代码,寄存器是ebx
VERSION_1_8
这" booleanizes"一个值,在C中产生0或1,如 cmp ebx,0
je equ1
mov ebx,0
jmp cont
equ1:
mov ebx,1
jmp cont
cont:
exit
。
答案 0 :(得分:11)
可能“最简单”或“最简单”,“不关心细节”回答如何确定:
; 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
为非零时设置为0({ebx
为非{ 1}}本身):
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
操作的完整位掩码,因此有时它更方便。
答案 1 :(得分:2)
使用标志,Luke
通过检查零标志来测试寄存器是否为零
如果寄存器通过某些影响标志的操作(或者更具体地说,零标志)获得其值,那么您不必执行任何操作,因为零标志已经反映了该寄存器中存储的值。
仅在需要时进行测试
如果您无法保证已设置标志,则必须使用测试操作
这些操作有两种形式:破坏性和非破坏性。
您可以在以下位置查看说明列表及其改动的标记: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.
请注意,由于与“部分寄存器写入”相关的停顿,使用字节寄存器可能会出现问题。
答案 2 :(得分:-3)
您可以使用:
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: