Связь и интернет Архив Программирование
   
Сделать стартовойСделать закладку            
   ПОИСК  
   
Главная / Турбо Ассемблер / Использование Турбо Ассемблера /
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++ вызываемые из программы на языке С++ функции Ассемблера могут делать все что угодно, но при этом они должны сохранять регистры BP, SP, CS, DS и SS. Хотя при выполнении функции Ассемблера эти регистры можно изменять, при возврате из вызываемой подпрограммы они должны иметь в точности такие значения, какие они имели при ее вызове.


Регистры AX, BX, CX, DX и ES, а также флаги могут произвольно изменяться.


Регистры DI и SI представляют собой особый случай, так как в Borland C++ они используются для регистровых переменных. Если в модуле С++, из которого вызывается ваша функция на Ассемблере, использование регистровых переменных разрешено, то вы должны сохранить регистры SI и DI, если же нет, то сохранять их не нужно.


Однако неплохо всегда сохранять эти регистры, независимо от того, разрешено или запрещено использование регистровых переменных. Трудно заранее гарантировать, что вам не придется компоновать данный модуль Ассемблера с другим модулем на языке С++, или перекомпилировать модуль С++ с разрешением использования регистровых переменных. При этом вы можете забыть, что изменения нужно также внести и в код Ассемблера.


Возврат значений


Вызываемые из программы на языке С++ функции на Ассемблере, так же как и функции С++, могут возвращать значения. Значения функций возвращаются следующим образом:


Тип возвращаемого значенияГде находится возвращаемое значение
unsigned charAX
charAX
enumAX
unsigned shortAX
shortAX
unsigned intAX
intAX
unsigned longDX:AX
longDX:AX
float регистр вершины стека сопроцессора 8087 (ST(0))
doubleрегистр вершины стека сопроцессора 8087 (ST(0))
long doubleрегистр вершины стека сопроцессора 8087 (ST(0))
near* AX
far*DX:AX



В общем случае 8- и 16-битовые значения возвращаются в регистре AX, а 32-битовые значения - в AX:DX (при этом старшие 16 бит значения находятся в регистре DX). Значения с плавающей точкой возвращаются в регистре ST(0), который представляет собой регистр вершины стека сопроцессора 8087 или эмулятора сопроцессора 8087, если используется эмулятор операций с плавающей точкой.


Со структурами дело обстоит несколько сложнее. Структуры, имеющие длину 1 или 2 байта, возвращаются в регистре AX, а структуры длиной 4 байта - в регистрах AX:DX. Трехбайтовые структуры и структуры, превышающие 4 байта должны храниться в области статических данных, при этом должен возвращаться указатель на эти статические данные. Как и все указатели, указатели на структуры, которые имеют ближний тип (NEAR), возвращаются в регистре AX, а указатели дальнего типа - в паре регистров AX:DX.


Давайте рассмотрим вызываемую из программы на языке С++ функцию на Ассемблере с малой моделью памяти FindLastChar, которая возвращает указатель на последний символ передаваемой строки. На языке С++ прототип этой функции выглядел бы следующим образом:


extern char * FindLastChar(char * StringToScan);


где StringToScan - это непустая строка, для которой должен возвращаться указатель на последний символ.


Функция FindLastChar имеет следующий вид:


.MODEL SMALL
.CODE
PUBLIC _FindLastChar
_FindLastChar PROC
push bp
mov bp,sp
cld ; в строковой инструкции нужно
; выполнять отсчет в прямом направлении
mov ax,ds
mov es,ax ; теперь ES указывает на ближний сегмент данных
mov di, ; теперь ES:DI указывает на начало передаваемой строки
mov al,0 ; найти нулевой символ, завершающий строку
mov cx,0ffffh ; работать в пределах 64К-1 байт
repne scasb ; найти нулевой символ
dec di ; установить указатель обратно на 0
dec di ; ссылка обратно на последний символ
mov ax,dx ; возвратить в AX указатель ближнего типа
pop bp
ret
_FindLastChar ENDP
END


Конечный результат, указатель на передаваемую строку, возвращается в регистре AX.


Вызов функции Турбо Ассемблера из Borland C++


Теперь мы рассмотрим пример программы на Borland C++, вызывающей функцию Турбо Ассемблера. Модуль Турбо Ассемблера COUNT.ASM содержит функцию LineCount, которая возвращает значение счетчика числа строк и символов в передаваемой строке:


