【问题标题】:Stuck at summing two arrays using MMX instructions using NASM卡在使用 NASM 使用 MMX 指令对两个数组求和
【发布时间】:2022-01-03 02:41:53
【问题描述】:

我被分配了以下任务:

给定两个具有 16 个元素的数组:NIZA RESW 16 和 NIZB RESW 16
在第三个数组 (NIZC RESW 16) 中存储以下值:NIZC[i]=NIZA[i]+NIZB[i] 使用 MMX 指令并使用 NASM 编译它

这是我目前得到的:

%include "apicall.inc"
%include "print.inc"

segment .data
unos1 db "Array A: ", 0
unos2 db "Array B: ", 0
ispisC db "Array C : ", 0

segment .bss
  NIZA RESW 16
  NIZB RESW 16
  NIZC RESW 16

segment .text
global start

start:
call init_console
mov esi,0
mov ecx, 16

mov eax, unos1
call print_string
call print_nl

unos_a:
call read_int
mov [NIZA+esi], eax
add esi, 2
loop unos_a

mov esi,0
mov ecx, 16
mov eax, unos2
call print_string
call print_nl

unos_b:
call read_int
mov [NIZB+esi], eax
add esi, 2
loop unos_b

movq mm0, qword [NIZA]
movq mm1, qword [NIZB]
paddq mm0, mm1
movq qword [NIZC], mm0


mov esi,NIZC
mov ecx,16
mov eax, ispisC
call print_string
call print_nl

ispis_c:
mov ax, [esi]
movsx eax, ax
call print_int
call print_nl
add esi, 2
loop ispis_c

APICALL ExitProcess, 0

编译给定数组后,用下面两个数组进行测试,第三个数组只存储了16个元素中的4个。(如下图所示)

有人知道为什么它只存储 16 个元素中的 4 个吗?任何帮助表示赞赏。
如果您对函数 print_string print_int print_nl 有任何疑问,这些函数用于通过将字符串压入 EAX 寄存器来打印出字符串、换行符和整数,还要注意这是一个 32 位程序。

【问题讨论】:

  • MMX,嗯? 90 年代后期打来电话,他们有一些 Pets.com 股票要卖给你。如今,SSE 及其继任者几乎普遍可用。
  • 我只看到NIZC 的一个存储,而且它似乎不在循环内,因此您只存储 4 个元素(64 位)似乎很自然。跨度>
  • 另外,mov [NIZA+esi], eax 似乎正在对应该是 16 位值的内容进行 32 位存储。我在你的循环中看到了对print_stringprint_nl等的调用;你确定他们保留了 ecx、esi 和任何其他相关的寄存器吗?最后,the loop instruction is a habit you don't want to develop
  • 使用调试器单步执行可能对理解代码做什么和不做什么很有帮助。你知道怎么做吗?如果没有,现在是学习的时候了。
  • 关于print_stringprint_nl 保留寄存器,你可以在这里看到代码,pastebin.com/ZtTn6RU7,我尝试只使用 mov 和递增来对两个数组求和,并且效果很好跨度>

标签: arrays assembly x86 nasm mmx


【解决方案1】:

有人知道为什么它只存储 16 个元素中的 4 个吗?

因为您让 MMX 指令仅对前 4 个数组元素进行操作。您需要一个循环来处理所有 16 个数组元素。

您的任务描述没有说明,但我看到您在打印之前对 NIZC 中的值进行了签名扩展,因此您似乎期待签名结果。我还看到您使用 PADDQ 对 4 个字大小的输入进行操作。这将不会总是给出正确的结果!例如。如果NIZA[0]=-1NIZB[0]=5,那么你会得到NIZC[0]=4,但是会发生从第一个单词到第二个单词的进位,留下NIZC[1] 错误。如果您使用正确版本的打包添加,则不会发生这种情况:PADDW

您很幸运,mov [NIZA+esi], eaxmov [NIZB+esi], eax 上的大小错误。因为 NIZANIZB 在内存中按照您分配给它们的相同顺序相互跟随,所以没有造成任何伤害。如果 NIZB 将被放置在 NIZA 之前,那么分配 NIZB[15] 将损坏 NIZA[0] .

以下是部分重写,其中我使用了输入 subroutine in order to not have to repeat myself

    mov   eax, unos1
    mov   ebx, NIZA
    call  MyInput
    mov   eax, unos2
    mov   ebx, NIZB
    call  MyInput

    xor   esi, esi
more:
    movq  mm0, qword [NIZA + esi]
    paddw mm0, qword [NIZB + esi]
    movq  qword [NIZC + esi], mm0
    add   esi, 8
    cmp   esi, 32
    jb    more
    emms                     ; (*)

    ...


MyInput:
    call  print_string
    call  print_nl
    xor   esi, esi
  .more:
    call  read_int           ; -> EAX
    mov   [ebx + esi], ax
    add   esi, 2
    cmp   esi, 32            ; Repeat 16 times
    jb    .more
    ret

(*) 有关 emms(空 MMX 状态)的信息,请参阅 https://www.felixcloutier.com/x86/emms

提示:您可以在一条指令中写入mov ax, [esi]movsx eax, axmovsx eax, word [esi]

【讨论】:

  • 对于这么小的循环,你可以完全展开它,特别是如果你切换到使用 XMM 寄存器,所以它只有两个 paddw xmm0, [NIZB] / ... / paddw xmm1, [NIZB+16] 假设你可以对齐你的数组16. 具有 MMX 而不是 SSE2 的 CPU 范围从 Pentium-MMX 到 Pentium-III,以及 AMD 到 Athlon-XP。 x86-64 保证 SSE2 可用,并且像 Pentium-4 这样的一些 32 位 CPU 具有它。 (我知道 cmets 已经指出 MMX 已经过时了,但在 2021 年的任何 MMX 答案中都值得一说!)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-01
  • 2014-01-29
相关资源
最近更新 更多