ASM_R4 (Взаимосвязь языков C и ассемблера)
Описание файла
Документ из архива "Взаимосвязь языков C и ассемблера", который расположен в категории "". Всё это находится в предмете "информатика" из , которые можно найти в файловом архиве . Не смотря на прямую связь этого архива с , его также можно найти и в других разделах. Архив можно найти в разделе "рефераты, доклады и презентации", в предмете "информатика, программирование" в общих файлах.
Онлайн просмотр документа "ASM_R4"
Текст из документа "ASM_R4"
Раздел 4: Вызов функций С из языка ассемблера
4.1 Вызов С и С++ из ассемблера
До сих пор рассматривалось, как разделить переменные между С или C++ и ассемблером, а также как вызывать внешние ассемблерные функции из программ, написанных на С или C++. Теперь подойдем к этому с другой стороны, т.е. к вызову функций С или C++ из ассемблерного модуля - это также возможно, но требует большего внимания.
Если функция не обладает параметрами, процесс достаточно прост. Надо объявить функцию С или C++ в директиве EXTRN и воспользоваться инструкцией call:
CODESEG
EXTRN _cfuntion:proc
......
call _cfunction
Здесь предполагается, что функция, названная _cfunction, существует в программе, подлежащей компоновке вместе с ассемблерным модулем.
Если функции требуются параметры, процесс усложняется. Простые параметры, такие как символы и целые числа часто передаются непосредственно в стек. Сложные переменные, такие как строки, структуры и множества, передаются посредством ссылок, т.е. по адресу. Кроме того, многие функции возвращают результат в специфичные регистры. При вызове функций С или C++ из языка ассемблера надо самим позаботиться о подобных нюансах.
Сперва рассмотрим простейший случай вызова функции с одним целочисленным параметром:
void showscore( int thescore)
{
printf(“\nThe score is: %d\n, thescore);
}
Чтобы вызвать функцию showscore из ассемблерного модуля, передавая значение переменной типа слова в качестве thescore, можно написать:
CODESEG
EXTRN showscore: proc
mov ах, 76 ; Присвоение score регистру
push ах ; Передача параметра в стек
call _showscore ; Вызов функции С
pop ах ; Фиксация стека
Прежде всего, значение score присваивается ах (любой другой регистр точно так же подойдет для этого), а затем выталкивается в стек перед вызовом showscore. После возврата из функции слово выталкивается из стека. Это необходимо потому, что в С и C++ вызывающая программа удаляет параметры из стека. Если имеется несколько параметров, может быть, будет лучше просто прибавить их общее число байтов к sp. Например, чтобы вызвать функцию, которая оперирует четырьмя 16-битовыми параметрами, можно воспользоваться следующими командами:
push [vl] ; Выталкивание четырех переменных
push [v2] ; (не показанных) в стек
push [v3]
push [v4]
call _aCfunction ; Вызов функции С
add sp, 8 ; Удаление параметров
Выталкивание нескольких параметров осуществляется в порядке, обратном тому, в каком они объявляются функции С или C++. Исходя из предположения, что функция fillstring определена как
void fillstring( unsigned char far *thestring, int stringLength, char fillchar );
для вызова этой функции из языка ассемблера и заполнения строковой переменной пробелами требуются несколько шагов. Сперва ассемблерный модуль объявляется строковой переменной:
DATASEG
PUBLIC _astring
_astring db 80 dup (0)
Затем этот же модуль объявляет fillstring в директиве EXTRN и вызывает функцию для заполнения строчной переменной пробелами:
CODESEG
EXTRN _fillstring: ргос
xor ah, ah ; Обнуление ст. половины ах
mov al,’ ‘ ; Присвоение пробела а1
push ах ; Проталкивание пар-ра fillchar
mov ах, 79 ; Присвоение длины строки ах
push ах ;Проталкивание пар-ра дл. строки
push ds ;Проталкивание сег-та адреса строки
mov ах, offset _astring ;Присвоение смещения адреса ах
push ах ;Проталкивание смещ. адреса строки
call _fillstring ; Вызов функции
add sp, 8 ; Удаление параметров из стека
Каждый из параметров - заполняющий символ, длина строки и 32-битовый указатель строковой переменной- проталкивается в стек в порядке, обратном перечисленному в определении функции. Применительно к указателю - сегмент адреса проталкивается перед смещением. После обращения к _fillstring к указателю стека sp добавляются 8 байт, удаляя параметры из стека.
Несмотря на то что в этом примере функция _fillstring в действительности написана на языке ассемблера, вызовы функций С и C++ ничем не отличаются.
4.2 Локальные переменные
В дополнение к переменным, объявленным в сегменте данных либо общим с программой С и С++, можно использовать локальные переменные, помещенные в стек создаваемых ассемблерных модулей. Локальные переменные существуют только во время выполнения функции. Стековая часть создается для переменных при запуске функции, а затем очищается перед ее завершением. Таким образом, другие функции могут использовать эти же области памяти для своих собственных локальных переменных, снижая общий объем памяти, требуемой для всей программы. Например:
void countup()
{
int i;
for (i = 0; i < 10; i++)
printer("%d", i);
}
Целая переменная i помещается в памяти в стек при запуске функции countup и существует только до тех пор пока выполняется эта функция. В ассемблерном модуле можно проделать тоже самое с помощью директивы LOCAL. Вот пример законченной функции:
PROС _cfunction NEAR
LOCAL i:Word=stacksize
push bp
mov bp, sp
sub sp, stacksize
mov [i], 0
@@10:
inc [ i ]
;
;--Код, использующий локальную переменную [i]
;
cmp [i], 10
jne @@10
mov sp, bp
pop bp
ret ; Возврат в точку вызова
ENDP _cfunction
Директива LOCAL в этом примере подготавливает переменную i типа Word (слово). Указание = stacksize назначает общее число байтов, занимаемое всеми локальными переменными - в данном случае 2 байта. Это значение вычитается из sp после подготовки адресации переменных в стек. Затем, для ссылки на i, используются такие инструкции, как mov, inc и crop. Благодаря директиве LOCAL ссылки типа [i] переводятся следующим образом:
mov [bp-2], 0
inc [bp-2]
и т.д. При использовании LOCAL нет необходимости вычислять отрицательные смещения относительно bp, чтобы определить местоположение переменных в стеке, -достаточно воспользоваться именами переменных.
Поскольку bp не изменяется во время выполнения этой функции, можно восстановить sp по средством bp, удаляя область локальной переменной из стека, или прибавить stacksize к sp с помощью команды
add sp, stacksize
Подходят оба метода, но восстановление sp посредством bp - быстрее. Можно также объявить несколько локальных переменных операторами, подобными следующему:
LOCAL i:Word; j:Word; c:Byte=stacksize
Теперь, после вычитания stacksize из указателя стека для резервирования области в стеке, можно использовать три локальные переменные - i, j и с. (Необходимо всегда делать LOCAL, что упрощает адресацию локальных переменных, это не создает область для переменных в памяти.)
4.3 Передача аргументов
Совместное использование C++ и ассемблера становится более сложным, когда к функциям добавляются аргументы. Приходится очень внимательно программировать, обращаясь к функциям из различных модулей и выбирая аргументы из стека. Но следующая директива берет на себя задачу сама произвести ломку имен и занести их в стек:
ARG c_offset:byte, k_pffset:word
Аргументы, перечисленные таким образом, не являются объектами данных; они смещаются в стеке относительно регистра bр. Использование ARG подобным образом позволяет ассемблеру вычислить смещения вместо нас - но мы должны специфицировать правильные типы данных. Символьная переменная в C++ является байтом в ассемблере, целая в C++ - эквивалентом ассемблерного слова и т.д.
Обратный процесс - передача аргументов из языка ассемблера в C++ - требует иной тактики:
proc _asmfunction C c_arg:byte, k_arg:word
“C” после имени функции указывает, что аргументы приводятся для языка С (т.е. они выталкиваются в стек в порядке справа налево). Остальное также, как и для директивы ARG.
В результате Turbo Assembler автоматически пишет инструкции для сохранения, инициализации и восстановления bp. При использовании этой альтернативной методики не приходится проводить точные операции по выталкиванию bp. За исключением этого отличия, в остальном процесс программирования остается тем же самым.
19