Студопедия

КАТЕГОРИИ:


Архитектура-(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 различных способов передачи параметров для процедур. Рассмотрим их по порядку.

<== предыдущая лекция | следующая лекция ==>
Лекция 9.1. Сложные структуры данных | Передача параметров по имени
Поделиться с друзьями:


Дата добавления: 2014-01-15; Просмотров: 564; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.01 сек.