12_Макросредства языка Ассемблер (В.Г. Баула - Введение в архитектуру ЭВМ и системы программирования), страница 5
Описание файла
Файл "12_Макросредства языка Ассемблер" внутри архива находится в папке "В.Г. Баула - Введение в архитектуру ЭВМ и системы программирования". PDF-файл из архива "В.Г. Баула - Введение в архитектуру ЭВМ и системы программирования", который расположен в категории "". Всё это находится в предмете "практика расчётов на пэвм" из 1 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 5 страницы из PDF
Таким образом,exitm прекращает обработку той части макроопределения, которая ограничена ближайшей при просмотревниз директивой endm.12(т.е. им отводится место в памяти Макропроцессора). У нас переменные периода генерации порождаются автоматически, при присваивании им первого значения. Так, в нашем макроопределениилокальная переменная периода генерации с именем K порождается при выполнении макрооператораприсваивания K=0, при этом ей, естественно, присваивается нулевое значение.Следующая важная компонента макросредств Ассемблера – это макроциклы (которые, конечно,должны быть в макросредствах, как в любом "солидном" алгоритмическом языке высокого уровня).
1В нашем макроопределении мы использовали один из видов макроциклов с именем irp. Этот макроцикл называется циклом с параметром, он очень похож на цикл с параметром языка Паскаль и имеет такой синтаксис (параметр цикла мы назвали именем i):irp i,<список цикла>тело циклаendmПараметр цикла является локальной в этом цикле переменной периода генерации, которая, в отличие от переменных периода генерации, определяемых пользователем как показано выше, принимает строковые значения. Список цикла (он заключается в угловые скобки) является последовательностью (возможно пустой) текстовых строк, разделённых запятыми (напомним, что в Макропроцессорестроки не заключаются в апострофы). В нашем последнем макроопределении такой список макроцикла<al,ah,bl,bh,cl,ch,dl,dh,AL,AH,BL,BH,CL,CH,DL,DH,Al,Ah,Bl,Bh,Cl,Ch,Dl,Dh,aL,aH,bL,bH,cL,cH,dL,dH>Этот список содержит 32 двухбуквенные текстовые строки.
Вообще говоря, его необходимо записывать в виде одного предложения Макроассемблера (что возможно, так как максимальная длина предложения в нашем Ассемблере около 130 символов), но мы для удобства изобразили его в две строки.Выше, при написании текста макроопределения oint мы записали этот список даже в виде четырёхстрок, что, конечно, тоже неправильно и сделано только для удобства восприятия нашего примера.Выполнение макроцикла с именем irp производится по следующему правилу. Сначала переменной цикла присваивается первое значение из списка цикла (первая строка текста), после чего выполняется тело цикла, при этом все вхождения в это тело имени параметра цикла заменяются на текущее значение этой переменой цикла. После этого параметру цикла присваивается следующее значение из списка цикла и т.д.
Ясно, что для пустого списка тело цикла не будет выполняться ни одногораза. В нашем примере тело цикла будет выполняться не более 32-х раз, при этом переменная i будетпоследовательно принимать значения двухсимвольных строк текста al,ah,bl и т.д.Как можно заметить, целью выполнения макроцикла в нашем примере является присваиваниепеременной периода генерации K значения единицы, если параметр макрокоманды совпадает по написанию с именем одного из коротких регистров нашего компьютера, причём это имя может задаваться как большими, так и малыми буквами алфавита в любой комбинации. Это позволяет распознать имя короткого регистра, как бы его ни записал пользователь, и присвоить переменной K значение единица, в противном случае переменная K сохраняет нулевое значение.2 После присваиванияпеременной периода генерации значения K=1 дальнейшее выполнение цикла бессмысленно, и мывыходим из него по директиве exitm за ближайшую вниз директиву endm.Обратите внимание, что макроцикл завершается такой же директивой endm, как и всё макроопределение.
Это позволяет рассматривать макроцикл как некоторое стандартное макроопределение,только без заголовка (т.е. без имени и без параметров).Далее в макроопределении расположен условный макрооператор нового для нас вида, который,однако, наиболее похож на условный оператор языка Паскаль:if <логическое выражение>ветвь then1Заметим, что в языках высокого уровня обычно есть далеко не все из рассматриваемых нами макросредств.2Для удобства программирования в некоторые Макроассемблеры введены условные макрооператоры, которые сравнивают две строки на равенство и неравенство без учёта регистра (при этом большие и маленькиелатинские буквы не различаются). Например, такие условные макрооператоры есть в старших версиях нашегоМакроассемблера MASM, их использование, конечно, существенно облегчает программирование макроопределений.13elseветвь elseendifНа этом примере мы познакомимся с логическими выражениями Макропроцессора.
Эти выражения весьма похожи на логические выражения Паскаля, только вместо логических констант true иfalse используются соответственно целые числа 1 и 0, а вместо знаков операций отношения используются, как и в некоторых других языках (например, в Фортране операция равенство обозначается как .EQ.), мнемонические двухбуквенные имена, которые перечислены ниже:1EQ=вместоNEвместо <>LT<вместоLEвместо <=GT>вместоGEвместо >=Таким образом, заголовок нашего условного макрооператораif K EQ 1 or type X EQ byteэквивалентен такой записи на Паскалеif (K=1)or (type X = byte) thenЗаметим, что в Паскале для этого примера нам необходимо использовать круглые скобки, так какоперация отношения = имеет меньший приоритет, чем операция логического сложения or.
В Макроассемблере же, наоборот, операции отношения (EQ,GT и т.д.) имеют более высокий приоритет,чем логические операции (or,and и not), а так как оператор type имеет больший приоритет, чемоператор EQ, то круглые скобки не нужны. По учебнику [5] Вам необходимо обязательно изучитьуровни приоритета всех операторов Ассемблера.Таким образом, наш условный макрооператор после вычисления логического выражения получает значение true, если K=1 (т.е.
параметр макрокоманды – это короткий регистр r8) или же дляслучая, когда type X EQ byte (т.е. параметр макрокоманды задан выражением, которое имеетформат m8). В остальных случаях (для параметра форматов r16,m16,i16) логическое выражениеимеет значение false. Когда это логическое выражение равно true, наше макроопределение вычисляет и помещает на регистр ax целочисленное значение, подлежащее выводу. И так как теперьэто значение имеет формат r16, то для его вывода можно использовать уже известную нам макрокоманду outint, а для значения false просто выводить значение параметра X.2Необходимо также заметить, что операции отношения LT,GT,LE и GE, как правило, рассматривают свои операнды как беззнаковые значения. Исключением является случай, когда Макропроцессор "видит", что некоторой переменной периода генерации явно присвоено отрицательное значение(т.е.
в присваиваемой константе есть знак минус). Например, рассмотрим следующий фрагмент программы:mov ax,ax; Чтобы была метка, type L=-1K = type L; Макропроцессор "видит" беззнаковое L=0FFFFhif K LТ 0; Берётся K=0FFFFh > 0 ==> false !. . .K = -1; Макропроцессор "видит" знаковую константу -1if K LT 0; Берётся K=-1 < 0 ==> true !L:1В отличие от Паскаля, эти операции отношения можно применять только к целочисленным операндам.Другими словами нельзя, например, написать сравнение на равенство текстовых строкif <X> EQ <ax>Для вычисления логического выражения от строковых значений, как мы уже знаем, используются специальныеусловные макрооператоры (ifb, ifidn, ifdif и некоторые другие).2Для простоты наше макроопределение никогда не задаёт второй параметр макрокоманды outint – ширину поля для вывода целого числа.14.
. .Как видим, этот вопрос в нашем Макроассемблере весьма запутан, его надо тщательно изучить,например, по учебнику [5].Не следует, конечно, думать, что мы написали совсем уж универсальное макроопределение длявывода любых целых чисел, которое всегда выдаёт либо правильный результат, либо диагностику обошибке в своих параметрах.
К сожалению, наше макроопределение не будет выводить значения аргументов форматов m8 и m16, если эти аргументы заданы без имён, по которым можно определить ихтип, например, вызов oint [bx] , будет считаться ошибочным. Это связано с тем, что ошибкувызовет оператор type [bx].Кроме того, например, при вызове с помощью макрокомандыoint --8будет получено макрорасширениеmov ax,--8outint ax(так как type ––8 = 0).
К сожалению, наш Макропроцессор не предоставляет хороших средств,позволяющих выявить синтаксические ошибки такого рода в макрокомандах.1 Показанные вышеошибки будут выявлены уже компилятором с Ассемблера при анализе полученного макрорасширения.Далее, Вам важно понять принципиальное отличие переменных языка Ассемблера и переменныхпериода генерации. Так, например, переменная Ассемблера с именем X может, например, определяться предложением резервирования памяти в сегменте данныхXdw13В то время как переменная периода генерации с именем Y может порождаться макрооператором присваиванияY = 13Главное – это уяснить, что эти переменные имеют разные и непересекающиеся временá существования.
Переменные периода генерации существуют только во время компиляции исходного модуляс языка Ассемблер на объектный язык (т.е. во время генерации объектного модуля, отсюда и их название), и заведомо уничтожаются до начала счёта. В то же время описанные в сегментах статическиепеременные Ассемблера, наоборот, существуют только во время счёта программы (от начало счётадо выполнения макрокоманды finish). Некоторые программисты не понимают этого и пытаютсяиспользовать значение переменной Ассемблера на этапе компиляции, например, пишут такой неправильный условный макрооператор:if X EQ 13Это сразу показывает, что они не понимают суть дела, так как на этапе компиляции хотят анализировать значение переменной X, которая будет существовать только во время счёта программы.