12_Макросредства языка Ассемблер (В.Г. Баула - Введение в архитектуру ЭВМ и системы программирования), страница 4
Описание файла
Файл "12_Макросредства языка Ассемблер" внутри архива находится в папке "В.Г. Баула - Введение в архитектуру ЭВМ и системы программирования". PDF-файл из архива "В.Г. Баула - Введение в архитектуру ЭВМ и системы программирования", который расположен в категории "". Всё это находится в предмете "практика расчётов на пэвм" из 1 семестр, которые можно найти в файловом архиве МГУ им. Ломоносова. Не смотря на прямую связь этого архива с МГУ им. Ломоносова, его также можно найти и в других разделах. .
Просмотр PDF-файла онлайн
Текст 4 страницы из PDF
Как и локальные имена, например, в языке Паскаль,они не видны извне макроопределения, которое в этом смысле играет роль блока, следовательно, в9других частях этого программного модуля также могут использоваться эти имена. Директива localдля Макропроцессора имеет следующий смысл. При каждом входе в макроопределение локальныеимена, перечисленные в этой директиве, получают новые уникальные значения. Обычно Макропроцессор выполняет это совсем просто: при первом входе в макроопределение заменяет локальныеимена L и T, например, на имена ??0001 и ??0002, при втором входе – на имена ??0003 и??0004 и т.д. Учтите, что в Ассемблере символ ? относится к буквам и может входить в имена, хотяпрограммисту и не рекомендуется использовать такие имена, чтобы не конфликтовать с именами,автоматически порождаемыми Макропроцессором.Например, для макрокомандыouttxt 'Привет!'будет построено макрорасширениеjmp??0001db'Привет!'db'$'??0001: push ds; запоминание dspush cspopds; ds:=cspush dx; сохранение dxmovdx,offset ??0002outstrpopdx; восстановление dxpopds; восстановление ds??0002Назначение директивы local становится понятным, когда мы рассмотрим, что будет, если этудирективу убрать из нашего макроопределения.
В этом случае у двух макрорасширений макрокоманды outtxt будут внутри одинаковые метки L и T, что повлечёт за собой ошибку, которая будет зафиксирована на следующем этапе, когда Ассемблер станет переводить программу на объектныйязык. Обязательно поймите, что при использовании директивы local внутри программного модуляна Ассемблере теперь не стало одинаковых имён.Обратите внимание, что второй фактический параметр (строку символов) – наше макроопределение располагает внутри макрорасширения (т.е.
в сегменте кода). А так как макрокоманда outstr"думает", что выводит текст из сегмента данных, то мы временно совместили сегменты данных икода, загрузив в регистр ds значение регистра cs.После изучения написанной нами макрокоманды outtxt у некоторых читателей может сложиться ошибочное впечатление, что теперь во время счёта программы изменяется её сегмент кода (т.е. наша программа стала самомодифицирующейся).
Обязательно поймите, что это не так!В качестве следующего примера рассмотрим такую проблему. Мы выводим значения знаковыхцелых чисел, используя макрокоманду outint. Эта макрокоманда, однако, позволяет выводить целые значения только форматов r16,m16 и i16. Если программисту необходимо часто выводить целые числа ещё и в форматах r8 и m8, то он, естественно, захочет написать для себя новое макроопределение, например с именем oint, которое обеспечивает такие более широкие возможности. Используя макроопределение outint как базовое, мы напишем новое макроопределение с именемoint. Ниже приведён вид этого макроопределения.ointmacro Xlocal Kifb <X>%out Нет аргумента в oint!11Будьте очень осторожны в употреблении русских букв в текстовых строках для вывода и даже в комментариях, так как наш Ассемблер MASM 4.0 относится к таким буквам очень "болезненно", иногда принимает закакие-то служебные символы и часто просто зацикливается при компиляции. В связи с этим внутри макроопределений рекомендуется использовать только латинские буквы, в наших примерах мы не делали этого из соображений наглядности.
Ну, не предназначен (или, как более правильно говорить, не локализован) этот Ассемблер для российских программистов ☺.10.errexitmendifK=0irp i,<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>ifidn <i>,<X>K=1exitmendifendmif K EQ 1 or type X EQ bytepush axmoval,Xcbwoutint axpopaxelseoutint XendifendmВ макроопределении oint используется много новых макросредств, поэтому мы сейчас оченьподробно прокомментируем его работу. Вслед за заголовком макроопределения находится уже знакомая нам директива с объявлением локального имени K, затем располагается условный макрооператор с именем ifb (if blank), который вырабатывает значение true, если в угловых скобках ему заданпустой параметр X (пустая строка символов).Директива Ассемблера %out предназначена для вывода во время компиляции диагностики обошибке, текст диагностики программист располагает сразу вслед за первым пробелом после именидирективы %out.
Таким образом, программист может задать свою собственную диагностику, которая будет выведена при обнаружении ошибки в макроопределении. В нашем примере диагностика"Нет аргумента в oint!" выводится, если программист забыл задать аргумент у макрокомандыoint.После того, как макроопределение обнаружит ошибку в своих параметрах, у программиста естьдве возможности задания дальнейших действий. Во-первых, можно считать выданную диагностикупредупредительной, и продолжать компиляцию программы с последующим получением (или, какговорят, генерацией) объектного модуля.
Во-вторых, можно считать обнаруженную ошибку фатальной, и запретить генерацию объектного модуля, в этом случае, естественно, будет невозможен ивыход программы на счёт. Заметим, однако, что при возникновении фатальной ошибки Ассемблер,тем не менее, будет продолжать проверку остальной части входного модуля на наличие других ошибок.В нашем макроопределении мы приняли второе решение и зафиксировали фатальную ошибку, очём предупредили Ассемблер с помощью директивы .err. Получив эту директиву, Ассемблер выдаст на экран, и вставит в протокол своей работы (листинг) диагностику о фатальной ошибке, обнаруженной в программе.
Эта ошибка носит обобщённое название forced error (т.е. ошибка, "навязанная" Ассемблеру Макропроцессором). 1После возникновения фатальной ошибки и выдачи директивы .err дальнейшая обработка макроопределения не имеет уже никакого смысла, и мы запретили эту обработку, выдав Макропроцессору директиву exitm. Эта директива в нашем случае прекращает процесс построения макрорасшире-1В следующей главе мы кратко рассмотрим схему работы Ассемблера и узнаем, что наш Ассемблер прикомпиляции дважды просматривает текст исходного модуля. Директивы %out и .err выполняются при каждом просмотре Ассемблером текста программы, то есть определяемая ими диагностика выводится дважды.Этого можно избежать, используя специальные условные макрооператоры с именами if1 и if2 и директивыerr1 и err2 (смотри, например, учебник [5]).11ния,1 и в нём остаются только те строки, которые попали туда до выполнения директивы exitm.
Например, если вызвать наше макроопределение макрокомандой oint без параметра, то будет получено такое макрорасширение:%out Нет аргумента в oint!.errИменно оно и будет подставлено на место ошибочной макрокоманды без параметра. На этом примеремы показали, как программист может предусмотреть свою собственную реакцию и диагностику наошибку в параметрах макрокоманды. Обратите внимание, что реакция на ошибку в макрокомандепроизводится именно на этапе обработки самой макрокоманды, а не позже, когда Ассемблер будетанализировать полученное макрорасширение.Обратим также внимание на следующую типичную ошибку: выдачу диагностики при компиляции программы пытаются сделать с помощью макрокоманды outstr, например, так:Diag:db "Нет аргумента в oint!$". .
.ifb <X>lea dx,Diagoutstr. . .При этом они не понимают, что выдача такой диагностики будет производиться не на этапе компиляции программы, как нам необходимо, а только на этапе счёта программы, до которого дело вообще не дойдёт, так как зафиксирована фатальная синтаксическая ошибка!Следующая директива Макроассемблера из последнего примераK=0является макрооператором присваивания и показывает использование нового важного понятия измакросредств нашего Ассемблера – так называемых переменных периода генерации. Это достаточно сложное понятие, и сейчас мы начнём разбираться, что это такое.Ещё раз напомним, что макросредства по существу являются алгоритмическим языком, поэтомуполезно сравнить эти средства, например, с таким алгоритмическим языком, как Паскаль.
Сначаламы познакомились с макроопределениями и макрокомандами, которые являются аналогами соответственно описаний процедур и операторов процедур Паскаля. Затем мы изучили некоторые из условных макрооператоров, являющихся аналогами условных операторов Паскаля, а теперь пришла очередь заняться аналогами операторов присваивания, переменных и циклов языка Паскаль в нашихмакросредствах.Макропеременные в нашем макроязыке называются переменными периода генерации.
Такоеназвание призвано подчеркнуть время существования этих переменных: они порождаются только напериод обработки исходного программного модуля на Ассемблере и генерации объектного модуля.Когда Ассемблер завершает компиляцию программного модуля, переменные периода генерацииуничтожаются, так как заканчивается выполнение самой программы Ассемблера.Как и переменные в Паскале, переменные периода генерации в Макроассемблере бывают глобальные и локальные. Глобальные переменные уничтожаются только после построения всего объектного модуля, а локальные – после выхода из того макросредства, в котором они порождены. Внашем Макроассемблере различают локальные переменные периода генерации макроопределения(они порождаются при входе в макроопределение по директиве local, и уничтожаются после построения макрорасширения), и локальные переменные – параметры макроциклов с именем irp (ониуничтожаются после выхода из этого цикла).
В нашем последнем макроопределении локальной является переменная периода генерации с именем K, о чём объявлено в директиве local, и переменнаяпериода генерации с именем i, которая является локальной в макроцикле irp.Переменные периода генерации могут принимать целочисленные, а параметры макроциклов –ещё и строковые значения. В нашем Ассемблере нет специальной директивы (аналога описания переменных var в Паскале), при выполнении которой порождаются переменные периода генерации1Точнее, директива exitm производит выход вниз за ближайшую незакрытую директиву endm, которая,как мы вскоре узнаем, может задавать конец не только макроопределения, но и макроциклов.