Низкоуровневое программирование для Дzenствующих

Вид материалаДокументы

Содержание


III. Макромир MASM
PURGE: PURGE macroname После этой директивы макро с именем macroname
Param0 – пример определения параметра.Param1:REQ
MASM анализирует текст макроопределения на наличие директивы exitm
Подобный материал:
1   ...   10   11   12   13   14   15   16   17   ...   42

III. Макромир MASM


Макрос представляет собой именованный участок исходного текста программы, который обрабатывается компилятором каждый раз в том месте, где вызывается макрос.

-= Подробней =-

Пример:
Создайте небольшой модуль с именем macro.asm.
И напишите в нём несколько строчек

.386

.data

.code

echo Hello!!!

echo Ты должен увидеть во время компиляции


end

Так действует директива echo. С помощью неё можно подсмотреть значения переменных.

Mycount = 1

%echo @CatStr(%Mycount)

Если вы не знаете, как это работает, не волнуйтесь, обо всём будет рассказано. А пока несколько экспериментов:

Напишите:

MyMacro macro reg


dec reg

endm


.code


mov eax,5

MyMacro reg

MyMacro reg

Взгляните на код программы под отладчиком. Что у вас получилось? Что будет, если вы измените текст внутри макроопределения?

Теперь напишите:

MyVar = 1


MyMacro macro


MyVar = MyVar+1

%echo MyVar = @CatStr(%MyVar)


endm


MyMacro

MyMacro

MyMacro

MyMacro

Каким будет вывод на экран во время компиляции?

С этого момента вам придётся различать в ассемблере ML две подсистемы: препроцессор и компилятор. Если компилятор переводит код мнемоник в машинный код, вычисляет значения меток и смещений, то препроцессор занимается вычислением выражений этапа компиляции, и что самое важное – процессом раскрытия макросов.

Подобно многим объектам мира программирования макро имеет два состояния в исходном тексте: определение, и использование.

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

MacroName macro paramlist
макроопределение
endm

При каждом вызове макро, а именно:



MacroName
или
mov eax, MacroName()



Будет анализироваться и исполнятся текст, заключённый в макро. Именно так это и реализовано в ML. Поскольку текст в макроопределении не компилируется, то естественно, вы не увидите сообщений об ошибке, даже если с точки зрения ассемблера эта ошибка будет в теле макроопределения. Однако ошибка появится при попытке вызова макроопределения, её могут выдать вам, либо сам препроцессор, либо компилятор, если текст, сгенерированный препроцессором является неверным с точки зрения компилятора.

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


MyMacro macro

echo Это макро 1

endm


MyMacro macro

echo Это макро 2

endm


MyMacro


Вы можете самостоятельно удалять макроопределения, из памяти препроцессора используя директиву PURGE:

PURGE macroname

После этой директивы макро с именем macroname перестаёт существовать. И вы освобождаете память для компилятора.

Конечно же, использование макро было бы бесполезным, если бы макро не имел формальных параметров. При вызове макро, препроцессор заменяет все имена формальных параметров их непосредственными значениями в теле макроопределения. Список формальных параметров разделяется запятой, и может иметь вид:

MyMacro macro param0, param1:REQ, param2 := <0>,param3:VARARG

Здесь:
Param0 – пример определения параметра.
Param1:REQ – ключевое слово REQ указывает на то, что этот параметр обязательный. То есть, если он не будет указан, вы получите ошибку этапа компиляции.
Param2:=<0> – пример параметра, который имеет значение по умолчанию. То есть если этот параметр не будет указан при вызове макро, он будет равен этому значению.

Заметьте, что при вызове макро параметр может быть не определён:

MyMacro param1,,param3

Значение второго параметра неопределенно.

Param3:vararg – становится именем параметра, который воспринимает всё остальное как строку. При этом запятые между параметрами так же попадают в строку, а значит число параметров макроса в принципе неограниченно.

Ограничениям являются особенности архитектуры компилятора. Так, например, компилятор имеет ограничение на длину логической строки, которая равна 512 байтам.

Конечно же, после параметра с директивой vararg не возможно объявить другие параметры.

Обратите внимание, что если при определении формального параметра в макро нет директивы – он считается необязательным параметром. Более подробно о вызове макро и значении параметров я расскажу далее.




-= Подробней =-

Пример:
Так что же происходит с формальными параметрами?
Посмотрите, как работает препроцессор ML:


MyMacro macro param1,param2


mov eax, param1

mov ebx, param2


endm


MyMacro var, 123


1. Препроцессор берёт текст внутри макро, и заменяет в нём все
слова param1, param2, на их значения:
«
mov eax, var
mov ebx, 123
»

2. Полученный текст вставляет на место вызова макро, и передаёт компилятору.

Вот интересно, а что будет если:


MyMacro macro param1,param2


MyMacro2 macro param1

mov eax, param1

mov ebx, param2

endm

endm


MyMacro var, 123



Можно различать два вида макро – макропроцедуры и макрофункции.

В официальном руководстве MASM различается четыре основных вида макро.
Text macros – текстовый макрос
Macro procedures – макро-процедура
Repeat blocks – блок повторения
Macro functions – макро-функция
Однако автор считает, что разделение макро на два вида – лучше систематизирует материал, и отражает суть темы.

Макрофункции в отличие от макропроцедур могут возвращать результат, и получают список формальных параметров в скобках, подобно функциям в С. Например:

mov eax,@GetModuleHandle()

Заметьте, что к макрофункции невозможно обратится как к макро, вы всегда должны заключать формальные параметры макрофункции между «()», иначе MASM не будет распознавать её как макрофункцию:

mov eax,@GetModuleHandle
error A2148: invalid symbol type in expression

: @GetModuleHandle

Препроцессор MASM анализирует текст макроопределения на наличие директивы exitm, и помечает макрос как макрофункцию.

Ключевое слово exitm , аналогично оператору return в C++, выполнение макро заканчивается, и возвращается необязательный параметр retval. Этот параметр – строка, которую должен вернуть макрос.


Таким образом, окончательно будем считать, что макро, не имеющие в себе вызова директивы exitm – это макропроцедуры, а макро, которые имеют exitm – это макрофункции.


;#######################################################

@GetModuleHandle macro

Invoke GetModuleHandle,0

exitm

endm

.code

; Это макрофункция так нельзя

@GetModuleHandle ;;– ошибка

; Так можно

@GetModuleHandle()

;########################################################

@GetModuleHandle macro

Invoke GetModuleHandle,0

endm

.code

; Это макрос. Так правильно

@GetModuleHandle

; Так можно, но всё равно это вызывает ошибку ?

; warning A4006: too many arguments in macro call

@GetModuleHandle()

; Это макро, а не макрофункция так нельзя!!!

mov eax,@GetModuleHandle

; И так нельзя

mov eax,@GetModuleHandle()

;########################################################



Что касается директивы endm, которая заканчивает каждое макроопределение, в руководстве написано, что при помощи неё так же можно указать возвращаемый параметр:
endm
Однако на практике это не так. ? Очень странно, хотя об этом чётко написано в руководстве.

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

@GetModuleHandle
;; Но не так:
mov eax,@MyMacro

Макрофункция может быть вызвана в любых выражениях:

;; Так:
mov eax,@GetModuleHandle()
;; И так:
@FunMacro()
;; И так:
@GetModuleHandle() EQU eax