AsiaRican
AsiaRican

Reputation: 75

how to test bits of an integer in Assembly

How do I test for the least significant bit that's unset if I'm given an integer N. My first attempt (gas syntax):

#define N      %ecx
#define return %eax

 /* prototype:
  * int leastSigUnsetBit(unsigned int N)
  */

.text
.global leastSigUnsetBit
leastSigUnsetBit:

movl N, 4(%esp)     

movl $-1, return
Loop:
    inc return
    bt return, N
    jc Loop
ret

Upvotes: 0

Views: 1912

Answers (1)

Johan
Johan

Reputation: 76577

You can test the lsb set using bsf (bit scan forward) or even better using tzcnt
I hope you are aware of the fact that bsf will return undefined data when the operand is zero. tzcnt fixes that.
If you want to test the LSB that's unset, you just invert the input using not and then test for the LSB set.

Note that tzcnt is both (nearly) reverse compatible and faster than bsf. tzcnt will be read by an old cpu as rep bsf reg,reg, however tzcnt modifies the flags differently from bsf:

bsf: ZF=0 -> input was 0.
tzcnt: ZF=0 -> output is 0, CF=0 -> input was zero.

The best solution is to use tzcnt, but make sure you've tested that the cpu supports tzcnt (or it will execute a bsf and your code will give incorrect results).

;Intel syntax
mov eax,not(-1)
tzcnt edx,eax     ;edx = 32     
mov eax,[random]
not eax           
tzcnt eax,eax

The other alternative is to test whether the operand is zero and adjust accordingly. bsf does alter the flags correctly, you can use that to compensate.

From: http://www.felixcloutier.com/x86/BSF.html

BSF ..... Flags Affected

The ZF flag is set to 1 if all the source operand is 0;

not eax
mov edx,32
bsf eax,eax       
cmovz eax,edx         //force result to 32 if zero inputted.

Note that as a matter of principle I don't write AT&T syntax, you'll have to reverse the operands where applicable.

tzcnt runs a lot faster than bsf (because the cpu does not it does not have to do undefined behaviour emulation).

Upvotes: 3

Related Questions