- •Глава 1. Сведения о компиляторе
- •1.1.Введение
- •1.2. Основные вопросы
- •1.3. Описание компилятора и документация
- •1.4. Компилятор и другие средства разработки
- •1.5. Набор возможностей компилятора
- •1.5.1. Стандарт ANSI Си
- •1.5.2. Оптимизация
- •1.5.3. Поддержка стандартной ANSI библиотеки
- •1.5.4. Гибкие модели памяти
- •1.5.5. Драйвер компилятора
- •Глава 2. Отличия 16-битового компилятора от ANSI
- •2.1. Введение
- •2.2. Основные вопросы
- •2.3. Отличия ключевых слов
- •2.3.1. Определение атрибутов переменных
- •2.3.2. Определение атрибутов функций
- •2.3.3. Inline функции
- •2.3.4. Переменные в определенных регистрах
- •2.3.4.1. Определение глобальных регистровых переменных
- •2.3.4.2. Определение локальных регистровых переменных
- •2.3.5. Комплексные числа
- •2.3.6. Целые размером в двойное слово
- •2.3.7. Ссылки на тип с помощью typeof
- •2.4. Отличия операторов
- •2.4.1. Метки как значения
- •2.4.2. Условные операторы с опущенными операндами
- •2.4.3. Диапазоны case
- •2.5. Отличия выражений
- •2.5.1. Двоичные константы
- •Глава 3. Использование компилятора в командной строке
- •3.1. Введение
- •3.2. Основные вопросы
- •3.3. Обзор
- •3.4. Соглашение для имен файлов
- •3.5. Опции
- •3.5.1. Опции, специфические для устройств dsPIC
- •3.5.2. Опции для управления типом результатов
- •3.5.3. Опции для управления диалектом Cи
- •3.5.5. Опции для отладки
- •3.5.6. Опции для управления оптимизацией
- •3.5.7. Опции для управления препроцессором
- •3.5.8. Опции для ассемблера
- •3.5.9. Опции для компоновщика
- •3.5.10. Опции для поиска в каталогах
- •3.5.11. Опции для соглашений по генерации кода
- •3.6. Переменные окружения
- •3.7. Предопределенные имена макро
- •3.9. Компиляция нескольких файлов в командной строке
- •3.10. Особенные символы
- •Глава 4. Среда периода исполнения
- •4.1. Введение
- •4.2. Основные вопросы
- •4.3. Адресное пространство
- •4.4. Запуск и инициализация
- •4.5. Пространства памяти
- •4.6. Модели памяти
- •4.6.1. Ближние и дальние данные
- •4.6.2. Ближний и дальний код
- •4.7. Расположение кода и данных
- •4.8. Программный стек
- •4.9. Использование стека в Си
- •4.11. Соглашения по вызову функций
- •4.11.1. Параметры функции
- •4.11.2. Возвращаемое значение
- •4.12. Соглашения о регистрах
- •4.13. Двоичная инверсия и модульная адресация
- •4.14.1. Загрузочные и защищенные константы
- •4.14.2. Строковые константы как аргументы
- •4.14.3. Переменные с квалификатором const в безопасной Flash
- •4.14.4. Модель совместимости объектов
- •Глава 5. Типы данных
- •5.1. Введение
- •5.2. Основные вопросы
- •5.3. Представление данных
- •5.4. Целые
- •5.5. С плавающей точкой
- •5.6. Указатели
- •Глава 6. Дополнительные типы указателей Си
- •6.1. Введение
- •6.2. Управляющие PSV указатели
- •6.2.1. Определение данных для управления доступом PSV
- •6.2.2. Управляемый доступ PSV
- •6.2.3. Рассмотрение ISR
- •6.3. PMP указатели
- •6.3.1. Инициализация PMP
- •6.3.2. Объявление нового пространства памяти
- •6.3.3. Определение переменных в пространстве PMP
- •6.4. Внешние указатели
- •6.4.1. Объявление нового пространства памяти
- •6.4.2. Определение переменных во внешнем пространстве
- •6.4.3. Определение способа доступа к пространству памяти
- •6.4.3.2. Функции записи
- •6.4.4. Пример внешней памяти
- •Глава 7. Файлы поддержки устройства
- •7.1. Введение
- •7.2. Основные вопросы
- •7.3. Файлы заголовков процессора
- •7.4. Файлы определения регистров
- •7.5. Использование SFR
- •7.6. Использование макросов
- •7.6.1. Макросы настройки битов конфигурации
- •7.6.2. Макросы использования ассемблера inline
- •7.6.3. Макросы выделения памяти данных
- •7.6.4. Макросы объявления ISR
- •7.7. Адресация EEDATA из Си - только для dsPIC30F
- •7.7.1. Доступ к EEDATA через PSV
- •7.7.2. Доступ к EEDATA посредством команд TBLRDx
- •7.7.3. Дополнительные источники информации
- •Глава 8. Прерывания
- •8.1. Введение
- •8.2. Основные вопросы
- •8.3. Написание программы обработки прерывания
- •8.3.1. Рекомендации по написанию ISR
- •8.3.3. Кодирование ISR
- •8.3.4. Использование макросов для объявления простых ISR
- •8.4. Запись вектора прерывания
- •8.4.1. Вектора прерываний dsPIC30F (без SMPS)
- •8.4.3. Вектора прерываний PIC24F
- •8.4.4. Вектора прерываний dsPIC33F/PIC24H
- •8.5. Сохранение контекста в ISR
- •8.7. Вложенные прерывания
- •8.8. Разрешение/запрещение прерываний
- •8.9. Разделение памяти между основной программой и ISR
- •8.9.1. Разработка проблем
- •8.9.2. Разработка решений
- •8.9.3. Пример приложения
- •8.10. Использование PSV в ISR
- •Глава 9. Совместное использование ассемблера и Си
- •9.1. Введение
- •9.2. Основные вопросы
- •9.3. Смесь переменных и функций на ассемблере и Си
- •9.4. Использование ассемблера inline
- •Приложение A. Определяемое реализацией поведение
- •A.12. Квалификаторы
- •A.13. Деклараторы
- •A.14. Операторы
- •A.17. Сигналы
- •A.18. Потоки и файлы
- •A.20. Errno
- •A.22. Abort
- •A.23. Exit
- •A.24. Getenv
- •A.25. Система
- •A.26. Strerror
- •Приложение B. Встроенные функции
- •B.2. Список встроенных функций
- •Приложение C. Диагностика
- •Приложение D. Компиляторы Си PIC18 и PIC24/dsPIC
- •D.6. Использование стека
- •D.11. Банк доступа
- •D.12. Inline ассемблер
- •D.13. Прагмы
- •D.14. Модели памяти
- •D.15. Соглашения о вызове
- •D.16. Код запуска
- •D.17. Управляемые компилятором ресурсы
- •D.18. Оптимизация
- •D.20. Определяемое реализацией поведение
- •D.21. Битовые поля
Руководство пользователя по 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. |
Глава 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 |
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. |
: "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 |
Это приемлемо (т.е. следующий оператор всегда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. |