; Вызываемая из С++ функция на Ассемблере с малой моделью
; памяти для подсчета числа строк и символов в завершающейся нулем
; "строке".
;
; Прототип функции:
; extern unsigned int LineCount(char * near StringToCount,
; unsigned int near * CharacterCountPtr);
;
; Ввод:
; char near * StringToCount: указатель на "строку", в
; которой нужно выполнить подсчет строк.
;
; unsigned int near * CharacterCountPtr: указатель на
; целую переменную, в которую нужно записать значение
; счетчика
NEWLINE EQU 0ah ; символ перевода строки в Си
.MODEL SMALL
.CODE
PUBLIC _LineCount
__LineCount PROC
push bp
mov bp,sp
push si ; сохранить регистровую
; переменную вызывающей программы
mov si,[bp+4] ; SI указывает на строку
sub cx,cx ; установить значение счетчика символов в 0
mov dx,cx ; установить в 0 счетчик строк
LineCountLoop:
lodsb ; получить следующий символ
and al,al ; это 0? конец строки?
jz EndLineCount ; да, выполнено
inc cx ; нет, подсчитать следующий символ
cmp al,NEWLINE ; это новая строка?
jnz LineCountLoop ; нет, проверить следующий символ
inc dx ; да, подсчитать еще одну строку
jmp LineCountLoop
EndLineCount:
inc dx ; подсчитать строку, которая завершается нулевым символом
mov [bx],cx ; задать значение переменной-счетчика
mov ax,dx ; возвратить счетчик строк в качестве значения счетчика
pop si ; восстановить регистровую переменную вызывающей программы
pop bp
ret
_LineCount ENDP
END


Следующий модуль на языке С++ с именем CALLCT.CPP представляет собой пример вызова функции LineCount:


char * TestString="Line 1\nline 2\nline 3";
extern "C" {
unsigned int LineCount(char * StringToCount,
unsigned int near * CharacterCountPtr); }
main()
{
unsigned int LCount;
unsigned int CCount;


Lcount = LineCount(TestString, &CCount);
printf("Lines: %d\nCharacters: %d\n", LCount, CCount);
}


Два модуля компилируются и компонуются вместе с помощью командной строки:


bcc -ms callct.cpp count.asm


Как здесь показано, функция LineCount будет работать только при компоновке с программами на языке С++, в которых используется малая модель памяти, так как в других моделях размеры указателей и адресов в стеке изменятся. Приведем пример версии функции LineCount (COUNTLG.ASM), которая будет работать с программами на С++, использующим большую модель памяти (но не малую модель: поскольку передаются дальние указатель, функция LineCount также описана, как функция дальнего типа):


; Вызываемая из С++ функция на Ассемблере для подсчета числа
; строк и символов в завершающейся нулем "строке".
;
; Прототип функции:
; extern unsigned int LineCount(char * far StringToCount,
; unsigned int far * CharacterCountPtr);
;
; Ввод:
; char far * StringToCount: указатель на "строку", в
; которой нужно выполнить подсчет строк.
;
; unsigned int far * CharacterCountPtr: указатель на
; целочисленную переменную, в которую нужно записать
; значение счетчика
NEWLINE EQU 0ah ; символ перевода строки в Си
.MODEL LARGE
.CODE
PUBLIC _LinaCount
_LineCount PROC
push bp
mov bp,sp
push si ; сохранить регистровую переменную вызывающей программы
push ds ; сохранить стандартный сегмент данных
lds si,[bp+6] ; DS:SI указывает на строку
sub cx,cx ; установить значение счетчика символов в 0
mov dx,cx ; установить в 0 счетчик строк
LineCountLoop:
lodsb ; получить следующий символ
and al,al ; это 0? конец строки?
jz EndLineCount ; да, выполнено
inc cx ; нет, подсчитать следующий символ
cmp al,NEWLINE ; это новая строка?
jnz LineCountLoop ; нет, проверить следующий символ
inc dx ; да, подсчитать еще одну строку
jmp LineCountLoop
EndLineCount:
inc dx ; подсчитать строку, которая завершается нулевым символом
les bx,[bp+10] ; ES:BX указывает на ячейку, в которой возвращается значение счетчика
mov es:[bx],cx ; задать значение переменной-счетчика
mov ax,dx ; возвратить счетчик строк в качестве значения счетчика
pop ds ; восстановить стандартный сегмент данных Си
pop si ; восстановить регистровую переменную вызывающей программы
pop bp
ret
_LineCount ENDP
END


Программу COUNTLG.ASM можно скомпоновать с CALLCT.CPP с помощью следующей командной строки:


bcc -ml callct.cpp countlg.asm


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


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

8  В тему

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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