Каждая процедура и функция Borland Pascal начинается и за-
канчивается стандартным набором операторов, которые позволяют ак-
тивизировать и деактивизировать процедуру или функцию.
Стандартным входом служит следующая группа операторов:
PUSH BP ; сохранить регистр ВР
MOV BP,SP ; установить границы стека
SUB SP,LocalSize ; выделить память для локальных пере-
; менных
В этом примере LocalSize - это размер локальных переменных.
Инструкция SUВ присутствует только в том случае, когда LocalSize
не равно нулю. Если тип обращения к процедуре является ближним,
то параметры начинаются с BP+4, если для вызова процедуры исполь-
зуется дальний тип обращения, то они начинаются с BP+6.
Для программ DOS код входа и выхода для подпрограммы, ис-
пользующей дальнюю модель вызова, тот же, что и для подпрограммы
с ближним типом вызов, но для возврата из подпрограммы использу-
ется инструкция RETF. Это справедливо также для программы
Windows, cкомпилированной в состоянии {$W-}.
Примечание: Об использовании процедур входа и выхода в
DLL рассказывается в Главе 11 "Динамически компонуемые биб-
лиотеки".
Стандартной группой операторов выхода является:
MOV SP,BP ; освободить память, выделенную для
; локальных переменных
POP BP ; восстановить регистр ВР
RET ParamSize ; удалить параметры и выполнить возврат
; управления
Здесь РаrамSizе - это размер параметров. Инструкция RET яв-
ляется инструкцией ближнего или дальнего типа, в зависимости от
типа обращения к процедуре.
В состоянии {$W+} (по умолчанию) в подпрограмме, использую-
щей дальнюю модель вызова, код выхода и выхода выглядит следующим
образом:
INC BP ; указывает на кадр стека FAR
PUSH BP ; сохранить регистр ВР
MOV BP,SP ; установить кадр стека
PUSH DS ; сохранить DS
SUB SP,LocalSize ; выделить память для локальных переменных
.
.
.
MOV SP,BP ; освободить память, выделенную для
; локальных переменных
POP BP ; восстановить регистр ВР
DEC PB ; настроить BP
RETF ParamSize ; удалить параметры и выполнить возврат
; управления
Код входа и выхода для экспортируемой подпрограммы
(процедуры или функции, скомпилированной с директивой
компилятора export) выглядит следующим образом:
mov AXC,DS ; загрузить селектор DS в AX
nop ; дополнительное пространство для
; корректировок
inc BP ; указывает на дальний кадр стека
push BP ; сохранить BP
mov BP,SP ; установить кадр стека
push DS ; сохранить DS
mov DS,AX ; инициализация регистра DS
sub SP,LocalSize; распределении локальных переменных
. ; (если они имеются)
.
.
pop DI ; восстановить DI
pop SI ; восстановить SI
lea SP,[BP-2] ; освободить память, выделенную для
; локальных переменных
pop DS ; восстановить DS
pop BP ; восстановить BP
dec BP ; настроить регистр BP
retf ParamSize ; удаление параметров и возврат
; управления
Для всех моделей вызова, если подпрограмма не содержит ло-
кальных переменных, инструкции выделения и освобождения памяти
для локальных переменных можно опустить.
При работе в реальном режиме, чтобы различать ближний и
дальний кадр стека, Windows требует, чтобы все кадры стека (вклю-
чая кадры стека экспортируемых подпрограмм) сохраняли в слове по
адресу [BP+0] нечетное значение BP. Кроме того, Windows требует,
чтобы слово по адресу [BP-2] содержало селектор сегмента данных
вызывающей программы. Это объясняет использование инструкций INC
BP, PUSH DS и DEC BP (сгенерированных в состоянии {$W+}) на входе
и выходе для подпрограмм far и export.
Заметим, что использование {$W+} требуют только реальный
режим Windows. Если вы не поддерживаете реальный режим, укажите
{$W-}. Вы получите программу меньшего размера и некоторый выигрыш
в скорости.
При разработке программы защищенного режима Windows может
оказаться полезным использование состояния {$W+}. Некоторые
средства отладки, отличные от средств Borland, требуют этого для
корректной работы.
По умолчанию Borland Pascal автоматически генерирует эффек-
тивные системные вызовы для процедур и функций, экспортируемых
прикладной программой. При компоновке прикладной программы в сос-
тоянии {$K+} (по умолчанию) отладчик ищет в каждой экспортируемой
точке входа последовательность инструкций MOV AX,DS с последующей
инструкцией NOP, заменяя их на MOV AX.DS на MOV AX,SS. Это изме-
нение ослабляет требование использования при создании программ
системного вызова подпрограмм API Windows MakeProcInstanc и
FreeProcInstance (хотя это не возбраняется). Можно также вызывать
экспортируемые точки входа из самой прикладной программы.
В состоянии {$K-} при создании динамически компонуемой биб-
лиотеки компоновщик Borland Pascal не модифицирует код входа и
выхода экспортированной точки входа. Если подпрограмма системного
вызова в приложении должна вызываться из другой прикладной прог-
раммы, выбирать состояние {$K-} не следует.
При загрузке прикладной программы или динамически компонуе-
мой библиотеки Windows ищет в каждой экспортируемой точке входа
последовательность инструкций MOV AX,DS с последующей инструкцией
NOP. Для прикладной программы Windows изменяет первые три байта
на три инструкции NOP, чтобы подготовить подпрограмму для исполь-
зования ее функцией Windows MakeProcInstance. Для библиотек
Windows изменяет первые три байта в инструкции MOV AX,xxxx, где
xxxx - селектор (адрес сегмента) сегмента динамических локальных
данных библиотеки.