Тема 7Сравнительная оценка использования макросов и процедур.
1. Макрос: Синтаксис макроса: % macro имя_какса число_параметров < тело макроса> % endmacro 2. Порядок действий: Синтаксис процедуры: procedure_name: procedure body …....................... RET Чтобы вызвать процедуру ВЫЗОВ имя_процедуры После выполнения процедуры управление передается вызывающей процедуре с помощью оператора RET
Разница между макросом и процедурой:
70. Интерфейс между ассемблером и языком С++
Система программирования VisualDSP++ предусматривает возможность вызывать подпрограммы на ассемблере из программ на C/C++, и наоборот, как из программ на ассемблере вызывать функции C/C++ [1]. Перед тем, как попытаться выполнить любую из этих операций, следует хорошо разобраться с моделью выполнения кода C/C++ (C/C++ run-time model), включая информацию о том, как организован стек, какие используются типы данных, и как обрабатываются аргументы (см. [4]). В конце будут рассмотрены примеры кода, показывающие смешивание в проекте кода C/C++ и ассемблера.
[Вызов подпрограмма ассемблера из программ C/C++] Перед вызовом подпрограммы на языке ассемблера из программы на языке C/C++ создайте прототип, чтобы определить аргументы для подпрограммы на ассемблере, и интерфейс из программы C/C++ для подпрограммы на ассемблере. Хотя даже допустимо использовать функцию в C/C++ без её прототипа, создание хорошо описанных прототипов настоятельно рекомендуется практикой разработки ПО. Когда прототип опущен, компилятор не может выполнить проверку типов аргументов, а также подразумевает, что возвращаемое значение имеет тип int, и использует правила формирования кода (promotion rules) Кернигана & Ричи (K& R) вместо правил ANSI [6]. Компилятор добавляет к именам любых внешних точек входа (функций, подпрограмм) префикс в виде одиночного нижнего подчеркивания (так называемая декорация имен, name mangling). Таким образом, декларируйте подпрограмму на языке ассемблера с именем, у которого в начале стоит символ подчеркивания. Модель run-time определяет некоторые регистры в качестве временных (scratch registers), и другие регистры резервируются, или они выделяются для специальных целей (dedicated registers). Scratch-регистры можно использовать в программе на языке ассемблера, не беспокоясь об их предыдущем содержании, т. е. их не надо сохранять на входе в подпрограмму и восстанавливать на выходе из подпрограммы. Если нужно больше места (или используется существующий код), и Вы хотите использовать зарезервированные регистры, то необходимо сохранять и восстанавливать их содержимое соответственно на входе и выходе подпрограммы. Используйте выделенные регистры или регистры стека только для их непосредственного предназначения; компилятор, библиотеки, отладчик и обработчики прерываний (ISR) зависят от наличия доступного стека, как определено этими регистрами. Компилятор также подразумевает, что состояние машины не меняется во время выполнения подпрограммы на языке ассемблера. Не меняйте любые режимы машины (например, определенные регистры могут использоваться для индикации кольцевой буферизации, когда их значения не равны нулю). Компилятор всегда выравнивает массивы в памяти по 32-разрядной границе слова, и компилятор нормально использует это знание при оптимизации доступа к содержимому памяти. Поэтому необходимо гарантировать, что массивы, определенные в коде ассемблера, к которым осуществляется доступ из кода C/C++, также выровнены подобным образом. Это обычно достигается вставкой перед определением массива в ассемблере директивы. align 4.
Если аргументы находятся в стеке, то они адресуются через смещение относительно указателя стека (SP) или указателя фрейма (FP) [2]. Хороший способ разобраться, как передавать аргументы через стек между программой C/C++ кодом на ассемблере - написать пустую заглушку подпрограммы на C/C++ и скомпилировать её в язык ассемблера (ниже приведен пример, как это делается). Скомпилировать код C/C++ в код ассемблера можно в среде VisualDSP IDDE, если поставить галочку Save temporary files в свойствах проекта (или использовать дополнительный ключ -save-temps командной строки компилятора [3]). Ниже показан пример, включающий присваивание глобальной volatile-переменной [5], чтобы показать, где могут быть найдены аргументы на входе в функцию asmfunc на ассемблере. // Код примера, показывающий интерфейс компилятора с ассемблером. // В нем глобальным переменным global_a, global_b, global_pglobal // просто присваиваются значения из аргументов функции. Это сделано // для демонстрации, как на ассемблере можно получить доступ // к аргументам функции, находящимся в стеке (тип каждой глобальной // переменной соответствует типу одного из аргументов):
int global_a; float global_b; int * global_p;
// Пустая демонстрационная функция, код которой компилируется // в код ассемблера: int asmfunc(int a, float b, int * p) { // Присваивание значений из аргументов, которые будут потом // найдены в коде ассемблера: global_a = a; global_b = b; global_p = p;
// Значение, которое будет загружено в регистр возврата: return 12345; } Когда этот пример кода C/C++ скомпилирован с опциями командной строки -save-temps и -no-annotate -O, получится следующий код на ассемблере: . section program; . align 2; _asmfunc: P0. L =. epcbss; P0. H =. epcbss; [P0+ 0] = R0; R0 = 0x1234 (X); [P0+ 4] = R1; [P0+ 8] = R2; RTS;
. _asmfunc. end: . global _asmfunc; . type _asmfunc, STT_FUNC;
. section data1;
. align 4; . epcbss: . byte _global_a[4]; . global _global_a; . type _global_a, STT_OBJECT; . byte _global_b[4]; . global _global_b; . type _global_b, STT_OBJECT; . byte _global_p[4]; . global _global_p; . type _global_p, STT_OBJECT; . epcbss. end:
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|