计算一个字节中的零和一的数目

时间:2019-02-27 18:31:56

标签: assembly x86

我以前发布过一个程序来查找一个字节中的总1s。现在,我尝试查找一个字节中的0。以下是我的代码:

MOV AL,1
MOV CX,08H
MOV BX,0000H
MOV DX,0000H

Zero:


SHR AL,01H

JZ ero

JNZ ZrO

ero: INC BX

ZrO: INC DX


LOOP Zero

hlt

程序没有给出正确的答案。我猜哪里错了吗?

2 个答案:

答案 0 :(得分:3)

从AL移出的位进入进位标志,而不是零标志。更改您的条件跳转:

    MOV AL,1     ; An investigated byte.    
    MOV CX,08H   ; Number of bits in the byte. 
    MOV BX,0000H ; Result: number of 1s.
    MOV DX,0000H ; Result: number of 0s.
Zero:SHR AL,01H  ; Shift the byte, least significant bit to CF.
    JNC ZrO
ero:INC BX      ; Count 1s. 
    JMP Skip
ZrO:INC DX      ; Count 0s.
Skip:LOOP Zero   ; Repeat CX times.
    hlt

顺便说一句,在新的英特尔处理器(NEHALEM)上有专门针对此任务的说明: https://www.felixcloutier.com/x86/popcnt

    MOV AL,1     ; An investigated byte.  
    XOR AH,AH    
    POPCNT BX,AX ; Count the number of 1s in AX and put result to BX.
    MOV DX,8
    SUB DX,BX    ; The number of 0s in AL is 8-BX.

答案 1 :(得分:2)

通常在编写汇编时,查看可能的优化很有趣。使用循环,可以像这样使用Z和C标志:

    MOV AL, <your value>
    MOV BL, 8
    CLC
Loop:
    SBB BL, 0
    SHR AL, 1
    JNZ Loop
Done:
    SBB BL, 0
    ; result is in BL
    HLT

在较旧的处理器上,更快的方法是拥有256个字节的表并进行查找。正如vitsoft所提到的,在现代处理器上,使用POPCNT指令可能是最快的(计算硬件中64位寄存器的所有位需要一个时钟周期。)

现在,如果您需要知道确切的时间,我的循环将不切实际,因为它会根据AL而有所不同。快速运行的另一种方法是展开循环:

    MOV AL, <your value>
    MOV BL, 8
    SHR AL, 1    ; 1
    SBB BL, 0
    SHR AL, 1    ; 2
    SBB BL, 0
    SHR AL, 1    ; 3
    SBB BL, 0
    SHR AL, 1    ; 4
    SBB BL, 0
    SHR AL, 1    ; 5
    SBB BL, 0
    SHR AL, 1    ; 6
    SBB BL, 0
    SHR AL, 1    ; 7
    SBB BL, 0
    SHR AL, 1    ; 8
    SBB BL, 0
    HLT

这很实用,因为您的分支数为零。现代处理器非常喜欢(尽管在这种情况下,由于我们只处理两个寄存器,所以我认为这并不是一个巨大的优势。)