Вызовы подпрограмм
Прямой дальний вызов
Этот вызов позволяет обратиться к подпрограмме из другого сегмента. В код команды, кроме кода операции 9Ah, входит полный адрес (сегмент плюс смещение) вызываемой подпрограммы. Обычно в исходном тексте программы с помощью описателя far ptr указывается, что вызов является дальним, хотя, если транслятор настроен на трансляцию в два прохода, этот описатель не обязателен. Структура программного комплекса, содержащая дальний вызов подпрограммы, может выглядеть следующим образом:
code1 segment assume CS:code1 main proc; Основная программа call far ptr subr; Код 9А dddd ssss … main endp code1 ends code2 segment assume CS:code2 subr proc far; Объявляем подпрограмму дальней … ret; Код СВ – дальний возврат subr endp code2 ends
Процедура-подпрограмма находится в другом сегменте команд той же программы. В коде команды dddd обозначает относительный адрес точки входа в подпрограмму в ее сегменте команд, a ssss – се сегментный адрес. При выполнении команды call процессор помещает в стек сначала сегментный адрес вызывающей программы, а затем относительный адрес возврата (рис. 2.17). Далее в сегментный регистр CS заносится 5555 (у нас это значение code2), а в IP – dddd (у нас это значение subr).
Поскольку процедура-подпрограмма атрибутом far объявлена дальней, команда ret имеет код, отличный от кода аналогичной команды ближней процедуры и выполняется по-другому: из стека извлекаются два верхних слова и переносятся в IP и CS, чем и осуществляется возврат в вызывающую программу, находящуюся в другом сегменте команд. В языке ассемблера существует и явное мнемоническое обозначение команды дальнего возврата – retf.
Рис. 2.17. Участие стека в механизме вызова дальней подпрограммы.
Косвенный ближний вызов
Адрес подпрограммы содержится либо в ячейке памяти, либо в регистре. Это позволяет, как и в случае косвенного ближнего перехода, модифицировать адрес вызова, а также осуществлять вызов не с помощью метки, а по известному абсолютному адресу. Структура программы с косвенным вызовом подпрограммы может выглядеть следующим образом:
code segment main proc; Основная программа … call DS:subadr; Код FF 16 dddd main endp subr proc near; Подпрограмма … ret; Код С3 subr endp code ends data segment … subadr dw subr; Ячейка с адресом подпрограммы data ends
Процедура-программа с атрибутом near находится в том же сегменте, что и вызывающая программа, а ее относительный адрес в ячейке subadr в сегменте данных. В коде команды dddd обозначает относительный адрес слова subadr в сегменте данных. Второй байт кода команды (16h в данном примере) зависит от способа адресации. Косвенный вызов позволяет использовать разнообразные способы адресации подпрограммы:
call BX; В ВХ адрес подпрограммы call[BX]; В ВХ адрес ячейки с адресом подпрограммы call[BX][SI];В ВХ адрес таблицы адресов подпрограмм, ;в SI индекс в этой таблице. tbl[SI];tbl – адрес таблицы адресов подпрограмм, ;в SI индекс в этой таблице
Косвенный дальний вызов
Отличается от косвенного ближнего вызова лишь тем, что подпрограмма находится в другом сегменте, а в ячейке памяти содержится полный адрес подпрограммы, включающий сегмент и смещение.
code1 segment main proc; Основная программа call dword ptr subadr; Код FF IE dddd … main endp code1 ends code2 segment subr proc far; Подпрограмма … ret; Код СВ subr endp code2 ends data segment … subadr dd subr; Двухсловная ячейка с ;адресом подпрограммы data ends
Процедура-подпрограмма с атрибутом far находится в другом сегменте команд той же программы, а ее полный двухсловный адрес – в ячейке subadr в сегменте данных. Второй байт кода команды (IE в данном примере) зависит от способа адресации. Косвенный дальний вызов, как и косвенный ближний, позволяет использовать различные способы адресации.