问题
I need to determine the number of negative and positive numbers in an array in assembler. It seems that the assembler doesn't recognizes them as negative numbers. How can I solve this problem? I define the array this way:
word_array db 3, -2, 11, -1, -2, -7, -5, -20
I have this function that counts the positive ones:
count_positives:
mov dx, word [word_array + 2*ecx - 2]
cmp edx, 0
JL skip
inc ebx
skip:
loopnz count_positives
回答1:
Count either negative or non-negative, and subtract that from the total count to get the other. If you need to count negative and positive, then you need two counters, and a test
followed by two branches (so that zero doesn't go into either counter).
Adapted from Sten's answer, but with some improvements.
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
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
The loop is 4 uops on Intel. (Or more likely 5, if both test/branch pairs hit the decoders in the same cycle so only one macro-fuses).
A branch-less version with sets bl / add edx, ebx
might work well.
You could maybe save slightly on code size by zeroing eax, then using scasw
in a loop to compare ax
with [esi], and increment esi by two, but it's not a good choice if you aren't using it with a rep
prefix.
If positive vs. non-negative matters:
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
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
mov ebp, -1 ; constant to test with
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:
test bp, word [esi] ; test reg, mem can micro-fuse, but test mem, imm can't
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 ; extra loop overhead compared to the scan-backwards decrementing an index reg I used last time
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
I used a different loop structure here just for variety. The loop should be 7 uops, but on Intel pre-Haswell, reading ebx after setcc writes bl will incur a partial-register penalty (an extra uop to merge the result into the full reg).
回答2:
Read the comments
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
来源:https://stackoverflow.com/questions/33685828/determine-number-of-negative-and-positive-numbers-in-an-array