Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Микропроцессорная техника Сторожок / MPLAB_C30_Руководство пользователя.pdf
Скачиваний:
161
Добавлен:
20.02.2016
Размер:
1.26 Mб
Скачать

Руководство пользователя по MPLAB® компилятору Си для PIC24 и dsPIC®

Глава 9. Совместное использование ассемблера и Си

9.1.Введение

 

Эта глава описывает, как использовать модули на ассемблере и Cи вместе. Здесь

 

даны примеры использования переменных и функций Си в ассемблерном коде и

 

 

 

.

 

примеры использования переменных и функций ассемблера в Си.

9.2.

Основные вопросы

 

A

 

 

 

Вопросы, обсуждаемые в этой главе:

 

 

 

Wilson

 

 

Смесь переменных и функций на ассемблере и Си — Отдельные модули на

 

языке ассемблера могут быть ассемблированы, а затем скомпонованы с

 

откомпилированными модулями на Cи.

 

 

Использование ассемблера inline — команды на языке ассемблера могут

 

вставляться непосредственно в код Cи. Inline ассемблер поддерживает как

 

простые операторы языка ассемблера (без параметров), так и расширенные (с

 

параметрами), где переменные Cи могут быть доступны, как операнды команд

 

ассемблера.

by

 

9.3.

 

 

Смесь переменных и функций на ассемблере и Си

Следующие рекомендации показывают способы взаимосвязи отдельных модулей на ассемблере с модулями Cи.

Следуйте соглашениями о регистрах описанным в п.4.12. «Соглашения о регистрах». В частности, регистры W0-W7 используются для передачи параметров. Функция на языке ассемблера будет получать параметры и должна передавать аргументы в вызываемые функции с помощью этих регистров.

Функции не вызванные в течение обработки прерывания должны сохранить регистры W8-W15. (Functions not called during interrupt handling must preserve registers W8W15.) Это значит, что величины в этих регистрах должны быть сохранены прежде, чем они будут модифицированы, и должны быть восстановлены перед возвратом в вызывающую функцию. Регистры W0-W7 могут использоваться без восстанавливая их величины.

Функции обслуживания прерываний должны сохранять все регистры. В отличие от вызова обычной функции, прерывание может произойти в любой момент в течение исполнения программы. При возврате в обычную программу, все регистры должны быть такими же, как они были перед возникновением прерывания.

Переменные или функции объявленные в пределах отдельного ассемблерного файла, на которые есть ссылки из исходного файла на Cи, должны быть объявлены как глобальные с использованием директивы ассемблера .global. Внешние символы должны предваряться по крайней мере одним подчеркиванием. Функция Си main в ассемблере называется _main и наоборот символ ассемблера

_do_something будет указан в Cи как do_something. Неописанные символы, использованные в файлах ассемблера, будут рассматриваться как внешние.Translated

Следующий пример показывает, как использовать переменные и функции как в ассемблере, так и в Cи, независимо от того, где они первоначально были определены.

Файл ex1.c определяет foo и cVariable, для использования в файле на ассемблере. Файл на Cи также показывает, как вызывать ассемблерную функцию

 

 

© 2008 Microchip Technology Inc.

DS51284H(ru) стр. 9-1

16-битовый компилятор Си. Руководство

asmFunction, и как получать доступ к определенной в ассемблере переменной asmVariable.

ПРИМЕР 9-1. ВЗАИМОДЕЙСТВИЕ СИ И АССЕМБЛЕРА

/*

** file: ex1.c */

extern unsigned int asmVariable;

 

 

extern void asmFunction(void);

 

.

unsigned int cVariable;

 

 

void foo(void)

 

 

{

 

 

asmFunction();

 

 

A

asmVariable = 0x1234;

 

 

 

 

 

}

 

 

 

Файл ex2.s определяет asmFuncti

и

asmVariable, как необходимо для

 

Wilson

 

использования в скомпонованном приложении. Файл на ассемблере также показывает, как вызывать Cи функцию foo, и как осуществлять доступ к определенной в Cи переменной cVariable.

;

; file: ex2.s

;

.text

.global _asmFunction

_asmVariable: .space 2

