Глава 6. Макросредства ассемблера и программирование в Windows
I
Большая часть данной главы будет носить справочный характер. Почему я привожу справочный материал в середине книги, а не в ее начале? Просто я убежден, что справка в начале книги может отбить всякую охоту читать дальше. Многое из того, что я привожу в данной главе. Вы уже знаете и, следовательно, Вам интересно будет читать материал этой главы.
Метки
1. Метка с двоеточием после имени определяет адрес следующей за меткой команды.
2. Директива LABEL
позволяет определить явно тип метки. Значение
же определенной таким образом метки равно адресу команды или данных, стоящих
далее. Например,LABEL L1 DWORD
.
3. Выражение ИМЯ PROC
определяет метку, переход на которую
обычно происходит по команде CALL
. Блок кода, начинающийся с такой
метки, называют процедурой. Впрочем, переход на такую метку можно осуществлять и
с помощью JMP
, как, впрочем, и команду CALL
можно
использовать для перехода на обычную метку. В этом, несомненно, состоит сила и
гибкость ассемблера.
4. В строке за меткой может стоять директива резервирования данных, например:
ERR DB 'Ошибка'
или NUM DWORD 0
. С точки зрения языка
высокого уровня таким образом мы определяем глобальную переменную. С точки же
зрения ассемблера нет никакой разницы между командой и данными, поэтому между
меткой, определяющей команду, и меткой, определяющей данные, нет никакой
разницы. Раз уж речь пошла о данных, перечислю их типы:BYTE
(DB)
- байт,WORD (DW)
- 2 байта,DWORD
(DD)
- 4 байта,FWORD (DF)
- 6 байт,QWORD
(DQ)
- 8 байт,TBYTE (DT)
- 10 байт.
5. С помощью директивы EQU
в терминах языков высокого уровня
определяются константы. Например - MES EQU "ERROR!"
, LAB EQU
145Н
. С помощью EQU
значение данной метке может быть
присвоено только один раз. С правой стороны от EQU
может стоять
выражение с использованием арифметических, логических и битовых операций. Вот
эти операции: "+
", "-
", "*
",
"/
", "MOD
"-ocтaток от деления, "AND
",
"OR
", "NOT
", "XOR
", "SHR
",
"SHL
". Используются также операции сравнения: EQ
,
GE
, GT
, LE
, LT
,
NE
. Выражение с операцией сравнения считается логическим и
принимает значение 0
, если условие не выполняется, и 1
- если выполняется. С помощью директивы "=
" можно присваивать
только целые значения, но зато производить переприсваивание. Заметим, что
выражение может являться операндом команды: MOV EAX,16*16-1
.
6. Метка "$
" всегда определяет текущий адрес.
7. В MASM метки, стоящие в процедуре, автоматически считаются локальными и,
следовательно, имена меток в процедурах могут дублироваться. В TASM все метки по
умолчанию считаются глобальными. Чтобы сделать метки, стоящие в процедуре
локальными, они должны иметь префикс @@
, а в начале программы
следует указать директиву LOCALS
(см. предыдущую главу).
Структура
1. Директива STRUC
позволяет объединить несколько разнородных
данных в одно целое. Эти данные называются полями. Вначале при помощи
STRUC
определяется шаблон структуры, затем с помощью директивы
< >
можно определить любое количество структур. Рассмотрим
пример:
STRUC COMPLEX RE DD ? IM DD ? STRUC ENDS ... ;в сегменте данных COMP1 COMPLEX <?> COMP2 COMPLEX <?>
Доступ к полям структуры осуществляется посредством точки:
COMP1.RE
.
2. Объединение. Объединение определяется при помощи ключевого слова
UNION
. От структуры объединение отличается только тем, что все поля
располагаются в структуре с нулевым смещением, т.е. накладываются друг на друга.
1. Условное ассемблирование дает возможность при трансляции обходить тот или
иной участок программы. Существует три вида условного ассемблирования.
а) IF выражение ... ENDIF б) IF выражение ... ELSE ... ENDIF в) IF выражение 1 ... ELSEIF выражение 2 ... ELSEIF выражение 3 ... ELSE ... ENDIFУсловие считается не выполненным, если выражение принимает значение
0
и выполненным, если выражение отлично от нуля.
2. Ассемблеры MASM и TASM поддерживают также несколько условных специальных директив, назовем некоторые из них.
а)
IFE выражение ... ELSEIFE ... ENDIFE
б) Операторы IF1
и IF2
проверяют первый и второй
проход при ассемблировании.
в) Оператор IFDEF
- проверяет, определено ли в программе
символическое имя, IFDEFN
- обратный оператор. И другие
IF
операторы. Они имеются в любом справочнике по ассемблеру.
г) Имеется целый набор директив, начинающихся с .ERR
. Например,
.ERRE
выражение - вызовет прекращение трансляции и сообщение об
ошибке, если выражение станет равным 0
.
Условное ассемблирование понадобится нам в конце главы для написания программы, транслируемой как в MASM, так и TASM.
Вызов процедур
1. С упрощенным вызовом процедур в MASM Вы уже познакомились. Это директива
INVOKE
. Процедура должна быть заранее определена с использованием
ключевого слова PROTO
. Например:
MessageBoxA PROTO :DWORD,:DWORD,:DWORD,:DWORD ... invoke MessageBox, h, ADDR TheMsg, ADDR TitleW, MB_OKЗдесь
h
- дескриптор окна, откуда вызывается сообщение,
TheMsg
- строка сообщения, TitleW
- заголовок окна,
MB_OK
- тип сообщения. ADDR
в данном случае синоним
OFFSET
.
2. Оказывается, в синтаксисе TASM тоже имеется свой упрощенный вызов.
EXTERN MESSAGEBOX:PROC ... call MessageBox PASCAL,h,ADDR TheMsg,ADDR TitleW, MB_OK
PASCAL
- тип вызова, точнее порядок следования параметров.
Можно поставить параметр C
, тогда порядок будет обратным.
Макроповторения
1. Повторение, заданное опеделенное число раз. Используется макродиректива
REPT
. Например:
A EQU 10 REPT 100 DB A ENDMБудет сгенерировано 100 директив
DB 10
. С этой директивой
удобно использовать оператор "=
", который позволяет изменять
значение переменной многократно, т.е. использовать выражение типа А = А +
5
.
2. Директива IRP
.
IRP параметр,<список> ... ENDMБлок будет вызываться столько раз, сколько параметров в списке. Например:
IRP REG, <EAX,EBX,ECX,EDX,ESI,EDI> PUSH REG ENDMПриведет к генерации следующих строк:
PUSH EAX PUSH EBX PUSH ECX PUSH EDX PUSH ESI PUSH EDI
3. Директива IRPC
.
IRPC параметр, строка Операторы ENDM
Пример:
IRPC CHAR,azklg CMP AL,'&CHAR&' JZ EndC ENDM EndC:
Данный фрагмент эквивалентен следующей последовательности:
CMP AL,'a' JZ EndC CMP AL,'z' JZ EndC CMP AL,'k' JZ EndC CMP AL,'l' JZ EndC CMP AL,'g' JZ EndC EndC:
Амперсант (&
) в последнем примере используется для того,
чтобы задать вычисление параметра блока повторения даже внутри кавычек.
Амперсант - это макрооперация, которая работает в блоке повторения, поскольку
блоки повторения представляют собой один из типов макрокоманды.
Общий вид макроопределения.
Имя MACRO параетры ... ENDMОпределив блок один раз, можно использовать его в программе многократно. Причем в зависимости от значений параметров заменяемый участок может иметь разные значения. Если заданный участок предполагается многократно использовать, например в цикле, макроопределение имеет несомненные преимущества перед процедурой, т.к. несколько убыстряет выполнение кода. Пример:
ЕХС MACRO par1,par2 PUSH par1 POP par2 ENDMДанное макроопределение приводит к обмену содержимым между параметрами.
ЕХС EAX,EBX
эквивалентно PUSH EAX\POP EAX
;
ЕХС MEM1,ESI
- PUSH MEM1\POP ESI
и т. д. Заметим, что
если первый параметр будет непосредственно числом, то это приведет к загрузке
данного числа во второй операнд.
Важным вопросом в связи с макроопределениями является проблема меток.
Действительно, если мы будем применять в макроопределении обычные метки, то при
использовании его более чем один раз возникнет коллизия. Коллизия эта
разрешается при помощи объявления локальных меток. Для этого используется
ключевое слово LOCAL
. Например,
ЕХС MACRO par1,par2 LOCAL EXI CMP par1,par2 JE EXI PUSH par1 POP par2 EXI: ENDM
Данное макроопределение можно использовать сколь угодно много раз - при
каждой подстановке ассемблер будет генерировать уникальную метку. Для выхода из
макроопределения (т.е. для прекращения генерации макроопределения) применяется
директива EXITM
. Она может понадобиться, если в макроопределении Вы
используете условные конструкции типа IF..ENDIF
.
Некоторые другие директивы транслятора ассемблера
1. Кроме объявлений с использованием директив PUBLIC
и
EXTERN
, возможно объявление при помощи директивы
GLOBAL
, которая действует, как PUBLIC
и
EXTERN
одновременно.
2. PURGE
имя макроса. Отменяет загрузку макроса. Используется
при работе с библиотекой макросов, чтобы не перегружать память38.
3. LENGTHOF
- определяет число элементов данных.
SIZEOF
- определяет размер данных (отсутствуют в TASM).
4. Директивы задания набора команд..8086
- разрешены только
команды микропроцессора 8086. Данная директива работает по
умолчанию..186
- разрешены команды 186..286
и
.286Р
- разрешены команды 286-ого микропроцессора. Добавка "P"
здесь и далее означает разрешение команд защищенного
режима..386
и .386P
- разрешение команд 386-ого
микропроцессора..486
и .486Р
- разрешение команд
486-ого процессора..586
и .586Р
- разрешены
команды Р5 (Pentium)..686
и .686Р
- разрешены
команды Pб (Pentium Pro, Pentium II)..8087
- разрешены команды
арифметического сопроцессора 8087..287
- разрешены команды
арифметического сопроцессора 287..387
- разрешены команды
арифметического сопроцессора 387..MMX
- разрешены команды
расширения ММХ.
5. Директивы управления листингом.NAME
- задать имя
модуля.TITLE
- определяет заголовок листинга.
По умолчанию и
имя модуля, и заголовок листинга совпадают с именем
файла.SUBTTL
- определяет подзаголовок
листинга.PAGE
- определяет размеры страницы листинга: длина,
ширина. Директива PAGE
без аргументов начинает новую страницу
листинга..LIST
- выдавать листинг..XLIST
-
запретить выдачу листинга..SALL
- подавить печать
макроопределений..SFCOND
- подвить печать условных блоков с
ложными условиями..LFCOND
- печатать условные блоки с ложными
условиями..CREF
- разрешить листинг перекрестных
ссылок..XCREF
- запретить листинг перекрестных ссылок.
38 В операционной системе MS DOS это было существенно.