АССЕМБЛЕР. Компоновщик. Загрузчик. Макрогенератор


Обработка макрокоманд.


            Теперь рассмотрим действия макрогенератора, когда он встречает макрокоманду (МК).

            Пусть, к примеру, в тексте программы есть такой фрагмент:

          исходный текст           окончательный текст

            |   ...                        ...

         28 |  M Q,50,[BX]                MOV AX,Q

         29 |  JMP LAB            ??0000: ADD AX,50

         30 |   ...                       MOV [BX],AX

                                          JMP LAB

                                           ...

            Пусть стек сейчас пуст, а счетчики макрогенератора имеют такие значения: НОМ=0000, УР=0, НС=28 (рис. а).

                                      ¦-------------¦

                                      ¦ L  | ??0000 ¦

                                      ¦-------------¦



                                      ¦ W  |  50    ¦

                                      ¦-------------¦

                                      ¦ OP |  ADD   ¦

                                      ¦-------------¦

                   ¦             ¦    ¦     16      ¦    ¦             ¦

                   ¦=============¦    ¦=============¦    ¦=============¦

                   ¦ Z  |  [BX]  ¦    ¦ Z  |  [BX]  ¦    ¦ Z  |  [BX]  ¦

                   ¦-------------¦    ¦------------ ¦    ¦-------------¦

                   ¦ Y  |  50    ¦    ¦ Y  |  50    ¦    ¦ Y  |  50    ¦

                   ¦-------------¦    ¦-------------¦    ¦-------------¦

                   ¦ X  |  Q     ¦    ¦ X  |  Q     ¦    ¦ X  |  Q     ¦

                   ¦-------------¦    ¦-------------¦    ¦-------------¦

 ¦             ¦   ¦     28      ¦    ¦     28      ¦    ¦     28      ¦

 ¦=============¦   ¦=============¦    ¦=============¦    ¦=============¦

 ¦/////////////¦   ¦/////////////¦    ¦/////////////¦    ¦/////////////¦

  НОМ=0000,УР=0     НОМ=0000,УР=1      НОМ=0001,УР=2      НОМ=0001,УР=1

     НС=28             НС=15              НС=12              НС=16


       а)                б)                 в)                 г)
            Итак, НС=28. В 28- й строке находится МК. Как макрогенератор узнает о том, что это МК? Выделив из строки мнемокод, макрогенератор "лезет" в ТМ и смотрит, нет ли там такого имени. Если есть, значит это МК, нет - нечто иное (например, директива макроязыка, что распознается по таблице директив этого языка, или обычное предложение ЯА). В нашем случае имя М имеется в ТМ, поэтому макрогенератор и узнает, что перед ним МК. С нею макрогенератор поступает так (рис. б).
            Во-первых, макрогенератор записывает в стек текущее значение НС (у нас - 28), т.е. номер строки с МК. Это как бы адрес возврата: сейчас макрогенератор перейдет к обработке строк из МО, но затем ему надо будет вернуться к МК; чтобы можно было осуществить такой возврат, номер строки с МК и запоминается в стеке. Во-вторых, используя данные из соответствующей строки ТМ, макрогенератор записывает в стек названия формальных параметров макроса (у нас - X, Y и Z), а рядом с ними записывает фактические параметры, взятые из МК (у нас - Q, 50 и [BX]). Тем самым создана таблица соответствия между формальными и фактическими параметрами, которая будет сейчас использоваться при макроподстановке (МП). В-третьих, макрогенератор увеличивает счетчик УР
на 1 (УР=1); это означает, что макрогенератор "входит" внутрь МО. В-четвертых, макрогенератор присваивает счетчику НС взятый из ТМ номер первой строки тела макроса (у нас НС=15); это значит, что макрогенератор переходит к обработке тела макроопределения.
            Итак, НС=15. Обработку любого предложения программы макрогенератор всегда начинает с проверки, где он сейчас находится - вне или внутри МО. Делается это просто, сравнением УР с 0: если УР=0, то - вне МО, а иначе внутри МО. Зачем это надо знать? А затем, что, находясь внутри МО, макрогенератор всегда начинает обработку предложения с замены в нем всех вхождений формальных параметров на соответствующие фактические параметры. (При этом он не портит текст программы, а строит копию данного предложения.) Делается это так: макрогенератор выделяет в предложении очередное имя и смотрит, есть ли оно среди формальных параметров в таблице соответствия (из стека). Если нет (как в нашем случае для имен MOV и AX), тогда макрогенератор ничего не делает, а если есть (как для X), тогда заменяет это имя на соответствующий фактический параметр:


MOV AX,X --> MOV AX,Q
            После такой замены макрогенератор смотрит, что получилось - обычное предложение ЯА или конструкция макроязыка. Для этого он выделяет мнемокод (у нас - MOV) и смотрит, нет ли такого имени в ТМ. Если есть, тогда это МК. Если же нет, тогда макрогенератор просматривает таблицу директив макроязыка и т.д. У нас - обычное предложение ЯА, поэтому макрогенератор передает его на обработку ассемблеру, другими словами, заносит в окончательный текст программы [записать справа от программы]. Когда ассемблер закончит обработку предложения, он возвратит управление макрогенератору, который увеличивает НС на 1 и идет дальше.
            Теперь НС=16. Поскольку УР?0, то макрогенератор прежде всего делает замену формальных параметров на фактические:
M1 ADD,Y  -->  M1 ADD,50
Далее макрогенератор выделяет мнемокод и по ТМ узнает, что это имя макрокоманды. Следовательно, очередное предложение - МК. Действия макрогенератора такие же, как и при обработке МК из 28-й строки (рис. в). В стек записывается текущее значение НС (16) и формальные параметры макроса M1 (OP, W) вместе с фактическими параметрами (ADD, 50). Кроме того, в макросе M1 имеется локальное имя (L), которое макрогенератор также записывает в стек, причем ему в соответствие ставится специмя с текущим значением НОМ
(у нас это ??0000), после чего значение НОМ увеличивается на 1 (НОМ=0001); на это специмя и будут заменяться все вхождения L в тело макроса. Далее УР увеличивается на 1 (УР=2), т.к. макрогенератор "входит" в новое МО, и в НС
записывается начало тела макроса M1
(12), взятое из ТМ. Начинается обработка тела макроса M1.
            Итак, НС=12. Поскольку УР?0, то макрогенератор прежде всего делает в предложении из 12-й строки замену формальных параметров на фактические параметры и замену локальных имен на специмена:
L: OP AX,W  -->  ??0000: ADD AX,50
Далее макрогенератор устанавливает, что получилось обычное предложение ЯА, поэтому передает его на обработку ассемблеру; другими словами, это предложение попадет в окончательный текст программы [записать]. После возврата ассемблером управления макрогенератору последний увеличивает НС на 1 и переходит к следующему предложению программы.


            Теперь НС=13. Это директива ENDM, признак конца МО. Действия макрогенератора в этом случае такие (рис. г). Во-первых, он очищает стек от всего того, что было записано сюда, когда началась обработка последней МК, и при этом восстанавливает в НС то значение, которое хранилось в стеке (у нас НС=16); это как раз номер строки с той МК, из-за которой макрогенератор попал в МО. Во-вторых, счетчик УР уменьшается на 1 (УР=1), чем фиксируется выход из МО. На этом обработка тела макроса M1
полностью закончена и соответствующее макрорасширение получено. Макрогенератор увеличивает НС на 1 (НС=17) и переходит к предложению, следующему за МК.
            Итак, НС=17. Поскольку снова УР?0, то выполняется замена формальных параметров на фактические, но уже, естественно, используется табличка соответствия, которая сейчас находится в верху стека:
MOV Z,AX  -->  MOV [BX],AX
Поскольку это обычное предложение ЯА, то макрогенератор его не обрабатывает, а передает ассемблеру, т.е. заносит в окончательный текст программы [записать], после чего увеличивает НС на 1.
            Теперь НС=18. Снова директива ENDM, завершающая МО. Действия макрогенератора мы уже знаем: стек очищается от информации, попавшей сюда при появлении МК из строки 28, и в НС восстанавливается значение, хранившееся в стеке (28), т.е. номер МК, из-за которой макрогенератор попал в МО, далее УР уменьшается на 1 (УР=0). На этом наконец-то закончилась обработка МК из 28-й строки.
            Макрогенератор увеличивает НС на 1 и идет дальше.
            Вот так макрогенератор "расправляется" с МК.
            В заключение хочу сделать пару замечаний. Во-первых, если внутри МО макрогенератор встречает директиву EXITM, то он выполняет такие же действия, что и при появлении директивы ENDM, т.е. покидает МО. Во-вторых, хочу обратить ваше внимание на то, что макрогенератор при обработке макрокоманд вовсю использует стек. Это очень важно, т.к. одни макросы могут обращаться к другим и, более того, допускаются рекурсивные макросы, поэтому и приходится спасать информацию о каждой МК в новом месте, а для этого и нужен стек.

Содержание раздела