Директивы условной компиляции
Последний тип макросредств — директивы условной компиляции.
Существует два типа этих директив:
- директивы компиляции по условию позволяют проанализировать определенные условия в ходе генерации макрорасширения и, при необходимости, изменить этот процесс;
- директивы генерации ошибок по условию также контролируют ход генерации макрорасширения с целью генерации или обнаружения определенных ситуаций, которые могут интерпретироваться как ошибочные.
С этими директивами применяются директивы управления процессом генерации макрорасширений EXITM и GOTO.
Директива EXITM не имеет операндов, и ее действие заключается в том, что она немедленно прекращает процесс генерации макрорасширения, начиная с того места, где она встретилась в макроопределении.
Директива GOTO имя_метки переводит процесс генерации макроопределения в другое место, прекращая тем самым последовательное разворачивание строк макроопределения. Метка, на которую передается управление, имеет специальный формат:
:имя_метки |
Примеры применения этих директив будут приведены ниже.
Директивы компиляции по условию
Данные директивы предназначены для организации выборочной трансляции фрагментов программного кода. Такая выборочная компиляция означает, что в макрорасширение включаются не все строки макроопределения, а только те, которые удовлетворяют определенным условиям. То, какие конкретно условия должны быть проверены, определяется типом условной директивы.
|
|
Введение в язык ассемблера этих директив значительно повышает его мощь.
Всего имеется 10 типов условных директив компиляции. Их логично попарно объединить в четыре группы:
- Директивы IF и IFE — условная трансляция по результату вычисления логического выражения.
- Директивы IFDEF и IFNDEF — условная трансляция по факту определения символического имени.
- Директивы IFB и IFNB — условная трансляция по факту определения фактического аргумента при вызове макрокоманды.
- Директивы IFIDN, IFIDNI, IFDIF и IFDIFI — условная трансляция по результату сравнения строк символов.
Условные директивы компиляции имеют общий синтаксис и применяются в составе следующей синтаксической конструкции:
IFxxx логическое_выражение_или_аргументыфрагмент_программы_1 ELSEфрагмент_программы_2ENDIF |
Заключение некоторых фрагментов текста программы — фрагмент_программы_1 и фрагмент_программы_2 — между директивами IFxxx, ELSE и ENDIF приводит к их выборочному включению в объектный модуль. Какой именно из этих фрагментов — фрагмент_программы_1 или фрагмент_программы_2 — будет включен в объектный модуль, зависит от конкретного типа условной директивы, задаваемого значением xxx, и значения условия, определяемого операндом (операндами) условной директивы логическое_выражение_или_аргумент(ы).
|
|
Синтаксические конструкции, соответствующие директивам условной компиляции, могут быть вложенными друг в друга (см. "Вложенность директив условной трансляции")
Директивы IF и IFE
Синтаксис этих директив следующий:
IF(E) логическое_выражениефрагмент_программы_1 ELSEфрагмент_программы_2ENDIF |
Обработка этих директив макроассемблером заключается в вычислении логического_выражения и включении в объектный модуль фрагмент_программы_1 или фрагмент_программы_2 в зависимости от того, в какой директиве IF или IFE это выражение встретилось:
- если в директиве IF логическое выражение истинно, то в объектный модуль помещается фрагмент_программы_1.
- Если логическое выражение ложно, то при наличии директивы ELSE в объектный код помещается фрагмент_программы_2. Если же директивы ELSE нет, то вся часть программы между директивами IF и ENDIF игнорируется и в объектный модуль ничего не включается. Кстати сказать, понятие истинности и ложности значения логического_выражения весьма условно. Ложным оно будет считаться, если его значение равно нулю, а истинным — при любом значении, отличном от нуля.
- директива IFE аналогично директиве IF анализирует значение логического_выражения. Но теперь для включения фрагмент_программы_1 в объектный модуль требуется, чтобы логическое_выражение имело значение “ложь”.
Директивы IF и IFE очень удобно использовать при необходимости изменения текста программы в зависимости от некоторых условий.
|
|
К примеру, составим макрос для определения в программе области памяти длиной не более 50 и не менее 10 байт (листинг 5).
Листинг 5. Использование условных директив IF и IFE<1>;prg_13_4.asm<2>masm<3>model small<4> stack 256<5> def_tab_50 macro len<6>if len GE 50<7>GOTO exit<8>endif<9> if len LT 10<10>:exit<11>EXITM<12>endif<13>rept len<14> db 0<15>endm<16>endm<17>.data<18>def_tab_50 15<19> def_tab_50 5<20>.code<21>main:<22> mov ax,@data<23> mov ds,ax<24>exit:<25> mov ax,4c00h<26> int 21h<27>end mainENDIF |
Введите и оттранслируйте листинг 5. При этом не забывайте о том, что условные директивы действуют на шаге трансляции, и поэтому результат их работы можно увидеть только после макрогенерации, то есть в листинге программы.
В нем вы увидите, что в результате трансляции строка 18 листинга 5 развернется в пятнадцать нулевых байт, а строка 19 оставит макрогенератор совершенно равнодушным, так как значение фактического операнда в строках 6 и 9 будет ложным. Обратите внимание, что для обработки реакции на ложный результат анализа в условной директиве мы использовали макродирективы EXITM и GOTO.
|
|
Другой интересный и полезный вариант применения директив IF и IFE — отладочная печать.
Суть здесь в том, что в процессе отладки программы почти всегда возникает необходимость динамически отслеживать состояние определенных программно- аппаратных объектов, в качестве которых могут выступать переменные, регистры микропроцессора и т. п. После этапа отладки отпадает необходимость в таких диагностических сообщениях. Для их устранения нужно корректировать исходный текст программы, после чего ее следует подвергнуть повторной трансляции. Но есть более изящный выход.
Можно определить в программе некоторую переменную, к примеру debug, и использовать ее совместно с условными директивами IF или IFE. К примеру,
<1>...<2>debug equ 1<3>...<4>.code<5>...<6>if debug<7>;любые команды и директивы ассемблера<8>;(вывод на печать или монитор)<9>endif |
На время отладки и тестирования программы вы можете заключить отдельные участки кода в своеобразные операторные скобки в виде директив IF и ENDIF (строки 6-9 последнего фрагмента), которые реагируют на значение логической переменной debug. При значении debug = 0 транслятор полностью проигнорирует текст внутри этих условных операторных скобок; при debug = 1, наоборот, будут выполнены все действия, описанные внутри них.
Директивы IFDEF и IFNDEF
Синтаксис этих директив следующий:
IF(N)DEF символическое_имяфрагмент_программы_1 ELSEфрагмент_программы_2ENDIF |
Данные директивы позволяют управлять трансляцией фрагментов программы в зависимости от того, определено или нет в программе некоторое символическое_имя. Директива IFDEF проверяет, описано или нет в программе символическое_имя, и если это так, то в объектный модуль помещается фрагмент_программы_1. В противном случае, при наличии директивы ELSE, в объектный код помещается фрагмент_программы_2.
Если же директивы ELSE нет (и символическое_имя в программе не описано), то вся часть программы между директивами IF и ENDIF игнорируется и в объектный модуль не включается.
Действие IFNDEF обратно IFDEF. Если символического_имени в программе нет, то транслируется фрагмент_программы_1. Если оно присутствует, то при наличии ELSE транслируется фрагмент_программы_2. Если ELSE отсутствует, а символическое_имя в программе определено, то часть программы, заключенная между IFNDEF и ENDIF, игнорируется.
В качестве примера рассмотрим ситуацию, когда в объектный модуль программы должен быть включен один из трех фрагментов кода. Какой из трех фрагментов будет включен в объектный модуль, зависит от значения некоторого идентификатора switch:
- если switch = 0, то сгенерировать фрагмент для вычисления выражения
- y = x*2**n;
- если switch = 1, то сгенерировать фрагмент для вычисления выражения
- y = x/2**n;
- если switch не определен, то ничего не генерировать.
Соответствующий фрагмент исходной программы может выглядеть так:
ifndef sw ;если sw не определено, то выйти из макросаEXITMelse ;иначе — на вычисление mov cl,nife sw sal x,cl ;умножение на степень 2 сдвигом влево else sar x,cl ;деление на степень 2 сдвигом вправо endifendif |
Как видим, эти директивы логически связаны с директивами IF и IFE, то есть их можно применять в тех же самых случаях, что и последние.
Есть еще одна интересная возможность использования этих директив. На уроке 4 мы обсуждали формат командной строки и говорили об опциях, которые в ней можно задавать. Вспомните одну из опций командной строки TASM — опцию
/dидентификатор=значение.
Ее использование дает возможность управлять значением идентификатора прямо из командной строки транслятора, не изменяя при этом текста программы.
В качестве примера рассмотрим листинг 6, в котором мы попытаемся с помощью макроса контролировать процесс резервирования и инициализации некоторой области памяти в сегменте данных.
Листинг 6. Инициализация значения идентификатора из командной строки<1>;prg_13_5.asm<2>masm<3>model small<4> stack 256<5>def_tab_50 macro len<6>ifndef len<7>display 'size_m не определено, задайте значение 10<SIZE_Mexitm<9>else<10>if len GE 50<11>GOTO exit<12>endif<13>if len LT 10<14>:exit<15>EXITM<16>endif<17>rept len<18> db 0<19>endm<20>endif<21>endm<22>;size_m=15<23>.data<24>def_tab_50 size_m<25><26>.code<27>main:<28> mov ax,@data<29> mov ds,ax<30>exit:<31> mov ax,4c00h<32> int 21h<33>end main |
Запустив этот пример на трансляцию, вы получите сообщение о том, что забыли определить значение переменной size_m. После этого попробуйте два варианта действий:
- Определите где-то в начале исходного текста программы значение этой переменной с помощью equ:
size_m equ 15 |
- Запустите программу на трансляцию командной строкой вида
- tasm /dsize_m=15 /zi prg_13_2,,,
В листинге 6 мы использовали еще одну возможность транслятора — директиву display, с помощью которой можно формировать пользовательское сообщение в процессе трансляции программы.
Директивы IFB и IFNB
Синтаксис этих директив следующий:
IF(N)B аргументфрагмент_программы_1 ELSEфрагмент_программы_2ENDIF |
Данные директивы используются для проверки фактических параметров, передаваемых в макрос. При вызове макрокоманды они анализируют значение аргумента, и в зависимости от того, равно оно пробелу или нет, транслируется либо фрагмент_программы_1, либо фрагмент_программы_1. Какой именно фрагмент будет выбран, зависит от кода директивы:
- Директива IFB проверяет равенство аргумента пробелу. В качестве аргумента могут выступать имя или число.
- Если его значение равно пробелу (то есть фактический аргумент при вызове макрокоманды не был задан), то транслируется и помещается в объектный модуль фрагмент_программы_1.
- В противном случае, при наличии директивы ELSE, в объектный код помещается фрагмент_программы_1. Если же директивы ELSE нет, то при равенстве аргумента пробелу вся часть программы между директивами IFB и ENDIF игнорируется и в объектный модуль не включается.
- Действие IFNB обратно IFB. Если значение аргумента в программе не равно пробелу, то транслируется фрагмент_программы_1.
- В противном случае, при наличии директивы ELSE, в объектный код помещается фрагмент_программы_1. Если же директивы ELSE нет, то вся часть программы (при неравенстве аргумента пробелу) между директивами IFNB и ENDIF игнорируется и в объектный модуль не включается.
В качестве типичного примера применения этих директив предусмотрим строки в макроопределении, которые будут проверять, указывается ли фактический аргумент при вызове соответствующей макрокоманды:
show macro regifb display 'не задан регистр'exitmendif...endm |
Если теперь в сегменте кода вызвать макрос show без аргументов, то будет выведено сообщение о том, что не задан регистр и генерация макрорасширения будет прекращена директивой exitm.
Дата добавления: 2019-09-13; просмотров: 235; Мы поможем в написании вашей работы! |
Мы поможем в написании ваших работ!