КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Лекция 2.4. Организация и использование подпрограмм
До сих пор мы рассматривали примеры программ, предназначенные для однократного выполнения. Но, приступив к программированию достаточно серьезной задачи, вы наверняка столкнетесь с тем, что у вас появятся повторяющиеся фрагменты кода. Одни из них могут состоять всего из нескольких команд, другие занимать и достаточно много места в исходном коде. В последнем случае эти фрагменты существенно затруднят чтение текста программы, снизят ее наглядность, усложнят отладку и послужат неисчерпаемым источником ошибок. В языке ассемблера есть несколько средств, решающих проблему дублирования фрагментов программного кода. К ним относятся: · процедуры; · макроподстановки (макроассемблер); · генерация и обработка программных прерываний. Процедура, или подпрограмма, — это основная функциональная единица декомпозиции (разделения на части) некоторой задачи. Процедура представляет собой группу команд для решения конкретной подзадачи и обладает средствами получения управления из точки вызова задачи более высокого уровня и возврата управления в эту точку. В простейшем случае программа может состоять из одной процедуры. Другими словами, процедуру можно определить как правильным образом оформленную совокупность команд, которая, будучи однократно описана, при необходимости может быть вызвана в любом месте программы. Для описания последовательности команд в виде процедуры в языке ассемблера используются две директивы: PROC и ENDP. Синтаксис описания процедуры таков: имя_процедуры PROC [[модификатор_языка ] язык] [расстояние ] [ARG список аргументов] [RETURN список_элементов] … команды, директивы ассемблера … [имя_процедуры ] ENDP Из описания видно, что в заголовке процедуры (директиве PROC) обязательным является только задание имени процедуры. Среди большого количества операндов директивы PROC следует особо выделить [расстояние]. Этот атрибут может принимать значения NEAR или FAR и характеризует возможность обращения к процедуре из другого сегмента кода. По умолчанию атрибут [расстояние] принимает значение NEAR. Процедура может размещаться в любом месте программы, но так, чтобы на нее случайным образом не попало управление. Если процедуру просто вставить в общий поток команд, то процессор воспримет команды процедуры как часть этого потока и, соответственно, начнет выполнять эти команды. Учитывая это обстоятельство, есть следующие варианты размещения процедуры в программе: · в начале программы (до первой исполняемой команды); · в конце программы (после команды, возвращающей управление операционной системе); · промежуточный вариант — внутри другой процедуры или основной программы (в этом случае необходимо предусмотреть обход процедуры с помощью команды безусловного перехода JМР); · в другом модуле (библиотеке DLL). Размещение процедуры в начале сегмента кода предполагает, что последовательность команд, ограниченная парой директив PROC и ENDP, будет размещена до метки, обозначающей первую команду, с которой начинается выполнение программы. Эта метка должна быть указана как параметр директивы END, обозначающей конец программы: model small .stack 100h .data .code my_proc procnear ret my_proc endp start: end start Объявление имени процедуры в программе равнозначно объявлению метки, поэтому директиву PROC в частном случае можно рассматривать как завуалированную форму определения программной метки. Поэтому сама исполняемая программа также может быть оформлена в виде процедуры, что довольно часто и делается с целью пометить первую команду программы, с которой должно начаться выполнение. При этом не забывайте, что имя этой процедуры нужно обязательно указывать в заключительной директиве END. Так, последний рассмотренный фрагмент эквивалентен следующему: model small .stack 100h .data .code my_proc procnear ret my_proc endp start proc start endp end start В этом фрагменте после загрузки программы в память управление будет передано первой команде процедуры с именем start. Размещение процедуры в конце программы предполагает, что последовательность команд, ограниченная директивами PROC и ENDP, находится следом за командой, возвращающей управление операционной системе: model small .stack 100h .data .code start: mov ax,4c00h int 21h;возврат управления операционной системе my_proc procnear ret my_proc endp end start Промежуточный вариант расположения тела процедуры предполагает ее размещение внутри другой процедуры или основной программы. В этом случае необходимо предусмотреть обход тела процедуры, ограниченного директивами PROC и ENDP, с помощью команды безусловного перехода JМР: model small .stack 100h .data .code start: jmp ml my_proc procnear ret my_proc endp ml: mov ax,4c00h int 21h;возврат управления операционной системе end start Последний вариант расположения описаний процедур — в отдельном сегменте кода — предполагает, что часто используемые процедуры выносятся в отдельный файл, который должен быть оформлен как обычный исходный файл и подвергнут трансляции для получения объектного кода. Впоследствии этот объектный файл с помощью утилиты tlink можно объединить с файлом, в котором данные процедуры используются. Этот способ предполагает наличие в исходном тексте программы еще некоторых элементов, связанных с особенностями реализации концепции модульного программирования в языке ассемблера. Как обратиться к процедуре? Так как имя процедуры обладает теми же атрибутами, что и обычная метка в команде перехода, то обратиться к процедуре, в принципе, можно с помощью любой команды перехода. Но есть одно важное свойство, которое можно использовать благодаря специальному механизму вызова процедур. Суть состоит в возможности сохранения информации о контексте программы в точке вызова процедуры. Под контекстом понимается информация о состоянии программы в точке вызова процедуры. В системе команд процессора есть две команды для работы с контекстом — CALL и RET. Команда CALL осуществляет вызов процедуры (подпрограммы). Синтаксис команды: call [модификатор] имя_процедуры Подобно команде JMP команда CALL передает управление по адресу с символическим именем имя_процедуры, но при этом в стеке сохраняется адрес возврата (то есть адрес команды, следующей после команды CALL). Команда RET считывает адрес возврата из стека и загружает его в регистры CS и EIP/IP, тем самым возвращая управление на команду, следующую в программе за командой CALL Синтаксис команды: ret [число] Необязательный параметр [число] обозначает количество элементов, удаляемых из стека при возврате из процедуры. Размер элемента определяется хорошо знакомыми нам параметрами директивы SEGMENT — use16 и use32 (или соответствующим параметром упрощенных директив сегментации). Если указан параметр usel6, то [число] — это значение в байтах; если use32 — в словах. Для команды CALL, как и для JMP, актуальна проблема организации ближних и дальних переходов. Это видно из формата команды, где присутствует параметр [модификатор]. Как и в случае команды JMP, вызов процедуры командой CALL может быть внутрисегментным и межсегментным. При внутрисегментном вызове процедура находится в текущем сегменте кода (имеет тип near), и в качестве адреса возврата команда CALL сохраняет только содержимое регистра IP/EIP, что вполне достаточно. При межсегментном вызове процедура находится в другом сегменте кода (имеет тип far), и для осуществления возврата команда CALL должна запомнить содержимое обоих регистров (CS и IP/EIP), при этом в стеке сначала запоминается содержимое регистра CS, затем — регистра IP/EIP. Важно отметить, что одна и та же процедура не может быть одновременно процедурой ближнего и дальнего типов. Таким образом, если процедура используется в текущем сегменте кода, но может вызываться и из другого сегмента программы, то она должна быть объявлена процедурой типа far. Подобно команде JMP, существуют четыре разновидности команды CALL. Какая именно команда будет сформирована, зависит от значения модификатора в команде вызова процедуры CALL и атрибута дальности в описании процедуры. Если процедура описана в начале сегмента данных с указанием дальности в ее заголовке, то при ее вызове параметр [модификатор] можно не указывать: транслятор сам разберется, какую команду CALL ему нужно сформировать. Если же процедура описана после ее вызова, например, в конце текущего сегмента или в другом сегменте, то при ее вызове нужно указать ассемблеру тип вызова, чтобы он мог за один проход правильно сформировать команду CALL. Значения модификатора такие же, как и у команды JМР, за исключением значения SHORT PTR. С директивой PROC используются еще несколько директив: ARG, RETURNS, LOCAL, USES. Их назначение — помочь программисту выполнить некоторые рутинные действия при вызове и возврате из процедуры (заодно и повысив надежность кода). Директивы ARG и RETURNS назначают входным и выходным параметрам процедуры, передаваемым через стек, символические имена. Директива USES в качестве параметров содержит имена используемых в процедуре регистров. При обработке этой директивы ассемблер формирует входной и выходной коды процедуры (из команд PUSH и POP), обеспечивающие сохранение и восстановление регистров. Директива LOCAL предназначена для выделения кадра стека для локальных переменных, что позволяет экономить память, занимаемую программой в целом. Процедуры могут получать или не получать параметры из вызывающей процедуры и могут возвращать или не возвращать результаты (процедуры, которые что-либо возвращают, называются функциями в языке Pascal, но ассемблер не делает каких-либо различий между ними). Параметры можно передавать с помощью одного из шести механизмов: · по значению; · по ссылке; · по возвращаемому значению; · по результату; · по имени; · отложенным вычислением. Параметры можно передавать в одном из пяти мест: · в регистрах; · в глобальных переменных; · в стеке; · в потоке кода; · в блоке параметров. Так что всего в ассемблере возможно 30 различных способов передачи параметров для процедур. Рассмотрим их по порядку.
Дата добавления: 2014-01-15; Просмотров: 593; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |