Передача параметров процедуры

Существуют несколько способов передачи параметров в процедуру.

1. Параметры можно передавать через регистры.

Если процедура получает небольшое число параметров, идеальным местом для их передачи оказываются регистры. Существуют соглашения о вызовах, предполагающие передачу параметров через регистры ECX и EDX. Этот метод самый быстрый, но он удобен только для процедур с небольшим количеством параметров.

2. Параметры можно передавать в глобальных переменных.

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

3. Параметры можно передавать в блоке параметров.

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

4. Параметры можно передавать через стек.

Передача параметров через стек – наиболее распространённых способ. Именно его используют языки высокого уровня, такие как С++ и Паскаль. Параметры помещаются в стек непосредственно перед вызовом процедуры.

При внимательном анализе этого метода передачи параметров возникает сразу два вопроса: кто должен удалять параметры из стека, процедура или вызывающая её программа, и в каком порядке помещать параметры в стек. В обоих случаях оказывается, что оба варианта имеют свои «за» и «против». Если стек освобождает процедура, то код программы получается меньшим, а если за освобождение стека от параметров отвечает вызывающая программа, то становится возможным вызвать несколько функций с одними и теми же параметрами просто последовательными командами CALL. Первый способ, более строгий, используется при реализации процедур в языке Паскаль, а второй, дающий больше возможностей для оптимизации, – в языке С++.

Основное соглашение о вызовах языка Паскаль предполагает, что параметры кладутся в стек в прямом порядке. Соглашения о вызовах языка С++, в том числе одно из основных соглашений о вызовах ОС Windows stdcall, предполагают, что параметры помещаются в стек в обратном порядке. Это делает возможной реализацию функций с переменным числом параметров (как, например, printf). При этом первый параметр определяет число остальных параметров.

push <параметр n >

...

push <параметр1>

call Procedure

В приведённом выше участке кода в стек кладутся несколько параметров и затем вызывается процедура. Следует помнить, что команда CALL также кладёт в стек адрес возврата. Таким образом, перед выполнением первой команды процедуры стек будет выглядеть следующим образом.

Адрес возврата оказывается в стеке поверх параметров. Однако поскольку в рамках своего участка стека процедура может обращаться без ограничений к любой ячейки памяти, нет необходимости перекладывать куда-то адрес возврата, а потом возвращать его обратно в стек. Для обращения к первому параметру используют адрес [ESP + 4] (прибавляем 4, т.к. на архитектуре Win32 адрес имеет размер 32 бита), для обращения ко второму параметру – адрес [ESP + 8] и т.д.

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

; Передача параметров и возврат из процедуры с использованием соглашения о вызовах stdcall

.686

.model flat, stdcall

option casemap: none

include \masm32\include\windows.inc

include \masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib

.data

x dd 0

y dd 4

.code

program:

push y; Кладём в стек два параметра размером по 4 байта

push x

call Procedure

push 0

call ExitProcess

Procedure proc

ret 8; В команде возврата указываем, что надо освободить 8 байт стека

Procedure endp

end program

; Передача параметров и возврат из процедуры с использованием соглашения о вызовах cdecl

.686

.model flat, c

option casemap: none

include \masm32\include\windows.inc

include \masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib

.data

x dd 0

y dd 4

.code

program:

push y; Кладём в стек два параметра размером по 4 байта

push x

call Procedure

add esp, 8; Освобождаем 8 байт стека

push 0

call ExitProcess

Procedure proc

ret; Используем команду возврата без параметров

Procedure endp

end program

5. Параметры можно передавать в потоке кода.

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

.686

.model flat, stdcall

option casemap: none

include \masm32\include\windows.inc

include \masm32\include\kernel32.inc

includelib \masm32\lib\kernel32.lib

.code

program:

call Procedure; Команда CALL кладёт в стек адрес следующей команды

db 'string',0; В нашем случае – адрес начала строки

push 0

call ExitProcess

Procedure proc

pop esi; Извлекаем из стека адрес начала строки

xor eax, eax; Обнуляем EAX, в нём будет храниться количество символов

L1: mov bl, [esi]; Заносим в регистр BL байт, хранящийся по адресу ESI

inc esi; Увеличиваем значение в регистре ESI на 1

inc eax; Увеличиваем значение в регистре EAX на 1

cmp bl, 0; Сравниваем прочитанный символ с нулём

jne L1; Если не 0, переходим к началу цикла

push esi; Кладём в стек адрес байта, следующего сразу за строкой

ret; Возврат из процедуры

Procedure endp

end program


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: