Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Финогенов-основы_языка_ассемблера.doc
Скачиваний:
26
Добавлен:
17.09.2019
Размер:
3.35 Mб
Скачать

Глава 2

Основы программирования

S3

jmp DS:go_addr ;Возможна имена сегмента

jmp dword ptr go_addr ;Если поле go_addr объявлено

;операторами dw jmp go_addi Характеристики ячейки должны

;быть известны

Для дальнего косвенного перехода, как и для ближнего, допустима адресация через регистр общего назначения, если в него поместить адрес поля с адресом перехода:

mov BX,offset go_addr jmp [BX]

Возможно также использование базово-индексной адресации, если в сег­менте данных имеется таблица с двухсловными адресами точек переходов.

2.7. Вызовы подпрограмм

Практически в любой программе, независимо от се содержания, встре­чаются участки, которые требуется выполнять (возможно, с небольшими изменениями) несколько раз по ходу программы. Такие повторяющиеся участки целесообразно выделить из общей программы, оформить в виде подпрограмм и обращаться к ним каждый раз, когда в основной програм­ме возникает необходимость их выполнения.

Подпрограмма, в зависимости от выполняемых ею функций, может требовать передачи из вызывающей программы определенных данных (на­зываемых аргументами, или параметрами), возвращать в вызывающую про­грамму результаты вычислений или обходиться и без того, и без другого.

Подпрограмма может быть оформлена в виде процедуры, и тогда имя этой процедуры будет служить точкой входа в подпрограмму:

drawline proc

; Подпрограмма -про цедура

ret endp

;Тело подпрограммы

;Команда возврата в вызывающую программу

drawlme

С таким же успехом можно обойтись без процедуры, просто пометив первую строку программы некоторой меткой:

drawline: ; Под программа, начинающаяся с метки

;Тело подпрограммы

ret ;Команда возврата в вызывающую программу Продолжение основной программы или ;другие подпрограммы

В любом случае вызов подпрограммы осущестшшется командой call. Подпрограмма должна завершаться командой ret, служащей для возврата управления в ту точку, откуда подпрограмма была вызвана.

Вопросы использования подпрограмм, передачи в них параметров и возвращения результата будут рассмотрены в следующей главе. Здесь мы остановимся только на таких принципиальных архитектурных вопросах,

как механизм выполнения и возможности команд call и ret. При этом надо иметь в виду, что синтаксические особенности и закономерности исполь­зования команд call и jmp во многом совпадают, и значительная часть пояснений к командам перехода справедлива и для команд вызова.

Команда вызова подпрограммы call может использоваться в 4 разно­видностях. Вызов может быть:

прямым ближним (в пределах текущего сегмента команд);

прямым дальним (в другой сегмент команд);

косвенным ближним (в пределах текущего сегмента команд через ячей­ку с адресом перехода);

косвенным дальним (в другой сегмент команд через ячейку с адресом

перехода).

Рассмотрим последовательно перечисленные варианты.

code segment main proc

call

Прямой ближний вызов. Как и в случае прямого ближнего перехода, в команде прямого вызова в явной форме указывается адрес (смещение) точки входа в подпрограмму; в качестве этого адреса можно использовать как имя процедуры, так и имя метки, характеризующей точку входа в подпрограмму. В код команды, кроме кода операции E8h, входит смеше­ние к вызываемой подпрограмме. В приведенном ниже примере подпрог­рамма оформлена в виде процедуры.

;Основная программа ;Код Е8 dddd

sub

Подпрограмма ;Код СЗ

main endp .

sub proc near

ret

sub endp code ends

Про цедура-программа находится в том же сегменте команд, что и вызывающая программа. В коде команды dddd обозначает смещение в сег­менте команд к точке входа в подпрограмму. При выполнении команды call процессор помещает адрес возврата (содержимое регистра IP) в стек выполняемой программы (рис. 2.16), после чего к текущему содержимому IP прибавляет dddd. В результате в IP оказывается адрес подпрограммы. Команда ret, которой заканчивается подпрограмма, выполняет обратную процедуру — извлекает из стека адрес возврата и заносит его в IP.

•\ Состояние стека после входа в подпрограмму

4— Исходное состояние SP

Рис. 2.16. Участие стека в механизме вызова ближней подпрограммы.

84

Глава'2

Основы программирования

85

Участие стека в механизме вызова подпрограммы и возврата из нее является решающим. Поскольку в стеке хранится адрес возврата, под­программа, сама используя стек, например, для хранения промежуточ­ных результатов, обязана к моменту выполнения команды ret вернуть стек в исходное состояние. Команда ret, естественно, никак не анализирует состояние или содержимое стека. Она просто снимает со стека верхнее слово, считая его адресом возврата, и загружает это слово в указатель команд IP. Если к моменту выполнения команды ret указатель стека ока­жется смещенным в ту или иную сторону, команда ret по-прежнему будет рассматривать верхнее слово стека, как адрес возврата, и передаст по нему управление, что неминуемо приведет к краху системы.

; Основная программа far ptr subr ;Код 9А dddd ssss

;Объявляем подпрограмму. ;дальней ;Код СВ — дальний возврат

Прямой дальний вызов. Этот вызов позволяет обратиться к подпрог­рамме из другого сегмента. В код команды, кроме кода операции 9Ah, входит полный адрес (сегмент плюс смещение) вызываемой подпрог­раммы. Обычно в исходном тексте программы с помощью описателя far ptr указывается, что вызов является дальним, хотя, если транслятор на­строен на трансляцию в два прохода, этот описатель не обязателен. Струк­тура программного комплекса, содержащая дальний вызов подпрограм­мы, может выглядеть следующим образом:

codel assume

segment CS:codel

main

proc call far

main codel

endp ends

code 2 assume

segment CS:code2

subr

proc far

ret

subr code 2

cndp ends

Процедура-подпрограмма находится в другом сегменте команд той же программы. В коде команды dddd обозначает относительный адрес точки входа в подпрограмму в ее сегменте команд, a ssssее сегментный адрес. При выполнении команды call процессор помещает в стек сначшга сегмен­тный адрес вызывающей программы, а затем относительный адрес возвра­та (рис. 2.17). Далее в сегментный регистр CS заносится ssss (у нас это значе­ние code2), а в IP — dddd (у нас это значение subr). Поскольку процедура-подпрограмма атрибутом far объявлена датьней, команда ret имеет код, отличный от кода аналогичной команды ближней процедуры и выполняет­ся по-другому: из стека извлекаются два верхних слова и переносятся в IP и CS, чем и осуществляется возврат в вызывающую программу, находящу­юся в другом сегменте команд. В языке ассемблера существует и явное мне­моническое обозначение команды дальнего возврата — retf.

^— Состояние стека после входа в подпрограмму

^— Исходное состояние SP Рис. 2.17. Участие стека в механизме вызова дальней подпрограммы.

Косвенный ближний вызов. Адрес подпрограммы содержится либо в ячейке памяти, либо в регистре. Это позволяет, как и в случае косвенного ближнего перехода, модифицировать адрес вызова, а также осуществлять вызов не с помощью метки, а по известному абсолютному адресу. Струк­тура программы с косвенным вызовом подпрограммы может выглядеть следующим образом:

code main

segment proc

;Основная программа

call DS:subadr ;Код FF 16 dddd main endp subr proc near ;Подпрограмма

ret endp ends segment

;Код СЗ

subr code data

subadr dw subr

;Ячейка с адресом подпрограммы

data

ends

Процедура-программа с атрибутом near находится в том же сегменте, что и вызывающая программа, а ее относительный адрес в ячейке subadr в сегменте данных. В коде команды dddd обозначает относительный адрес слова subadr в сегменте данных. Второй байт кода команды (I6h в данном примере) зависит от способа адресации. Косвенный вызов позволяет ис­пользовать разнообразные способы адресации подпрограммы:

call ВХ ;В ВХ адрес подпрограммы

call [ВХ) ;В ВХ адрес ячейки с адресом подпрограммы

call [BX][SI] ;B ВХ адрес таблицы адресов подпрограмм,

;в SI индекс в этой таблице, call tbl[SI] ;tbl — адрес таблицы адресов подпрограмм,

;в SI индекс в этой таблице

Косвенный дальний вызов. Отличается от косвенного ближнего вызо­ва лишь тем, что подпрограмма находится в другом сегменте, а в ячейке

86