Одной и распространенных ситуаций применения макроконструкций представляет генерация таблиц для поиска. Например, нам нужен набор чисел типа byte от 1 до 100. В общем случае мы могли бы написать так:
.data nums byte 1, 2, 3, 4, 5, 6, ....., 99, 100
Писать вручную - не очень удобно, тем более всегда можно допустить ошибку на таком большом диапазоне чисел. И вместо ручного создания массива чисел можно использовать циклы:
.data index = 1 last = 101 nums equ this byte while index lt last byte index index = index + 1 endm .code main proc xor rax, rax mov al, nums ; al = 1 ret main endp end
Здесь чуть больше кода. Для хранения добавляемого в массив значения определена переменная index, а для хранения максимального значения - константа last. Для задания массива nums применяется следующая запись:
nums equ this byte
Цикл while на каждой итерации определяет в массиве nums один байт, который равен index, пока index не станет равным last. То есть в итоге мы опять же получим массив nums со 100 числами.
while index lt last byte index index = index + 1 endm
И мы можем пойти чуть дальше и определить макрос, который будет генерировать определенный диапазон значений. Например, создадим таблицу для преобразования символов из нижнего регистра в верхний. Для этого определим следующий код:
; макрос генерирует последовательность чисел от start до last createRange macro start, last index = start while index lt last byte index index = index + 1 endm endm .data character byte "y" ; символ для преобразования в верхний регистр ; Таблица для преобразования символов из нижнего регистра в верхний table equ this byte createRange 0, "a" ; на позиции 0-96 добавляем символьные коды от 0 до "a" не включая createRange "A", "Z"+1 ; на позиции 97-122 добавляем символьные коды от "A" до "Z" включая createRange "z"+1, 128 ; на позиции 123-127 добавляем оставшиеся символьные коды таблицы ASCII .code main proc xor rax, rax mov al, character ; AL = 121 - код символа y lea rbx, table xlat mov character, al ; AL = 89 (код символа Y) ret main endp end
Макрос createRange принимает два параметра - числовой код начального символа диапазона и числовой код конечного символа. Для генерации диапазона применяется цикл while. В секции .data
с помощью
этого макроса генерируем три поддиапазона:
createRange 0, "a" ; на позиции 0-96 добавляем символьные коды от 0 до "a" не включая createRange "A", "Z"+1 ; на позиции 97-122 добавляем символьные коды от "A" до "Z" включая createRange "z"+1, 128 ; на позиции 123-127 добавляем оставшиеся символьные коды таблицы ASCII
В общем случае по каждому индексу в таблице table будет располагать равный индексу числовой код символа (например, по индексу 10 - числовой код 10) за исключением диапазаона индексов 97-122, поскольку они представляют числовые коды символов в нижнем регистре. И на эти позиции мы повторно помещаем числовые коды символов в верхнем регистре - от "A" до "Z". Таким образом, мы получаем таблицу с числовыми кодами символов.
Для замены числового кода символа применяется инструкция xlat, которая фактически аналогична инструкции
mov al, [rbx + al * 1]
Эта инструкция использует текущее значение регистра AL в качестве индекса в массиве, базовый адрес которого находится в RBX. Она выбирает байт по этому индексу в массиве и копирует этот байт в регистр AL.