程序集 x86:如何计算 32 位长整数中的设置位数

Posted

技术标签:

【中文标题】程序集 x86:如何计算 32 位长整数中的设置位数【英文标题】:Assembly x86: How to count the number of set bits in a 32-bit long int 【发布时间】:2016-09-20 21:31:52 【问题描述】:

我是一个新手,我正在尝试编写代码来计算 32 位整数中设置的位数。

我写了这个,但我得到了很多错误:

.586
.model flat,stdcall
.stack 4096

; Windows libraries

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

.DATA

data:   .LONG 0x0F0F0101
count:  .BYTE 0x00

.TEXT

_main:  NOP
        MOVB $0x00, %CL
        MOVL data, %EAX
comp:   CMPL $0x00, %EAX
        JE end
        SHRL %EAX
        ADCB $0x0, %CL
        JMP comp
end:    MOVB %CL, count
        RET

我有以下错误:

C:\Users\RaiN\Desktop\Test\test1.asm(20) : error A2034: must be in segment block
C:\Users\RaiN\Desktop\Test\test1.asm(25) : error A2108: use of register assumed to ERROR
C:\Users\RaiN\Desktop\Test\test1.asm(25) : error A2008: syntax error : .
C:\Users\RaiN\Desktop\Test\test1.asm(26) : error A2108: use of register assumed to ERROR
C:\Users\RaiN\Desktop\Test\test1.asm(26) : error A2008: syntax error : .
C:\Users\RaiN\Desktop\Test\test1.asm(28) : error A2008: syntax error : .
C:\Users\RaiN\Desktop\Test\test1.asm(29) : error A2108: use of register assumed to ERROR
C:\Users\RaiN\Desktop\Test\test1.asm(30) : error A2008: syntax error : $0x00
C:\Users\RaiN\Desktop\Test\test1.asm(31) : error A2008: syntax error : dato
C:\Users\RaiN\Desktop\Test\test1.asm(32) : error A2108: use of register assumed to ERROR
C:\Users\RaiN\Desktop\Test\test1.asm(32) : error A2008: syntax error : CMPL
C:\Users\RaiN\Desktop\Test\test1.asm(34) : error A2008: syntax error : !%
C:\Users\RaiN\Desktop\Test\test1.asm(35) : error A2008: syntax error : $0x0
C:\Users\RaiN\Desktop\Test\test1.asm(37) : error A2108: use of register assumed to ERROR
C:\Users\RaiN\Desktop\Test\test1.asm(37) : error A2008: syntax error : MOVB
C:\Users\RaiN\Desktop\Test\test1.asm(38) : error A2088: END directive required at end of file
C:\Users\RaiN\Desktop\Test\test1.asm(33) : error A2107: cannot have implicit far jump or call to near label
C:\Users\RaiN\Desktop\Test\test1.asm(36) : error A2107: cannot have implicit far jump or call to near label

我该如何解决这个问题?

非常感谢!!!

【问题讨论】:

作弊:用C编写,使用-S开关编译,然后查看生成的汇编程序! 你使用什么汇编程序?您的输出看起来非常像您尝试将 GNU 汇编器语法与 Microsoft 汇编器一起使用。这行不通。 我使用 WinAsm Studio。对于测试,我需要使用 Microsoft Assembler @Yimin Rong 作弊将使用 POPCNT 指令。 ;) 这并不是一个新问题。 Last time我写了这个,要求是使用内联汇编。但是把它变成“真正的”汇编器应该不会太难。也就是说,Bit Twiddling Hacks 中的 5's 和 3's 方法已知比 popcnt 指令更快(取决于特定 cpu 的实现),并且不需要 asm。 【参考方案1】:

新代码是:

.text
.global main
main:
    movq %rsp, %rbp #for correct debugging
    # write your code here
.DATA
stringa:    .ASCIZ "Questa è la stringa di caratteri ASCII che usiamo come esempio"

lettera:     .BYTE 'e'
conteggio:   .BYTE 0x00

.TEXT
_main:        NOP
              MOV $0x00, %CL
              LEA stringa, %ESI
              MOV lettera, %AL
comp:         CMPB $0x00, (%ESI)
              JE fine
              CMP (%ESI), %AL
              JNE poi
              INC %CL
poi:          INC %ESI
              JMP comp
fine:         MOV %Cl, conteggio

              INT $0x27              
              RET

【讨论】:

既然您将此作为“答案”发布,这是否意味着问题已解决?还是这里还有问题?【参考方案2】:

您要计算的内容称为Hamming Weight 您可以通过使用Logical shifts 和位掩码来实现此目的

这是一个简单的程序。我假设您使用的是 x86 程序集。

.386
.model flat,stdcall
option casemap:none

include kernel32.inc
includelib kernel32.lib

.code
start:
    mov eax,1h             ;your number in eax 100 is sample here
    mov ecx,0h             ;is the counter register
    xor edx,edx            ;is done to make edx 0, you can also do mov edx,0
notDoneWithNumber:
    cmp eax,0
    je done
    mov edx,eax            ;edx is here a compare register, not nice, but it works
    shr eax,1              ;we push all the bits one place to the right, bits at position 1 will be "pushed out of the byteword"
    and edx,1h             ;make sure we only get, wether the last bit is set or not(thats called bitmaking) 
    cmp edx,0h
    jz notDoneWithNumber   ;if the found value is a zero we can skip the inc of the count register
    inc ecx
    jmp notDoneWithNumber
done:                      ;register ecx will now hold your hamming weight
end start

【讨论】:

感谢您的代码,但我必须改进我之前发布的解决方案...可以构建代码但我无法在 DOS 上执行它们。我不明白如何插入 I/O 功能和系统暂停。 哦,我以为你只需要一个有效的算法。说到DOS,恐怕我真的帮不了你,对不起。 and edx, 1 已经根据结果设置了 ZF。你不需要cmp edx, 0。您也可以通过使用shr eax / jnz notDone 作为循环底部的跳转来简化分支。即如果移位留下任何非零位,则重复循环。 (如果您在屏蔽它后无条件地 add ecx, edx,而不是有条件地运行 inc,则此方法效果最佳。与使用难以预测的分支跳过 inc 相比,仅添加零要有效得多。) 当然,与graphics.stanford.edu/~seander/… 或特别是 SSSE3 pshufb 相比,整个一次一位的算法非常慢(将输入切成 4 位半字节,并将其用作并行 LUT 索引)。或者,popcnt 指令当然是在支持它的 CPU 上一次处理一个整数的理想选择。 是的,你说得对,algortihm 非常慢。老实说,我对组装也很陌生,我想给出一个答案,这很容易理解并且结构清晰。

以上是关于程序集 x86:如何计算 32 位长整数中的设置位数的主要内容,如果未能解决你的问题,请参考以下文章

用C语言实现大整数的运算?64bit整数可以用一个字符数组来保存它

用c语言实现大整形运算,64位长整型的加减法,输入限制为64位长度整数

未能加载的文件或程序集.怎么解决

将 ISR 链接到向量中断 80x86 32 位 AT&T 程序集

x86 OS 的 64 位处理器上的 64 位整数运算

指令,操作系统位数,32位与64位 x86 arm 处理器 ,概念概览