 |
@testSqfd endp
Использование Extern "C" для упрощения компоновки
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
При желании вы можете использовать в ассемблерных функциях
неисправленные имена, не пытаясь выяснить, как должны выглядеть
правленные. Использование нескорректированных имен защитит ваши
ассемблерные функции от возможных изменений алгоритма в будущем.
Borland С++ позволяет определять в программах С++ стандартные
имена функций С++, как в следующем примере:
extern "C" {
int add(int *a, int b);
}
Любые функции, объявленные внутри фигурных скобок, получат
имена в стиле языка Си. Ниже показаны соответствующие определения
в ассемблерном модуле:
public _add
_add proc
Объявление ассемблерной функции в блоке extern "C" позволит
вам избежать проблем со "откорректированными именами". При этом
улучшится и читаемость кода.
Турбо Ассемблер 3.0/tasm/#2-2 = 79 =
Модели памяти и сегменты
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
Чтобы данная функция Ассемблера могла могла вызываться из
С++, она должна использовать ту же модель памяти, что и программа
на языке С++, а также совместимый с С++ сегмент кода. Аналогично,
чтобы данные, определенные в модуле Ассемблера, были доступны в
программе на языке С++ (или данные С++ были доступны в программе
Ассемблера), в программе на Ассемблере должны соблюдаться согла-
шения языка С++ по наименованию сегмента данных.
Модели памяти и обработку сегментов на Ассемблере может ока-
заться реализовать довольно сложно. К счастью, Турбо Ассемблер
сам выполняет почти всю работу по реализации моделей памяти и
сегментов, совместимых с Borland C++, при использовании упрощен-
ных директив определения сегментов.
Упрощенные директивы определения сегментов и Borland C++
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
Директива .MODEL указывает Турбо Ассемблеру, что сегменты,
создаваемые с помощью упрощенных директив определения сегментов,
должны быть совместимы с выбранной моделью памяти (TINY - крохот-
ной, SMALL - малой, COMPACT - компактной, MEDIUM - средней,
LARGEбольшой или HUGE - громадной) и управляет назначаемым по
умолчанию типом (FAR или NEAR) процедур, создаваемых по директиве
PROC. Модели памяти, определенные с помощью директивы .MODEL,
совместимы с моделями Borland C++ с соответствующими именами.
Наконец, упрощенные директивы определения сегментов .DATA,
.CODE, .DATA?, .FARDATA, .FARDATA? и .CONST генерируют сегменты,
совместимые с Borland C++.
Например, рассмотрим следующий модуль Турбо Ассемблера с
именем DOTOTAL.ASM:
.MODEL SMALL ; выбрать малую модель памяти
; (ближний код и данные)
.DATA ; инициализация сегмента данных,
; совместимого с Borland C++
EXTRN _Repetitions:WORD ; внешний идентификатор
PUBLIC _StartingValue ; доступен для других модулей
_StartValue DW 0
.DATA? ; инициализированный сегмент
; данных, совместимый с Borland C++
RunningTotal DW ?
.CODE ; сегмент кода, совместимый с
; Borland C++
PUBLIC _DoTotal
_DoTotal PROC ; функция (в малой модели памяти
; вызывается с помощью вызова
; ближнего типа)
mov cx,[_Repetitions] ; счетчик выполнения
Турбо Ассемблер 3.0/tasm/#2-2 = 80 =
mov ax,[_StartValue]
mov [RunningTotal],ax ; задать начальное
; значение
TotalLoop:
inc [RunningTotal] ; RunningTotal++
loop TotalLoop
mov ax,[RunningTotal] ; возвратить конечное
; значение (результат)
ret
_DoTotal ENDP
END
Написанная на Ассемблере процедура _DoTotal при использова-
нии малой модели памяти может вызываться из Borland C++ с помощью
оператора:
DoTotal();
Заметим, что в процедуре DoTotal предполагается, что где-то
в другой части программы определена внешняя переменная
Repetitions. Аналогично, переменная StartingValue объявлена, как
общедоступная, поэтому она доступна в других частях программы.
Следующий модуль Borland C++ (который называется SHOWTOT.CPP) об-
ращается к данным в DOTOTAL.ASM и обеспечивает для модуля
DOTOTAL.ASM внешние данные:
extern int StartingValue;
extern int DoTotal(word);
int Repetitions;
main()
{
int i;
Repetitions = 10;
StartingValue = 2;
print("%d\n", DoTotal());
}
Чтобы создать из модулей DOTOTAL.ASM и SHOWTOT.CPP выполняе-
мую программу SHOWTOT.EXE, введите команду:
bcc showtot.cpp dototal.asm
Если бы вы захотели скомпоновать процедуру _DoTotal с прог-
раммой на языке C++, использующей компактную модель памяти, то
пришлось бы просто заменить директиву .MODEL на .MODEL COMPACT, а
если бы вам потребовалось использовать в DOTATOL.ASM сегмент
дальнего типа, то можно было бы использовать директиву .FARDATA.
Короче говоря, при использовании упрощенных директив опреде-
ления сегментов генерация корректного упорядочивания сегментов,
моделей памяти и имен сегментов труда не составляет.
Турбо Ассемблер 3.0/tasm/#2-2 = 81 =
Старые директивы определения сегментов и Borland C++
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
Коснемся теперь проблемы организации интерфейса Турбо Ассем-
блера с кодом языка С++, где используются директивы определения
сегментов старого типа (стандартные директивы определения сегмен-
тов). Например, если вы замените в модуле DOTOTAL.ASM упрощенные
директивы определения сегментов директивами старого типа, то по-
лучите следующее:
DGROUP group _DATA,_BSS
_DATA segment word public 'DATA'
EXTRN _Repetitions:WORD ; внешний идентификатор
PUBLIC _StartingValue ; доступен для других модулей
_StartValue DW 0
_DATA ends
_BSS segment word public 'BSS'
RunningTotal DW ?
_BSS ends
_TEXT segment byte public 'CODE'
assume cs:_TEXT.ds:DGROUP,ss:DGROUP
PUBLIC _DoTotal
_DoTotal PROC ; функция (в малой модели памяти
; вызывается с помощью вызова
; ближнего типа)
mov cx,[_Repetitions] ; счетчик выполнения
mov ax,[_StartValue]
mov [RunningTotal],ax ; задать начальное
; значение
TotalLoop:
inc [RunningTotal] ; RunningTotal++
loop TotalLoop
mov ax,[RunningTotal] ; возвратить конечное
; значение (результат)
ret
_DoTotal ENDP
_TEXT ENDS
END
Данная версия директив определения сегментов не только длин-
нее, то также и хуже читается. К тому же при использовании в
программе на языке С++ различных моделей памяти ее труднее изме-
нять. При организации интерфейса с Borland C++ в общем случае в
использовании старых директив определения сегментов нет никаких
преимуществ. Если же вы тем не менее захотите использовать при
организации интерфейса с Borland C++ старые директивы определения
сегментов, вам придется идентифицировать корректные сегменты, со-
ответствующие используемым в коде на языке С++ моделям памяти.
Простейший способ определения, какие сегментные директивы
старых версий должны выбираться для компоновки с той или иной
программой Borland С++, заключается в компиляции главного модуля
программы на Borland С++ для желаемой модели памяти с параметром
-S, что тем самым заставит Borland С++ сгенерировать ассемблерную
Турбо Ассемблер 3.0/tasm/#2-2 = 82 =
версию соответствующей программы на Borland С++. В этой версии
кодов Си вы сможете найти все старые сегментные директивы, ис-
|