Константы

Последнее обновление: 02.07.2023

Ассемблер позволяет также определять константы - значения, которые не изменяются в процессе программы. Для этого применяется следующий синтаксис:

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

Вместо знака равно = можно использовать директиву 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 и $

Операторы 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
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850