_asmFunction:

by

mov #0,w0

 

mov w0,_cVariable

 

return

 

 

.global

begin

 

_begin:

 

 

call foo

 

 

return

 

 

.bss

 

 

.global

asmVariable

.align 2

 

 

Translated

 

.

В файле на Cи, ex1.c, внешние ссылки на символы, объявленные в ассемблерном файле, декларированы с использованием стандартного ключевого слова extern; заметьте, что asmFunction, или _asmFunction в ассемблерном исходнике, является void функцией и была объявлена соответственно.

В файле на ассемблере, ex1.s, символы _asmFunction, _begin и _asmVariable

делаются глобально видимыми с помощью директивы .global ассемблера и могут быть доступны из любого другого исходного файла. Символ _foo используется только как ссылка, без объявления, следовательно, ассемблер рассматривает его, как внешний.

Следующий пример показывает, как вызывать функцию ассемблера с двумя параметрами. Си функция main из call1.c вызывает asmFunction из call2.s с двумя параметрами.

DS51284H(ru) стр. 9-2

© 2008 Microchip Technology Inc.

Глава 9. Совместное использование ассемблера и Си

ПРИМЕР 9-2. ВЫЗОВ ФУНКЦИИ АССЕМБЛЕРА ИЗ СИ

/*

** file: call1.c */

extern int asmFunction(int, int);

 

 

int x;

 

 

 

 

void

 

 

 

 

main(void)

 

 

 

{

 

.

 

 

x = asmFunction(0x100, 0x200);

 

 

}

 

 

 

 

 

 

 

Функция на ассемблере суммирует два параметра и возвращает результат.

 

;

A

 

 

 

 

 

 

; file: call2.s

 

 

 

;

 

 

 

 

.global _asmFunction

 

 

 

 

_asmFunction:

 

 

 

 

add w0,w1,w0

 

 

 

 

return

 

 

 

 

.end

 

 

 

 

Передача параметров в Cи описанаWilsonподробно в п.4.11. «Соглашения по вызову

 

 

функций». В предыдущем примере, два целых аргумента передавались в регистрах

 

 

W0 и W1. Возвращаемый результат целого типа передавался через регистр W0.

 

 

Более сложные списки параметров могут использовать другие регистры и требуется

 

 

определенная аккуратность следования рекомендациям при написании

 

 

by

 

 

 

 

ассемблерных текстов от руки.

 

 

9.4.

Использование ассемблера inline

 

 

 

 

Оператор asm может быть использован в пределах Си функции для включения

 

 

строки ассемблерного кода в генерируемый компилятором ассемблерный текст.

 

 

Inline ассемблер имеет две формы: простую и расширенную.

 

 

В простой форме, команда ассемблера записывается с использованием

 

 

синтаксиса:

 

 

 

 

asm ("команда");

 

 

 

 

где команда является разрешенной конструкцией ассемблера. Если вы пишете

 

 

inline ассемблер для программ ANSI Cи, пишите __asm__ вместо asm.

 

 

 

 

 

Примечание В простой форме inline ассемблера может быть передана только

 

 

одна строка.

 

 

 

 

Translated

 

 

В расширенной форме ассемблера использующей asm, операнды команды определяются с использованием выражений Cи. Расширенный синтаксис:

asm("шаблон" [ : [ "уточнение"(выходной операнд) [ ,... ] ] [ : [ "уточнение"(входной операнд) [ ,... ] ]

[ "затирание" [ ,... ] ]

]

]);

Вы должны определить шаблон команды ассемблера, плюс строку уточнения операнда для каждого операнда. Шаблон определяет мнемонику команды и необязательные заполнители для операндов. Строки уточнения определяют уточнения для операнда, например, что операнд должен быть в регистре (обычный случай), или, что операнд должен быть непосредственной величиной.

© 2008 Microchip Technology Inc.

DS51284H(ru) стр. 9-3

16-битовый компилятор Си. Руководство

Буквы и модификаторы уточнений, поддерживаемые компилятором, приведены в таблице 9-1 и таблице 9-2, соответственно.

