Подпрограммы

Подпрограммы (процедуры)

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

Различают два основных механизма передачи параметров процедуре:

- по значению;

- по ссылке.

Пусть procedure - имя процедуры, а value - имя некоторой области памяти (переменная). Тогда вызов процедуры с передачей параметра по значению выглядит следующим образом:

mov AX, word ptr value ; процедура получает копию

call procedure ; входного параметра

Вызов с передачей параметра по ссылке будет выглядеть так:

mov AX, offset value ; процедура получает адрес

call procedure ; входного или выходного параметра

Для обмена информацией между вызывающим кодом и процедурой могут использоваться такие каналы:

- регистры;

- стек;

- глобальные переменные;

- поток кода;

- блок параметров.

Передача параметров в регистрах

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

Передача параметров через стек

Параметры помещаются в стек перед вызовом процедуры. Именно такой метод используют языки высокого уровня (C, Pascal). Для чтения параметров из стека применяют не команду POP, а регистр BP, в который помещают адрес вершины стека после входа в процедуру:

push параметр1 ; поместить параметр в стек

push параметр2

call procedure

add SP, 4 ; освободить стек от параметров

............

procedure PROC near

push BP

mov BP, SP

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

mov AX, [BP+4] ; считать параметр2. Его адрес [BP+4], потому что ; при выполнении команды call в стек поместили адрес возврата (2 байта для
; процедуры типа NEAR), а потом еще и BP - 2 байта.

mov BX, [BP+6] ; считать параметр1.

(остальные команды)

pop BP

ret

procedure ENDP

При использовании стека для передачи параметров процедуры возникает два вопроса: кто должен удалять параметры из стека (процедура или вызывающий код) и в каком порядке помещать параметры в стек. Все возможные варианты ответа на этот вопрос имеют свои "за" и "против". Так, например, если стек освобождает процедура (команда ret число_байтов), то код программы получается меньшим, а если за освобождение стека от параметров отвечает вызывающая функция, то становится возможным последовательными командами CALL вызвать несколько функций с одними и теми же параметрами. Первый способ - более строгий (Pascal), второй - дает больше возможностей для оптимизации (С). Кроме того, в языке С параметры помещают в стек в обратном порядке (справа налево), так что становятся возможными функции с изменяемым числом параметров.

Передача параметров в потоке кода

Передаваемые данные размещаются прямо в коде программы, сразу после команды call, например (так реализовна процедура print в одной из стандартных библиотек процедур для ассемблера UCRLIB):

call print

DB "This ASCIZ-line will be printed", 0

(следующая команда)

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

Лабораторное задание 3_2.

Передача параметров в потоке кода (так же как и передача параметров в стеке в обратном порядке) позволяет передавать различное число параметров или один параметр различной длины.

Передача параметров в блоке параметров

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

Локальные переменные

Наиболее распространенный способ хранения локальных переменных - стек. Принято распологать локальные переменные сразу после сохраненного значения регистра BP, так что на них можно ссылаться как [BP-2], [BP-4], [BP-6] и т.д.

Рассмотрим процедуру examp, в которой используются параметры x, y, z и локальные переменные a, b, c (все эти величины предполагаются 2-х байтовыми):

examp proc near

x equ [BP+8] ; параметры процедуры

y equ [BP+6]

z equ [BP+4]

a equ [BP- 2] ; локальные переменные

b equ [BP- 4]

c equ [BP- 6]

push BP ; сохранить BP

mov BP, SP ; установить BP для себя

sub SP, 6 ; зарезервировать 6 байт для локальных переменных

(тело процедуры)

mov SP, BP ; выбросить из стека локальные переменные

pop BP ; восстановить BP вызвавшей процедуры

ret 6 ; возврат с удалением трех параметров из стека

examp endp

При вызове процедуры examp стек будет выглядеть следующим образом:

[BP+8] = x

[BP+6] = y

[BP+4] = z

[BP+2] = IP

BP® [BP+0] = BP

[BP- 2] = a

[BP- 4] = b

SP® [BP- 6] = c

¯

В связи с тем, что последовательности команд

push BP

mov BP, SP

sub SP, 6

и

mov SP, BP

pop BP

используются очень часто при построении процедур, были введены специальные команды ENTER и LEAVE, которые их заменяют. С использованием этих команд процедура examp будет выглядеть следующим образом:

examp proc near

x equ [BP+8]

y equ [BP+6]

z equ [BP+4]

a equ [BP- 2]

b equ [BP- 4]

c equ [BP- 6]

enter 6, 0 ; второй параметр - уровень вложенности процедуры

(тело процедуры)

leave

ret 6

examp endp

Активизационной записью процедуры называется следующая информация, размещаемая в стеке: параметры процедуры, IP (адрес возврата), BP (предыдущее значение).

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