<<
>>

Процедуры

До сих пор мы рассматривали примеры программ, предназначенные для одно- кратного выполнения. Но, приступив к программированию достаточно серь- езной задачи, вы наверняка столкнетесь с тем, что у вас появятся повторяющиеся фрагменты кода.
Одни из них могут состоять всего из нескольких команд, другие занимать и достаточно много места в исходном коде. В последнем случае эти фраг- менты существенно затруднят чтение текста программы, снизят ее наглядность, усложнят отладку и послужат неисчерпаемым источником ошибок. В языке ас- семблера есть несколько средств, решающих проблему дублирования фрагментов программного кода. К ним относятся: ш процедуры; й макроподстановки (макроассемблер); й генерация и обработка программных прерываний. В данной главе рассматриваются только основные понятия, относящиеся к вы- зову процедур. Ввиду важности этого вопроса мы продолжим его изучение в гла- ве 15 в контексте темы модульного программирования на ассемблере. Актуальная для программирования под Windows проблема разработки библиотек DLL на ас- семблере описана в [8]. Макроассемблеру посвящена глава 14. Процедура, или подпрограмма, — это основная функциональная единица деком- позиции (разделения на части) некоторой задачи. Процедура представляет собой группу команд для решения конкретной подзадачи и обладает средствами получе- ния управления из точки вызова задачи более высокого уровня и возврата управ- ления в эту точку. В простейшем случае программа может состоять из одной про- цедуры. Другими словами, процедуру можно определить как правильным образом оформленную совокупность команд, которая, будучи однократно описана, при не- обходимости может быть вызвана в любом месте программы. Для описания последовательности команд в виде процедуры в языке ассембле- ра используются две директивы: PROC и ENDP. Синтаксис описания процедуры таков (рис. 10.3). Из рисунка видно, что в заголовке процедуры (директиве PROC) обязательным является только задание имени процедуры.
Среди большого количества операн- дов директивы PROC следует особо выделить [расстояние]. Этот атрибут может при- нимать значения NEAR или FAR и характеризует возможность обращения к проце- дуре из другого сегмента кода. По умолчанию атрибут [расстояние] принимает значение NEAR. Процедура может размещаться в любом месте программы, но так, чтобы на нее случайным образом не попало управление. Если процедуру просто вставить в об- щий поток команд, то процессор воспримет команды процедуры как часть этого потока и, соответственно, начнет выполнять эти команды. Учитывая это обстоя- тельство, есть следующие варианты размещения процедуры в программе: =S в начале программы (до первой исполняемой команды); Ш в конце программы (после команды, возвращающей управление операционной системе); и промежуточный вариант — внутри другой процедуры или основной програм- мы (в этом случае необходимо предусмотреть обход процедуры с помощью ко- манды безусловного перехода J М Р); ⅜ в другом модуле (библиотеке DLL). Размещение процедуры в начале сегмента кода предполагает, что последова- тельность команд, ограниченная парой директив PROC и ЕNDР, будет размещена до метки, обозначающей первую команду, с которой начинается выполнение програм- мы. Эта метка должна быть указана как параметр директивы END, обозначающей конец программы: Объявление имени процедуры в программе равнозначно объявлению метки, поэтому директиву PROC в частном случае можно рассматривать как завуалиро- ванную форму определения программной метки. Поэтому сама исполняемая про- грамма также может быть оформлена в виде процедуры, что довольно часто и де- лается с целью пометить первую команду программы, с которой должно начаться выполнение. При этом не забывайте, что имя этой процедуры нужно обязательно указывать в заключительной директиве END. Т акой синтаксис мы уже неоднократ- но использовали в своих программах.
Так, последний рассмотренный фрагмент эквивалентен следующему: model small .stack lθθh .data • code mу_рrос procnear ret ` mу_рrос endp start proc start endp end start В этом фрагменте после загрузки программы в память управление будет пере- дано первой команде процедуры с именем start. Размещение процедуры в конце программы предполагает, что последователь- ность команд, ограниченная директивами PROC и ЕNDР, находится следом за коман- дой, возвращающей управление операционной системе: model small .stack 1ОО1i .data .code start: mov ах,4с00h int 21h ; возврат управления операционной системе mу_рrос procnear ret my_proc endp end start ' Промежуточный вариант расположения тела процедуры предполагает ее раз- мещение внутри другой процедуры или основной программы. В этом случае необ- ходимо предусмотреть обход тела процедуры, ограниченного директивами PROC и ENDP, с помощью команды безусловного перехода ЗМР: model small ' .stack 10Оh .data .code start: ' jmp ml mу_рrос procnear ret mу_рrос endp ml: mov ах,4с00h int 21h ; возврат управления операционной системе end start Последний вариант расположения описаний процедур — в отдельном сегменте кода — предполагает, что часто используемые процедуры выносятся в отдельный файл, который должен быть оформлен как обычный исходный файл и подвергнут трансляции для получения объектного кода. Впоследствии этот объектный файл с помощью утилиты tliпk можно объединить с файлом, в котором данные процеду- ры используются. С утилитой tlink мы познакомились в главе 6. Этот способ пред- полагает наличие в исходном тексте программы еще некоторых элементов, свя- занных с особенностями реализации концепции модульного программирования в языке ассемблера. Поэтому в полном объеме этот способ будет рассмотрен в гла- ве 15. Как обратиться к процедуре? Так как имя процедуры обладает теми же атрибу- тами, что и обычная метка в команде перехода, то обратиться к процедуре, в прин- ципе, можно с помощью любой команды перехода. Но есть одно важное свойство, которое можно использовать благодаря специальному механизму вызова процедур.
Суть состоит в возможности сохранения информации о контексте программы в точке вызова процедуры. Под контекстом понимается информация о состоянии программы в точке вызова процедуры. В системе команд процессора есть две ко- манды для работы с контекстом — CALL и RET. * Команда CALL осуществляет вызов процедуры (подпрограммы). Синтаксис ко- манды: call [модификатор] имя_процедуры Подобно команде JMP команда CALL передает управление по адресу с символи- ческим именем имя_процедуры, но при этом в стеке сохраняется адрес возврата (то есть адрес команды, следующей после команды CALL). Команда RET считывает адрес возврата из стека и загружает его в регистры CS и EIP/IP, тем самым возвращая управление на команду, следующую в програм- ме за командой CALL Синтаксис команды: ret [число] Необязательный параметр [число] обозначает количество элементов, удаляе- мых из стека при возврате из процедуры. Размер элемента определяется хоро- шо знакомыми нам параметрами директивы SEGMENT — usеl6 и usеЗ2 (или соот- ветствующим параметром упрощенных директив сегментации). Если указан параметр 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 и атрибута дальности в описании процедуры. Если процедура описана в начале сегмента данных с указанием дальности в ее заголовке, то при ее вызове параметр
Рис. 10.4. Содержимое стека до и после выполнения команды вызова процедуры ближнего типа

Рис. 10.5. Содержимое стека до и после выполнения команды вызова процедуры дальнего типа

[модификатор] можно не указывать: транслятор сам разберется, какую команду CALL ему нужно сформировать. Если же процедура описана после ее вызова, например, в конце текущего сегмента или в другом сегменте, то при ее вызове нужно указать ассемблеру тип вызова, чтобы он мог за один проход правильно сформировать ко- манду CALL. Значения модификатора такие же, как и у команды ЗМР, за исключе- нием значения SHORT PTR. С директивой PROC используются еще несколько директив: ARG, RETURNS, LOCAL, USES. Их назначение — помочь программисту выполнить некоторые рутинные дей- ствия при вызове и возврате из процедуры (заодно и повысив надежность кода). Директивы ARG и RETURNS назначают входным и выходным параметрам процеду- ры, передаваемым через стек, символические имена. Директива USES в качестве параметров содержит имена используемых в процедуре регистров. При обработке этой директивы ассемблер формирует входной и выходной коды процедуры (из команд PUSH и POP), обеспечивающие сохранение и восстановление регистров. Ди- ректива LOCAL предназначена для выделения кадра стека для локальных перемен- ных, что позволяет экономить память, занимаемую программой в целом. Подроб- но эти директивы обсуждаются в главе 15. Необходимо заметить, что в данном разделе приведена информация о порядке описания процедур, принятом в ТАSМ. Описание и использование процедур в МАSМ имеет особенности, о которых можно узнать из материала главы 15. Последний и, наверное, самый важный вопрос, возникающий при работе с про- цедурами, — как правильно передать параметры процедуре и вернуть результат? Этот вопрос тесно связан с концепцией модульного программирования и подроб- но будет рассматриваться в главе 15. С примерами использования процедур вы можете познакомиться в листингах подпрограмм, предназначенных для вычисле- ния четырех основных арифметических действий с двоичными и десятичными (BCD) числами и находящихся среди прилагаемых к книге файлов в каталоге гла- вы 8*. Кроме того, вопросы организации рекурсивных и вложенных процедур рас- смотрены в [8].

<< | >>
Источник: В. И. Юров. Assembler. Учебник для вузов. 2-е изд. 2003

Еще по теме Процедуры:

  1. Схема «Процедуры комфортизации».
  2. Схема «Процедуры комфортизации».
  3. 3. Процедуры банкротства гражданина
  4. ИТАК, ПРОЦЕДУРА КОМФОРТИЗАЦИИ.
  5. ИТАК, ПРОЦЕДУРА КОМФОРТИЗАЦИИ.
  6. 28. Процедура наблюдения
  7. ПРОЦЕДУРЫ И РИТУАЛЫ
  8. Процедуры и техники.
  9. Процедура комфортизации («Послушный» и «непослушный» мозг»).
  10. 3.3. Процедура экспериментирования
  11. § 4. Порядок работы парламента. Законодательная процедура
  12. 5.3. Социометрические процедуры
  13. 3.2. Процедура рассмотрения и утверждения проекта
  14. 5. Основные процедуры банкротства