Подготовка
Для сборки программ нужно установить NASM, а также GCC, если его почему-то нет из коробки в вашем дистрибутиве Linux.
Архитектуру amd64 и имеющиеся инструкции посмотрите тут: x86 Architecture Overview.
Я приготовил вам стильный-модный-молодёжный универсальный makefile с переменными и выводом возврата программы:
EXEC = $(F)
SRC = $(F).asm
OBJ = $(F).o
BUILD_EXEC = ld
ifdef C
BUILD_EXEC = gcc -no-pie
endif
all: $(EXEC)
@echo "---> running $(EXEC)"
@./$(EXEC) || echo "---> $(EXEC) exit code: $$?"
$(EXEC): $(OBJ)
$(BUILD_EXEC) $? -o $@
$(OBJ): $(SRC)
nasm -f elf64 $?
clean:
rm -f $(EXEC) $(OBJ)
Он позволяет собирать произвольные .asm
файлы двумя способами. Для начала соберём hello.asm
:
section .data
message: db "Hello, World!", 10
message_len equ $-message
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, message_len
syscall
mov rax, 60
xor rdi, rdi
syscall
Это можно сделать так:
make F=hello
Поскольку переменная F в мейкфайле не объявлена, её нужно передавать как параметр make и после знака равенства указывать имя файла без расширения.
А теперь более читаемый вариант предыдущей программы:
%define SYS_WRITE 1
%define STDOUT 1
%define SYS_EXIT 60
section .data
message: db "Hello, World!", 10
message_len equ $-message
section .text
global _start
_start:
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, message
mov rdx, message_len
; syscall(SYS_WRITE, STDOUT, message, message_len)
syscall
mov rax, SYS_EXIT
xor rdi, rdi
; syscall(SYS_EXIT, exit_code)
syscall
Простые вычисления
Изучите арифметические инструкции из этого примера:
%define SYS_EXIT 60
section .text
global _start
_start:
mov rbx, 5
add rbx, 3
mov rax, 2
sub rbx, rax
imul rbx, 2
; Попробуйте выполнить эти операции вместо предыдущей
; imul rbx, rax, 17
; mul rbx
mov rdx, 0
mov rax, 1000
idiv rbx
mov rdi, rax
mov rax, SYS_EXIT
syscall
Результат вычислений возвращается в ОС и будет выведен при запуске собранной программы мейкфайлом:
$ make F=calc
nasm -f elf64 calc.asm
ld calc.o -o calc
---> running calc
---> calc exit code: 83
Но поскольку код возврата не может быть больше 255, серьёзным этот подход не назовёшь. Используем Glibc:
%define SYS_EXIT 60
global main
extern printf
section .data
format:
db "%ld", 10, 0
section .text
main:
mov rbx, 5
add rbx, 3
mov rax, 2
sub rbx, rax
imul rbx, 2
mov rdx, 0
mov rax, 1000
idiv rbx
mov rdi, format
mov rsi, rax
xor rax, rax
sub rsp, 8
call printf
add rsp, 8
xor rdi, rdi
mov rax, SYS_EXIT
syscall
Собрать можно например так:
make F=calc2 C=1
Makefile проверяет только наличие переменной C
, поэтому в неё можно подавать любое значение.
Ветвление
Посмотрим как можно сделать простое ветвление:
%define SYS_EXIT 60
%define SYS_WRITE 1
%define STDOUT 1
section .data
msg_equal: db "rbx is 42", 10
msg_equal_len equ $-msg_equal
msg_unequal: db "rbx is not 42", 10
msg_unequal_len equ $-msg_unequal
section .text
global _start
_start:
mov rbx, 42
cmp rbx, 42
jne unequal
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, msg_equal
mov rdx, msg_equal_len
syscall
jmp done
unequal:
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, msg_unequal
mov rdx, msg_unequal_len
syscall
done:
xor rdi, rdi
mov rax, SYS_EXIT
syscall
Также обратите внимание на условные инструкции (cmovl
) в maxofthree
из примера Mixing C and Assembly Language в NASM Tutorial.
Задание
Теперь вы можете объединить полученные знания и написать на ассемблере свой вариант ЛР №1 по C. Используйте перевёрнутые частные из условий задач и целочисленное деление. Также необходимо организовать ввод параметров с использованием scanf
.