ТАБЛИЦА 9-1. БУКВЫ УТОЧНЕНИЙ, ПОДДЕРЖИВАЕМЫЕ КОМПИЛЯТОРОМ

Буква

Уточнение

aТребование WREG

bВспомогательный регистр деления W1

cВспомогательный регистр умножения W2

dРегистры данных общего назначения W1-W14

 

 

e

.

Вспомогательные регистры кроме деления W2-W14

g

A

Разрешается любой регистр, память или непосредственный целый операнд,

 

кроме тех регистров, которые на являются регистрами общего назначения

iРазрешается непосредственный целый операнд (постоянное значение). Это включает символические константы, значение которых будет известно только во время ассемблирования

rРазрешается регистровый операнд при условии, что он будет в регистре общего назначения

vAWB регистр W13

 

 

w

Wilson

Регистр аккумулятор A - B

x

Регистры предварительной выборки x W8-W9

y

Регистры предварительной выборки y W10-W11

z

Регистры предварительной выборки MAC W4-W7

 

0,1,...,9 Разрешается операнд, который соответствует заданному номеру операнда в

 

by

 

шаблоне. Если цифра используется вместе с буквами внутри одной

 

альтернативы, цифра должна рассматриваться последней. По умолчанию,

 

%n представляет первый регистр для операнда (n). Для доступа к второму,

 

третьему или четвертому регистру используйте букву-модификатор.

 

 

T

Операнд ближнего или дальнего типа

 

Translated

U

Операнд ближнего типа

ТАБЛИЦА 9-2. МОДИФИКАТОРЫ УТОЧНЕНИЙ, ПОДДЕРЖИВАЕМЫЕ КОМПИЛЯТОРОМ

Буква

Уточнение

 

 

=

Означает операнд только для записи в данной команде: прежнее значение

 

теряется и заменяется выходными данными.

 

 

+

Означает операнд как для чтения, так и для записи в данной команде.

 

 

&

Означает, что данный операнд является «рано затираемым» (earlyclobber),

 

который модифицируется перед тем, как инструкция закончит использование

 

входных операндов. Таким образом этот операнд может не находиться в

 

регистре, который используется как входной операнд или как часть

 

некоторого адреса памяти.

 

 

d

Второй регистр для операнда номер n, т.е. %dn..

 

 

q

Четвертый регистр для операнда номер n, т.е. %qn..

 

 

t

Третий регистр для операнда номер n, т.е. %tn..

 

 

ПРИМЕР 9-3. ПЕРЕДАЧА ПЕРЕМЕННЫХ СИ

Данный пример демонстрирует, как использовать команду swap (которую компилятор обычно не использует):

asm ("swap %0" : "+r"(var));

Здесь var — это выражение Си для операнда, который является одновременно входным и выходным. Уточняется, что операнд может быть типа r, что значит регистровый операнд. Знак “+” в “+r” обозначает, он является одновременно входным и выходным.

Каждый операнд описывается строкой, уточняющей операнд, с последующим выражением Си в скобках. Двоеточие отделяет шаблон ассемблера от первого

DS51284H(ru) стр. 9-4

© 2008 Microchip Technology Inc.

: "w2");
: "U" (nvar)

Глава 9. Совместное использование ассемблера и Си

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

Если нет выходных операндов, но есть входные операнды, тогда должно быть два последовательных двоеточия, окружающих место, где должны быть выходные операнды. Компилятор требует, чтобы выражения для выходных операндов были выражениями локализации (L-values). Входные операнды не обязательно должны выражениями локализации. Компилятор не может проверить, имеют ли операнды типы данных, приемлемые для выполняемых команд. Он не анализирует шаблон

команды ассемблера и не знает, что он означает, и поймет ли его ассемблер. Расширенная возможность asm чаще всего используется. для машинных команд, о

существовании которых сам компилятор не знает. Если выходное выражение не

может быть непосредственно адресуемым (например, это — битовое поле),

уточнение должно предоставлять регистр. В этомA

случае компилятор использует

Wilson

 

регистр как выход asm, а затем сохранит этот регистр в выходе. Если выходные операнды доступны только для записи, компилятор предположит, что величины в этих операндах уничтожаются до команды и нет нужды их создавать.

