assembler. Учебник для вузов_Юров В.И_2003 -637с (862834), страница 67
Текст из файла (страница 67)
Описание их назначения приведено сразу после заголовка в телекаждого макроопределения. Если вынести эти макроопределения в отдельныйфайл, то впоследствии можно использовать их при написании других программ.Посмотрите на модернизированный исходный текст программы из листинга 6.1в листинге 14.1 (строки 54-68). Если не обращать внимания на некоторые неясные моменты, то сам сегмент кода стал внешне более читабельным, и даже можносказать, что в его новых командах появился какой-то смысл. Откомпилируйте листинг 14.1 и получите файл листинга.
После этого сравните текст программы дои после обработки ассемблером. Вы увидите, что исходный текст изменился, — после строк программы, в которых были макрокоманды, появились фрагменты текста. Содержимое этих фрагментов определяется содержимым макроопределений(шаблонов), заданных в начале программы. Видно, что путем ввода параметров300Глава 14. Макросредства языка ассемблерадля макрокоманды можно модернизировать содержимое фактического текста, замещающего макрокоманду.Функционально макроопределения похожи на процедуры. Сходство в том, чтои те, и другие достаточно один раз где-то описать, а затем вызывать их специальным образом. На этом сходство заканчивается и начинаются различия, которыев зависимости от целевой установки можно рассматривать и как достоинства, и какнедостатки.ш В отличие от процедуры, текст которой неизменен, макроопределение в процессе макрогенерации может меняться в соответствии с набором фактическихпараметров.
При этом коррекции могут подвергаться как операнды команд, таки сами команды. Процедуры в этом отношении менее гибки.И При каждом вызове макрокоманды ее текст в виде макрорасширения вставляется в программу. При вызове процедуры процессор осуществляет передачууправления на начало процедуры, находящейся в некоторой области памятив одном экземпляре. Код в этом случае получается более компактным, хотя быстродействие несколько снижается за счет необходимости осуществления переходов.Макроопределение обрабатывается компилятором особым образом. Для тогочтобы использовать описанное макроопределение, его нужно «активизировать»с помощью макрокоманды. Для этого в нужном месте исходного кода программына основе текста заголовка макроопределения указывается следующая синтаксическая конструкция:имя_макрокоманды список_фактических_аргументовВ результате применения данной синтаксической конструкции соответствующая строка исходного текста программы заменяется строками из тела макроопределения.
Но это не простая замена. Обычно макрокоманда содержит некоторыйсписок аргументов (список_фактических_аргументов), которыми корректируетсямакроопределение. Места в теле макроопределения, которые будут замещатьсяфактическими аргументами из макрокоманды, обозначаются с помощью так называемых формальных аргументов. Таким образом, в результате применения макрокоманды в программе формальные аргументы в макроопределении замещаютсясоответствующими фактическими аргументами; в этом и заключается учет контекста.
Процесс такого замещения называется макрогенерацией, а результатом этогопроцесса является макрорасширение.К примеру, рассмотрим самое короткое макроопределение в листинге 14.1 —clearjrg. Как отмечено ранее, результаты работы макроассемблера можно узнать,просмотрев файл листинга после трансляции. Покажем несколько его фрагментов, которые демонстрируют, как был описан текст макроопределения dear_rg (строки 24-27), как был осуществлен вызов макрокоманды dear_rg с фактическим параметром ах (строка 74) и как выглядит результат работы макрогенератора,сформировавшего команду ассемблера хог ах,ах (строка 75):<24><25><2б><27>clear_r macrorg;очистка регистра rgхог rg.rgendmМакрокоманды<74><75>301clear_r ax000Е 33 СО хог а х , а хВ итоге мы получили то, что и требовалось, — команду очистки заданного регистра, в данном случае АХ.
В другом месте программы вы можете выдать ту же макрокоманду, но уже с другим именем регистра. Если у вас есть желание, можетепровести эксперименты с этой и другими макрокомандами. Каждый фактическийаргумент макрокоманды представляет собой строку символов, для формированиякоторой применяются определенные правила.» Строка может состоять:П из последовательности символов без пробелов, точек, запятых, точек с запятой;П из последовательности любых символов, заключенных в угловые скобки(<.. .>), в которой можно указывать как пробелы, так и точки, запятые, точкис запятыми (мы упоминали об этом операторе выделения ассемблера приобсуждении директивы EQU).• Для того чтобы указать, что некоторый символ внутри строки, представляющей фактический параметр, является собственно символом, а не чем-то иным,например, некоторым разделителем или ограничивающей скобкой, применяется специальный оператор !.
Этот оператор ставится непосредственно переднужным символом, и его действие эквивалентно заключению этого символав угловые скобки. Таким образом, оператор ! также является оператором выделения, но одиночного символа.я Для вычисления в строке, представляющей фактический параметр в макрокоманде, некоторого константного выражения в начале этого выражения нужнопоставить знак % (процент):% константное_выражениеЗначение константное_выражение вычисляется и подставляется в текстовом видев соответствии с текущей системой счисления1.Теперь обсудим вопрос, как транслятор распознает формальные аргументы в теле макроопределения для их последующей замены фактическими аргументами?Прежде всего по их именам в заголовке макроопределения. В процессе генерации макрорасширения компилятор ассемблера ищет в тексте тела макроопределения последовательности символов, совпадающие с теми последовательностямисимволов, из которых состоят формальные параметры.
После обнаружения такогосовпадения формальный параметр из тела макроопределения замещается соответствующим фактическим параметром из макрокоманды. Этот процесс называетсяподстановкой аргументов. Здесь нужно отметить еще раз особо список формальных аргументов в заголовке макроопределения. В общем случае он может содерПод текущей системой счисления понимается то, как интерпретируются транслятором числа илистроки с фиксированным числовым значением — как двоичные, десятичные или шестнадцатиричные числа. По умолчанию транслятор трактует их как десятичные.
Ассемблер имеет специальнуюдирективу .radix, которая дает возможность изменить текущую систему счисления. В качестве операнда директивы .radix может быть значение 2,10 или 16, что означает выбор, соответственно, двоичной, десятичной или шестнадцатеричной системы счисления.302Глава 14. Макросредства языка ассемблеражать не только разделенные запятыми формальные элементы, но и некоторую дополнительную информацию. Полный синтаксис формального аргумента следующий:имя_формального_аргумента[:тип]Здесь тип может принимать значения:II REQ — это значение говорит о том, что требуется обязательное явное заданиефактического аргумента при вызове макрокоманды;т =<любая_строка> — если аргумент при вызове макрокоманды не задан, то в соответствующие места в макрорасширении по умолчанию будет вставлено значение любая_строка (будьте внимательны: символы, входящие в операнд любая_строка, должны быть заключены в угловые скобки).Однако распознать в теле макроопределения формальный аргумент ассемблерможет не всегда.
Это, например, может не произойти в случае, когда он являетсячастью некоторого идентификатора. В этом случае последовательность символовформального аргумента отделяют от остального контекста с помощью специального оператора замены (символа &). Этот прием часто используется для заданиямодифицируемых идентификаторов и кодов операций. К примеру, определим макрос, который предназначен для генерации в программе некоторой таблицы, причем параметры этой таблицы можно задавать с помощью аргументов макрокоманды:def tabletabl_&typeendm.datadef tab!def_tablmacrod&typetype:=b,l.en:REQlen dup (0)ьдеw,5После трансляции текста программы, содержащей эти строки, получатся следующие макрорасширения:tabl_b dbtabl_w dw10 dup (0)10 dup (0)Символ & можно применять и для распознавания формального аргумента в строке, заключенной в двойные кавычки (" "). Например,num_charmacromessageподсчитать количество (num) символов в строкеjmp mlelemdb "Строка &message содержит";число символов в строке m e s s a g e в коде ASCIInum db 2 dup (0)db " с и м в о л о в " , 1 0 , 1 3 , ' $ ';конец строки;дпя вывода функцией 09hml:;вывести elem на экранendmВ связи с последним фрагментом разберем ситуацию, когда тело макроопределения содержит метку или имя в директиве резервирования и инициализации данных.
Если некоторая макрокоманда вызывается в программе несколько раз, то приМакрокоманды303макрогенерации один и тот же идентификатор будет определен несколько раз, что,естественно, транслятор посчитает ошибкой. Для выхода из подобной ситуацииприменяют директиву LOCAL, которая имеет следующий синтаксис:local список_идентификаторовЭту директиву необходимо указывать непосредственно за заголовком макроопределения. Результатом работы директивы LOCAL будет генерация в каждом экземпляре макрорасширения уникальных имен для всех идентификаторов, перечисленных в операнде список_идентификаторов.
Эти уникальные имена имеют вид??хххх, где хххх — шестнадцатеричное число. Для первого идентификатора в первом экземпляре макрорасширения хххх = 0000, для второго — хххх = 0001 и т. д. Контроль правильности размещения и использования этих уникальных имен берет насебя транслятор ассемблера. Для того чтобы окончательно разобраться в деталях,введем и оттранслируем листинг 14.2. В нем помимо некоторых ранее рассмотренных макрокоманд содержится макрокоманда num_char. Ее назначение — подсчитывать количество символов в строке, адрес которой передается этой макрокоманде в качестве фактического параметра. Строка должна удовлетворять требованию,предъявляемому к строке, предназначенной для вывода на экран функцией 09hпрерывания 21h, то есть заканчиваться символом $.