Часто используемые в примерах имена
При оформлении макросов используются специальные директивы. Текст любого макроопределения начинается директивой macro, перед ней указывается имя макроса, а после нее, в той же строке, список аргументов, если таковые имеются, в данном примере это reg.
Другая, часто используемая директива – endm. В зависимости от контекста она указывает конец макроопределения или оператора, что и показано в примере 2.12.
Тела макроопределений примера 2.12 состоят из директивы повторения irp. После нее, в той же строке, указываются параметр г и имя списка аргументов, заключенное в угловые скобки. Оно должно совпадать с именем, указанным в директиве macro. В следующей строке записывается повторяемая команда, один из операндов которой г соответствует параметру директивы irp.
В общем случае тело директивы irp может состоять из нескольких команд или содержать другие директивы, поэтому нужен признак конца директивы endm.
Обнаружив в тексте программы макроопределение, Макроассемблер проверяет его синтаксис и запоминает имя и текст, не включая его в тело задачи. Исполнение макроопределения (вставка команд в тело задачи) будет производиться при каждом макровызове.
Параметры макровызова
Макровызовы или макрокоманды – это вставка текста макроопределения в нужных местах программы с подстановкой конкретных параметров, если они имеются. Для макроопределений примера 2.12 макровызов состоит из имени PushReg или PopReg и списка регистров, заключенного в угловые скобки. Угловые скобки позволяют использовать запятые между именами регистров, входящих в список.
Обнаружив макровызов, Макроассемблер находит и обрабатывает соответствующее определение, в результате чего формируются обычные команды, которые сразу компилируются и полученный код вставляется в тело задачи.
В частности, при вызове макроопределений примера 2.12 директива irp повторяется столько раз, сколько имен содержит список reg. Название каждого имени выбирается из списка и подставляется в команду push или pop вместо параметра r.
В макроопределениях примера 2.12 разнообразие имен не ограничено явно, допустимо указание любых величин, которые могут быть операндами команд push и pop. Ими могут быть не только имена 16 – и 32-разрядных регистров, но и имена переменных, кроме того, команда push может сохранять в стеке значения констант (непосредственное указание сохраняемой величины). Все эти величины можно использовать в макровызове. Например, во многих подпрограммах будет использоваться такой вариант макровызовов:
PushReg <fs, gs, Cur_win> и PopReg <Cur_win, gs, fs>
Обратите внимание, имена в списках PushReg и PopReg расположены в обратном порядке, поскольку работа со стеком организована по принципу "последнее записанное – первое считанное".
Альтернативным способом сохранения группы регистров в стеке и восстановления их оттуда одной командой являются операции pusha и рора, не имеющие параметров. Их исполняют все модели микропроцессоров, кроме Intel 8086. Эти операции сохраняют в стеке или восстанавливают из него регистры в таком порядке:
PUSHA › ах, ex, dx, bx, sp, bp, si, di. РОРА › di, si, bp, sp, bx, dx, ex, ax.
Начиная с модели Intel 80386, микропроцессоры поддерживают операции pushad и popad, также не имеющие параметров. Они сохраняют в стеке или восстанавливают из него 32-разрядные регистры в таком порядке:
PUSHAD › eax, ecx, edx, ebx, esp, ebp, esi, edi. POPAD › edi, esi, ebp, esp, ebx, edx, ecx, eax.
В отличие от макрокоманд, четыре описанные операции сокращают не только исходный текст программы, но и размер задачи и время выполнения операции, поскольку вместо восьми команд используется одна. Их недостаток в том, что обязательно сохраняются все восемь указанных регистров, но не сохраняются сегментные регистры. Поэтому в каждом конкретном случае надо решать, что лучше использовать – указанные операции или макрокоманды.