Язык ассемблера - Базовый синтаксис
В этой главе описываются базовая структура, правила синтаксиса и соглашения о написании программ на ассемблере NASM, помогая вам понять скелет кода на ассемблере.
Базовая структура программы на ассемблере
Полная программа на ассемблере NASM обычно состоит из следующих частей:
Пример
; Пример базовой структуры программы на NASM
section .data ; Секция данных: хранит инициализированные данные
; Определите переменные и константы здесь
msg db 'Hello, EXAMPLE!', 0xA
len equ $ - msg
section .bss ; Секция BSS: хранит неинициализированные данные
; Здесь зарезервировать область памяти
buffer resb 64 ; Зарезервировать буфер размером 64 байта
section .text ; Секция кода: хранит исполняемые инструкции
global _start
_start:
; Здесь напишите логику программы
mov eax, 4
mov ebx, 1
mov ecx, msg
mov edx, len
int 0x80
mov eax, 1
mov ebx, 0
int 0x80
| Раздел | Назначение | Характеристики |
|---|---|---|
.data | Хранит инициализированные глобальные переменные и константы | Размер и содержимое определяются во время компиляции, хранятся в исполняемом файле |
.bss | Хранит неинициализированные глобальные переменные | Пространство выделяется только во время выполнения, не занимает размер исполняемого файла |
.text | Хранит исполняемые машинные инструкции | Только для чтения, содержит весь логический код программы |
Как минимум
.textсекции требуются для формирования корректной программы на ассемблере. Если данных нет, можно опустить.dataи.bssсекцию.
Формат инструкции ассемблера
Общий формат каждой инструкции ассемблера:
[标签:] 指令助记符 [操作数1 [, 操作数2 [, 操作数3]]] [; 注释]
Пояснение каждой части:
| Часть | Обязательно? | Описание |
|---|---|---|
| Label | Optional | Символическое имя, представляющее адрес памяти, заканчивающееся двоеточием |
| Мнемоника инструкции | Обязательный | Например, mov, add, sub и т. д., сообщает процессору, что делать |
| Операнд | Необязателен (у некоторых инструкций нет операнда) | Объект данных, с которым работает инструкция; может быть регистром, адресом памяти или непосредственным значением |
| Комментарий | Optional | Начинается с точки с запятой и продолжается до конца строки |
Пример
; Только инструкция, без операнда
ret ; Возврат из подпрограммы
; Инструкция + один операнд
push eax ; Поместить значение eax в стек
inc ecx ; Увеличить ecx на 1
; Инструкция + два операнда (наиболее распространённый случай)
mov eax, 42 ; Скопировать 42 в регистр eax
add ebx, ecx ; ebx = ebx + ecx
; С меткой
loop_start: ; Метка: обозначает начало цикла
dec ecx ; Вычесть 1 из ecx
jnz loop_start ; Если ecx не равен 0, перейти обратно к loop_start
Соглашения о комментариях
NASM используетточку с запятой (;)для обозначения комментариев; содержимое от точки с запятой до конца строки игнорируется ассемблером.
Пример
; Вычислить сумму двух чисел и вывести результат
mov eax, 10 ; Комментарий в строке: поместить 10 в eax
add eax, 20 ; Комментарий в строке: добавить 20 к eax, теперь eax = 30
Комментарии чрезвычайно важны в коде на ассемблере. Без комментариев даже автор может не понять код на ассемблере через несколько недель. Выработайте привычку писать комментарий к каждой инструкции.
Правила именования идентификаторов
Идентификаторы (метки, имена переменных, имена констант и т. д.) должны подчиняться следующим правилам:
| Правило | Описание |
|---|---|
| Символы, входящие в состав | Буквы, цифры, подчёркивание _, точка ., вопросительный знак ?, @, $, # и т. д. |
| Начальный символ | Должно начинаться с буквы, подчёркивания, точки или вопросительного знака; не может начинаться с цифры. |
| Чувствительность к регистру | По умолчанию чувствительны к регистру (можно изменить с помощью параметров компиляции) |
| Зарезервированные слова | Не могут иметь такое же имя, как мнемоники инструкций, имена регистров или ключевые слова NASM. |
Пример
my_variable: ; Начинается с буквы + подчёркивания
.loop_start: ; Начинается с точки (локальная метка)
?error_handler: ; Начинается с вопросительного знака
counter2: ; Буква + цифра
; Недопустимые идентификаторы (только для справки, не используйте)
; 1st_value: ; Ошибка: не может начинаться с цифры
; mov: ; Ошибка: mov — зарезервированное слово
; my-variable: ; Ошибка: дефис не является допустимым символом
Псевдоинструкции (директивы)
ДирективыЭто команды для ассемблера, а не инструкции для процессора; они управляют процессом ассемблирования и определяют структуры данных.
| Директивы | Назначение | Пример |
|---|---|---|
db | Определить байт (1 байт) | byte_val db 0x55 |
dw | Определить слово (2 байта) | word_val dw 0x1234 |
dd | Определить двойное слово (4 байта) | dword_val dd 0x12345678 |
equ | Определить константу | MAX_SIZE equ 256 |
resb | Зарезервировать байтовое пространство | buffer resb 128 |
resw | Зарезервировать пространство под слово | wbuf resw 64 |
resd | Зарезервировать пространство под двойное слово | dbuf resd 32 |
%define | Константа, определённая макросом | %define COUNT 10 |
Соглашения о регистре
NASM по умолчанию для меток и идентификаторовчувствителен к регистру.:
Пример
section .data
msg db 'EXAMPLE', 0 ; Определить переменную msg
section .text
global _start
_start:
mov eax, MSG ; Ошибка: MSG и msg различаются (если только не включён режим нечувствительности к регистру)
mov eax, msg ; Верно: msg точно совпадает с определением
MOV EAX, 42 ; Синтаксически верно: мнемоники инструкций нечувствительны к регистру
mov eax, 42 ; Рекомендуемый стиль: используйте нижний регистр для лучшей читаемости
Мнемоники инструкций и имена регистров нечувствительны к регистру (
MOV、Mov、movтот же эффект), но рекомендуемый стиль — единообразно в нижнем регистре.
Числовое представление
NASM поддерживает числовое представление в нескольких системах счисления:
Пример
mov eax, 42 ; Десятичное: запишите число напрямую
mov eax, 0x2A ; Шестнадцатеричное: 0x (рекомендуемый стиль)
mov eax, 2 ; Шестнадцатеричное: суффикс h
mov eax, 0o52 ; Восьмеричное: префикс 0o
mov eax, 52o ; Восьмеричное: суффикс o
mov eax, 101010b ; Двоичное: суффикс b
mov eax, 0b101010 ; Двоичное: префикс 0b
Рекомендуется:
0xиспользуйте префикс для шестнадцатеричных чисел (например,0x2A), поэтому его меньше шансов спутать с метками.
Полный пример синтаксиса
Следующая программа всесторонне использует вышеуказанные элементы синтаксиса, чтобы вычислить сумму чисел от 1 до 10 и вывести её:
Пример
; Вычислить 1+2+...+10 и вывести полученный символ
section .data
result db 0 ; Сохранить результат вычисления (1 байт)
newline db 0xA ; Символ новой строки
section .text
global _start
_start:
; Инициализировать регистры и переменные
mov ecx, 10 ; Счётчик цикла: обратный отсчёт от 10
mov eax, 0 ; eax хранит накопленную сумму, изначально 0
sum_loop: ; Метка начала цикла
add eax, ecx ; eax = eax + ecx
dec ecx ; ecx уменьшается на 1
jnz sum_loop ; Если ecx != 0, продолжить цикл
; На этом этапе eax = 55 (10+9+...+1)
add eax, '0' ; Преобразовать число в ASCII-символ ('0'=48, 55+48=103='g', неверно)
; Фактическая демонстрация требует более сложного преобразования, см. в последующих главах
; Здесь выводится только результат (упрощённая демонстрация)
mov [result], al ; Сохранить накопленный результат в result
; Выход из программы
mov eax, 1
mov ebx, 0
int 0x80
Другие расширенияПримечание: в приведённом выше примере прямое добавление '0' корректно только тогда, когда число находится в диапазоне 0-9. Преобразование чисел с двумя или более цифрами будет подробно объяснено в последующих главах.