assembler. Учебник для вузов_Юров В.И_2003 -637с (862834), страница 81
Текст из файла (страница 81)
Это делается перед вызовом процедуры. Остальные регистры нужно сохранять по необходимости, но, на360Глава 15. Модульное программированиемой взгляд, хорошим тоном является сохранение и последующее восстановлениевсех регистров, которые подвергаются изменению внутри процедуры.Передача аргументов в процедуру на ассемблере из программы на С осуществляется также через стек, но порядок их размещения в стеке является обратнымрассмотренному ранее для связи Pascal—ассемблер.
В качестве примера используем ту же задачу. После передачи управления ближнего типа процедуре на ассемблере стек должен выглядеть так, как показано на рис. 15.3, а.Оперативная памятьОперативная память 0000:0000 Гооооюооо!<ss:sp ->ss:ffff->ipkolУхchДно стека4Старшие адресаоперативной памятиаss:sp ->Оперативная память0000:0000]ss:sp-^./+1ss:ffff->Старшие адресаоперативной памятиб+4+6+8+10ss:ffff->. . . .bp.'PkolУXchДно стекаСтаршие адресаоперативной памятивРис. 15.3. Изменение содержимого стека при передаче управления в связке С—ассемблерПроцедуры на ассемблере получают доступ к аргументам, переданным в стеке,посредством регистра В Р. Принцип доступа тот же, что и рассмотренный ранее(рис.
15.3, б). Прежде всего в начало процедуры ассемблера необходимо вставитькод пролога:pushbpmov bp.spПосле этого доступ к аргументам в стеке осуществляется по смещению относительно содержимого ВР, например:movax,[bp+4]переписать значение chиз стека в ахmovbx,[bp+6]значение х - в регистр ЬхПри организации связи С—ассемблер можно использовать также директиву ARG.Это избавит нас от необходимости подсчитывать смещения в стеке для доступак аргументам и позволит обращаться к ним просто по именам:arg c h : b y t e ; x : w o r d ; y : w o r d ; k o l : w o r dpush bpmov bp.spmov a x , [ c h ]mov b x , [ x ]переписать значение chиз стека в ахзначение х — в регистр ЬхЧтобы не повторяться, рассмотрим, как изменятся вызываемый и вызывающиймодули (листинги 15.19 и 15.20) для связи С—ассемблер по сравнению с листингами 15.17 и 15.18.Связь ассемблера с языками высокого уровня361Листинг 15.19.
Вызывающий модуль на C++//prg!5_19.cpp#include <stdio.h>#include <conio.h>extern "C" void asmproc(char ch, unsigned x,unsigned y, unsigned kol);void main (void){clrscrC);asmproc( ' a' , 2, 3, 5) ;asmproc( ' s ' , 9, 2, 7) ;Листинг 15.20. Вызываемая процедура на ассемблере;prg!5_20. asmMASMMODEL small, с ;модепь памяти и тип кодаSTACK256PUBLIC _asmproc ;символ подчеркивания обязателен.codemain:_asmprocprocС near с : BYTE, x: BYTE ,y:BYTE.kol:WORDmov dh,У;у-координата символа в dhmov dl, X; х-координата символа в dlmov ah. 02h ; номер службы BIOS; вызов прерывания BIOSint 10hmov ah, 09h ; номер службы BIOSmov ex, kol ;kol - количество "выводов" в схmov Ы,07h ;маска вывода в Ыxor bh.
bhmov al, с;с - символ в al; вызов прерывания BIOSint 10h; возврат из процедурыret_asmprocendpend mainЧто касается передачи аргументов в связке С—ассемблер, то здесь, как видите,все довольно прозрачно. В листинге 15.20 мы используем директиву MODEL с операндом С и директиву PROC с указанием языка С. Этим мы доверяем компиляторусамому сформировать коды пролога и эпилога, а также организовать обращениек переменным в стеке по их именам. Но при использовании конкретных программных средств организация такой связи выглядит намного проблематичней.
Нев последнюю очередь это связано с тем, что компиляторы языков C/C++ разрабатываются множеством фирм — в отличие от Pascal, компилятор для которого выпускает практически одна фирма Borland. Это обстоятельство, на мой взгляд, —основная причина сложности связи С—ассемблер, так как каждая фирма реализует ее по-своему (хотя суть и остается практически неизменной). Поэтому, как мнекажется, нет смысла рассматривать множество частных случаев, тем более что этоне является целью данной книги.
Обращайтесь к документации на ваш компилятор C/C++. Во избежание излишних сложностей, по возможности применяйте ассемблерные вставки в программах на C/C++.Как правило, компиляторы позволяют связывать модули на C/C++ и ассемблере с использованием средств командной строки. Так как этот процесс довольнохорошо стандартизован, есть смысл его рассмотреть. В качестве примера выберемкомпилятор С++.5.0 фирмы Inprise (Borland). Типовая последовательность шаговвыглядит примерно так.Глава 15. Модульное программирование3621. Составить текст программы на C++ (см. листинг 15.19).
В этой программе объявить процедуру asmproc внешней:extern void asmproc(char ch, unsigned x,unsigned y, unsigned kol);2. Выполнить трансляцию модуля C++ и получить объектный модуль:Ьсс -с prg!5_19.cppПараметр -с здесь означает, что выполняется только компиляция исходногофайла, загрузочный модуль не создается.
Результатом этого шага будет создание объектного модуля prg!5_19.obj.3. Составить текст процедуры на ассемблере (см. листинг 15.20), в которой объявить процедуру asmproc общедоступной с помощью директивы PUBLIC. Заметьте,что идентификатору asmproc предшествует символ подчеркивания (_asmproc).Компилятор C/C++ добавляет знак подчеркивания ко всем глобальным идентификаторам. Более того, некоторые компиляторы (VC++) могут кроме символа подчеркивания добавлять в конце исходного идентификатора комбинацию символов @пп, где пп означает количество байтов, занимаемых аргументамипроцедуры в стеке (см., например, листинг 16.2 в главе 16).4. Выполнить трансляцию программы на ассемблере:tasm prg!5_20,5.Выполнить объединение объектных модулей:bcc -ms prg!5_19.obj prg!5_20.objИсполняемому модулю будет присвоено имя prgl5_19.exe.
Параметр -ms определяет модель памяти.Компилятор Borland C++ предоставляет другую возможность для получениязагрузочного модуля. Скопируйте файл tasm.exe в каталог. ДЫп пакета Borland C++.Запустите исходные файлы на трансляцию командной строкой видаbcc prg!5_19.cpp prg!5_20.asmВ результате будет получен файл prgl5_19.exe. Компилятор Borland C++ всюработу организует сам: обрабатывает файл prg!5_19.cpp; вызывает трансляторtasm.exe, который выполняет трансляцию файла prg!5_20.asm; передает компоновщику объектные модули prg!5_19.obj и prg!5_20.obj. В результате создается загрузочный модуль prgl5_19.exe.Как возвратить результат в программу на С из процедуры на ассемблере? Дляэтого существуют стандартные соглашения (табл.
15.3). Перед возвратом управления в программу на С в программе на ассемблере необходимо поместить результат или сформировать указатель в заданных регистрах. Для иллюстрации работыс функцией С, текст которой написан на ассемблере, рассмотрим листинги 15.21и 15.22. В них функция, написанная на ассемблере, подсчитывает сумму элементов массива.
В функцию передаются адрес массива и его длина. Результат суммирования элементов массива возвращается обратно в вызывающую программу на С.Таблица 15.3. Возврат аргументов из процедуры на ассемблере в программу на C/C++Тип возвращаемого значения (C++)Место записи результатаUnsigned charАХCharАХСвязь ассемблера с языками высокого уровняТип возвращаемого значения (C++)EnumAXUnsigned shortAXShortAXUnsigned intAXIntAXUnsigned longDX:AX363Место записи результатаLongDX:AXУказатель nearAXУказатель farDX:AXЛистинг 15.21.
Вызывающий модуль на C/C++/*prg!5_21.c*/#include <stdio.h>extern int sum_asm(int massiv[],int count);main(){int mas[5]={l,2,3,4,5};int len=5;int sum;sum=sum_asm(mas,I en) ;printf("%d\n",sum) ;return(O);}Листинг 15.22. Вызываемая процедура на ассемблере;prgl5_22.asmMASMMODELsmall.stack 100h.codepublic _sum_asm_sum_asm proc С near adr_mas:word,len_mas:wordmov ax,0mov cx,len_mas ;длину массива - в схmov si,adr_mas ;адрес массива - в sicycl: add ax, [si] ;сложение аккумулятора с элементом массиваadd si,2адресовать следующий элемент массиваloop cyclret;возврат из функции, результат - в ах_sum_asmendpendОбратите внимание на то, что листинг 15.19 содержит текст исходного файлас расширением .срр, а листинг 15.21 — с расширением .с.
Соответственно, сами исходные тексты в части организации межмодульного взаимодействия также различаются.Дополнительную информацию о связи С—ассемблер вы найдете в [18].Несколько слов об организации связи С—ассемблер для компилятора VisualC/C++. Один из подходов к ее реализации — оформление ассемблерной программы в виде функции из библиотеки DLL.
В этом случае можно уйти от «фирменных» проблем связи, возникающих из-за несовпадения форматов информации364Глава 15. Модульное программированиев объектных файлах Borland и Microsoft, и писать функции TASM и MASM длясвязи с программами Visual C/C++.