在下面的代码中。如果我想访问变量的个别位并将其与1进行比较。像cmp ax,1和keep一样,bit的位置增加1来获得下一个位,这样我就可以计算在该变量中设置为1的位数。这可能吗?
。Data
var dw 1234
.code
这不是很清楚,如果您的目标是16位或32位架构。
逐个计数位的典型方法如下:
mov cx, 1 // set mask bit
mov bx, 0 // initialise counter to accumulate bits
next_bit:
test ax, cx // check if the current bit is set, ZF==1 if not
jz skip_increment // if ZF==1, do not increment
inc bx
skip_increment:
add cx, cx // shift CX left by addition (8000h + 8000h will overflow to zero)
jnz next_bit // if the mask did not yet overflow, continue loop
这个片段已经有很多优化:
- 我们不需要计算
1 << j
, - 我们不需要有一个迭代计数
j
- 溢出(0x8000 <<)1) == 0,当跳转到循环开始 时检测到
如果我们愿意丢弃ax
,我们可以将ax
右移,其好处是当ax
中没有更多的位可计数时,我们可以立即跳出循环。
mov cx, 0
next_bit: test ax, 1
jz skip_increment
inc cx
skip_increment:
shr ax, 1
jnz next_bit
我们能做得更好吗?我们需要向前跳转来跳过增量,这看起来不太有效。
xor cx, cx // another way to clear the counter register
next_bit:
shr ax, 1 // shift out the LSB in ax to carry
adc cx, 0 // increment with carry
test ax, ax // now we need to re-test ax for zero
jnz next_bit
奇怪的是,x86指令集包含jcxz
- jump if cx==0,而不是相反:jump if cx != 0。如果是这样,我们就可以交换ax和cx的含义,并跳转到循环的开始,而不需要额外的测试。
但是,我们仍然可以重新排列循环,以便我们复制一些代码,以允许循环处理前一次迭代和当前迭代的元素。
sub cx, cx // another way to clear cx, but also ensure CF==0
next_bit:
adc cx, 0 // add the carry from previous iteration
shr ax, 1 // move the LSB to carry, sets ZF, iff ax==0
jnz next_bit
adc cx, 0 // add the carry from last iteration