 |
тов, то описания идентификаторов EXTRN в сегментах дальнего типа
не должны размещаться ни в каком сегменте, так как Турбо Ассем-
блер рассматривает идентификаторы, описанные в данном сегменте,
как связанные с данным сегментом. Это имеет свои недостатки: Тур-
бо Ассемблер не может проверить возможность адресации к идентифи-
катору, описанному, как внешний (EXTRN), вне любого сегмента и
поэтому не может в случае необходимости сгенерировать определе-
ние сегмента или сообщить вам, что была попытка обратиться к дан-
ной переменной, когда сегмент не был загружен корректным значени-
ем. Тем не менее Турбо Ассемблер генерирует для ссылок на такие
внешние идентификаторы правильный код, но не может обеспечить
обычную степень проверки возможности адресации к сегменту.
Если вы все-таки захотите, то можно использовать для явного
описания каждого внешнего идентификатора сегмента старые директи-
вы определения сегментов, а затем поместить директиву EXTRN для
этого идентификатора внутрь описания сегмента. Это довольно уто-
мительно, поэтому если вы не хотите обеспечивать загрузку коррек-
тного значения сегмента при обращении к данным, то проще всего
просто разместить описания EXTRN для идентификаторов дальнего
типа вне всех сегментов. Предположим, например, что файл
FILE1.ASM содержит следующее:
.
.
.
Турбо Ассемблер 3.0/tasm/#2-2 = 91 =
.FARDATA
FileVariable DB 0
.
.
.
и он компонуется с файлом FILE2.ASM, который содержит:
.
.
.
.DATA
EXTRN FileVariable:BYTE
.CODE
Start PROC
mov ax,SEG FileVariable
mov ds,ax
.
.
.
SEG FileVariable не будет возвращать корректного значения
сегмента. Директива EXTRN размещена в области действия директивы
файла FILE2.ASM DATA, поэтому Турбо Ассемблер считает, что пере-
менная FileVariable должна находиться в ближнем сегменте DATA
файла FILE2.ASM, а не в дальнем сегмента DATA.
В следующем коде FILE2.ASM SEG FileVariable будет возвращать
корректное значение сегмента:
.
.
.
.DATA
@CurSeg ENDS
EXTRN FileVariable:BYTE
.CODE
Start PROC
mov ax,SEG FileVariable
mov ds,ax
.
.
.
"Фокус" здесь состоит в том, что директива @CurSeg ENDS за-
вершает сегмент .DATA, поэтому, когда переменная FileVariable
описывается, как внешняя, никакая сегментная директива не дейс-
твует.
Командная строка компоновщика
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
Простейший способ скомпоновать модули Borland C++ с модулями
Турбо Ассемблер 3.0/tasm/#2-2 = 92 =
Турбо Ассемблера состоит в том, чтобы ввести одну командную стро-
ку Borland C++, после чего он выполнит всю остальную работу. При
задании нужной командной строки Borland C++ выполнит компиляцию
исходного кода Си, вызовет Турбо Ассемблер для ассемблирования, а
затем вызовет утилиту TLINK для компоновки объектных файлов в вы-
полняемый файл. Предположим, например, что у вас есть программа,
состоящая из файлов на языке Си MAIN.CPP и STAT.CPP и файлов Ас-
семблера SUMM.ASM и DISPLAY.ASM. Командная строка:
bcc main.cpp stat.cpp summ.asm display.asm
выполняет компиляцию файлов MAIN.CPP и STAT.CPP, ассемблирование
файлов SUMM.ASM и DISPLAY.ASM и компоновку всех четырех объектных
файлов, а также кода инициализации С++ и необходимых библиотечных
функций в выполняемый файл MAIN.EXE. При вводе имен файлов Ас-
семблера нужно только помнить о расширениях .ASM.
Если вы используете утилиту TLINK в автономном режиме, то
генерируемые Турбо Ассемблером объектные файлы представляют собой
стандартные объектные модули и обрабатываются также, как объек-
тные модули С++. Описание TLINK в автономном режиме см. в Прило-
жении С.
Турбо Ассемблер 3.0/tasm/#2-2 = 93 =
Взаимодействие между Турбо Ассемблером и Borland C++
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
Теперь, когда вы понимаете, как нужно строить и компоновать
совместимые с С++ модули Ассемблера, нужно знать, какой код можно
помещать в функции Ассемблера, вызываемые из С++. Здесь нужно
проанализировать три момента: получение передаваемых параметров,
использование регистров и возврат значений в вызывающую програм-
му.
Передача параметров
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
Borland C++ передает функциям параметры через стек. Перед
вызовом функции С++ сначала заносит передаваемые этой функции па-
раметры, начиная с самого правого параметра и кончая левым, в
стек. В С++ вызов функции:
.
.
.
Test(i, j, 1);
.
.
.
компилируется в инструкции:
mov ax,1
push ax
push word ptr DGROUP:_j
push word ptr DGROUP:_i
call near ptr _Test
add sp,6
где видно, что правый параметр (значение 1), заносится в стек
первым, затем туда заносится параметр j и, наконец, i.
При возврате из функции занесенные в стек параметры все еще
находятся там, но они больше не используются. Поэтому непосредс-
твенно после каждого вызова функции Borland C++ настраивает ука-
затель стека обратно в соответствии со значением, которое он имел
перед занесением в стек параметров (параметры, таким образом, от-
брасываются). В предыдущем примере три параметра (по два байта
каждый) занимают в стеке вместе 6 байт, поэтому Borland C++ до-
бавляет значение 6 к указателю стека, чтобы отбросить параметры
после обращения к функции Test. Важный момент здесь заключается в
том, что в соответствии с используемыми по умолчанию соглашениями
Си/C++ за удаление параметров из стека отвечает вызывающая прог-
рамма.
Функции Ассемблера могут обращаться к параметрам, передавае-
мым в стеке, относительно регистра BP. Например, предположим, что
Турбо Ассемблер 3.0/tasm/#2-2 = 94 =
функция Test в предыдущем примере представляет собой следующую
функцию на Ассемблере (PRMSTACK.ASM):
.MODEL SMALL
.CODE
PUBLIC _Test
_Test PROC
push bp
mov bp,sp
mov ax,[bp+4] ; получить параметр 1
add ax,[bp+6] ; прибавить параметр 2
; к параметру 1
sub ax,[bp+8] ; вычесть из суммы 3
pop bp
ret
_Test ENDP
Как можно видеть, функция Test получает передаваемые из
программы на языке Си параметры через стек, относительно регистра
BP. (Если вы помните, BP адресуется к сегменту стека.) Но откуда
она знает, где найти параметры относительно BP?
На Рис. 18.2 показано, как выглядит стек перед выполнением
первой инструкции в функции Test:
i = 25;
j = 4;
Test(1, j, 1);
. .
. .
. .
|