确定数组中的负数和正数

时间:2015-11-13 04:13:57

标签: 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

2 个答案:

答案 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归零来节省代码大小,然后在循环中使用scaswax与[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中)。