Умножение соответствующих значений в массиве - PullRequest
0 голосов
/ 18 мая 2019

Я хочу написать программу x86, которая умножает соответствующие элементы на 2 массива (array1[0]*array2[0] и т. Д. До 5 элементов) и сохраняет результаты в третьем массиве. Я даже не знаю с чего начать. Любая помощь с благодарностью.

1 Ответ

5 голосов
/ 19 мая 2019

Первое, что вы захотите получить, это ассемблер, я лично большой поклонник NASM, на мой взгляд, у него очень чистый и лаконичный синтаксис, это также то, с чем я начал, так что именно это я буду использовать для этот ответ Помимо NASM у вас есть:

  • GAS

    Это ассемблер GNU, в отличие от NASM, существуют версии для многих архитектур, поэтому директивы и способ работы будут примерно такими же, как и инструкции, если вы переключаете архитектуры. Однако у GAS есть неприятный недостаток: он несколько недружелюбен к людям, которые хотят использовать синтаксис Intel.

  • FASM
    Это плоский ассемблер, это ассемблер, написанный на ассемблере. Как и NASM, это неприемлемо для людей, которые хотят использовать синтаксис AT & T. У этого есть несколько грубых краев, но некоторые люди, кажется, предпочитают это для приложений DOS (особенно потому, что у него есть порт DOS) и голой металлической работы.

Теперь вы, возможно, читаете «Синтаксис AT & T» и «Синтаксис Intel» и задаетесь вопросом, что под этим подразумевается. Это диалекты ассемблера x86, они оба собираются на одном и том же машинном коде, но отражают немного разные взгляды на каждую инструкцию. Синтаксис AT & T имеет тенденцию быть более многословным, в то время как синтаксис Intel имеет тенденцию быть более минимальным, однако некоторые части синтаксиса AT & T имеют более хорошие порядки операндов, чем синтаксис Intel, хорошей демонстрацией различия является инструкция mov:

Синтаксис AT & T:

movl (0x10), %eax

Это значит получить длинное значение (1 dword, или 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

Если вы хотите получить полный 64-битный результат 32-битного умножения, используйте один операнд mul. Но обычно вам нужен только результат, равный ширине входных данных, и в этом случае imul наиболее эффективен и прост в использовании. Смотрите ссылки в x86 wiki для документации и учебников.

Вы заметите, что эта программа не имеет выходных данных. Я не собираюсь рассказывать о написании алгоритма для печати чисел, потому что мы будем здесь весь день, это упражнение для читателя (или см. Этот вопрос & 10 * *)

Однако, тем временем мы можем запустить нашу программу в gdbtui и проверить данные, использовать скрипт сборки для сборки, а затем открыть вашу программу с помощью команды gdbtui arrays. Вы хотите ввести эти команды:

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

И GDB отобразит результаты.

...