На этом шаге мы рассмотрим общий алгоритм передачи параметров через стек.
Здесь нам хотелось бы рассмотреть подробнее вопрос о передаче параметров через стек. Мы уже останавливались на нем (шаг 36 в разделе "Assembler: 16-битное программирование"). Это не единственный способ передачи параметров, но именно через стек передаются параметры API-функциям, поэтому на это необходимо обратить внимание. Состояние стека до и после вызова процедуры приводится на рисунке 1.
Рисунок 1 демонстрирует стандартный вход в процедуру, практикующийся в таких языках высокого уровня, как Паскаль и Си.
Рис.1. Схема передачи параметров в процедуру (стек растет сверху вниз)
При входе в процедуру выполняется стандартная последовательность команд:
PUSH EBP
MOV EBP, ESP
SUB ESP, N; N - количество байт для локальных переменных.
Адрес первого параметра определяется как [ЕВР+08Н], что мы уже неоднократно использовали. Адрес первой локальной переменной, если она зарезервирована, определяется как [ЕВР - 4] (имеется в виду переменная типа DWORD). В конце процедуры идут команды:
MOV ESP, ЕВР POP EBP RET M
Здесь M - объем, взятый у стека для передачи параметров.
Такого же результата можно добиться, используя команду
ENTER N, 0
PUSH EBP MOV EBP, ESP SUB ESP
LEAVE
MOV ESP, ЕВР РОР ЕВР
в конце процедуры. Эти команды появились еще у 286-го процессора и дали возможность несколько оптимизировать транслируемый код программы, особенно в тех случаях, когда речь идет о больших по объему модулях, создаваемых на языке высокого уровня.
Хотелось бы остановиться еще на одном вопросе, связанном со структурой процедуры и ее вызова. Существуют два основных подхода к передаче параметров, или, как еще говорят, соглашения о передаче параметров. Условно первый подход можно назвать Си-подходом, а второй - Паскаль-подходом. Первый подход предполагает, что процедура "не знает", сколько параметров находится в стеке. Естественно, в этом случае освобождение стека от параметров должно происходить после команды вызова процедуры, например, с помощью команды POP или команды ADD ESP,N (N - количество байт, занятых параметрами). Второй подход основан на том, что количество параметров фиксировано, поэтому стек можно освободить в самой процедуре. Это достигается за счет выполнения команды RET N (N - количество байт в параметрах). Как вы уже, наверное, догадались, вызов функций API осуществляется, чаще всего, по второй схеме.
Со следующего шага мы перечислим общие принципы построения оконных приложений.