确定寄存器的值是否等于零的最简单方法是什么?
What's the easiest way to determine if a register's value is equal to zero or not?
我在 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 中一样。
您可以使用:
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:
可能 "easiest" 或最简单的 "not-caring about details" 回答如何 确定 是:
; 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".
有一个关于设置标志等的详细 [=32=] 推理。还有一个 link 另一个类似的答案,但从性能的角度推理为什么这是最好的方法。 :)
如何在ebx
为零时将其他一些寄存器(我会选择eax
)设置为1,在ebx
非零时设置为0(非破坏性方式ebx
本身):
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
为zero/non-zero时如何将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
为 zero/non-zero 时如何将 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
再举一个例子,我很喜欢这个:
当 ebx
为 zero/non-zero 并且您已经有值 1
时,如何将目标寄存器(示例中的 eax
)设置为 ~0 (-1)/0在某些寄存器中(例如 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—more specifically, at: http://ref.x86asm.net/coder32-abc.html
mov
和 lea
指令从不改变标志,因此需要帮助。大多数其他指令至少设置一个标志。
不要创建错误的依赖关系
如果您需要测试一个寄存器是否为零,但不想改变它的值,您可以使用 test
指令。
您不应该使用 or
或 and
指令来检查寄存器,因为 CPU 可能不知道 or/and
可以非破坏性地使用并且可能无法应用某些优化。
这个的技术术语是 'false dependency'。寄存器需要 ebx 并且 'thinks' 它最近被更改了,所以它等待结果最终确定。
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.
请注意,由于与 "partial register writes" 相关的停顿,字节寄存器的使用可能会出现问题。
我在 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 中一样。
您可以使用:
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:
可能 "easiest" 或最简单的 "not-caring about details" 回答如何 确定 是:
; 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".
有一个关于设置标志等的详细 [=32=] 推理。还有一个 link 另一个类似的答案,但从性能的角度推理为什么这是最好的方法。 :)
如何在ebx
为零时将其他一些寄存器(我会选择eax
)设置为1,在ebx
非零时设置为0(非破坏性方式ebx
本身):
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
为zero/non-zero时如何将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
为 zero/non-zero 时如何将 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
再举一个例子,我很喜欢这个:
当 ebx
为 zero/non-zero 并且您已经有值 1
时,如何将目标寄存器(示例中的 eax
)设置为 ~0 (-1)/0在某些寄存器中(例如 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—more specifically, at: http://ref.x86asm.net/coder32-abc.html
mov
和 lea
指令从不改变标志,因此需要帮助。大多数其他指令至少设置一个标志。
不要创建错误的依赖关系
如果您需要测试一个寄存器是否为零,但不想改变它的值,您可以使用 test
指令。
您不应该使用 or
或 and
指令来检查寄存器,因为 CPU 可能不知道 or/and
可以非破坏性地使用并且可能无法应用某些优化。
这个的技术术语是 'false dependency'。寄存器需要 ebx 并且 'thinks' 它最近被更改了,所以它等待结果最终确定。
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.
请注意,由于与 "partial register writes" 相关的停顿,字节寄存器的使用可能会出现问题。