我想编写一个x86程序,该程序将2个数组的相应元素(array1[0]*array2[0]
等乘以5个元素)相乘并将结果存储在第三个数组中。我什至不知道从哪里开始。任何帮助将不胜感激。
答案 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
后缀有操作数长度。%
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将显示结果。