NASM предоставляет различные директивы определения резервирования места для хранения переменных. В ассемблере директива определения используется для выделения дискового пространства. Она может использоваться для резервирования или инициализации одного/нескольких байт.
Хранения инициализированных данных
Синтаксис стейтмента выделения памяти для инициализированных данных следующий:
1 |
[имя_переменной] директива_определения начальное_значение [,начальное_значение]... |
Где имя_переменной
является идентификатором для каждого пространства для хранения. Ассемблер связывает значение смещения для каждого имени переменной, определенного в сегменте данных.
Есть 5 основных форм директивы определения:
Директива | Цель | Пространство для хранения |
DB | Определяет Byte | Выделяет 1 байт |
DW | Определяет Word | Выделяет 2 байта |
DD | Определяет Doubleword | Выделяет 4 байта |
DQ | Определяет Quadword | Выделяет 8 байт |
DT | Определяет 10 Byte | Выделяет 10 байт |
Ниже приведены примеры использования директив определения:
1 2 3 4 5 6 |
choice DB 'y' number DW 12345 neg_number DW -12345 big_number DQ 123456789 real_number1 DD 1.234 real_number2 DQ 123.456 |
Обратите внимание, что:
каждый байт символа хранится как его ASCII-значение в шестнадцатеричном формате;
каждое десятичное значение автоматически конвертируется в 16-битный двоичный эквивалент и сохраняется в виде шестнадцатеричного числа;
процессор использует прямой порядок байтов;
отрицательные числа конвертируются в форму «two’s complement»;
короткие и длинные числа типа с плавающей точкой представлены с использованием 32 или 64 бит, соответственно.
Следующая программа показывает использование директивы определения:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
section .text global _start ; должно быть объявлено для линкера (gcc) _start: ; сообщаем линкеру входную точку mov edx,1 ; длина сообщения mov ecx,choice ; сообщение для вывода mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data choice DB 'y' |
Результат выполнения программы:
y
Хранение неинициализированных данных
Директивы резервирования используются для резервирования пространства для неинициализированных данных. Директивы резервирования принимают один операнд, который определяет количество единиц пространства, которое будет зарезервировано. Каждая директива определения имеет связанную директиву резервирования.
Есть 5 основных форм директив резервирования:
Директива | Цель |
RESB | Резервирует Byte |
RESW | Резервирует Word |
RESD | Резервирует Doubleword |
RESQ | Резервирует Quadword |
REST | Резервирует 10 Byte |
Несколько определений
Вы можете иметь несколько стейтментов определения данных в программе. Например:
1 2 3 |
choice DB 'Y' ; ASCII-значение для y = 79H number1 DW 12345 ; 12345D = 3039H number2 DD 12345679 ; 123456789D = 75BCD15H |
Ассемблер выделяет смежную память для нескольких определений переменных.
Несколько инициализаций
Директива TIMES позволяет выполнить несколько инициализаций одного значения. Например, массив с именем marks
длиной 9
может быть определен и инициализирован нулем следующим образом:
1 |
marks TIMES 9 DW 0 |
Директива TIMES полезна при определении массивов и таблиц. Следующая программа выводит на экран 9 звёздочек:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
section .text global _start ; должно быть объявлено для линкера (ld) _start: ; сообщаем линкеру входную точку mov edx,9 ; длина сообщения mov ecx, stars ; сообщение для написания mov ebx,1 ; файловый дескриптор (stdout) mov eax,4 ; номер системного вызова (sys_write) int 0x80 ; вызов ядра mov eax,1 ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data stars times 9 db '*' |
Результат выполнения программы:
*********
Определение констант
NASM предоставляет несколько директив, определяющих константы:
директива EQU;
директива %assign;
директива %define.
Директива EQU
Директива EQU используется для определения констант. Её синтаксис следующий:
ИМЯ_КОНСТАНТЫ EQU-выражение
Например:
1 |
TOTAL_STUDENTS equ 50 |
Затем вы можете использовать эту константу в программе:
1 2 |
mov ecx, TOTAL_STUDENTS cmp eax, TOTAL_STUDENTS |
Операндом стейтмента EQU может быть выражение:
1 2 3 |
LENGTH equ 20 WIDTH equ 10 AREA equ length * width |
Вышеприведенный фрагмент кода определит AREA
как 200
.
Еще один пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
SYS_EXIT equ 1 SYS_WRITE equ 4 STDIN equ 0 STDOUT equ 1 section .text global _start ; должно быть объявлено для линкера (gcc) _start: ; сообщаем линкеру входную точку mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, msg1 mov edx, len1 int 0x80 mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, msg2 mov edx, len2 int 0x80 mov eax, SYS_WRITE mov ebx, STDOUT mov ecx, msg3 mov edx, len3 int 0x80 mov eax,SYS_EXIT ; номер системного вызова (sys_exit) int 0x80 ; вызов ядра section .data msg1 db 'Hello, programmers!',0xA,0xD len1 equ $ - msg1 msg2 db 'Welcome to the world of,', 0xA,0xD len2 equ $ - msg2 msg3 db 'Linux assembly programming! ' len3 equ $- msg3 |
Результат выполнения программы:
Hello, programmers!
Welcome to the world of,
Linux assembly programming!
Директива %assign
Директива %assign может быть использована для определения числовых констант. Эта директива допускает переопределение. Например, вы можете определить константу TOTAL
следующим образом:
1 |
%assign TOTAL 10 |
Затем в коде вы можете переопределить её:
1 |
%assign TOTAL 20 |
Эта директива является чувствительной к регистру.
Директива %define
Директива %define позволяет определять как числовые, так и строковые константы. Эта директива похожа на директиву #define
в языке С. Например, вы можете определить константу PRT
следующим образом:
1 |
%define PTR [EBP+4] |
Вышеприведенный код заменяет PTR
на [EBP+4]
.
Эта директива также допускает переопределение и является чувствительной к регистру.
Хотелось бы все-таки чуть подробнее разбирать код примеров.
Что значит 0xA,0xD в конце строк?
Почему длина сообщения определяется выражением $ — msg1?
0xA, 0xD это коды спец символов, которые переводят вывод на следующую строку.
$ — msg1 означает что от текущего адреса отнимается адрес сообщения.
$ после объявления строки сдвигается ровно на размер строки, а значит что $ — msg1 будет давать размер строки.
> коды спец символов, которые переводят вывод на следующую строку
Ок. А зачем они идут подряд? Ведь оба делают одно и то же, как я понял.
В Unix системах можно только 0xA, а вот в виндовых вроде и то и то используется