Связь и интернет Архив Программирование
   
Сделать стартовойСделать закладку            
   ПОИСК  
   
Главная / Турбо Ассемблер / Использование Турбо Ассемблера /
8  Perl
8  PHP
8  JavaScript
8  HTML
8  DHTML
8  XML
8  CSS
8  C / C++
8  Pascal и Delphi
8  Турбо Ассемблер
8  MySQL
8  CASE-технологии
8  Алгоритмы
8  Python
8  Обратная связь
8  Гостевая книга
Новости о мире


Интерфейс Турбо Ассемблера и Borland C++ - Программирование от RIN.RU
Интерфейс Турбо Ассемблера и Borland C++



Старые директивы определения сегментов и 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 С++ сгенерировать ассемблерную версию соответствующей программы на Borland С++. В этой версии кодов Си вы сможете найти все старые сегментные директивы, используемые Турбо Cи; просто скопируйте их в вашу ассемблерную часть программы.


Вы также можете посмотреть, как будут выглядеть соответствующие старые директивы, скомпилировав их обычным образом (без параметра -S) и использовав TDUMP - утилиту, поставляемую Турбо Ассемблером, чтобы получить все записи определения сегмента. Используйте следующую командную строку:


tdump -OI segdef module.obj


Значения по умолчанию: когда необходимо загружать сегменты?


В некоторых случаях вызываемые из языка С++ функции Ассемблера могут использовать (загружать) для обращения к данным регистры DS и/или ES. Полезно знать соотношение между значениями сегментных регистров при вызове из Borland C++, так как иногда Ассемблер использует преимущества эквивалентности двух сегментных регистров. Давайте рассмотрим значения сегментных регистров в тот момент, когда функция Ассемблера вызывается из Borland C++, а также соотношения между сегментными регистрами, и случаи, когда в функции Ассемблера требуется загружать один или более сегментных регистров.


При входе в функцию Ассемблера из Borland C++ регистры CS и DS имеют следующие значения, которые зависят от используемой модели памяти (регистр SS всегда используется для сегмента стека, а ES всегда используется, как начальный сегментный регистр):


Значения регистров при входе в Ассемблер из Borland C++
МодельCSDS
Крохотная_TEXTDGROUP
Малая_TEXTDGROUP
Компактная_TEXTDGROUP
Средняя имя_файла_TEXT DGROUP
Большая имя_файла_TEXT DGROUP
Громаднаяимя_файла_TEXT имя_вызывающего_файла_DATA



Здесь "имя_файла" - это имя модуля на Ассемблере, а "имя_вызывающего_файла" - это имя модуля Borland C++, вызывающего модуль на Ассемблере.


В крохотной модели памяти _TEXT и DGROUP совпадают, поэтому при входе в функцию содержимое регистра CS равно содержимому DS. При использовании крохотной, малой и компактной модели памяти при входе в функцию содержимое SS равно содержимому регистра DS.


Когда же в функции на Ассемблере, вызываемой из программы на языке С++, необходимо загружать сегментный регистр? Отметим для начала, что вам никогда не придется (более того, этого не следует делать) загружать регистры SS или CS: при дальних вызовах, переходах или возвратах регистр CS автоматически устанавливается в нужное значение, а регистр SS всегда указывает на сегмент стека и в ходе выполнения программы изменять его не следует (если только вы не пишете программу, которая "переключает" стеки. В этом случае вам нужно четко понимать, что вы делаете).


Регистр ES вы можете всегда использовать так, как это требуется. Вы можете установить его таким образом, чтобы он указывал на данные с дальним типом обращения, или загрузить в ES сегмент-приемник для строковой функции.


С регистром DS дело обстоит иначе. Во всех моделях памяти Borland C++, кроме сверхбольшой, регистр DS при входе в функцию указывает на статический сегмент данных (DGROUP), и изменять его не следует. Для доступа к данным с дальним типом обращения всегда можно использовать регистр ES, хотя вы можете посчитать, что для этого временно нужно использовать регистр DS (если вы собираетесь осуществлять интенсивный доступ к данным), что исключит необходимость использования в вашей программе множества инструкций с префиксом переопределения сегмента. Например, вы можете обратиться к дальнему сегменту одним из следующих способов:


.
.
.
.FARDATA
Counter DW 0
.
.
.
.CODE
PUBLIC _AsmFunction
_AsmFunction PROC
.
.
.
mov ax,@FarData
mov es,ax ; ES указывает на сегмент данных с дальним типом обращения
inc es:[Counter] ; увеличить значение счетчика
.
.
.
_AsmFunction ENDP
.
.
.


или иначе:


.
.
.
.FARDATA
Counter DW 0
.
.
.
.CODE
PUBLIC _AsmFunction
_AsmFunction PROC
.
.
.
assume ds:@FarData
mov ax,@FarDAta
mov ds,ax ; DS указывает на сегмент данных с дальним типом обращения
inc [Counter] ; увеличить значение счетчика
assume ds:@Data
mov ax,@Data
mov dx,ax ; DS снова указывает на DGROUP
.
.
.
_AsmFunction ENDP
.
.
.


Второй вариант имеет то преимущество, что при каждом обращении к дальнему сегменту данных в нем не требуется переопределение ES:. Если для обращения к дальнему сегменту вы загружаете регистр DS, убедитесь в том, что перед обращением к другим переменным DGROUP вы его восстанавливаете (как это делается в приведенном примере). Даже если в данной функции на Ассемблере вы не обращаетесь к DGROUP, перед выходом из нее все равно обязательно нужно восстановить содержимое DS, так как в Borland C++ подразумевается, что регистр DS не изменялся.


