Assembly x86:在32位数字中计数从1到0的转换

Assembly x86:在32位数字中计数从1到0的转换,assembly,x86,nasm,Assembly,X86,Nasm,我喜欢在32位数字中计算1到0的转换数。我使用shr,发现从0到1的转换次数是以相反的顺序进行的,但我没有得到所需的结果。我该怎么办 extern printf SECTION .data msg: db "The number of transitions are : %d",10,0 inta1: dd 1234567 ; integer 1234567

我喜欢在32位数字中计算1到0的转换数。我使用
shr
,发现从0到1的转换次数是以相反的顺序进行的,但我没有得到所需的结果。我该怎么办

extern printf                   
SECTION .data                   
    msg:      db "The number of transitions are : %d",10,0  
    inta1:    dd 1234567   ; integer 1234567        
    num:      dd 4660  
SECTION .text                    

global main               
main:     
    mov eax, [num]    
    mov ecx,32    
    mov ebx,0    
.loop:  dec ecx  
    cmp ecx,0  
    jl .exit   
    shr eax,1  
    jc .loop  
    dec ecx  
    shr eax, 1  
    jnc .loop  
    inc ebx  
    jmp .loop  
.exit:  
    push ebx  
    push    dword msg          
    call    printf             
    add     esp, 8  
输出:


转换的数量为:2


而对于4660(
0000000000000001001000110100
)而言,1到0的转换次数是4。

基本问题是,您的代码查找一个
0
,后跟一个
1
,如果不匹配,则重新开始。因此,如果它看到
00
,它将重新开始寻找另一个
0
,如果下一位是
1
,它将重新开始。因此,当过渡前面有偶数个
0
s时,您将错过过渡

另请注意,您说您想要1到0的转换,但您正在寻找0到1的转换,但您正在从右到左(lsb到msb)寻找,因此这可能是正确的,具体取决于您想要的位顺序

显而易见的解决方案是将
jnc.loop
更改为不重新开始,而只返回到前面的
dec ecx
,尽管您还需要
cmp ecx,0;jl.从那里退出

注意:当结果(在
eax
中)全部为0位时,您还可以通过使用
Z
标志(由
shr
指令设置)来摆脱
ecx
和计数位数的需要

综上所述,这给了你一些类似的东西:

.loop1: shr eax, 1
        jc .loop1
.loop2: jz .exit
        shr eax, 1
        jnc .loop2
        inc ebx
        jmp .loop1

琐事:几个月前,我在尝试实现基于位图的内存管理器的一个方面时想到了这一点

我发现的最有效的解决方案如下(我根据您的示例代码对其进行了调整):

请注意,计算是在没有循环的情况下完成的。这是一种纯粹的位摆弄,设置位最终由
POPCNT
指令进行计数,从而得到所需的结果

作为示例-应用于4660的给定值,该算法的功能如下:

00000000000000000001001000110100 --- MOV EBX, EAX

00000000000000000010010001101000 --- SHL EBX, 1
11111111111111111110110111001011 --- NOT EAX
00000000000000000010010001001000 --- AND EBX, EAX => 'and' the last two lines
=> POPCNT (count the bits of the last line) to get the result of 1 to 0 transitions.

你得到了什么值?转换的数量是:2最后一个
jmp.loop
应该是
jmp.loop1
(+1)。你可以在底部用
jnz
构造循环,然后用
测试eax,eax/jmp loop\u入口点
输入它。这样,循环中就少了一条指令(没有无条件分支)。还请注意,您可以使用
adc ebx,0将
CF
添加到
ebx
。或
setc dl
/
添加bl,dl
。无论哪种方式,您都可以避免移位位上的条件分支,因为这可能无法很好地预测。@SaumyaSahay,如果您必须在书本和
popcnt
之间进行选择,您应该选择
popcnt
(书本会过时)。@Saumya Sahay:是的,如果您手动实现一个例程来计算
DWORD
中的位,则可以在不使用
POPCNT
的情况下完成此操作。这项工作已经完成,非常烦人,但幸运的是,目前几乎所有的处理器都支持多年。它是被介绍的。依我看,对于处理器功能而言,这可能是一个不错的年龄。
00000000000000000001001000110100 --- MOV EBX, EAX

00000000000000000010010001101000 --- SHL EBX, 1
11111111111111111110110111001011 --- NOT EAX
00000000000000000010010001001000 --- AND EBX, EAX => 'and' the last two lines
=> POPCNT (count the bits of the last line) to get the result of 1 to 0 transitions.