装配中的位测试(BT)

时间:2016-02-29 05:30:43

标签: assembly

我使用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 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

测试仪:

  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);

我的代码适用于bit = 0-5,但是在bit = 6时,它不适用。这是为什么?我确定这是我对比特级别操作的误解。

1 个答案:

答案 0 :(得分:2)

这是对您的问题的回复,但不一定是解决方案的答案。虽然你的汇编代码效率很低,但是当我第一次看到它时,我没有看到它的任何逻辑缺陷,所以我认为问题不在你的汇编代码中,而是在 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在评论中提出建议,使代码更有效率。由于我认为关注的是测试程序,我更关心它是如何被调用的。我在名为bittest.c 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;
}

我使用以下命令汇编,编译并将代码链接到名为bittest的程序:

gcc -m32 -c isbitset.S -o isbitset.o
gcc -m32 bittest.c isbitset.o -o bittest

或者你也可以这样做:

gcc -m32 bittest.c isbitset.S -o bittest

我这样运行:

./bittest

-m32确保您生成32位可执行文件。如果您在64位系统上进行编译,这可能会产生很大的不同,该系统默认为Linux使用的64-bit System V ABI。您使用32位调用约定编写了汇编代码,因此如果从编译为64位的 C 程序调用它,它将无法工作。

在这种情况下,代码不会生成任何断言,这意味着这些位符合我的预期。

这个答案还提供了Minimal Complete Verifiable Example,并且是您如何创建人们可以测试的问题的示例。