我以前发布过一个程序来查找一个字节中的总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
程序没有给出正确的答案。我猜哪里错了吗?
答案 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
这很实用,因为您的分支数为零。现代处理器非常喜欢(尽管在这种情况下,由于我们只处理两个寄存器,所以我认为这并不是一个巨大的优势。)