Первое, что вы захотите получить, это ассемблер, я лично большой поклонник 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 отобразит результаты.