ПРИМЕР 9-4. ЗАТИРАЕМЫЕ РЕГИСТРЫ

Некоторые команды затирают специфические регистры аппаратуры. Для того, чтобы описать это, вставьте третье двоеточие после входных операндов, с последующим перечислением имен затираемых регистров аппаратуры (задаваемых как строки, разделенные запятыми). Вот пример:

asm volatile ("mul.b %0"

: /* выходных операндовby нет */

В этом случае, операнд nvar является char переменной, объявленной в ближнем пространствеTranslatedданных, как определено уточнением "U". Если команда ассемблера может изменить регистр флагов (коды условий), добавьте "cc" в список затираемых

регистров. Если команда ассемблера модифицирует память непредсказуемым способом, добавьте "memory" к списку затираемых регистров. Это заставит компилятор не сохранять значения памяти кешированные в регистрах при исполнении команды ассемблера (This will cause the compiler to not keep memory values cached in registers across the assembler instruction.).

ПРИМЕР 9-5. ИСПОЛЬЗОВАНИЕ НЕСКОЛЬКИХ КОМАНД АССЕМБЛЕРА

Вы можете поместить более одной команды ассемблера в единственный шаблон asm, разделяя команды символами новой строки (записанными как \n). Входные операнды и адреса выходных операндов гарантируются благодаря неиспользованию любых затираемых регистров, так, чтобы вы можете читать и записывать затираемые регистры столько раз, сколько вам нравится. Вот пример нескольких команд в шаблоне, предполагается, что подпрограмма _foo принимает аргументы в регистрах W0 и W1:

asm ("mov %0,w0\nmov %1,W1\ncall _foo"

:/* нет выходных */

:"g" (a), "g" (b)

:"W0", "W1");

В этом примере уточнения "g" указывают общий тип входных операндов.

ПРИМЕР 9-6. ИСПОЛЬЗ. ‘&’ ПРОТИВ ЗАТИРАНИЯ ВХОДН. РЕГИСТРОВ

Если выходной операнд не имеет модификатор уточнения &, компилятор может разместить его в том же регистре, что и не имеющий к нему отношения входной операнд, в предположении, что входные будут использованы до того, как выходные

© 2008 Microchip Technology Inc.

DS51284H(ru) стр. 9-5

int
exprgood(int a, int b)
{

16-битовый компилятор Си. Руководство

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

int

exprbad(int a, int b)

{

 

 

 

int c;

 

 

__asm__("add %1,%2,%0\n sl %0,%1,%0"

 

: "=r"(c) : "r"(a), "r"(b));

.

 

 

 

return(c);

A

}

 

 

 

Намерение было вычислить величину (a + b) << a. Тем не менее, как записано,

вычисленная величина может быть или может не быть равна этому значению.

Wilson

модифицирован

Правильное кодирование сообщает компилятору, что операнд c

прежде, чем asm команда будет завершена, используя входные операнды следующим образом:

int c;

__asm__("add %1,%2,%0\n sl %0,%1,%0" : "=&r"(c) : "r"(a), "r"(b));

}

ПРИМЕР 9-7. СООТВЕТСТВИЕby ОПЕРАНДОВ

return(c);

КогдаTranslatedкоманда ассемблера имеет операнд для чтения-записи, или операнд, в

котором должны быть изменены только некоторые биты, вы обязаны логически разделить их функционирование на два отдельных операнда: один входной операнд и один выходной операнд только для записи. Взаимосвязь между ними выражена уточнениями, которые говорят, что они должны быть в одном и том месте при выполнении команды. Вы можете использовать одно и то же или разные выражения Cи для обоих операндов. Например, команда сложения с bar в качестве операндаисточника только для чтения и операнда-приемника для чтения-записи foo:

asm ("add %2,%1,%0" : "=r" (foo)

: "0" (foo), "r" (bar));

Уточнение "0" для операнда 1 сообщает, что он должен занимать то же место, что и операнд 0. Цифра в уточнении допускается только во входном операнде и должна ссылаться на выходной операнд. Только цифра в уточнении может гарантировать, что один операнд будет на том же месте, что и другой. Одного лишь факта, что foo — это значение обоих операндов, недостаточно, чтобы гарантировать, что они будут на одном и том же месте в сгенерированном коде ассемблера. Следующий пример не будет работать:

asm ("add %2,%1,%0"

:"=r" (foo)

:"r" (foo), "r" (bar));

Различные оптимизации или перезагрузка могут привести к тому, что операнды 0 и 1 будут в разных регистрах. Например, компилятор мог бы найти копию величины foo

водном регистре и использовать его как операнд 1, но создать выходной операнд 0

вдругом регистре (копируя его впоследствии в собственный адрес foo).

DS51284H(ru) стр. 9-6

© 2008 Microchip Technology Inc.

#define disi(n) \
Глава 9. Совместное использование ассемблера и Си
ПРИМЕР 9-8. НАИМЕНОВАНИЕ ОПЕРАНДОВ
Также допустимо определять входные и выходные операнды с использованием символических имен, которые могут быть упомянуты в пределах кодового шаблона ассемблера. Эти имена задаются в квадратных скобках перед строкой уточнения, и могут использоваться в кодовом шаблоне ассемблера, как %[name] вместо знака процента сопровожденного номером операнда. Используя поименованные операнды, вышеуказанный пример мог бы быть закодирован следующим образом:
asm ("add %[foo],%[bar],%[foo]" : [foo] "=r" (foo)

: "0" (foo), [bar] "r" (bar));

 

ПРИМЕР 9-9. ИЗМЕНЧИВОСТЬ ОПЕРАТОРА.

ASM

Вы можете предохранить asm команду от удаления,A

значительного перемещения

или комбинирования, с помощью ключевого слова volatile после asm. Например:

asm volatile ("disi #%0" \

:/* нет выхода */ \

:"i" (n))

В этом случае уточняющая буква "i" обозначает непосредственный операнд, как требует инструкция disi.

ПРИМЕР 9-10. ИЗМЕНЕНИЕ ПОСЛЕДОВАТЕЛЬНОСТИ ИСПОЛНЕНИЯ

Wilson

Есть специальные меры предосторожности, которые должны быть приняты при by

изменении последовательности исполнения программы в пределах inline операторов ассемблера.

Например, не способа сообщать компилятору, что результатом выполнения inline asm оператора будет изменение последовательности исполнения программы. Управление должно быть передано оператору ассемблера и всегда возвращено следующему за ним оператору.

Правильная последовательность исполнения: asm("call foo" : /* выходы */

:/* входы */

:"w0", "w1", "w2", "w3", "w4", "w5", "w6", "w7");

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

Неправильная последовательность исполнения:

Translated/* следующей оператор */

asm("bra OV, error");

/* следующей оператор */ return 0;

asm("error: "); return 1;

Это неприемлемо, т.к. компилятор предположит, что следующий оператор, return 0, всегда будет выполнен, тогда как этого может не быть. В этой ситуации, asm("error: ") и следующий оператор будут удалены, поскольку они недостижимы. Подробнее см. информацию относительно меток в операторах asm.

© 2008 Microchip Technology Inc.

DS51284H(ru) стр. 9-7

asm("cp0 _foo\n\t" "bra nz, eek\n\t"
"; немного ассемблера\n\t" "bra eek_end\n\t" "eek:\n\t"
"; еще ассемблер\n" "eek_end:" : /* выходы */
: /* входы */ : "cc");
/* следующий оператор */
16-битовый компилятор Си. Руководство
Приемлемая последовательность исполнения:

Это приемлемо (т.е. следующий оператор всегдаA . будет выполнен, независимо от ветвления в операторе asm), но может не работать как ожидается. См. дальнейшую информацию относительно меток в операторах asm. Заметьте, что, с помощью

Метки и последовательность исполнения:

идентификации cc как затираемых, код указывает, что флаги статуса в этом

операторе дальше недействительны.

inline void foo() {

Wilson

Кроме всего, метки в операторах ассемблера могут вести себя неожиданно при определенных опциях оптимизации. Встраивание может привести к тому, что метки в рамках операторов ассемблера будут определены многократно.

Также средства процедурных абстракций (-mpa), не принимают синтаксис локальных меток. Смотри следующий пример:

 

by

asm("do #6, loopend");

/* код на Си */

 

asm("loopend: ");

return;

 

Translated

 

}

 

Это плохо по ряду причин. Во-первых, оператор asm вводит неявное управление последовательностью исполнения, о котором компилятор не знает. Во-вторых, если foo() — встраиваемая функция (inline), метка loopend будет определяться много раз. В-третьих, код Cи может быть плохо оптимизирован, поскольку компилятор не может видеть циклическую структуру. Этот пример опровергает правило, что оператор asm не должен вмешиваться в управление последовательностью исполнения; asm("loopend:") не всегда передаст управление следующему оператору.

Решение состоит в использовании синтаксиса локальных меток, как описано в «MPLAB® Assembler, Linker and Utilties for PIC24 MCUs and dsPIC® DSCs User’s Guide» (DS51317), и как в следующем примере:

inline void foo() { asm("do #6, 0f"); /* код на Си */ asm("0: "); return;

}

Вышеприведенная форма немного лучше, по крайней мере это устраняет многократное определение меток. Тем не менее, средства процедурных абстракций (-mpa), не принимают метки в форме 0:.

ПРИМЕР 9-11. ИСПОЛЬЗОВАНИЕ ТРЕБУЕМЫХ РЕГИСТРОВ

Некоторые команды в наборе команд dsPIC требуют, чтобы операнды были в конкретном регистре или группе регистров. В таблице 9-1 приводятся некоторые буквы уточнения, которые могут быть приемлемы для удовлетворения ограничениям команд, которые вы хотите использовать.

DS51284H(ru) стр. 9-8

© 2008 Microchip Technology Inc.

Глава 9. Совместное использование ассемблера и Си

Если уточнения не достаточны или вы хотите назначить конкретные регистры для использования в операторах ассемблера, вы можете использовать расширения для наименования регистров, предоставляемые компилятором для ваших нужд (и уменьшения необходимости помечать регистры, как затираемые), как показывает нижеприведенный фрагмент кода. Этот фрагмент использует фиктивную команду, которая имеет немного странные требования к регистрам:

{ register int in1 asm("w7");

 

 

register int in2 asm("w9");

 

 

register int out1 asm("w13");

 

 

register int out2 asm("w0");

 

.

in1 = some_input1;

A

 

in2 = some_input2;

 

 

 

__asm__ volatile ("funky_instruction %2,%3,%0; = %1" :

/* выходы */ "=r"(out1),

"=r"(out2) :

Wilson

"r"(in2));

/* входы

*/ "r"(in1),

/* используйте out1 и out2

в обычном Cи

*/

 

}

В этом примере, команда funky in truction имеет один явный выход, out1, и один неявный выход, out2. Оба указаны в шаблоне ассемблера, чтобы компилятор мог проследить правильное использование регистра (хотя неявный выход указан в тексте комментария). Показанный ввод — нормальный. С другой стороны, использован синтаксис расширенного декларатора регистра, чтобы назначать конкретные аппаратные регистры, которые удовлетворяют ограничениям нашей вымышленной funky_instruction.

ПРИМЕР 9-12. ОБРАБОТКАby ВЕЛИЧИН, БОЛЬШИХ INT

Буквы уточнений и модификаторов могут быть использованы, чтобы иденти-

asm("mov %1, %0" : "r"(foo) : "r"(bar));

фицировать различные объекты, которыми приемлемо заменять конкретный операнд,Translatedтакой как %0:

Этот пример показывает, что значение, хранимое в foo, должно быть перемещено в bar. Код примера выполняет эту задачу, если foo или bar не больше, чем int.

По умолчанию, %0 представляет первый регистр для операнда (0). Для доступа ко второму, третьему, или четвертому регистрам, используйте букву модификатора определенную в таблице 9-2.

© 2008 Microchip Technology Inc.

DS51284H(ru) стр. 9-9

16-битовый компилятор Си. Руководство

Для заметок.

A . Wilson by Translated

DS51284H(ru) стр. 9-10

© 2008 Microchip Technology Inc.

Соседние файлы в папке Микропроцессорная техника Сторожок