Gcc 装配中的钻头测试(BT)
我使用bt测试汇编中是否设置了某些位。我的代码如下所示:Gcc 装配中的钻头测试(BT),gcc,assembly,x86,Gcc,Assembly,X86,我使用bt测试汇编中是否设置了某些位。我的代码如下所示: #define bv %eax //just renaming reg to make reading easier #define bit %edx //rename reg /* * bitIsSetBV(unsigned int *bv, unsigned int bit) * this function returns 0 if bit at location bit is not set * it returns
#define bv %eax //just renaming reg to make reading easier
#define bit %edx //rename reg
/*
* bitIsSetBV(unsigned int *bv, unsigned int bit)
* this function returns 0 if bit at location bit is not set
* it returns 1 if it is set
*/
.text
.global bitIsSetBV
bitIsSetBV:
movl 4(%esp), bv //make room on stack for bv
movl 8(%esp), bit //make room on stack for bit
bt bit, (bv) //if bv[bit] is 0, set CF to 0, else, set CF to 1
jc bitSet // if CF is 1, jump to bitSet
jnc bitNotSet // if CF is 0, jump to bitNotSet
bitNotSet:
movl $0, bv //return 0, bit is not set
jmp Done // jump to Done
bitSet:
movl $1, bv // return 1, bit is set
jmp Done // jump to Done
Done:
ret //end of code
./bittest
测试人员:
我的代码适用于位=0-5,但在位=6时,它不适用。为什么呢?我肯定这是我对位级操作的误解。这是对您问题的回答,但不一定是有解决方案的答案。虽然您的汇编代码效率很低,但当我第一次看到它时,我没有看到它的任何逻辑缺陷,因此我假设问题不在您的汇编代码中,而是在您的C代码中的某个地方。我昨晚确实要求你提供你的C代码,但没有后续行动 我从下面的汇编代码中提取了多余的跳转和分支,但是您的原始代码应该可以工作。我把它放在一个名为
isbitset.S
的程序集文件中:
#define bv %eax //just renaming reg to make reading easier
#define bit %edx //rename reg
/*
* bitIsSetBV(unsigned int *bv, unsigned int bit)
* this function returns 0 if bit at location bit is not set
* it returns 1 if it is set
*/
.text
.global bitIsSetBV
bitIsSetBV:
movl 4(%esp), bv //make room on stack for bv
movl 8(%esp), bit //make room on stack for bit
bt bit, (bv) //if bv[bit] is 0, set CF to 0, else, set CF to 1
jc bitSet // if CF is 1, jump to bitSet
bitNotSet:
movl $0, bv //return 0, bit is not set
jmp Done // jump to Done
bitSet:
movl $1, bv // return 1, bit is set
Done:
ret //end of code
我推荐Peter Cordes在评论中提出的建议,以提高代码的效率。因为我认为问题在于测试程序,所以我更关心它是如何被调用的。我用C语言创建了一个简单的测试程序,名为bitest.C
:
#include <assert.h>
extern int bitIsSetBV(unsigned int *bv, unsigned int bit);
int main()
{
unsigned int a = 0xe1; /* which is binary 11100001 */
unsigned int *bv = &a;
assert(bitIsSetBV(bv, 0) == 1); /* 0001 */
assert(bitIsSetBV(bv, 1) == 0);
assert(bitIsSetBV(bv, 2) == 0);
assert(bitIsSetBV(bv, 3) == 0);
assert(bitIsSetBV(bv, 4) == 0); /* 1110 */
assert(bitIsSetBV(bv, 5) == 1);
assert(bitIsSetBV(bv, 6) == 1);
assert(bitIsSetBV(bv, 7) == 1);
return 0;
}
或者,您也可以这样做:
gcc -m32 bittest.c isbitset.S -o bittest
我是这样运行的:
#define bv %eax //just renaming reg to make reading easier
#define bit %edx //rename reg
/*
* bitIsSetBV(unsigned int *bv, unsigned int bit)
* this function returns 0 if bit at location bit is not set
* it returns 1 if it is set
*/
.text
.global bitIsSetBV
bitIsSetBV:
movl 4(%esp), bv //make room on stack for bv
movl 8(%esp), bit //make room on stack for bit
bt bit, (bv) //if bv[bit] is 0, set CF to 0, else, set CF to 1
jc bitSet // if CF is 1, jump to bitSet
jnc bitNotSet // if CF is 0, jump to bitNotSet
bitNotSet:
movl $0, bv //return 0, bit is not set
jmp Done // jump to Done
bitSet:
movl $1, bv // return 1, bit is set
jmp Done // jump to Done
Done:
ret //end of code
./bittest
-m32
确保生成32位可执行文件。如果您是在默认为Linux使用的64位系统上编译此文件,那么这会产生很大的不同。您使用32位调用约定编写了汇编代码,因此,如果从编译为64位的C程序调用,它将无法工作
在本例中,代码不会生成任何断言,这意味着这些位与我预期的一致
这个答案还提供了一个例子,说明了如何创建一个人们可以测试的问题。
只需重命名reg以使阅读更容易。
这条评论很有趣。我甚至没有看那两行代码,我想知道BT
是如何使用一对内存操作数的。也许我已经老了,但是这两个定义使得代码更难理解,因为你没有向我们展示定义bv
的测试C代码,所以我们不知道它实际上是如何定义和分配的。尽管您的代码效率低下,但这种冗余是完全不必要的:jnc bitNotSet
。删除该行,因为您之前已经在指令上设置了进位测试,因此,如果进位设置失败,则不得设置该行。它将直接进入下一条指令,而下一条指令恰好是bitNotSet
的代码jmp Done
在标签Done
之前也可以删除,因为您要跳转到下一条指令。movl 4(%esp),bv//在堆栈上为bv腾出空间
对该代码的注释不匹配。堆栈已经保存了这些值,您正在将它们复制到寄存器中。你没有在堆栈上为它们腾出空间。bt位,(bv)//如果bv[bit]为0,将CF设置为0,否则,将CF设置为1
这是怎么回事?这只是解引用bv
并在位测试位
。。。如何在C代码中定义bv
?使用xor%eax,%eax/bt/setc%al
而不是在进位标志上进行分支。