Borland Pascal выполняет передачу параметров, используя стек процессора или сопроцессора (для параметров типа Single, Double, Extended или Coinp). Параметры всегда вычисляются и помещаются в стек в том порядке, в котором они использованы в объявлении процедуры, т. е. слева направо.
19.2.1. Параметры-значения и параметры-переменные
Параметры значения — это параметры, значение которых нельзя изменить подпрограммой, в которую они передаются. Так же, как и другие компиляторы, Borland Pascal не всегда передает эти параметры в подпрограмму только с использованием стека процессора, конкретный метод определяется типом этих параметров.
Параметры-значения скалярного типа (Boolean, Char, Shortint, Byte, Integer, Word, Longint и перечислимые типы) передаются в процедуру путем помещения их значения в стек. Если при этом размер параметра составляет 1 байт, то он помещается в стек в виде целого слова, но информация, содержащаяся в старшем байте этого слова, не может быть использована. Параметры размером в 2 и 4 байта помещаются в стек в виде слова и двойного слова соответственно. По соглашениям, принятым для процессора 8086, первым в стек помещается старшее слово четырехбайтных параметров.
Параметры-значения вещественного типа Real помещаются в стек в виде шести-бпйтного значения. Для всех остальных типов (Single, Double, Extended и C-onip) выполняются стандартные для компиляторов Borland соглашения — они передаются в стек процессора вместе с другими параметрами командами вызова сопроцессора.
Параметры-значения указателей всех типов помещаются в стек в виде дальних указателей - сначала слово, содержащее сегмент, затем слово, содержащее смещение. Таким образом, по соглашениям Intel, значение сегмента занимает старший адрес. Для загрузки значения указателя ассемблерные программы могут использовать команды LDS или LES.
Строковые параметры, независимо от их размера, никогда не помещаются непосредственно в стек. Вместо этого Borland Basca) помещает в стзк указатель на строку, имеющий тип FAR. Вызываемая подпрограмма не должна изменять значение строки, описываемое данным указателем, и при необходимости может работать с копией этой строки.
Записи, массивы и объекты, имеющие размер 1, 2 или 4 байта, передаются непосредственно через стек. Для всех других размеров (включая 3 байта) в стек помещается указатель, причем для модификации этих параметров в таком случае необходимо использовать их локальную копию.
Множества так же, как и строки, никогда не передаются через стек непосредственно, а только с помощью указателя, который адресует 32-байтовое представление множества. Первый бит младшего байта в таком представлении всегда соответствует базовому элементу множества с порядковым чилимт""" "
Параметры-переменные всегда передаются в процедуры одним и тем же способом — через указатель дальнего типа на их содержимое.
19.2.2. Работа со стеком
По соглашениям, принятым в Borland Pascal, вызываемая процедура должна перед возвратом управления выполнить очистку стека от переданных в нее параметров.
Для этого можно использовать два способа. Первый из них заключается в применении команды RET n, где n - это размер переданных параметров в байтах. Вторым способом является сохранение адреса возврата в регистре или в памяти и последовательное извлечение всех параметров процедуры из стека с помощью команды POP. Применение команд POP позволяет выполнить оптимизацию программы по скорости, а также уменьшает размер подпрограммы, поскольку каждая из них занимает всего один байт.
При использовании директив .MODEL, PROC и ARG Turbo Assembler автоматически добавляет нужное значение во все команды RET подпрограммы.
19.2.3. Осуществление доступа к параметрам из ассемблерных процедур
При передаче управления ассемблерной процедуре вершина стека содержит адрес возврата (одно или два слова в зависимости от того, является ли процедура дальнего или ближнего типа) и расположенные над ним передаваемые параметры.
Для доступа к этим параметрам Turbo Assembler поддерживает три метода:
• с использованием регистра ВР;
• с использованием другого базового или индексного регистра;
• с помощью команд POP.
Два первых метода очень похожи и применяются в самом Borland Pascal при вызове процедур. Третий метод лучше всего использовать, когда в вызываемой ассемблерной процедуре не используются локальные параметры.
Пример использования регистра ВР:
CODE SEGMENT
ASSUME cs:CODE AsmProc PROC FAR;procedure AsmProc(i,j:word);
PUBLIC AsmProc j EQU WORD PTR [BP+6] i EQU WORD PTR [BP+8]
push bp
mov bp,sp
mov ax,i
В приведенном примере для обращения к параметрам использованы текстовые ' выражения, что позволяет улучшить читаемость текста программы. Для создания таких выражений может использоваться только директива EQU, которая; с отличие от директивы =, не позволяет переопределять идентификаторы, однажды определенные в данном модуле. При использовании такого способа описания параметров необходимо задавать несовпадающие мнемонические имена переменных в процедурах ассемблерного модуля или помещать каждую процедуру в отдельный срайл.
Более удобно для использования базовой адресации через регистр ВР для вы полнения доступа к параметрам применение директивы ARG. Если в процедуре используется директива ARG, то Turbo Assembler автоматически определяет смещение указанных в ней параметров относительно содержимого регистра ВР, а также вычисляет размер всего блока параметров для последующего использования в ко манде RET. При использовании этой директивы отпадает необходимость указыва-п. неповторяющиеся имена для обозначения параметров разных процедур.
Предыдущий пример с использованием директивы ARG имеет следующий вид:
CODE SEGMENT
ASSUME cs:CODE I AsmProc PROC FAR;procedure AsmProc (i;j:word);
PUBLIC AsmProc
ARG j:WORD, i:WORD = RetBytes
push bp
mov bp,sp
mov ax,i
Директива Turbo Assembler ARG связывает идентификаторы i и / с передавав мыми в процедуру параметрами. Строка
ARG j:WORD, i:WORD=RetBytes
автоматически назначает идентификатору i значение WORD PTR [BP + б], идентификатору^ — значение WORD PTR [ВР+8], а идентификатору RetByres - значение 4 (общий размер блока параметров в байтах) для дальнейшего использования в процедуре. Указанные значения включают вычисления, необходимые для учета помещения в стек регистра BP и адреса возврата. Если изменить тип процедуры на NEAR, то идентификатор г автоматически приобретет значение [BP + 4], а/ — [BP + G).
Директива ARG должна указывать параметры в порядке, обратном тому, который используется в списке параметров объявления этой процедуры или функции в модуле Borland Pascal. Таким образом, последний параметр процедуры Pascal должен быть первым в директиве ARG ассемблерного модуля и т. д.
Второй особенностью применения директивы ARG в ассемблерных процедурах, предназначенных для вызова из Borland Pascal, является необходимость дополнения байтовых параметров до размера слова. Например, если в программе на Borland Pascal объявлена функция
function AsmProc (i,j:Char):string; external;
то директива ARG в ассемблерном тексте этой функции должна иметь следующий вид:
ARJ j:byte:.2, i:byte:2 = RetBytes RETURNS ResStr: DWORD
Указание ":2" после каждого аргумента заставляет Turbo Assembler генерировать команды для размещения каждого аргумента в стеке в виде слова, старший байт которого имеет неопределенное значение.
Функции, возвращающие значение типа String (подобно указанной в примере), могут использовать необязательный параметр RETURNS директивы ARG, который позволяет применять символьное имя, эквивалентное временному указателю на результат функции. Переменная, указанная в RETURNS, не влияет на суммарный размер блока переменных директивы ARG.
Еще более удобна для разработки ассемблерных процедур, предназначенных для вызова из Borland Pascal, директива .MODEL. Для программ Borland Pascal в качестве модели памяти следует всегда выбирать модель LAR-GE.
Пример ассемблерной функции, в которой используется'директива .MODEL:
.MODEL LARGE.PASCAL •CODE
AsmProc PROC FAR i:byte,j:byte RETURNS result:DWORD PUBLIC AsmProc mov ax,i
ret
Как можно заметить, использование директивы .MODEL позволяет описывать параметры процедуры прямо при ее объявлении директивой PROC, причем параметры в объявлении следуют в том же порядке, в котором они указаны в Pascal- модуле. Кроме того, выбор модели PASCAL приводит к автоматической генерации кодов пролога и эпилога процедуры.
В качестве базового регистра для доступа к параметрам процедуры используются регистры ВХ, SI или DI. Единственная особенность использования этих регистров заключается в том, что все они по умолчанию используют регистр. DS, а не SS. По этой причине при применении регистров ВХ, SJ или DI в качестве базовых необхо-
димо указывать префикс переопределения сегмента или загружать регистр SS нужным значением.
Пример использования регистра ВХ в качестве базового:
CODE SEGMENT
ASSUME cs:CODE AsmProc PROC FAR
iprocedure AsmProc (i,j: integer);
PUBLIC AsmProc j EQU WORD PTR ss:[bx+4] i EQU WORD PTR ss:[bx+6]
mov bp.sp
mov ax,i
Такой подход оправдан в случае применения небольших по размеру процедур,
поскольку при использовании регистра ВХ нет необходимости в сохранении и восстановлении его содержимого, как для регистра ВР.