При использовании в функциях, вызываемых из С++, сверхбольшой модели памяти работать с регистром DS нужно несколько по-другому. В сверхбольшой модели памяти Borland C++ совсем не использует DGROUP. Вместо этого каждый модуль имеет свой собственный сегмент данных, который является дальним сегментом относительно всех других модулей в программе (нет совместно используемого ближнего сегмента данных). При использовании сверхбольшой модели памяти на входе в функцию регистр DS должен быть установлен таким образом, чтобы он указывал на этот дальний сегмент данных модуля и не изменялся до конца функции, например:


.
.
.
.FARDATA
.
.
.
.CODE
PUBLIC _AsmFunction
_AsmFunction PROC
push ds
mov ax,@FarData
mov ds,ax
.
.
.
pop ds
ret
_AsmFunction ENDP
.
.
.


Заметим, что исходное состояние регистра DS сохраняется при входе в функцию _AsmFunction с помощью инструкции PUSH и перед выходом восстанавливается с помощью инструкции POP. Даже в сверхбольшой модели памяти Borland C++ требует, чтобы все функции сохраняли регистр DS.


Общедоступные и внешние идентификаторы


Программы Турбо Ассемблера могут вызывать функции С++ и ссылаться на внешние переменные Си. Программы Borland C++ аналогичным образом могут вызывать общедоступные (PUBLIC) функции Турбо Ассемблера и обращаться к переменным Турбо Ассемблера. После того, как в Турбо Ассемблере устанавливаются совместимые с Borland C++ сегменты (как описано в предыдущих разделах), чтобы совместно использовать функции и переменные Borland C++ и Турбо Ассемблера, нужно соблюдать несколько простых правил.


Подчеркивания и язык Си


Если вы пишете на языке Си или С++, то все внешние метки должны начинаться с символа подчеркивания (_). Компилятор Си и С++ вставляет символы подчеркивания перед всеми именами внешних функций и переменных при их использовании в программе на Си/С++ автоматически, поэтому вам требуется вставить их самим только в ассемблерных кодах. Вы должны убедиться, что все ассемблерные обращения к функциям и переменным Си начинаются с символа подчеркивания, и кроме того, вы должны вставить его перед именами всех ассемблерных функций и переменных, которые делаются общими и вызываются из программы на языке Си/С++.


Например, следующая программа на языке Си (link2asm.cpp):


extrn int ToggleFlag();
int Flag;
main()
{
ToggleFlag();
}


правильно компонуется со следующей программой на Ассемблере (CASMLINK.ASM):


.MODEL SMALL
.DATA
EXTRN _Flag:word
.CODE
PUBLIC _ToggleFlag
_ToggleFlag PROC
cmp [_Flag],0 ; флаг сброшен?
jz SetFlag ; да, установить его
mov [_Flag],0 ; нет, сбросить его
jmp short EndToggleFlag ; выполнено
SetFlag:
mov [_Flag],1 ; установить флаг
EndToggleFlag:
ret
_ToggleFlag ENDP
END


При использовании в директивах EXTERN и PUBLIC спецификатора языка Си правильно компонуется со следующей программой на Ассемблере (CSPEC.ASM):


.MODEL SMALL
.DATA
EXTRN C Flag:word
.CODE
PUBLIC C ToggleFlag
ToggleFlag PROC
cmp [Flag],0 ; флаг сброшен?
jz SetFlag ; да, установить его
mov [Flag],0 ; нет, сбросить его
jmp short EndToggleFlag ; выполнено
SetFlag:
mov [Flag],1 ; установить флаг
EndToggleFlag:
ret
ToggleFlag ENDP
END


Примечание: Метки, на которые отсутствуют ссылки в программе не Си (такие, как SetFlag) не требуют предшествующих символов подчеркивания.


Турбо Ассемблер автоматически при записи имен Flag и ToggleFlag в объектный файл поместит перед ними символ подчеркивания.


Различимость строчных и прописные символов в идентификаторах


В именах идентификаторов Турбо Ассемблер обычно не различает строчные и прописные буквы (верхний и нижний регистр). Поскольку в С++ они различаются, желательно задать такое различие и в Турбо Ассемблере (по крайней мере для тех идентификаторов, которые совместно используются Ассемблером и С++). Это можно сделать с помощью параметров /ML и /MX.


Переключатель (параметр) командной строки /ML приводит к тому, что в Турбо Ассемблере во всех идентификаторах строчные и прописные символы будут различаться (считаться различными). Параметр командной строки /MX указывает Турбо Ассемблеру, что строчные и прописные символы (символы верхнего и нижнего регистра) нужно различать в общедоступных (PUBLIC) идентификаторах, внешних (EXTRN) идентификаторах глобальных (GLOBAL) идентификаторах и общих (COMM) идентификаторах. В большинстве случаев следует также использовать параметр /ML.


<<<  НазадВперед  >>>
 1  2  3  4  5  6  7  8  9 


 8  Комментарии к статье  8 8  Обсудить в чате

8  В тему

Начало работы на Турбо Ассемблере

Использование директив и параметров

Общие принципы программирования

Объектно-ориентированное программирование

Использование выражений и значений идентификаторов

Директивы выбора процессора

Использование моделей памяти программы и сегментации

Определение типов данных

Задание и использование счетчика адреса

Описание процедур

Управление областью действия идентификаторов

Определение данных

Расширенные инструкции

Использование макрокоманд

Использование условных директив

Интерфейс с компоновщиком

Генерация листинга

Интерфейс Турбо Ассемблера с Турбо Паскалем

 
  
  
    Copyright ©  RIN 2003 - 2004      * Обратная связь