В отличие от переменных константы представляют неизменяемые значения. Ассемблер NASM позволяет определять константы двумя способами.
Директива EQU позволяет определить константы, которые будут заменяться ассемблером на из значения при компиляции программы. Синтаксис данной инструкции:
название_константы equ значение_константы
В качестве названия константы используется набор символов, обычно некоторое описательное название, которое указывает на предназначение данной константы.
В качестве значения используется некоторое число или выражение, которое возвращает число. Например:
global _start const1 equ 1 const2 equ 2 const3 equ 3 section .text _start: mov rdi, const1 ; RDI = 1 add rdi, const2 ; RDI = RDI + 2 = 3 add rdi, const3 ; RDI = RDI + 3 = 6 mov rax, 60 syscall
В данном случае с помощью директивы equ определяются три контанты: const1, const2, const3. Каждой из них сопоставляется числовое значение 1, 2, 3.
Далее мы можем использовать эти константы как стандартные числа, и ассемблер при компиляции заменит эти константы на их значения. То есть предыдущий код фактически эквивалентен следующему:
global _start section .text _start: mov rdi, 1 ; RDI = 1 add rdi, 2 ; RDI = RDI + 2 = 3 add rdi, 3 ; RDI = RDI + 3 = 6 mov rax, 60 syscall
Но использование описательных имен вместо их значений в тексте программы позволяет легче ориентироваться в коде программы, быстрее понять предназначение тех или иных значений.
Стоит отметить, что после названия константы при ее определении может идти двоеточие, которое необязательно:
const1: equ 1
Константы могут динамически вычисляться
global _start first equ 0 second equ first + 8 ; second = 8 third equ second + 8 ; third = 16 section .text _start: mov rdi, first ; RDI = 0 add rdi, second ; RDI = RDI + 8 = 8 add rdi, third ; RDI = RDI + 16 = 24 mov rax, 60 syscall
Выражения, которые возвращают значение констант, могут применять стандартные арифметические операции, как сложение, вычитание, умножение, где операндами могут быть в том числе и другие константы. Так, в данном случае определяются три константы, которые определяют смещение элементов в массиве nums. Значение константы second зависит от константы first, значение third - от значения константы second. Но в любом случае значения этих констант вычисляется на этапе компиляции.
Соответственно мы можем использовать константы в том числе и в качестве смещения для вычисления адреса. Например, получим значение третьего элемента массива из 8-байтовых чисел в программе на Linux:
global _start section .data nums dq 5, 6, 7 first equ 0 second equ first + 8 ; second = 8 third equ second + 8 ; third = 16 section .text _start: mov rdi, [nums + third] ; RDI = 7 mov rax, 60 syscall
Вместо константы third мы могли бы использовать конкретное число - 16, но third как описательное имя позволит лучше описать намерение, которое заложено в программу.
Аналогичный пример на Windows:
global _start section .data nums dq 5, 6, 7 first equ 0 second equ first + 8 ; second = 8 third equ second + 8 ; third = 16 section .text _start: mov rax, qword [rel nums + third] ; RDI = 7 ret
Также константы могут применяться для определения значений переменных. Пример программы для Linux:
global _start section .data num1 dq intSize ; num1 = 4 num2 dq intSize + 4 ; num2 = 4 + 4 = 8 intSize equ 4 section .text _start: mov rdi, [num2] ; RDI = 8 mov rax, 60 syscall
В данном случае переменные num1 и num2 получают соответственно значения константы intSize и результат выражения (intSize+4).
Аналогичный пример для Windows:
global _start section .data num1 dq intSize ; num1 = 4 num2 dq intSize + 4 ; num2 = 4 + 4 = 8 intSize equ 4 section .text _start: mov rax, qword [rel num2] ; RAX = 7 ret