Assembly 确定数组中负数和正数的数目
我需要在汇编程序中确定数组中负数和正数的数目。似乎汇编程序没有将它们识别为负数。我怎样才能解决这个问题?我这样定义数组:Assembly 确定数组中负数和正数的数目,assembly,x86,nasm,32-bit,Assembly,X86,Nasm,32 Bit,我需要在汇编程序中确定数组中负数和正数的数目。似乎汇编程序没有将它们识别为负数。我怎样才能解决这个问题?我这样定义数组: word_array db 3, -2, 11, -1, -2, -7, -5, -20 我有一个计算积极因素的函数: count_positives: mov dx, word [word_array + 2*ecx - 2] cmp edx, 0 JL skip inc ebx skip: loopnz count_positives 阅读评论 proc: mov
word_array db 3, -2, 11, -1, -2, -7, -5, -20
我有一个计算积极因素的函数:
count_positives:
mov dx, word [word_array + 2*ecx - 2]
cmp edx, 0
JL skip
inc ebx
skip:
loopnz count_positives
阅读评论
proc:
mov si, data ; si points to the data
mov cx, [len] ; cx gets the length of the data
shr cx,1 ; the length was in bytes, we want words
mov bx, 0
mov dx, cx
checkNext:
mov ax, [si]
text ax, ax ; alternatively: test ax, 8000h
js isNegative
inc bx ; counting positive numbers
isNegative:
add si, 2 ; moving to next word
loop checkNext ; decrease cx, jump if not 0
sub dx, bx ; bx has the positive numbers, dx - the negative ones
ret ; done
data dw -1,2,-3,4
len dw $-data
您正在加载DX的低16位,而高16位(包括符号位)保留了以前存在的垃圾。使用16位操作数大小进行比较
计算负数或非负数,然后从总计数中减去负数得到另一个 如果需要计数负数和正数,则需要两个计数器,以及一个
test
或cmp
后接两个分支(以便零不会进入任何一个计数器)
根据Sten的回答改编,但有一些改进。请注意,测试值,-1
相当于cmp值,0
section .rodata
word_array dw -1,2,-3,4
len equ $-word_array ; length in bytes. assembler constant, so we can mov reg, imm8/imm32 rather than loading it as data.
section .text
;; clobbers ESI, ECX. Returns in EAX, EDX
proc:
mov esi, word_array ; esi points to the array. In MASM, use OFFSET word_array
mov ecx, len/2 - 1 ; [esi + ecx*2] points to the last element
xor edx, edx ; non_neg_count = 0
countloop:
; cmp [esi + ecx*2], 0 ; This can't macro-fuse (memory and immediate operand). Also can't micro-fuse on SnB, because of a 2-reg addressing mode
movsx eax, word [esi + ecx*2] ; use a 2-reg addressing mode to save loop overhead, since this there's no ALU execution port component to this insn. It doesn't need to micro-fuse to be one uop
test eax, eax ; can macro-fuse with js
js isNegative
inc edx ; counting non-negative numbers
isNegative:
dec ecx ; can macro-fuse with jge, but probably won't unless alignment stops it from being decoded in the same cycle as the earlier test/js
jge countloop ; jge, not jnz, because we want ecx from [0 : len-1], rather than [1 : len]
; after the loop, ecx=-1, edx=non_neg_count
; neg_count = array_count - non_neg_count
mov eax, len/2
sub eax, edx ; eax = neg_count
ret ; return values in eax, edx
在英特尔上,循环为4个UOP。(如果两个测试/分支对在同一个周期中命中解码器,则在Haswell之前的Sandybridge上更可能为5,因此只有一个宏融合。HSW可以在单个解码组中进行2个宏融合)
带有设置bl/添加edx、ebx
的无分支版本可能运行良好
通过将eax归零,然后在循环中使用scasw
将ax
与[esi]进行比较,并将esi增加两倍,您可以稍微节省代码大小,但这通常不是一个好的性能选择
如果是正面与非负面问题: 如果需要,零计数是
n-eax-edx
,其中n
是元素数
我在这里使用了不同的循环结构,只是为了多样性。循环应为7个UOP
在setcc写入bl之后读取ebx可以避免部分寄存器合并惩罚,因为我们在循环外对ebx进行异或归零。(保存/恢复EBX的上下文切换或中断将消除该性能优势,但对于短循环,可能仍然值得将xor归零从循环中提升出来。)您使用
db
将word_数组声明为字节。也许您打算使用dw
(对于16位字)。比如:word\u数组dw3、-2、11、-1、-2、-7、-5、-20
。同样,由于您使用dx
使用edx
寄存器的低16位,因此我建议您更改为cmp-dx,0
而不是cmp-edx,0
,如果您不比较寄存器的右侧部分,您的负数可能会显示为正数。实际上,请使用test-dx,dx
,或者完全跳过加载并使用cmp字[word_数组+2*ecx-2],0
。不要使用循环
指令,它很慢。使用dec ecx/jnz
@PeterCordes:因为我只是在评论,所以我试图说明为什么现有代码可能无法产生正确的结果。我认为在将长度输入cx后,您缺少了mov dx,cx
,因为您使用dx时没有初始化它。
section .rodata
word_array dw -1,2,0,-3,4
len equ $-word_array ; length in bytes. assembler constant, so we can mov reg, imm8/imm32 rather than loading it as data.
section .text
;; clobbers ESI, EDI, EBP. Returns in EAX, EDX
proc_pos_and_neg:
mov esi, word_array ; esi points to the array. In MASM, use OFFSET word_array
xor edx, edx ; pos_count = 0
xor eax, eax ; neg_count = 0
lea edi, [esi + len] ; points one past the end of the array
xor ebx, ebx ; clear upper portion, because setcc r32 isn't available, only setcc r8 :(
countloop:
cmp word [esi], 0
setg bl ; 0 or 1, depending on array[i] > 0
lea edx, [edx + ebx] ; add without affecting flags
setl bl
add eax, ebx ; can clobber flags now
add esi, 2 ; simple pointer-increment
cmp esi, edi
jb countloop ; loop while our pointer is below the pointer to one-past-the-end
ret ; neg_count in eax, pos_count in edx