→ Пошук по сайту       Увійти / Зареєструватися
Знання Асемблер Основи програмування

Виклики підпрограм

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

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

Підпрограма може бути оформлена у вигляді процедури, і тоді ім'я цієї процедури служитиме точкою входу в підпрограму:

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

. . . ;Тіло підпрограми

ret ;Команда повернення в зухвалу програму

drawline endp 

З таким же успіхом можна обійтися без процедури, просто помітивши перший рядок програми деякою міткою:

drawline: ;Подпрограмма, що починається з мітки

. . . ;Тіло підпрограми

ret ;Команда повернення в зухвалу програму

. . . ;Продовження основної програми або
;інші підпрограми 

У будь-якому випадку виклик підпрограми здійснюється командою call. Підпрограма повинна завершуватися командою ret, службовці для повернення управління в ту крапку, звідки підпрограма була викликана.

Питання використання підпрограм, передачі в них параметрів і повернення результату будуть розглянуті в наступному розділі. Тут ми зупинимося тільки на таких принципових архітектурних питаннях, як механізм виконання і можливості команд call і ret. При цьому треба мати на увазі, що синтаксичні особливості і закономірності використання команд call і jmp багато в чому збігаються, і значна частина пояснень до команд переходу справедлива і для команд виклику.

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

  • прямим ближнім (в межах поточного сегменту команд);
  • прямим дальнім (у інший сегмент команд);
  • непрямим ближнім (в межах поточного сегменту команд через осередок з адресою переходу);
  • непрямим дальнім (у інший сегмент команд через осередок з адресою
  • переходу).

Розглянемо послідовно перераховані варіанти.

Прямий ближній виклик. Як і у разі прямого ближнього переходу, в команді прямого виклику в явній формі указується адреса (зсув) точки входу в підпрограму; як ця адреса можна використовувати як ім'я процедури, так і ім'я влучні, що характеризує точку входу в підпрограму. У код команди, окрім коди операції E8h, входить зсув до підпрограми, що викликається. У приведеному нижче прикладі підпрограма оформлена у вигляді процедури.

code segment

main proc ;Основная програма
.
call sub ;Код Е8 dddd
.
main endp

sub proc near ;Подпрограмма
.
ret ;Код СЗ

sub endp

code ends 

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

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

Участь стека в механізмі виклику підпрограми і повернення з неї є вирішальною. Оскільки в стеку зберігається адреса повернення, підпрограма, сама використовуючи стек, наприклад, для зберігання проміжних результатів, зобов'язана до моменту виконання команди ret повернути стек в початковий стан. Команда ret, природно, ніяк не аналізує стан або вміст стека. Вона просто знімає із стека верхнє слово, вважаючи його за адресу повернення, і завантажує це слово в покажчик команд IP. Якщо до моменту виконання команди ret покажчик стека виявиться зміщеним в ту або іншу сторону, команда ret як і раніше розглядатиме верхнє слово стека, як адресу повернення, і передасть по ньому управління, що неминуче приведе до краху системи.

Прямий дальній виклик. Цей виклик дозволяє звернутися до підпрограми з іншого сегменту. У код команди, окрім коди операції 9ah, входить повна адреса (сегмент плюс зсув) підпрограми, що викликається. Зазвичай в початковому тексті програми за допомогою описувача far ptr указується, що виклик є дальнім, хоча, якщо транслятор налаштований на трансляцію в два проходи, цей описувач не обов'язковий. Структура програмного комплексу, що містить дальній виклик підпрограми, може виглядати таким чином:

codel segment

assume Cs:codel

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

call far ptr subr ; Код 9а dddd ssss
.
main endp

codel ends

code2 segment

assume Cs:code2

subr proc far ;Объявляем підпрограму дальньої
.
ret ;Код СВ - дальнє повернення

subr endp

code2 ends 

Процедура-підпрограма знаходиться в іншому сегменті команд тієї ж програми. У коді команди dddd позначає відносну адресу точки входу в підпрограму в її сегменті команд, а 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 ;Код СЗ

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 індекс в цій таблиці 

Непрямий дальній виклик. Відрізняється від непрямого ближнього виклику лише тим, що підпрограма знаходиться в іншому сегменті, а в елементі пам'яті міститься повна адреса підпрограми, що включає сегмент і зсув.

codel segment

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

call dword ptr subadr ;Код FF IE dddd
.
main endp

codel ends

code2 segment

subr proc far ;Подпрограмма
.
ret ;Код СВ

subr endp

code2 ends

data segment
.
subadr dd subr ;Двухсловная осередок з
;адресою підпрограми

data ends 

Процедура-підпрограма з атрибутом far знаходиться в іншому сегменті команд тієї ж програми, а її повна двухсловний адреса - в осередку subadr в сегменті даних. Другий байт коди команди (IE в даному прикладі) залежить від способу адресації. Непрямий дальній виклик, як і непрямий ближній, дозволяє використовувати різні способи адресації.

загрузка...
Сторінки, близькі за змістом