Параметры макроса

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

С помощью параметров макросы могут получать извне некоторые данные. Параметры указываются в объявлении макроса через запятую после слова macro:

имя_макроса macro параметр1, параметр2, ... параметрN
    код макроса
endm

При вызове макроса после его имени указываются через запятую значения для параметров:

имя_макроса значение_параметра1, значение_параметра2, ... значение_параметраN

Например, определим макроса, который складывает 128-разрядное число с 64-разрядным и оба числа получает через параметры:

.data
    num qword 0ffffffffffffffffh,   ; младшие 64 бит
                               4    ; старшие 64 бит
.code
add128 macro l64, h64, number64
    add l64, number64
    adc h64, 0 
endm 

main proc
    mov rax, num        ; младшие 64 бит - в RAX
    mov rdx, num[8]     ; старшие 64 бит - в RDX
    mov rbx, 3
    add128 rax, rdx, rbx    ; вставляем макрос
    ret
main endp
end

В данном случае макрос называется add128 и принимает три параметра: l64 (младшие 64 бита 129-разрядного числа), h64 (старшие 64 бита 129-разрядного числа), number64 (прибавляемое число)

add128 macro l64, h64, number64
    add l64, number64
    adc h64, 0 
endm

Внутри макроса проводим все арифметические операции с числами. При этом на момент написания макроса мы можем не знать, как конкретно будут передаваться эти числа - через какие регистры и т.д.

При вызове макроса в процедуре main передаем его параметрам значения по позиции:

add128 rax, rdx, rbx

То есть параметру l64 передается значение регистра RAX, параметру h64 - значение регистра RDX, а параметру number64 - регистр RBX.

В качестве значений для параметров мы можем передавать в макросы регистры (как в случае выше), переменные, константы, главное, чтобы они удовлетворяли требованиям инструкций, с которыми они используются.

Проверка параметров

Вполне может быть ситуация, когда по какой-то причине макросу будет передано меньшее количество значений, чем у него есть параметров. К примеру, если мы используем макрос, который написан не нами и к исходному коду которого у нас нет доступа. Например:

add128 rax, rdx

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

  • ifnb arg: условие истинно, если значение для параметра не указано

  • ifdif arg1, arg2: условие истинно, если оба параметра различаются (учитывается регистр символов)

  • ifdifi arg1, arg2: условие истинно, если оба параметра различаются (регистр символов не учитывается)

  • ifidn arg1, arg2: условие истинно, если оба параметра идентичны (учитывается регистр символов)

  • ifidni arg2, arg2: условие истинно, если оба параметра идентичны (регистр символов не учитывается)

  • ifb arg: условие истинно, если значение для параметра не указано. Фактически является сокращением выражения ifidn <arg>, <>

В данном случае arg, arg1, arg2 - значения, которые передаются операторам if

Проверим наличие значения для параметра:

.data
    num qword 0ffffffffffffffffh,   ; младшие 64 бит
                               4    ; старшие 64 бит
.code
add128 macro l64, h64, number64
    ; проверяем наличие параметра number64
    ifb <number64>
        .err <add128 requires 3 operands>
    endif

    add l64, number64
    adc h64, 0 
endm 

main proc
    mov rax, num        ; младшие 64 бит - в RAX
    mov rdx, num[8]     ; старшие 64 бит - в RDX
    mov rbx, 3
    add128 rax, rdx     ; передаем только два значения

    ret
main endp
end

В коде макроса с помощью выражения

ifb <number64>

проверяем отсутствие значения для параметра number64. И если это значение отсутствует, выполняем выражение .err

.err <add128 requires 3 operands>

Оператору .err в угловых скобках передается сообщение об ошибке, которое будет отображаться при компиляции при отстуствии значения для number64.

В качестве альтернативы проверки значения параметра мы можем использовать суффикс :req, который указывает, что параметр обязательный (required):

add128 macro l64:req, h64:req, number64:req
    add l64, number64
    adc h64, 0 
endm 

В этом случае, если параметру не будет передано значение, в процессе компиляции мы получим ошибку типа следующей:

