ПСЕВДОКОМАНДЫ И КОМАНДЫ ОПРЕДЕЛЕНИЯ ДАННЫХ Операторы языка ассемблера, используемые для управления программой, можно классифицировать следующим образом: определения имен (EQU,BSET); определения данных (DB,DW); определения памяти (DS); условного ассемб- лирования (IF, ELSE, ENDIF); прекращения ассемблирования и конца ленты (END,EOT); управления программными сегментами (ORG, ASEG, DSEG, CSEG); программных связей (PUBLIC,EXTRN,NAME,STKLN). Псевдокоманды определения имен Ассемблер автоматически присваивает значение именам,которые опре- делены как метки команд. Эти значения определяются равными текущему значению счетчика команд в процессе ассемблирования команды. Но в про- грамме можно определить другие имена и присвоить им значения, исполь- зуя псевдокоманды EQU и SET. Имена, определенные псевдокомандой EQU,не могут быть переопределены во время текущего процесса ассемблирования. Если же имя определено псевдокоманой SET, оно может быть впоследствии многократно переопределено. Поскольку однажды определенные имена сох- раняют значение на протяжении всей программы,может возникнуть ситуация многократного определения имен,если псевдокоманды EQU и SET содержатся в макроопределениях. Эту ошибку можно устранить с помощью псевдокоман- ды LOCAL. Имена, определяемые псевдокомандами EQU и SET, не должны от- деляться двоеточием от псевдокоманды. ПСЕВДОКОМАНДА EQU присваивает расположенному в поле метки имени зна- чение выражения в поле операнда. Псевдокоманда ONES EQU 0FFH поместит имя ONES в таблицу символов и присвоит ему значение FF. Выра- жение в поле операнда не может быть внешним именем или включать внеш- нее имя. Вычисление значения выражения в псевдокоманде EQU производит- ся по модулю 2^16, и таким образом, оно всегда находится в пределах от 0 до 65 535 включительно. ПСЕВДОКОМАНДА SET присваивает имени в поле метки значение выражения, расположенного в поле операнда. Ассемблер вводит это имя в таблицу символов и всякий раз, когда выражение для этого имени вычисляется за- ново, ассемблер заменяет это значение в таблице. Функции псевдокоманды SET идентичны функциям псевдокоманды EQU,за исключением того, что одно и то же имя может быть многократно переопределено псевдокомандами SET в процессе ассемблирования. Значение выражения всега вычисляется по модулю 2^16. ПРИМЕР 1. Использование псевдокоманды SET в программе (здесь и далее содержащая шестнадцатеричные цифры левая колонка включает коды прог- раммы, получаемой в результате трансляции): KRIST SET 5D; ОПРЕДЕЛЕНИЕ ИМЕНИ KRIST C605 ADI KRIST KRIST SET 10H-6Q; ПЕРЕОПРЕДЕЛЕНИЕ KRIST C60A ADI KRIST ОПЕРАТОРЫ ОПРЕДЕЛЕНИЯ ДАННЫХ И ПАМЯТИ Операторы DB (определение байта) и DW (определение слова) позволяют определить данные в программе, DS резервирует память. В поле операнда данные могут быть специфицированы либо выражениями, либо строками сим- волов. ОПЕРАТОР DB.Специфицированные оператором DB данные располагаются по- следовательно в памяти, начиная с адреса, указанного значением счeтчи- ка команд. Синтаксис оператора DB следующий (поле комментария опущено, символы < > означают синтаквическую конструкцию): <имя>: <список выражений или строк> Поле операндов оператора DB может содержать список, включающий до восьми выражений и (или) строк, разделенных запятыми. Поскольку длина оператора DB ограничена,не всегда существует возможность поместить все восемь элементов списка в одной строке. Этот вопрос решается использо- ванием двух или большего числа операторов DB с более коротким списком выражений. Значение выражения должно находиться в границах от -256 до 255 вклю- чительно. Строка символов может содержать не болеее 128 символов в ко- де КОИ-8,заключенных в апострофы. Если в списке исключаются выражения, значения которых являются 16-битовыми данными, они должны быть преоб- разованы операторами HIGH или LOW, определяющими, какой из байтов бу- дет использован в выражении - старший или младший. Если операторы HIGH или LOW отсутствуют, ассемблер использует по умолчанию оператор LOW и выдает сообщение об ошибке. Имя метки в поле указывает на начальное значение счетчика команд и адресует первый размещаемый в памяти опера- тором DB байт.В следующем примере метка ONE указывает на букву Т стро- ки символов 'TIME' 54494D45 ONE : DB 'TIME' A3 HERE : DB 0A3H FD0A CONST : DB -03H, 5*2 ОПЕРАТОР DW определяет в памяти 16-битовые значения из списка выра- жений поля операнда, начиная с текущего значения счетчика команд. Син- таксис оператора DW: <имя> : DW <список выражений или строк> Младшие 8 бит первго выражения запоминаются по адресу, определяемому текущим значением счетчика команд, а старшие 8 бит - в следующем байте памяти, т.е. производится инверсное представление 16-битовых данных для запоминания старшего и младшего байтов. Такой процесс повторяется до тех пор,пока не будет размещен весь список, который может содержать до восьми элементов. Элемент списка выражений в этом операторе озна- чает одно слово ( 16 бит, 2 байта ), которое обычно является адресом. Если значением выражения являются однобайтные данные,то они дополняют- ся старшим нулевым байтом до слова. В поле операнда можно включать строки символов, содержащие один или два символа в коде КОИ-8, заклю- ченные в апострофы, но при этом следует помнить, что символы будут за- помнены в программе в инверсном порядке. Использование строки длиной более двух символов недопустимо. Если в поле имени присутствует метка, ей присваивается значение ад- реса первого байта размещаемого по данному оператору DW выражения (это младший байт значения первого выражения в списке). Пусть COMP и FILL метки, определенные ранее в программе. Метка COMP имеет значение 3B1C, метка FILL-3EB4. Тогда в результате ассемблирования четырех операторов будут получены следующие коды программы: 1C3B ADDR : DW COMP B43E FILL : DW FILL 41004241 STRING : DW 'A' 'AB' 0400 FOUR : DW 4H Оператор DW обычно используют в программе для запоминания адресов. ОПЕРАТОР DS применяют для определения областей памяти, используемых для размещения данных в ходе выполнения программы. Синтаксис оператора DS <имя> : DS <выражение> Значение выражения определяет число байт, которые должны быть заре- зервированы в памяти. Любое имя, встречающееся в поле операндов, долж- но быть предварительно определено в ходе ассемблирования до использо- вания оператора DS. В отличие от операторов DB и DW оператор DS не оп- ределяет какие-либо данные программы при ассемблировании. Содержимое резервируемой памяти заранее непредсказуемо, если программа не выпол- няет ее начальную установку. Если в поле имени присутствует метка, ее значение будет указывать на первый байт зарезервированной области (оно равно значению счетчика команд). Если значение выражения равно нулю, то память не резервируется, но в случае присутствия метки ей присва- ивается значение счетчика команд. При ассемблировании директивы DS счетчик команд увеличивается в соответствии со значением, указанным в операнде. ПРИМЕР 2. Использование оператора DS: BUFF : DS 72; РЕЗЕРВИРОВАНИЕ 72 БАЙТ ДЛЯ БУФЕРА Организация доступа к определенным операторами DB и DW данным-двух- шаговая процедура: сначала указывается, где размещаются данные, а за- тем процессор выбирает указанное значение из памяти и загружает его в регистр (обычно аккумулятор). ПРИМЕР 3. Последовательность загрузки символа * в коде КОИ-8 в ак- кумулятор: NAME : DB '*'; ОПРЕДЕЛЕНИЕ СИМВОЛА LXI B, NAME; ЗАГРУЗКА АДРЕСА LDAX B; ЗАГРУЗКА СИМВОЛА * В АККУМУЛЯТОР Если необходимо прочесть несколько символов, следует программно орга- низовать процесс их последовательного чтения, например: ALPHA : DB 'AB' ; ОПРЕДЕЛЕНИЕ ALPHA LXI B, ALPHA ; ЗАГРУЗКА АДРЕСА LDAX B ; ЗАГРУЗКА СИМВОЛА А INX B ; ЗАГРУЗКА АДРЕСА LDAX B ; ЗАГРУЗКА СИМВОЛА В Для ассемблера безразлично, что лишь один из двух символов, помеченных меткой ALPHA, доступен в данном участке программы. Программа самостоя- тельно организует процесс считывания данных по адресу ALPHA+1. Такое написание программы приемлемо для малых об'емов данных.Если определена область данных большого об'ема, следует программно организовывать цик- лические процессы, которые будут выполняться до тех пор, пока не будут исчерпаны данные. В этом случае не расходуется память на линейные участки программы. Доступ к байтам для обращения к отдельным полям буфера ( см. пример 2) может быть осуществлен только с использованием имен BUFF+1, BUFF+2,. ..., BUFF+71. Обращение к отдельным полям можно упростить переименова- нием отдельных областей буфера с помощью псевдокоманды EQU. Пусть, на- пример, область буфера BUFF нужно разбить на следующие функциональные поля: идентификации ( 6 байт с именем ID ); имени ( 20 байт с именем NAMЕ ); номера (10 байт с именем NUMBER); номера учреждения ( 5 байт с именем DEPT ); второго номера (10 байт с именем SSNO); даты ( 5 байт с именем DATE ); описания работы (16 байт с именем DESC). Эти поля области BUFF могут получить имена с помощью следующей груп- пы операторов языка ассемблера : BUFF: DS 72; РЕЗЕРВИРОВАНИЕ БУФЕРА ID EQU BUFF; ОПРЕДЕЛЕНИЕ ПОЛЯ ИДЕНТИФИКАЦИИ ( 6 БАЙТ ) NAME EQU BUFF+6; ОПРЕДЕЛЕНИЕ ПОЛЯ ИМЕНИ ( 20 БАЙТ ) NUMBER EQU BUFF+26; ОПРЕДЕЛЕНИЕ ПОЛЯ НОМЕРА ( 10 БАЙТ ) DEPT EQU BUFF+36; ОПРЕДЕЛЕНИЕ ПОЛЯ НОМЕРА УЧРЕЖДЕНИЯ ( 5 БАЙТ ) SSNO EQU BUFF+41; ОПРЕДЕЛЕНИЕ ПОЛЯ ВТОРОГО НОМЕРА ( 10 БАЙТ ) DATE EQU BUFF+51; ОПРЕДЕЛЕНИЕ ПОЛЯ ДАТЫ ( 5 БАЙТ ) DESC EQU BUFF+56; ОПРЕДЕЛЕНИЕ ПОЛЯ ОПИСАНИЯ РАБОТЫ ( 16 БАЙТ ) Отдельное наименование полей буфера данных упрощает обращение к ним и улучшает программу как документ. Псевдокоманды EQU моцут быть вставле- ны в любое место программы, но их написание желательно снабжать более полными комментариями. ПСЕВДОКОМАЙДЫ УСЛОВНОГО АССЕМБЛИРОВАНИЯ ПСЕВДОКОМАНДЫ IF, ELSE и ENDIF позволяют ассемблировать фрагменты программы условно, т.е. в слячае выполнения определенного заданного условия. Условное ассемблирование целесообразно использовать, если программа требует выполнения отдельных фрагментов из некоторых общих. Псевдокоманды IF, ELSE и ENDIF используются взаимосвязанно, синтаксис их следующий: <имя>: IF <выражение> ... <имя>: ELSE ... <имя>: ENDIF Ассемблер анализирует выражение в области операнда псевдокоманды IF и, если нулевой (самый младший) бит в значении выражения равен 1, т.е. выражение истинно, ассемблирует все команды, расположенные между псев- докомандами IF и ELSE, или, если отсутствует ELSE, между IF и ENDIF. Если нулевой бит в значении выражения равен 0, т.е. значение выражения ложно,операторы,расположенные между директивами IF и ELSE (IF и ENDIF при отсутствии ELSE), ассемблером игнорируются. Опараторы между псев- докомандой IF и соответствующей ей псевдокомандой ENDIF определяются как блок IF-ENDIF. Превдокоманда ELSE необязательна и может включаться только один раз между IF и ENDIF. Псевдокоманда ELSE по смыслу проти- воположна IF: если нулевой бит в значении выражения псевдокоманды IF равен 0, все операторы между ELSE и ENDIF ассемблируются; если нулевой бит равен 1-игнорируются. В псевдокомандах ELSE и ENDIF операнды не используются. Блоки IF-ENDIF могут вкладываться друг в друга. Максимальный уровень вложенности - восемь. В блоках IF-ENDIF могут встречаться макроопреде- ления и, наоборот, блоки IF-ENDIF могут встречаться в макроопределени- ях. Если макроопределение начинается в блоке IF, а заканчивается после псевдокоманды ELSE, ассемблирована будет лишь часть макроопределения. Поэтому блоки IF-ENDIF должны начинаться и заканчиваться в одних и тех же макроопределениях. ПРИМЕР 4. Простейший блок IF-ENDIF: COND1: IF EXPR EQ 0 ... ; АССЕМБЛИРУЕТСЯ, ЕСЛИ ВЫРАЖЕНИЕ ... ; EXPR EQ 0 ИСТИННО ENDIF ПРИМЕР 5. Более сложный блок IF-ENDIF,включающий псевдокоманду ELSE: COND2: IF EXPR EQ 0 ... ; АССЕМБЛИРУЕТСЯ, ЕСЛИ ВЫРАЖЕНИЕ ... ; EXPR EQ 0 ИСТИННО ELSE ... ; АССЕМБЛИРУЕТСЯ, ЕСЛИ ВЫРАЖЕНИЕ ... ; EXPR EQ 0 ЛОЖНО ENDIF ПРИМЕР 6. Вложенные блоки IF-ENDIF: COND 3: IF EXPR EQ 0 ... ; ВЫРАЖЕНИЕ EXPR EQ 0 ИСТИННО IF MODE EQ 0 ... ; EXPR EQ 0 И MODE EQ 1 ИСТИННЫ ENDIF ELSE ... ; EXPR EQ 0 ЛОЖНО IF MODE EQ 2 ... ; EXPR EQ 0 ЛОЖНО, MODE EQ 2 ИСТИННО ELSE ... ; EXPR EQ 0 И MODE EQ 2 ЛОЖНЫ ENDIF ENDIF ПСЕВДОКОМАНДЫ ПРЕКРАЩЕНИЯ АССЕМБЛИРОВАНИЯ И КОНЦА ЛЕНТЫ ПСЕВДОКОМАНДА END фиксирует конец исходной программы и завершает процесс просмотра ассемблером текста программы на языке ассемблера. Синтаксис псевдокоманды: <имя> END <выражение> В исходной программе может быть использована только одна псевдокоманда END, и она должна быть последним оператором. Если в псевдокоманде присутствует операнд, значение выражения испо- льзуется в качестве начального адреса для размещения программы, в про- тивном случае ассемблер выполняет трансляцию модуля,начиная с нулевого адреса. Если программой редактора связей компонуются вместе несколь- ко программных модулей, то только один из них (главный) может иметь начальный адрес. ПСЕВДОКОМАНДА EOT специфицирует физический конец перфоленты для ас- семблирования одного фрагмента исходного файла, отперфорированного на нескольких лентах. Синтаксис директивы EOT <имя>: EOT Когда ассемблер встречает псевдокоманду EOT, на консоль оператора вы- дается сообщение 'NEXT TAPE'. Оператор устанавливает новую перфоленту, вводит с консоли символ пробела, и процесс ассемблирования продолжает- ся. ПСЕВДОКОМАНДЫ УПРАВЛЕНИЯ ПРОГРАММНЫМИ СЕГМЕНТАМИ АБСОЛЮТНЫЕ ПРОГРАММЫ Если не используется механизм перемещаемости, по умолчанию ассемблер сгенерирует для программы псевдокоманду АSEG, которая определяет, что программа будет ассемблироваться в неперемещенном виде, и установит счетчик команд для ассемблера. Счетчик команд выполняет в процессе ас- семблирования такие же функции, как и при выполнении программы: он указывает ассемблеру следующий доступный байт памяти независимо от то- го, ассемблируется ли код операции или данные. ПСЕВДОКОМАНДА ORG устанавливает счетчик команд в соответствии со значением выражения в поле ее операнда. Синтаксис псевдокоманды ORG: <имя>: ORG <выражение> В процессе ассемблирования значение выражения всегда вычисляется по модулю 2^16. Любое имя, входящее в выражение, предварительно должно быть определено. Следующая машинная команда или данные будут ассембли- рованы с указанного адреса. Если перед новым оператором программы от- сутствует псевдокоманда ORG, счетчик команд принимает равное нулю зна- чение. В программу может быть введено произвольное число псевдокоманд ORG, которые не обязательно располагать в возрастающей по адресам по- следовательности, но следует предупредить возможность перекрытия прог- раммных участков. Если в псевдокоманде ORG есть метка, ей присваивает- ся значение счетчика команд в момент появления псевдокоманды. Пусть текущее значение счетчика команд 0FH, и встретилась псевдокоманда ORG: PAG 1: ORG 0FFH ; СЧЕТЧИКУ КОМАНД ПРИСВАИВАЕТСЯ ; ЗНАЧЕНИЕ 0FFH Метке PAG 1 присваивается значение 0FH и следующая команда или байт данных будут размещены по адресу 0FFH. ПЕРЕМЕЩАЕМЫЕ ПРОГРАММЫ Реализация механизма перемещаемых программ требует включения в язык ассемблера дополнительных псевдокоманд и добавления в операционную систему микро-ЭВМ трех новых системных программ, позволяющих обрабаты- вать перемещаемые программы: библиотекаря, редактирования связей и перемещающего загрузчика. Механизм перемещаемых программ значительно облегчает написание и отладку сложных программ путем разбиения их на более мелкие модули. Перемещаемые программы или программные модули могут использовать три счетчика команд. Псевдокоманды ASEG, DSEG и CSEG определяют, какой счетчик команд будет использован. Независимо от взаимного расположения псевдокоманд ASEG,DSEG и CSEG в программе, ассемблер формирует модуль, содержащий четыре сегмента: кодовый; данных; стека; памяти. Программы редактирования связей и перемещающего загрузчика используются для то- го, чтобы связать каждые четыре сегмента из различных модулей в четыре сегмента результирующего модуля и разместить их в памяти. ПСЕВДОКОМАНДА ASEG указывает ассемблеру на использование счетчика команд абсолютного сегмента для размещения программы в абсолютном про- граммном сегменте. Синтаксис псевдокоманды ASEG: <имя>: ASEG Операнды в этой псевдокоманде не используются. Все операторы исходной программы на языке ассемблера, следующие за псевдокомандой ASEG, ас- семблируются в абсолютном виде. Назначение на абсолютное размещение сохраняется до тех пор,пока не встретятся псевдокоманды CSEG или DSEG. Счетчик команд по директиве ASEG устанавливается в значение, равное нулю. Эту псевдокоманду можно использовать для установления нового значения счетчика команд. Начиная ассемблирование, по умолчанию ассемблер подразумевает, что первым оператором программы является псевдокоманда ASEG. Поэтому, если создается перемещаемый модуль, псевдокоманды DSEG и CSEG должны пред- шествовать первому оператору, определяющему машинные команды или дан- ные. Если все эти псевдокоманды в программе отсутствуют, программа ас- семблируется в абсолютном виде и может быть сразу же выполнена без об- работки ее программами редактирования связей и перемещающего загрузчи- ка. В перемещаемом программном модуле может быть размещен абсолютный сегмент по заданным адресам, например, подпрограммы обработки прерыва- ний, активизируемые командой RST, требуют размещения в строго опреде- ленных адресах памяти. ПСЕВДОКОМАНДА CSEG позволяет ассемблировать машинные команды и дан- ные в перемещаемый модуль, используя счетчик команд кодового сегмента. Синтаксис псевдокоманды / \ <имя>: CSEG { INPAGE } , \ PAGE / где символы { } обозначают альтернативу. Если в программе содержится несколько псевдокоманд CSEG, все они должны иметь одинаковые операнды. Операнды псевдокоманды CSEG не ис- пользуются при ассемблировании, но запоминаются в об'ектном файле для дальнейшего использования программами редактирования связей и переме- щающего загрузчика. Программа перемещающего загрузчика использует эту информацию для определения границ размещения, когда текущий кодовый сегмент будет соединяться с другими кодовыми сегментами в программе. Значения операндов псевдокоманды следующие: отсутствие операнда-кодо- вый сегмент может быть размещен со следующего доступного байта ; PAGE - кодовый сегмент должен быть размещен с начала следующей страницы (границы страниц кратны 256 (или 100H),начиная с 0; длина страницы 256 байт); INPAGE-кодовый сегмент должен быть размещен в текущей странице, начиная со свободного байта. Действие псевдокоманды CSEG сохраняется до появления псевдокоманд ASEG и DSEG. Начальное значение счетчика команд в кодовом сегменте равно 0. Для присвоения нового значения счетчику команд используется псевдокоманда ORG. Псевдокоманда CSEG используется для фрагментов про- грамм, которые должны быть размещены в ПЗУ, т.е. для машинных команд и программных констант. ПСЕВДОКОМАНДА DSEG позволяет ассемблировать машинные команды и дан- ные в перемещаемый модуль, используя счетчик команд сегмента данных. Синтаксис псевдокоманды DSEG: / INPAGE \ <имя>: DSEG { PAGE } . \ / Если в программе используется несколько псевдокоманд DSEG, они должны снабжаться одинаковыми операндами. Операнды псевдокоманды DSEG имеют тот же смысл, что и операнды псевдокоманды CSEG, но применительно к сегменту данных. Связи между операндами псевдокоманд DSEG и CSEG нет, т.е. кодовый сегмент может иметь побайтное размещение, а сегмент дан- ных - страничное. Действие псевдокоманды сохраняется до появления псевдокоманд ASEG или CSEG. При использовании псевдокоманды DSEG счетчик команд используется для программных элементов, которые должны быть размещены в ОЗУ. Начальное значение счетчика команд в сегменте данных равно 0. Для изменения зна- чения счетчика команд используется псевдокоманда ORG. ПСЕВДОКОМАНДА ORG в перемещаемых модулях используется для задания значения используемому счетчику команд. Синтаксис псевдокоманды ORG: <имя>: ORG <выражение> При использовании псевдокоманды ORG в перемещаемых программных сегмен- тах выражение может быть либо абсолютным, либо перемещаемым, определе- нным в текущем сегменте. Не допускается определения псевдокоманды в другом сегменте. Таким образом, существует три варианта использования счетчика команд, хотя в каждой точке программы используется только один из них. Конкретный вариант зависит от того, какая из псевдокоманд была предварительно использована - ASEG, CSEG или DSEG. ПСЕВДОКОМАНДЫ ПРОГРАММНЫХ СВЯЗЕЙ Модульное программирование и механизм перемещаемости дают возможность ассемблировать и тестировать несколько различных программ, которые впоследствии будут об'единены и выполнены как единая программа. Это приводит к необходимости передачи данных между отдельными программами. для реализации межпрограммных связей используются дополнительные псев- докоманды. Программа может передавать данные или команды в другую про- грамму, если известен их действительный адрес, но если обе программы перемещаемые,- это невозможно. В таком случае программа может получить доступ к данным или командам в другой программе, если их метки описаны в псевдокоманде PUBLIC той программы,где они были определены. Посколь- ку ассемблер указывает на ошибки при использовании любого имени или метки, не определенных в программе, список определенных во внешней программе меток следует об'явить в текущем модуле псевдокомандой EXTRN ПСЕВДОКОМАНДА PUBLIC позволяет каждое имя из списка имен, указанного в поле операнда, использовать в других программах, если в них эти име- на об'явлены в псевдокоманде EXTRN. Синтаксис псевдокоманды PUBLIC: <имя>: PUBLIC <список имен> Каждый элемент списка имен должен быть именем или меткой данных или команды в этой программе. Если в списке несколько имен, их разделяют запятыми. Например, PUBLIC SIN, COS SQRT; ОПРЕДЕЛЕНИЕ ОБЩИХ МЕТОК Зарезервированные и внешние имена не могут быть включены в список. Каждое имя может быть об'явлено псевдокомандой PUBLIC в текущем про- граммном модуле лишь однократно. Псевдокоманда PUBLIC может распола- гаться в любом месте программного модуля. Если элементы списка имен в операнде псевдокоманды не определены в текущем модуле, выдается сооб- щение об ошибке. ПСЕВДОКОМАНДА EXTRN сообщает ассемблеру список имен, используемых в текущем модуле, а определенных в других программных модулях. Синтаксис псевдокоманды: <имя>: EXTRN <список имен> Каждое имя или метка списка имен псевдокоманды EXTRN должны быть опре- делены псевдокомандой PUBLIC в других программах, только тогда их мож- но использовать в данном модуле. Если список имен состоит более чем из одного имени, имена разделяют запятыми. Если имя в списке имен операн- да определено в этом же модуле, либо это - зарезервированное имя, то эффект будет тот же, что и при многократном определении имен - ассемб- лер выдает сообщение об ошибке. Псевдокоманда EXTRN может встретится в любом месте программы. В мо- дуле имена можно определить как внешние лишь однажды. Например: EXTRN FADD, FSUB ; ОПРЕДЕЛЕНИЕ ВНЕШНИХ МЕТОК Имена, об'явленные псевдокомандой PUBLIC, не могут быть об'явлены псевдокомандой EXTRN. Если в этой псевдокоманде пропущено какое либо внешнее имя, в программе оно будет неопределенным. Его необходимо либо подключить к списку имен в операнде псевдокоманды, либо об'явить в другой псевдокоманде EXTRN. ПСЕВДОКОМАНДА NAME определяет имя об'ектного модуля, получаемого в результате ассемблирования. Синтаксис псевдокоманды: <имя> NAME <имя модуля> В псевдокоманде должен быть операнд, отвечающий требованиям составле- ния имен. Имя модуля необходимо для того, чтобы можно было обращаться к нему для составления последовательности модулей и их компоновки в единый модуль программой редактирования связей, например: NAME MAIN ; МОДУЛЬ ИМЕНУЕТСЯ MAIN Псевдокоманда NAME должна предшествовать первому оператору определе- ния данных или порождающему машинную команду, но может следовать за комментариями и псевдокомандами языка ассемблера. Если имя в операнде псевдокоманды ошибочно или псевдокоманда NAME отсутствует, ассемблер именует модуль именем MODULE. ПСЕВДОКОМАНДА STKLN. Несмотря на число компонуемых модулей, резуль- тирующий модуль будет использовать один стек. Псевдокоманда STKLN поз- воляет задать необходимое количество байт в каждом модуле для резерви- рования памяти под стек. Синтаксис псевдокоманды: <имя>: STKLN <выражение> Выражение в поле операнда должно принимать значение максимально возмо- жного в конкретном случае размера стека. Если эта псевдокоманда опуще- на, ассемблер формирует размер стека равным 0. Это удобно в случае, когда несколько программ об'единяются в одну. Тогда будет сгенерирован один стек, и только один программный модуль должен будет содержать указание размера стека, но при раздельной отладке модулей необходимо резервировать размер стека в каждом модуле, использующем стек. Если программа содержит несколько псевдокоманд STKLN, только последняя бу- дет устанавливать размер стека, например: STKLN 40H ; МАКСИМАЛЬНЫЙ РАЗМЕР СТЕКА 64 БАЙТА Зарезервированные имена STACK и MEMORY позволяют управлять адресами, которые определяются в результате работы программы перемещающего заг- рузчика. STACK - символическое обозначение начала верхушки стека. Ука- занием заданного этим именам адреса устанавливается начальное значение указателя стека. Кроме того, имя STACK можно использовать для базиро- вания данных, обращаясь к ним следующим образом: STACK+1, STACK+2, ... MEMORY - символическое обозначение первого свободного байта памяти по- сле программы. К этой памяти так же можно обращаться как к базирован- ной, используя выражения MEMORY+1, MEMORY+2, ... При автономном тестировании раздельно ассемблированных модулей в них следует выполнять ряд начальных установок в случае, если эти установки для текущего модуля выполняются в каких-либо других модулях. Одна из важнейших процедур начальной установки - начальная загрузка указателя стека. Программа перемещающего загрузчика определяет начало стека, ус- танавливая значение имени STACK. Тестируемый модуль должен включать оператор начальной загрузки значения указателя стека перед первой ис- пользующей его командой: LXI SP, STACK ; УСТАНОВКА ЗНАЧЕНИЯ УКАЗАТЕЛЯ СТЕКА МАКРОСРЕДСТВА Макросредства дают возможность размещать указанные в них наборы ко- манд в программе несколько раз, модифицируя параметры. Основные воз- можности, представляемые макросредствами: освобождают от необходимости дублирования фрагментов программы, уменьшая механические ошибки; об- ласть действия имен, используемых в макроопределении, может быть лока- лизована так, что они будут иметь смысл только внутри макрорасширения - это исключит дублирование имен при использовании макросредств и ма- кровызовы могут быть использованы произвольное число раз в любом месте программы без дублирования имен; допущенные ошибки исправляются один раз в макроопределении; использование библиотеки макросредств позволит избежать дублирования труда программистов; значительно повышаются на- глядность программ и их структуризация. Ассемблер включает следующие операторы макросредств: MACRO, ENDM, LOCAL, REPT, IRP, IRPC, EXITM. МАКРООПРЕДЕЛЕНИЕ Перед использованием в программе макросредства должны быть определе- ны. Макроопределение начинается оператором MACRO, снабженным именем, по которому в дальнейшем имеется возможность вызывать с помощью макро- вызова описанное макроопределение. В заголовке макроопределения кроме имени оператора MACRO указывается список формальных параметров, кото- рые будут при создании макрорасширения заменены фактическими парамет- рами. Набор операторов макроопределения между операторами MACRO и ENDM называется телом макроопределения. Оператор MACRO имеет синтаксис: <имя> MACRO <список формальных параметров> Имя в поле имени задает имя тела макроопределения. В качестве имени оператора MACRO допустимо любое имя, удовлетворяющее правилам построе- ния имен. Имя всегда должно присутствовать и не должно отделятся от оператора двоеточием, а только пробелами. В качестве формальных пара- метров можно использовать любой набор допустимых имен, разделенных за- пятыми. Список имен может быть пустым. Использование зарезервированных в ассемблере имен в качестве формальных параметров может привести к непредсказуемым результатам. Например, если в качестве формальных па- раметров использованы имена A, B, C, то макроподстановка будет выпол- нена, но внутри макроопределения нельзя будет использовать аккумулятор и регистры B и C. В качестве формальных параметров может применяться строка символов кода КОИ. В строку формальные параметры соединяются символом &. В тело макроопределения могут быть включены любые машинные команды и некоторые псевдокоманды. Отличаться друг от друга макрорас- ширения будут в тех местах, где встречаются формальные параметры, т.е. имена, стоящие в списке имен в поле операндов оператора MACRO. ПРММЕР 7. Макроопределения MAC1 с формальными параметрами C1,C2,C3: MAC1 MACRO C1, C2, C3 MOVES: LHLD C1 MOV A, M LHLD C2 MOV B, M LHLD C3 MOV C, M ENDM Если вызывать это макроопределение более одного раза и не использо- вать оперетор LOCAL, ассемблер выдаст сообщение о многократном опреде- лении метки, так как метка MOVES будет включена в программу несколько раз. ОПЕРАТОР ENDM имеет синтаксис ENDM и используется для завершения макроопределения, являясь последним его оператором. Этот же оператор требуется для завершения блоков повторе- ния операторов REPT, IRP, IRPC. Использование полей имени и операндов в операторе ENDM недопустимо. ОПЕРАТОР LOCAL определяет локальные в текущем макроопределении мет- ки, создавая уникальное значение имени каждый раз, когда после макро- вызова строится макрорасширение с этой меткой. Синтаксис оператора: LOCAL <список меток> Список определяет метки, имеющие значение только в пределах макроопре- деления. Каждый раз, когда по макровызову строится макрорасширение, ассемблер будет присваивать локальным именам, указанным в списке, но- вые имена в виде ?? nnnn. Первым будет назначено имя ??0001, вторым ??0002 и т.д. Каждая новая метка в следующих макрорасширениях будет именоваться с увеличением номера в имени с целью исключения для всех макрорасширений в программе многократного определения имен (пользова- тель не должен использовать имена вида ??nnnn, где nnnn- числовые зна- чения от 0001 до 9999). Формальные параметры макроопределений не могут быть включены в список меток оператора LOCAL. Эти значения всегда ло- кальны в данном макроопределении. Локальные имена могут быть определе- ны только внутри макроопределения, причем может быть использовано про- извольное количество операторов LOCAL, но все они должны находиться между первым оператором MACRO и первым оператором, порождающим машин- ную команду в макроопределении. Недопустимо использовании имени в поле имени оператора LOCAL. При рассмотрении макроопределения MAC1 говорилось о потенциальной ошибке, которая возникает, если указанное макроопределение вызвать многократно. Метку MOVES в макроопределении нужно локализовать опера- тором LOCAL: MAC2 MACRO C1, C2, C3 LOCAL MOVES MOVES: LHLD C1 MOV A, M LHLD C2 MOV B, M LHLD C3 MOV C, M ENDM Если в программе MAC2- единственное макроопределение, то при двух его вызовах первый раз метка MOVES трансформируется в ??0001, второй раз - в ??0002. Оператор REPT позволяет повторить последовательность операторов меж- ду REPT и ENDM столько раз, сколько определено значением выражения в поле операнда. Синтаксис оператора REPT: <имя> REPT <выражение> Если операндом является имя или выражение, они должны быть полностью определены до оператора REPT. Включение повторяемых блоков происходит, когда ассемблер встречает оператор REPT. Если выражение в поле операн- да отсутствует, выполняется однократная генерация макрорасширения, со- держащего операторы между REPT и ENDM. ПРИМЕР 8. Макроопределение, макрорасширенид которого выполнит сдвиг содержимого аккумулятора на четыре позиции вправо: SHIFT 4: REPT 4 RRC ENDM ПРИМЕР 9. Генерация исходных операторов для программы, которая запол- няет 5 байт памяти значением содержимого аккумулятора: Исходный текст Листинг, сгенерированный ассемблером LHLD CNTR 1 LHLD CNTR 1 REPT 5 MOV M, A MOV M, A INX H INX H MOV M, A ENDM INX H MOV M, A INX H MOV M, A INX H MOV M, A INX H ОПЕРАТОР IRP позволяет повторить определенную последовательность операторов программы, заменяя при каждом повторении формальный пара- метр фактическим из заданного списка. Синтаксис оператора: <имя>: IRP <формальный параметр>, <<список>> Поле операнда долнжно содержать один формальный параметр и список фак- тических параметров, заключенный в угловые скобки Оператор IRP обраба- тывается следующим образом : повторяется набор операторов в теле IRP-ENDM, при этом выполняется замена формального параметра текущим, фактическим по очереди, нечиная слева. Число повторений определяется длиной списка фактических параметров. Список фактических параметров должен быть заключен в угловые скобки и разделяться зяпятыми. Допуска- ется пустой список. В этом случае будет сгенерирована одна копия и формальный параметр везде будет заменен нулем. ПРИМЕР 10. Последовательность операторов, которя читает байты данных в памяти и располагает их, начиная с адреса Исходный текст Листинг сгенерированный ассемблером LXI H, ONES LXI H, ONES IRP X, LDA FLD1 LDA X MOV M, A MOV M, A INX H INX H LDA FLD2 ENDM MOV M, A INX H LDA 3300H MOV M, A INX H ОПЕРАТОР IRPC позволяет повторить определенную последовательность операторов программы, заменяя при каждом повторении формальный пара- метр фактическим из заданного текста. Синтаксис оператора: <имя> IRPC <формальный параметр> , <текст> Оператор указывает последовательность команд, которые должны быть пов- торены с заменой формального параметра каждым символом текста, укашан- ного в поле операнда. Если текст заключен в угловые скобки, любой ог- раничитель оказавшийся в нем, должен обрабатываться как символ и поэ- тому должен заменять при расширении формальный параметр. Количество замен и повторений определяет длина текста (текст длиной n символов вызывает n замен). Пустой текст определяет нулевой фактический пара- метр и в этом случае оператор IRPC генерирует одну копию макроопрделе- ния, заменяя нулевым значением формальный параметр. ПРИМЕР 11. Использование оператора IRPC: Исходный текст Листинг, сгенерированный ассемблером LHLD DATE-1 LHLD DATE-1 MDATE: IRPC X, 1984 INX H INX H MVI M, 1 MVI M, X INX H ENDM MVI M, 9 INX H MVI M, 8 INX H MVI M, 4 ОПЕРАТОР EXITM позволяет закончить обработку макроопределения. Син- таксис оператора: <имя> EXITM Оператор дает возможность закончить макрорасширение или один из опера- торов повторения REPT, IRP, IRPC до появления оператора ENDM. Если встречается оператор EXITM, ассемблер игнорирует все операторы макро- определения между EXITM и ENDM данного макроопределения. Оператор EXITM может быть использован совместно с ENDM, но не вместо него. Если использовать вложенные макросредства, оператор EXITM позволяет завер- шить макрорасширение данного уровня и всех последующих. Поле операндов оператора EXITM должно быть пустым. Оператор EXITM, например, позволя- ет завершить макрорасширение, если при ассемблировании условие X EQ 0 будет истинным: MAC MACRO X, Y ... IF X EQ 0 EXITM ENDIF ... ENDM Специальные разделители макросредств. В некоторых случаях обычные правила создания макроопределений неприменимы. Например, необходимо определить три фактических параметра, и второй из них - запятая. Запи- сав этот список в виде PRM1 ,,,PRM3, получим четыре фактических пара- метра, из которых второй и третий пропущены. В этот список можно вклю- чить запятую, используя угловые скобки: PRM1, <,>, PRM3. Угловые скоб- ки - это специальные разделители макросредств языка ассемблера, ис- пользуемые для включения ограничителей в фактические параметры. В ма- кросредствах используют следующие основные разделители. & - амперсанд. Используется для конкатенации (соединения) текстов в формальных параметрах. При построении макрорасширения любой амперсанд, заключенный между формальными параметрами в макроопределении, исполь- зуется для их конкатенации, и в этом случае смежные формальные параме- тры заменяются одним фактическим. Формальные параметры, заданные стро- ками символов, распознаются вместе только в том случае, если соединены амперсандом. Если этот символ не связывает смежных формальных парамет- ров, он не исключается, а входит как часть текста в макрорасширение. Амперсанд должен непосредственно связывать тексты, пробелы не допус- каются. Если вложенные макроопределения содержат амперсанды, то снача- ла обрабатываются амерсанды текущего уровня вложенности. Ко времени выполнения программы для правильного создания макрорасширения все ам- персанды должны быть исключены. В комментариях амперсанд не распознае- тся как разделитель. < > - угловые скобки. Используются для ограничения текста, который содержит другие ограничители. Например, пробел является ограничителем. Если фактический параметр содержит пробел, то этот параметр должен быть заключен в угловые скобки (например, команда MOV A, C). Это спра- ведливо для любого другого ограничителя, который должен быть воспринят как часть фактического параметра. В случае вложенных макровызовов ис- пользуется одна пара угловых скобок для каждого уровня вложенности. ; ; - парные точка с запятой. Используются перед комментариями в ма- кроопределениях для включения этих комментариев в макрорасширения при их генерации. Остаются они и в макроопределениях. ! - восклицательный знак. Помещается перед символом [ обычно ограни- чителем, который должен быть введен в фактический параметр как литерал (самоопределенная величина, используемая в качестве операнда)]. В ча- стности, восклицательный знак используют для ввода в фактический пара- метр угловых скобок. Для ввода восклицательного знака как литерал ис- пользуется пара символов !!. Восклицательный знак всегда сохраняется в процессе макрогенерации до тех пор, пока не будет построен фактический параметр. При замене формального параметра фактическим восклицательный знак теряется, если он не используется в дальнейшем для кодирования другого фактического параметра. Вложенность макроопределений. Макроопределения могут иметь вложенную структуру. Так как к телу макроопределения относится любой текст (вк- лючая вложенное макроопределение), ограниченный операторами MACRO и ENDM, ассемблер допускает макроопределения произвольной вложенности. После того как для макроопределения более высокого уровня построено макрорасширение, может быть вызвано макроопределение более низкого уровня. Макроопределения более низких уровней не могут быть обработаны до тех пор, пока все вложенные макроопределения более высоких уровней не будут вызваны и для них не будут построены макрорасширения. Вложенное макроопределение может задавать новое макроопределение или переопределять существующее в зависимости от того, является ли имя вложенного макроопределения новой меткой, или оно предварительно уста- новлено формальным параметром в макроопределении более высокого уров- ня. Поэтому в момент вызова макроопределения более высокого уровня макроопределение более низкого уровня может быть определено различными способами, если оба содержат общие формальные параметры. Блоки IRP, IRPC и REPT макроопределения могут быть вложены в другие макроопределения, создаваемые операторами IRP, IRPC, REPT или MACRO. Список фактических параметров, заключенных в угловые скобки операторов IRP или IRPC, может быть элементом списка фактических параметров, т.е. элементом списка параметров может быть список, например: LIST MACRO PRM1, PRM2 ... ENDM ... LIST > МАКРОВЫЗОВ Макроопределение может быть вызвано произвольное число раз с помощью макровызовов, которые содержат имя макроопределения и произвольное чи- сло фактических параметров, которые заменят формальные при создании макрорасширения. Во время ассемблирования вместо каждого макровызова по телу макроопределения генерируется макрорасширение; формальные па- раметры заменяются фактическими. Синтаксис макровызова: <имя> : <имя макроопределения> <список фактических параметров> Ассемблер должен встретить макроопределение до макровызова, в против- ном случае макровызов будет воспринят как неверный код команды. Ассем- блер при каждом вхождении в программу имени макроопределения будет ге- нерировать макрорасширение в точке макровызова. В макровызове существенно расположение фактических параметров, так как при макрогенерации используется принцип позиционного соответствия параметров: первый фактический параметр заменяет первый формальный, второй фактический - второй формальный и т.д. Используя макровызов, необходимо позаботиться о последовательности и значениях фактических параметров, поставив их в соответствие формальным в макроопределении. Пробел воспринимается как ограничитель. Поэтому когда фактический па- раметр содержит пробел, его необходимо заключить в угловые скобки. Если макровызов содержит больше фактических параметров, чем формаль- ных в макроопределении, лишние параметры игнорируются. Если фактичес- ких параметров меньше, чем формальных, недостающие снабжаются нулевыми значениями. ПРИМЕР 12. Рассмотрим два последних вызова макроопределения MAC2 (см. оператор LOCAL). Макроопределение MAC2 загружает аккумулятор зна- чением, расположенным по адресу, задаваемому первым фактическим пара- метром; регистр B - значением, определяемым вторым параметром; регистр C - значением, определяемым третьим параметром. Первый макровызов MAC2 используют для инверсного расположения элементов трехбайтного массива; второй - для сложения содержимого аккумулятора с регистром B и сравне- ния результата с содержимым регистра C. Исходный текст Листинг, сгенерированный ассемблером MAC2 FLD, FLD+1, FLD+2 ??0001: LHLD FLD MOV M, A MOV A,M DCX H LHLD FLD+1 MOV M, B MOV B, M DSX H LHLD FLD+2 MOV M, C MOV C,M MAC2 3300H, BYTE, CHECK MOV M,A ADD B DCX H CMP C MOV M, B JNZ LAB DCX H ... MOV M, C ??0002: LHLD 3300H MOV A, M LHLD BYTE MOV B, M LHLD CHECK MOV C, M ADD B CMP C JNZ LAB ... Вложенные макровызовы. Макровызовы могут быть вложены в макроопределе- ния. Максимально допустимый уровень вложенности равен восьми. Вызывае- мые макросредства не нуждаются в определении, если включают уже опре- деленные макросредства, но они должны быть определены перед макрогене- рацией при макровызове. Макроопределение может также содержать рекур- сивные макровызовы не более восьми уровней вложенности. Это можно про- контролировать использованием условного ассемблирования. ПРИМЕР 13. Пять рекурсивных макровызовов макроопределения: RECALL: PRM1 SET5 RECALL MACRO ... IF PRM1 NEO PRM1 SET PRM1 - 1 RECALL; РЕКУРСИВНЫЙ МАКРОВЫЗОВ ENDIF ... ENDM МАКРОРАСШИРЕНИЕ Когда вызывается макроопределение, макроподстановка фактических па- раметров выполняется по одному из двух правил. 1. Замена формального параметра фактическим является простой тексто- вой подстановкой. Параметры не принимают значения до тех пор, пока не будет построено макрорасширение. 2. Если фактическому параметру в макровызове предшествует символ %, значение фактическому параметру присваивается до создания макрорасши- рения. Параметр представляется десятичным числом, имеющим значение па- раметра. В случае использования в операторе IRPC знака %, предшествующего фактическому параметру, текст воспринимается как один параметр, т. е. выполняется генерация одного макрорасширения как результат непосредст- венной замены формального параметра значением элементов строки. Обычно используется первое правило замены фактическими параметрами формаль- ных. Использование символа % для предварительного присвоения значений параметрам необходимо только тогда, когда значения параметров различны в разных константах макрорасширения по сравнению с их глобальными зна- чениями в выходном макроопределении. ПРИМЕР 14. В макроопределении SHIFT при макровызове выполняется ге- нерация команд сдвига содержимого аккумулятора. Передаваемые в макро- вызове параметры определяют число сдвигов содержимого аккумулятора и направление сдвигов (влево или вправо). Макроопределение SHIFT исполь- зует псевдокоманду условного ассемблирования IF для определения перво- го параметра. Макроопределение REPT вложено в макроопределение SHIFT: SHIFT MACRO X, Y IF X EQ 'R' REPT Y RAR ENDM ENDIF IF X NE 'L' EXITM ELSE REPT Y RAL ENDM ENDIF ENDM Типичные макровызовы приведенного макроопределения, например, следу- ющие: SHIFT 'R', 3 SHIFT 'L' % COUNT - 1 Макровызов SHIFT 'R', 3 вызывает генерацию трех команд RAR. Второй макровызов показывает возможность использования выражения в качестве параметра. Это выражение примет определенное значение еще до построе- ния макрорасширения. Допустим, что где-то в программе имя COUNT полу- чило значение, равное шести. Тогда макровызов SHIFT 'L', COUNT - 1 сгенерирует пять команд RAL. Макрорасширение SHIFT не будет сгенериро- вано вообще, если первый параметр не 'R' или не 'L'. Например, макро- вызовы SHIFT 5 и SHIFT 'B', 2 не повлекут за собой создания каких-либо об'ектных кодов. Макроопределение SHIFT можно определить по-другому, используя символ конкатенации текстов для создания команд RAL и RAR: SHIFT MACRO X, Y REPT Y RA & X ENDM ENDM ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ МАКРОСРЕДСТВ Вложенные макроопределения. Макроопределение MOVE содержит вложенное макроопределение IRPC. Третий операнд макроопределения - строка симво- лов, используемая оператором IRPC: MOVE MACRO X, Y, Z IRPC PRM, Z LHLD X & & PRM SHLD Y & & PRM ENDM ENDM Пусть программа содержит макровызов MOVE SPC, DST, 123. Третий пара- метр макровызова передается оператору IRPC, что эквивалентно вызову оператора IRPC PRM, 123. Сгенерированное по указанному макровызову ма- крорасширение будет следующим: LHLD SRC 1 SHLD DST 1 LHLD SRC 2 SHLD DST 2 LHLD SRC 3 SHLD DST 3 Вложенные макровызовы. Если нужно сгенерировать несколько операторов определения данных DB 0 со своими метками, используют два макроопреде- ления: INC и BLOCK. Макроопределение INC следующее: INC MACRO F 1, F 2 F1 & F2 DB 0 ENDM Макроопределение BLOCK задает число операторов DB и определяет их мет- ки: BLOCK MACRO NUMB, PREFIX COUNT SET 0 REPT NUMB COUNT SET COUNT + 1 JNC PREFIX, % COUNT; ВЛОЖЕННЫЙ МАКРОВЫЗОВ ENDM ENDM Макровызов BLOCK, 3, LAB сгенерирует следующую последовательность ко- дов: BLOCK 3, LAB COUNT SET 0 REPT 3 COUNT SET COUNT + 1 INC LAB, % COUNT ENDM COUNT SET COUNT + 1 INC LAB, % COUNT LAB 1 DB 0 COUNT SET COUNT + 1 INC LAB, % COUNT LAB 2 DB 0 COUNT SET COUNT + 1 INC LAB, % COUNT LAB 3 DB 0 В некоторых случаях использование макросредств нежелательно из-за дефицити памяти. Различные варианты вызовов подпрограмм с использова- нием макровызовов можно сгруппировать в три метода. Во всех случаях первый макровызов SBMAC генерирует полное макрорасширение, которое оп- ределяет программу SUBR и ее вызов. Каждый последующий макровызов SBMAC генерирует только команду вызова подпрограммы SUBR. Метка SUBR должна быть глобальной, чтобы ее можно было вызвать в любом макрорас- ширении. Метод 1 (использование вложенных макроопределений). Макроопределение SBMAC содержит собственное переопределение с помощью вложенного макро- определения: SBMAC MACRO SBMAC MACRO CALL SUBR ENDM CALL SUBR LINK: JMR DUN SUBR: ... ... RET DUN: ENDM Первый макровызов SBMAC выполняет генерацию полного макрорасширения, в котором происходит переопределение SBMAC и вызов подпрограммы SUBR. Последующие макровызовы SUBR сгенерируют лишь команду CALL SUBR в ма- крорасширениях. Для обхода подпрограммы после ее первого вызова испо- льзуется команда перехода, помеченная LINK. Метод 2 (использование средств условного ассемблирования). Переклю- чатель FIRST принимает значение, равное TRUE перед первым вызовом SBMAC. SBMAC определяется следующим образом: TRUE EQU 0FFH FALSE EQU 0 FIRST SET TRUE SBMAC MACRO CALL SUBR IF FIRST FIRST SET FALSE LINK JMP DUN SUBR: ... ... RET DUN: ENDIF ENDM В результате первого вызова макроопределения SBMAC выполнится генера- ция полного макрорасширения, включая вызов подпрограммы SUBR и ее те- ла: SBMAC CALL SUBR IF FIRST LINK: JMR DUN SUBR: ... ... RET DUN: ENDIF В последующих макровызовах имя FIRST переопределено (его значение - - FALSE) и операторы в блоке IF - ENDIF игнорируются. В макрорасшире- нии присутствует лишь оператор CALL SUBR. Метод 3 (использование средств условного ассемблирования с операто- ром EXITM). В процессе макрогенерации оператор EXITM позволяет выпол- нить выход из макроопределения SBMAC. Выход по оператору EXITM осуще- ствляется при условии, что значение переключателя FIRST ложно: TRUE EQU 0FFH FALSE EQU 0 FIRST SET TRUE SBMAC MACRO CALL SUBR IF NOT FIRST EXITM ENDIF FIRST SET JMP FALSE SUBR: ... DUN ... RET DUN: ENDM