Ассемблер позволяет также определять константы - значения, которые не изменяются в процессе программы. Для этого применяется следующий синтаксис:
label = expression
label
представляет имя константы, и с помощью знака равно ей присваивается некоторое значение. Например:
number = 128
Здесь определена константа number, которая равна 128.
Константы можно определять в любом месте программы - в секциях .code
и .data
или вообще вне какой-либо секции.
number = 128 ; определяем константу .code main proc mov eax, number ; Помещаем в регистр EAX константу number ret main endp end
Стоит отметить, что и константы, и данные для чтения из секции .const
, представляют такие значения, которые нельзя изменить. Однако доступ к секции .const определяется
операционной системой - она устанавливает для области памяти секции .const
, что она доступна только для чтения. Кроме того, для переменных .const
явным образом указывается тип данных при определении.
Константы могут предоставлять значение для переменных:
number = 128 .data i32 dword number ; передаем переменной i32 значение константы number .code main proc mov eax, i32 ret main endp end
Вместо знака равно = можно использовать директиву equ
number equ 128
Хотя и символ = и директива equ
применяются для определения констант, между этими двумя способами есть небольшие различия. Так, символ = позволяет переопределить константу в различных
частях программы:
number = 128 .data i32 dword number ; передаем переменной i32 значение константы number .code number = 256 ; переопределяем константу main proc mov eax, i32 ; EAX = 128 ret main endp end
Другое различие состоит в том, что константы, определенные с помощью =, должны быть представлены как целые числа 64-битной или меньшей разрядности . С оператором = можно использовать даже строки длиной восемь или меньше символов (что подходит для 64-битного значения).
text = "hello" .code main proc mov rax, text ret main endp end
Оператор equ
не имеет такого ограничения.
Оператор = вычисляет значение числового выражения и сохраняет это значение для подстановки везде, где эта константа появляется в программе. Директива equ, если ее операнд можно преобразовать в числовое значение, будет работать точно так же. Однако, если операнд equ не может быть преобразован в числовое значение, то директива equ сохранит свой операнд как текстовые данные и подставит эти текстовые данные вместо константы
letters equ "ABCDEFGHIJKL" .data text byte letters .code main proc mov rax, 0 mov al, text ; AL = 65 - числовой код первого символа константы letters ret main endp end
Директива equ
также позволяет задать сложные выражения. Например:
reg64 equ <rbx> .code main proc mov rbx, 22 mov rax, reg64 ; RAX = RBX = 22 ret main endp end
Здесь определена константа reg64, которая представляет регистр RBX. Чтобы присвоить константе выражение, применяются угловые скобки. В итоге везде, где применяется данная константа, фактически будет использоваться регистр RBX.
Константы поддерживают ряд операций. Арифметические операции:
-
(унарное отрицание): фактически умножает значение константы на -1
*
: умножение константы
/
: целочисленное деление
mod
: возвращает остаток от деления
+
: сложение
-
: вычитание
n1 = 7 n2 = 4 .code main proc mov rdx, n1 + n2 ; в регистре RDX число 11 mov rax, n1 mod n2 ; в регистре RAX число 3 ret main endp end
Также поддерживается ряд операций сравнения:
EQ
: возвращает true, если оба операнда равны.
NE
: возвращает true, если оба операнда не равны.
LT
: возвращает true, если левый операнд меньше правого операнда.
LE
: возвращает true, если левый операнд меньше или равен правому операнду
GT
: возвращает true, если левый операнд больше правого операнда.
GE
: возвращает истину, если левый операнд больше или равен правому операнду.
true
в ассемблере означает число -1 (или 0FFFFFF...FFh), false
число 0. Таким образом, мы можем сравнивать две константы, а результат сравнения - число -1 или 0 поместить в
регистр:
n1 = 5 n2 = 2 .code main proc mov rdx, n1 EQ n2 ; в регистре RDX число 0 - условие не верно mov rax, n1 NE n2 ; в регистре RAX число -1 - условие верно ret main endp end
Здесь условие n1 EQ n2
возращает false
, так как числа n1 и n2 не равны, поэтому в регистр rdx будет помещено число 0. А вот условие n1 NE n2
возвращает true, поэтому в регистре RAX будет число -1.
Также доступны логические операции:
AND
: возвращает результат поразрядной операции AND двух операндов
OR
: возвращает результат поразрядной операции OR двух операндов
NOT
: возвращает результат поразрядной инверсии операнда
n1 = 5 n2 = 6 .code main proc mov rax, n1 AND n2 ; n1 AND n2 = 101 AND 110 = 100 = 4 ret main endp end
Стоит отметить, что MASM сразу же интерпретирует значение константного выражения во время сборки. Он не заменяет константные выражения соответствующими инструкциями. Например
n1 = 4 n2 = 3 sum = n1 + n2 .code main proc mov eax, sum ret main endp end
После сборки программы здесь константа sum будет иметь значение 7 - оно вычисляется на этапе компиляции.
Операторы this и $ имеют специальное значение и синонимичны друг другу. Они возвращают текущее смещение относительно начала секции, которая их содержит. Данное смещение еще называется счетчик адреса или location counter. Обычно данный оператор применяется для является вычисления размера блока данных:
.data buffer byte 1, 2, 3, 4, 5, 6 len = $-buffer ; от текущего адреса отнимаем адрес переменной buffer .code main proc mov rax, len ; len = 6 ret main endp end
Здесь константа len
равна результату выражения $-buffer
, то от текущего адреса отнимаем адрес переменной buffer. Таким образом, len будет содержать размер
блока buffer в байтах. Учитывая, что buffer представляет набор чисел byte, то фактически len будет содержать количество чисел в buffer.
Подобным образом можно вычислять размер более сложных данных:
.data buffer byte 1, 2, 3, 4, 5, 6 byte 7, 8, 9, 10, 11 len = $-buffer ; len = 11
Оператор this отличается от оператора $ тем, что позволяет после себя указать тип данных - this тип_данных
, где тип данных - один из стандартных типов ассемблера (byte, sbyte, word, sword и т.д.):
.data buffer byte 1, 2, 3, 4, 5, 6 len = this dword - buffer ; len представляет тип dword .code main proc mov eax, len ; len = 6 ret main endp end