assembler. Учебник для вузов_Юров В.И_2003 -637с (862834), страница 50
Текст из файла (страница 50)
К ним относятся:процедуры;макроподстановки (макроассемблер);генерация и обработка программных прерываний.В данной главе рассматриваются только основные понятия, относящиеся к вызову процедур. Ввиду важности этого вопроса мы продолжим его изучение в главе 15 в контексте темы модульного программирования на ассемблере. Актуальнаядля программирования под Windows проблема разработки библиотек DLL на ассемблере описана в [8]. Макроассемблеру посвящена глава 14.Процедура, или подпрограмма, — это основная функциональная единица декомпозиции (разделения на части) некоторой задачи.
Процедура представляет собойгруппу команд для решения конкретной подзадачи и обладает средствами получения управления из точки вызова задачи более высокого уровня и возврата управления в эту точку. В простейшем случае программа может состоять из одной процедуры. Другими словами, процедуру можно определить как правильным образомоформленную совокупность команд, которая, будучи однократно описана, при необходимости может быть вызвана в любом месте программы.Для описания последовательности команд в виде процедуры в языке ассемблера используются две директивы: PROC и ENDP.Синтаксис описания процедуры таков (рис. 10.3).Из рисунка видно, что в заголовке процедуры (директиве PROC) обязательнымявляется только задание имени процедуры. Среди большого количества операндов директивы PROC следует особо выделить [расстояние].
Этот атрибут может принимать значения NEAR или FAR и характеризует возможность обращения к процедуре из другого сегмента кода. По умолчанию атрибут [расстояние] принимаетзначение NEAR.Процедура может размещаться в любом месте программы, но так, чтобы на нееслучайным образом не попало управление. Если процедуру просто вставить в общий поток команд, то процессор воспримет команды процедуры как часть этого220Глава 10.
Команды передачи управленияимя_процедуры PROC [[модификатор_языка ] язык] [расстояние ][ARG спаггм япгл/мвитли 1[RETURN список_элементов]команды,директивыассемблера[имя_процедуры ]ЗЭГОЛОВОКпроцедурыТелопроцедуры:мпоENDPКонецпроцедурыРис. 10.3. Синтаксис описания процедуры в программепотока и, соответственно, начнет выполнять эти команды. Учитывая это обстоятельство, есть следующие варианты размещения процедуры в программе:в начале программы (до первой исполняемой команды);в конце программы (после команды, возвращающей управление операционнойсистеме);ii промежуточный вариант — внутри другой процедуры или основной программы (в этом случае необходимо предусмотреть обход процедуры с помощью команды безусловного перехода J М Р);в другом модуле (библиотеке DLL).Размещение процедуры в начале сегмента кода предполагает, что последовательность команд, ограниченная парой директив PROC и ENDP, будет размещена дометки, обозначающей первую команду, с которой начинается выполнение программы.
Эта метка должна быть указана как параметр директивы E N D , обозначающейконец программы:modelsmall.stack 100h.data.codemy_proc procnearretmy_proc endpstart:end startОбъявление имени процедуры в программе равнозначно объявлению метки,поэтому директиву PROC в частном случае можно рассматривать как завуалированную форму определения программной метки. Поэтому сама исполняемая программа также может быть оформлена в виде процедуры, что довольно часто и делается с целью пометить первую команду программы, с которой должно начатьсявыполнение. При этом не забывайте, что имя этой процедуры нужно обязательноуказывать в заключительной директиве E N D . Такой синтаксис мы уже неоднократно использовали в своих программах. Так, последний рассмотренный фрагментэквивалентен следующему:modelsmall.stack 100hБезусловные переходы221.data.codemy_proc procnearretmy_proc endpstartprocstartendpend startВ этом фрагменте после загрузки программы в память управление будет передано первой команде процедуры с именем start.Размещение процедуры в конце программы предполагает, что последовательность команд, ограниченная директивами PROC и ENDP, находится следом за командой, возвращающей управление операционной системе:model small.stack 100h.data.codestart:mov ax,4c00hint 21h ;возврат управления операционной системеmy_proc procnearretmy_proc endpend startПромежуточный вариант расположения тела процедуры предполагает ее размещение внутри другой процедуры или основной программы.
В этом случае необходимо предусмотреть обход тела процедуры, ограниченного директивами PROCи ENDP, с помощью команды безусловного перехода ЗМР:model small.stack 100h.data.codestart:jmp mlmy_proc procnearretmy_proc endpml:mov ax,4c00hint 21h ;возврат управления операционной системеend startПоследний вариант расположения описаний процедур — в отдельном сегментекода — предполагает, что часто используемые процедуры выносятся в отдельныйфайл, который должен быть оформлен как обычный исходный файл и подвергнуттрансляции для получения объектного кода. Впоследствии этот объектный файлс помощью утилиты tlink можно объединить с файлом, в котором данные процедуры используются.
С утилитой tlink мы познакомились в главе 6. Этот способ предполагает наличие в исходном тексте программы еще некоторых элементов, связанных с особенностями реализации концепции модульного программирования222Глава 10. Команды передачи управленияв языке ассемблера. Поэтому в полном объеме этот способ будет рассмотрен в главе 15.Как обратиться к процедуре? Так как имя процедуры обладает теми же атрибутами, что и обычная метка в команде перехода, то обратиться к процедуре, в принципе, можно с помощью любой команды перехода.
Но есть одно важное свойство,которое можно использовать благодаря специальному механизму вызова процедур.Суть состоит в возможности сохранения информации о контексте программыв точке вызова процедуры. Под контекстом понимается информация о состояниипрограммы в точке вызова процедуры. В системе команд процессора есть две команды для работы с контекстом — CALL и RET.ш Команда CALL осуществляет вызов процедуры (подпрограммы). Синтаксис команды:call [модификатор] имя_процедурыПодобно команде JMP команда CALL передает управление по адресу с символическим именем имя_процедуры, но при этом в стеке сохраняется адрес возврата(то есть адрес команды, следующей после команды CALL).li Команда RET считывает адрес возврата из стека и загружает его в регистры CSи EIP/IP, тем самым возвращая управление на команду, следующую в программе за командой CALL Синтаксис команды:ret [число]Необязательный параметр [число] обозначает количество элементов, удаляемых из стека при возврате из процедуры.
Размер элемента определяется хорошо знакомыми нам параметрами директивы SEGMENT — use!6 и use32 (или соответствующим параметром упрощенных директив сегментации). Если указанпараметр usel6, то [число] — это значение в байтах; если use32 — в словах.Для команды CALL, как и для JMP, актуальна проблема организации ближнихи дальних переходов. Это видно из формата команды, где присутствует параметр[модификатор]. Как и в случае команды JMP, вызов процедуры командой CALL может быть внутрисегментным и межсегментным.» При внутрисегментном вызове процедура находится в текущем сегменте кода(имеет тип near), и в качестве адреса возврата команда CALL сохраняет толькосодержимое регистра IP/EIP, что вполне достаточно (рис.
10.4).№ При межсегментном вызове процедура находится в другом сегменте кода (имееттип far), и для осуществления возврата команда CALL должна запомнить содержимое обоих регистров (CS и IP/EIP), при этом в стеке сначала запоминаетсясодержимое регистра CS, затем — регистра IP/EIP (рис. 10.5).Важно отметить, что одна и та же процедура не может быть одновременно процедурой ближнего и дальнего типов.
Таким образом, если процедура используетсяв текущем сегменте кода, но может вызываться и из другого сегмента программы,то она должна быть объявлена процедурой типа far. Подобно команде JMP, существуют четыре разновидности команды CALL. Какая именно команда будет сформирована, зависит от значения модификатора в команде вызова процедуры CALLи атрибута дальности в описании процедуры. Если процедура описана в началесегмента данных с указанием дальности в ее заголовке, то при ее вызове параметрБезусловные переходы223Процедура ближнего типа my_proc:Оперативная памятьооооюооо]Оперативная память0000:0000J\dSS-SP-i.•>^'P.i^|SS:ffff -»Дно стекаA'Старшие адреса ОПСтек до командыcall my_procSS:ffff ->Дно стекаCSAIСтаршие адреса ОПСтек после командыcall my_procРис.
10.4. Содержимое стека до и после выполнения команды вызова процедурыближнего типаПроцедура дальнего типа ту_ргос:Оперативная памятьОперативная память0000:0000!0000:0000 JSS:SP->SS'.SP ->SS:ffff->Дно стекаСтаршие адреса ОПСтек до командыcall my_procSS:ffff -»Старшие адреса ОПСтек после командыcall my_procРис. 10.5. Содержимое сте.ка до и после выполнения команды вызова процедурыдальнего типа[модификатор] можно не указывать: транслятор сам разберется, какую команду CALLему нужно сформировать.
Если же процедура описана после ее вызова, например,в конце текущего сегмента или в другом сегменте, то при ее вызове нужно указатьассемблеру тип вызова, чтобы он мог за один проход правильно сформировать команду CALL. Значения модификатора такие же, как и у команды ЗМР, за исключением значения SHORT PTR.С директивой PROC используются еще несколько директив: ARG, RETURNS, LOCAL,USES. Их назначение — помочь программисту выполнить некоторые рутинные действия при вызове и возврате из процедуры (заодно и повысив надежность кода).Директивы ARG и RETURNS назначают входным и выходным параметрам процедуры, передаваемым через стек, символические имена.
Директива USES в качествепараметров содержит имена используемых в процедуре регистров. При обработке224Глава 10. Команды передачи управленияэтой директивы ассемблер формирует входной и выходной коды процедуры (изкоманд PUSH и POP), обеспечивающие сохранение и восстановление регистров. Директива LOCAL предназначена для выделения кадра стека для локальных переменных, что позволяет экономить память, занимаемую программой в целом. Подробно эти директивы обсуждаются в главе 15.Необходимо заметить, что в данном разделе приведена информация о порядкеописания процедур, принятом в TASM.
Описание и использование процедурв MASM имеет особенности, о которых можно узнать из материала главы 15.Последний и, наверное, самый важный вопрос, возникающий при работе с процедурами, — как правильно передать параметры процедуре и вернуть результат?Этот вопрос тесно связан с концепцией модульного программирования и подробно будет рассматриваться в главе 15. С примерами использования процедур выможете познакомиться в листингах подпрограмм, предназначенных для вычисления четырех основных арифметических действий с двоичными и десятичными(BCD) числами и находящихся среди прилагаемых к книге файлов в каталоге главы 81.
Кроме того, вопросы организации рекурсивных и вложенных процедур рассмотрены в [8].Условные переходыДо сих пор мы рассматривали команды перехода с «безусловным» принципом действия, но в системе команд процессора есть большая группа команд, призванныхсамостоятельно принимать решение о том, какая команда должна выполнятьсяследующей. Решение принимается в зависимости от определенных условий, определяемых конкретной командой перехода. Процессор поддерживает 18 командусловного перехода, позволяющих проверить:отношение между операндами со знаком (больше или меньше);отношение между операндами без знака (выше или ниже)2;состояниями арифметических флагов ZF, SF, CF, OF, PF (но не AF).Команды условного перехода имеют одинаковый синтаксис:jcc метка_переходаКак видно, мнемокод всех команд начинается с символа «j» — от слова jump(прыжок).