我需要确定汇编程序中数组中的负数和正数。似乎汇编程序并没有将它们识别为负数。我怎么解决这个问题?我用这种方式定义数组:
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
答案 0 :(得分:1)
阅读评论
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
答案 1 :(得分:1)
计算为负数或非负数,并从总计数中减去该数以获得另一个。如果你需要计算负数和正数,那么你需要两个计数器,一个test
后跟两个分支(这样零就不会进入任何一个计数器)。
改编自Sten的答案,但有一些改进。
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
英特尔的循环次数为4次。 (或者更可能是5,如果两个测试/分支对在同一周期中击中解码器,那么只有一个宏保险丝。)
sets bl / add edx, ebx
的无分支版本可能效果很好。
您可以通过将eax归零来节省代码大小,然后在循环中使用scasw
将ax
与[esi]进行比较,并将esi增加2,但它不是如果您没有使用rep
前缀,那么这是一个不错的选择。
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
我在这里使用了不同的循环结构,只是为了变化。循环应该是7微秒,但在英特尔前Haswell上,在setcc写入bl之后读取ebx将导致部分寄存器惩罚(额外的uop将结果合并到完整的reg中)。