数组中对应值的乘法

时间:2019-05-18 20:45:09

标签: arrays assembly x86 multiplication

我想编写一个x86程序,该程序将2个数组的相应元素(array1[0]*array2[0]等乘以5个元素)相乘并将结果存储在第三个数组中。我什至不知道从哪里开始。任何帮助将不胜感激。

1 个答案:

答案 0 :(得分:5)

您首先要获得的是汇编器,我个人是NASM的忠实拥护者,我认为它的语法非常简洁明了,这也是我从此开始的,这就是我将要使用的语法。这个答案。 除了NASM,您还有:

  • GAS

    这是GNU汇编程序,与NASM不同,许多体系结构都有版本,因此,如果切换体系结构,指令和工作方式将与指令大致相同。但是,GAS确实具有不利的一面,即它对希望使用Intel语法的人不友好。

  • FASM
    这是平面汇编程序,它是用Assembly编写的汇编程序。像NASM一样,它对想要使用AT&T语法的人也不友好。它有一些粗糙的边缘,但有些人似乎更喜欢在DOS应用程序中使用(尤其是因为它有DOS端口)和裸机。

现在您可能正在阅读“ AT&T语法”和“英特尔语法”,并想知道这是什么意思。这些是x86汇编的方言,它们都汇编为同一机器代码,但反映出对每条指令的思考方式略有不同。 AT&T语法趋向于更冗长,而Intel语法趋向于最小化,但是AT&T语法的某些部分具有比Intel语法更好的操作数排序,mov指令的一个很好的证明是:

AT&T语法:

movl (0x10), %eax

这意味着获取长值(1个双字,也称为4个字节)并将其放入寄存器eax中。注意以下事实:

  • mov后缀有操作数长度。
  • 内存地址用括号括起来(您可以将它们视为C中的指针取消引用)
  • 寄存器的前缀为%
  • 指令将左操作数移到右操作数

Intel语法:

mov eax, [0x10]

请注意以下事实:

  • 我们不需要在指令后加上操作数大小,汇编程序会推断出来,在某些情况下它不能,在这种情况下,我们在地址旁边指定大小。
  • 该寄存器没有前缀
  • 方括号用于寻址内存
  • 第二个操作数被移入第一个操作数

此答案将使用Intel语法。
在计算机上安装NASM之后,您将需要一个简单的构建脚本(当您开始编写较大的程序时,请使用Makefile或其他适当的构建系统,但现在可以了):

nasm -f elf arrays.asm
ld -o arrays arrays.o -melf_i386
rm arrays.o
echo
echo " Done building, the file 'arrays' is your executable"

请记住chmod +x脚本,否则您将无法执行该脚本。 现在,获取代码以及解释所有含义的注释:

global _start ; The linker will be looking for this entrypoint, so we need to make it public

section .data ; We're going on to describe our data here
    array_length equ 5 ; This is effectively a macro and isn't actually being stored in memory
    array1 dd 1,4,1,5,9 ; dd means declare dwords
    array2 dd 2,6,5,3,5

    sys_exit equ 1

section .bss ; Data that isn't initialised with any particular value
    array3 resd 5 ; Leave us 5 dword sized spaces

section .text
_start:
    xor  ecx,ecx     ; index = 0 to start
    ; In a Linux static executable, registers are initialized to 0 so you could leave this out if you're never going to link this as a dynamic executable.

    _multiply_loop:
        mov eax, [array1+ecx*4] ; move the value at the given memory address into eax
        ; We calculate the address we need by first taking ecx (which tells us which
        ; item we want) multiplying it by 4 (i.e: 4 bytes/1 dword) and then adding it
        ; to our array's start address to determine the address of the given item
        imul eax, dword [array2+ecx*4] ; This performs a 32-bit integer multiply
        mov dword [array3+ecx*4], eax ; Move our result to array3

        inc ecx ; Increment ecx
        ; While ecx is a general purpose register the convention is to use it for
        ; counting hence the 'c'
        cmp ecx, array_length ; Compare the value in ecx with our array_length
        jb _multiply_loop ; Restart the loop unless we've exceeded the array length

    ; If the loop has concluded the instruction pointer will continue
_exit:
    mov eax, sys_exit ; The system call we want
    ; ebx is already equal to 0, ebx contains the exit status
    mov ebp, esp ; Prepare the stack before jumping into the system
    sysenter ; Call the Linux kernel and tell it that our program has concluded

如果想要32位乘法的完整64位结果,请使用单操作数mul。但是通常您只希望结果的宽度与输入的宽度相同,在这种情况下,imul是最有效且最容易使用的结果。请参阅x86 tag wiki中的链接以获取文档和教程。

您会注意到该程序没有输出。我不会写算法来打印数字,因为我们会整天待在这里,这是读者(或see this Q&A)的练习

但是,与此同时,我们可以在gdbtui中运行程序并检查数据,使用构建脚本进行构建,然后使用命令gdbtui arrays打开程序。您需要输入以下命令:

layout asm
break _exit
run
print (int[5])array3

GDB将显示结果。