hello.asm(14) : error A2125:missing macro argument

Другое решение проблемы отсутствующих параметров - установка для параметров значений по умолчанию:

.data
    num qword 0ffffffffffffffffh,   ; младшие 64 бит
                               4    ; старшие 64 бит
.code
add128 macro l64:=<rax>, h64:=<rdx>, number64:=<0>
    add l64, number64
    adc h64, 0 
endm 

main proc
    mov rax, num        ; младшие 64 бит - в RAX
    mov rdx, num[8]     ; старшие 64 бит - в RDX
    mov rbx, 3
    add128              ; без аргументов
    ret
main endp
end

Для установки значения по умолчанию с помощью оператора := параметру присваивается в угловых скобках значение по умолчанию. Например:

l64:=<rax>

В данном случае, если параметру l64 не передано значения, то он берет значение из регистра RAX. Аналогичным образом можно установить конкретные значения. Например, для параметра number64 значение по умолчанию - число 0:

number64:=<0>

Оператор opattr

Чтобы проверить, что именно представляет аргумент макроса, применяется оператор opattr:

opattr выражение

В качестве операнда он принимает некоторое выражение - это может быть и аргумент макроса, и какое-то более сложное по структуре выражение.

opattr возвращает целочисленное значение, которые представляет битовую маску и которое позволяет определить тип выражения:

Бит

Значение

0

Выражение представляет метку

1

Выражение представляет перемещаемое значение

2

Выражение представляет константу

3

Выражение представляет прямую адресацию

4

Выражение представляет регистр

5

устравшее (выражение содержит неопределенный идентификатор)

6

Выражение представляет обращение к памяти стека

7

Выражение ссылается на внешний идентификатор

Применим оператор opattr:

.data
    num qword 0ffffffffffffffffh,   ; младшие 64 бит
                               4    ; старшие 64 бит
.code
add128 macro l64, h64, number64
    ; проверяем, что l64 и h64 - регистры
    if (((opattr l64) and 10h) AND ((opattr h64) and 10h))
        add l64, number64
        adc h64, 0 
    else
        .err <the first two operands of add128 should be registers>
    endif
endm 

main proc
    mov rax, num        ; младшие 64 бит - в RAX
    mov rdx, num[8]     ; старшие 64 бит - в RDX
    mov rbx, 3
    add128 rax, 4, 3     ; второй параметр должен быть регистром, а не константой

    ret
main endp
end

Здесь мы проверяем, что первые два параметра макроса - l64 и h64 являются регистрами. Если параметр - регистр, то выражение opattr l64 (как и opattr h64) возвратит байт, у которого установлен 4 бит. Чтобы проверить его установку, мы можем применить логическое умножение на 10h и ли число 16 в лесятичной системе

(opattr l64) and 10h

И если 4-й бит установлен, то выражение возвратит 1. Подобным образом проверяем и второй параметр макроса.

Если первые два параметра представляют регистры, то выполняем сложение, если нет - то генерируем ошибку с определенным сообщением.

Для проверки параметра можно использовать следующие маски:

  • 0: неопределенный символ или некорректное выражение

  • 34 / 22h: доступ к памяти в виде [reg + const]

  • 36 / 24h: константа

  • 37 / 25h: имя процедуры или метка с символом :

  • 38 / 26h: выражение в виде offset var, где var - переменная из секции .data

  • 42 / 2Ah: глобальный идентификатор (например, из секции .data)

  • 43 / 2Bh: выражение доступа к памяти в виде [reg + label], где label представляет имя процедуры или метка

  • 48 / 30h: регистр общего назначения, XMM, YMM, ZMM, ST, либо другой регистр специального назначения

  • 98 / 62h: обращение к стеку в виде [rsp + xxx] и [rbp + xxx]

Подобным образом мы можем проверить и конктерный регистр:

add128 macro l64, h64, number64
    ; проверяем, представляет ли l64 регистр RAX
    ifidn <l64>, <rax>
        ; действия, если регистр - RAX
    else
       ; действия, если регистр - не RAX
    endif
endm 
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850