Книга по работе с WinAVR и AVR Studio / AVR_17_2011
.pdf
СПРАВОЧНЫЙ МАТЕРИАЛ
Книга по работе |
|
|
|
|
|
Роман Абраш |
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
с WinAVR и AVR Studio |
|
|
|
|
|
г. Новочеркасск |
|||||||||
|
|
|
|
E-mail: arv@radioliga.com |
|||||||||||
|
|
|
|
|
|
|
|
Входные операнды должны быть определены как доступ- |
|||||||
|
|
Продолжение. Начало в №1-12/2010; №1-4/2011 |
|
|
|||||||||||
|
|
|
|
ные только для чтения (без символа-модификатора). Одна- |
|||||||||||
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
|
|
|
ко, в случае, когда единственный операнд используется и |
|||||||
Операнды оператора asm() |
|
|
|
как входное и как выходное значение, существует способ |
|||||||||||
Параметры оператора asm, используемые в качестве опе- |
|
обойти это ограничение: необходимо вместо символа опе- |
|||||||||||||
рандов инструкций ассемблера, обозначаются, как было сказа- |
|
ранда использовать номер соответствующего операнда в ин- |
|||||||||||||
но ранее, определенными символами, помещаемыми в двой- |
|
струкции: |
|
|
|
|
|
|
|
||||||
ные кавычки (см. таблицу 4). |
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|||||
В таблице 4 перечислены все допустимые символы для обо- |
|
asm volatile(«swap %0» : «=r» (value) : «0» (value)); |
|
||||||||||||
значения параметров. Обнаружив такой символ, компилятор само- |
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
||||||||
стоятельно подставит вместо него соответствующее значение из |
|
Пример показывает, как переменная value используется |
|||||||||||||
числа допустимых. При этом могут быть странные на первый взгляд |
|
в качестве входного значения и в нее же помещается ре- |
|||||||||||||
ошибки, если символ выбран программистом неверно. Например, |
|
зультат ассемблерной команды, меняющей тетрады байта. |
|||||||||||||
программист применил для операнда-приемника инструкции ORI |
|
“0” – это номер операнда команды SWAP. Такая запись ука- |
|||||||||||||
символ “r”. Компилятор может выбрать для этого любой регистр из |
|
зывает компилятору использовать для операнда-приемника |
|||||||||||||
числа имеющихся в его распоряжении в текущий момент. При этом |
|
результата то же самое значение, что было выбрано для опе- |
|||||||||||||
может быть выбран и регистр, например, r3, что приведет к ошибке, |
|
ранда-источника. Однако следует знать, что компилятор |
|||||||||||||
т.к. для инструкции ORI допустимо применять только регистры из |
|
может выбрать одно и то же значение и для источника и для |
|||||||||||||
старшей половины. Компилятор может выбрать и «правильный» |
|
приемника, даже если это явно не указано. Обычно это не |
|||||||||||||
регистр, и это может привести к сомнениям: почему ранее работав- |
|
опасно, но может быть фатальным, если значение операн- |
|||||||||||||
шая без ошибок ассемблерная вставка вдруг стала вызывать ошиб- |
|
да-результата модифицируется другими ассемблерными ин- |
|||||||||||||
ку. Поэтому правильным выбором для ORI будет символ “d”. |
|
струкциями до выполнения сохранения результата операто- |
|||||||||||||
В таблице 5 приведены все инструкции ассемблера, требу- |
|
ра asm. |
|
|
|
|
|
|
|
||||||
ющие операндов, с указанием подходящих им символов. Одна- |
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
||||||||
ко, эти требования недостаточно строгие, т.к. не ограничивают, |
|
asm volatile(«in %0,%1» |
«\n\t» |
|
|
|
|||||||||
например, диапазон номеров битов от 0 до 7 и т.п. |
|
|
«out %1, %2» |
«\n\t» |
|
|
|
||||||||
|
|
: «=&r» (input) |
|
|
|
||||||||||
Любой символ может предваряться символом-модификатором: |
|
|
|
|
|
||||||||||
|
|
: «I» (_SFR_IO_ADDR(port)), «r» (output) |
|
||||||||||||
= – обозначает, что операнд только для записи. Использу- |
|
|
); |
|
|
|
|
|
|||||||
ется для операндов результата. |
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|||||
+ – обозначает, что операнд для записи и чтения. |
|
|
|
|
|
|
Таблица 5 |
||||||||
& – обозначает, что для вывода должен использоваться но- |
|
|
|
|
|
|
|||||||||
вый регистр. |
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
Инструкция |
|
Символы операндов |
Инструкция |
Символы операндов |
|
|
||||||
Используемые для результата операнды всегда должны быть |
|
|
|
||||||||||||
|
|
|
|
|
|
|
|
|
|||||||
adc |
|
r, r |
|
add |
r, r |
|
|
||||||||
только для записи и иметь вид «леводопустимого выражения». |
|
|
|
|
|||||||||||
|
|
|
|
|
|
|
|
|
|||||||
adiw |
|
w, I |
|
and |
r, r |
|
|
||||||||
Компилятор не проверяет совместимость типов операнда и при- |
|
|
|
|
|||||||||||
сваемого ему значения. |
|
|
|
andi |
|
d, M |
|
asr |
r |
|
|||||
|
|
|
|
|
Таблица 4 |
|
bclr |
|
I |
|
bld |
r, I |
|
||
|
|
|
|
|
|
|
|
brbc |
|
I, label |
|
brbs |
I, label |
|
|
Символ |
Что обозначает |
Допустимые |
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
||||||
значения |
|
|
bset |
|
I |
|
bst |
r, I |
|
|
|||||
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
a |
Старшие регистры без указателей |
r16 … r23 |
|
|
cbi |
|
I, I |
|
cbr |
d, I |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
b |
Базовые регистровые пары – указатели |
y, z |
|
|
com |
|
r |
|
cp |
r, r |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
d |
Старшие регистры |
r16 … r31 |
|
|
cpc |
|
r, r |
|
cpi |
d, M |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
e |
Регистровые пары – указатели |
x, y, z |
|
|
cpse |
|
r, r |
|
dec |
r |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
q |
Указатель стека |
SPH:SPL |
|
|
elpm |
|
t, z |
|
eor |
r, r |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
r |
Любой регистр |
r0 … r31 |
|
|
in |
|
r, I |
|
inc |
r |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
t |
Вспомогательный регистр |
r0 |
|
|
ld |
|
r, e |
|
ldd |
r, b |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
w |
Специальный старший регистр |
r24, r26, r28, r30 |
|
|
ldi |
|
d, M |
|
lds |
r, label |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
x |
Регистровая пара X |
x (r27:r26) |
|
|
lpm |
|
t, z |
|
lsl |
r |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
y |
Регистровая пара Y |
y (r29:r28) |
|
|
lsr |
|
r |
|
mov |
r, r |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
z |
Регистровая пара Z |
z (r31:r30) |
|
|
movw |
|
r, r |
|
mul |
r, r |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
G |
Kонстанта в формате с плавающей точкой |
0.0 |
|
|
neg |
|
r |
|
or |
r, r |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
I |
6-битовое положительное число (константа) |
0 … 63 |
|
|
ori |
|
d, M |
|
out |
I, r |
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
J |
6- битовое отрицательное число (константа) |
-63 … 0 |
|
|
pop |
|
r |
|
push |
r |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
K |
Целочисленная константа |
2 |
|
|
rol |
|
r |
|
ror |
r |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
L |
Целочисленная константа |
0 |
|
|
sbc |
|
r, r |
|
sbci |
d, M |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
l |
Младшие регистры |
r0 … r15 |
|
|
sbi |
|
I, I |
|
sbic |
I, I |
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
M |
Однобайтная константа |
0 … 255 |
|
|
sbiw |
|
w, I |
|
sbr |
d, M |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
N |
Целочисленная константа |
-1 |
|
|
sbrc |
|
r, I |
|
sbrs |
r, I |
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
O |
Целочисленная константа |
8, 16, 24 |
|
|
ser |
|
d |
|
st |
e, r |
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
P |
Целочисленная константа |
1 |
|
|
std |
|
b, r |
|
sts |
label, r |
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
Q |
Адрес памяти по указателю Y или Z со смещением |
|
|
|
sub |
|
r, r |
|
subi |
d, M |
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
R |
Целочисленная константа |
-6 to 5 |
|
|
swap |
|
r |
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Радиолюбитель – 05/2011 |
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
СПРАВОЧНЫЙ МАТЕРИАЛ
|
В этом примере сначала осуществляется считывание порта |
|
|
То есть вопреки всему ранее сказанному надо использовать |
|||||||||
|
port, а затем другое значение в этот же самый порт выводится. |
|
нижний регистр для указания младшего байта операнда! Это про- |
||||||||||
|
Компилятор вполне может выбрать для ввода и вывода один и |
|
блема реализации компилятора GCC текущей версии40. |
||||||||||
|
тот же регистр, например, r5, при этом значение r5, считанное |
|
|
|
|
|
|
|
|
|
|||
|
первой командой еще до того, как будет записано эпилогом в |
|
|
Зависимости оператора asm() |
|||||||||
|
переменную output, будет изменено значением, загруженным |
|
|
Как было сказано, последним элементом оператора asm мо- |
|||||||||
|
из переменной input для вывода во второй команде. Чтобы это- |
|
жет быть список зависимостей. Этот список может отсутствовать |
||||||||||
|
го избежать, применен модификатор &, который указывает ком- |
|
вместе с отделяющим его двоеточием. Однако если внутри ас- |
||||||||||
|
пилятору выбрать для операнда источника обязательно другой |
|
семблерной вставки программист использует значения регистров, |
||||||||||
|
регистр, т.е. не совпадающий ни с одним из выбранных для дру- |
|
не указанных в списках операндов, он обязан уведомить компиля- |
||||||||||
|
гих операндов “r”. |
|
|
|
тор об этом. В этом случае в код пролога будет добавлены коман- |
||||||||
|
Нередка ситуация, когда ассемблерная вставка должна ма- |
|
ды сохранения перечисленных в этом списке регистров, а в коде |
||||||||||
|
нипулировать числами более одного байта. В этом случае воз- |
|
эпилога эти значения будут восстановлены в прежнем виде. |
||||||||||
|
никает проблема с обращением к нескольким байтам, состав- |
|
|
Например, при помощи следующего кода осуществляется ато- |
|||||||||
|
ляющим один операнд. Для обозначения регистров, в которых |
|
марное увеличение 8-битной переменной: |
||||||||||
|
хранится значение переменной, используются дополнительные |
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|||||
|
символы – заглавные латинские символы «A», «B», «C» и т.д. |
|
asm volatile( |
|
|
|
|
|
|
||||
|
Такая запись указывает компилятору выбрать очередной ре- |
|
|
«cli» |
|
«\n\t» |
|
|
|
||||
|
|
|
«ld r24, %a0» |
«\n\t» |
|
|
|
||||||
|
гистр для хранения очередного байта, составляющего перемен- |
|
|
|
|
|
|||||||
|
|
|
«inc r24» |
|
«\n\t» |
|
|
|
|||||
|
ную. Младший байт соответствует символу «А», более старшие |
|
|
«st %a0, r24» |
«\n\t» |
|
|
|
|||||
|
последовательно назначаются для «В», «С» и т.д. Программист |
|
|
«sei» |
|
«\n\t» |
|
|
|
||||
|
|
: |
|
|
|
|
|
|
|||||
|
может использовать соответственно «%A0» для обращения к |
|
|
|
|
|
|
|
|||||
|
|
|
: «e» (ptr) |
|
|
|
|
|
|||||
|
младшему байту первого операнда, «%A1» – младшему байту |
|
|
: «r24» |
|
|
|
|
|
|
|||
|
второго операнда и т.д. |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
В этом примере производится перестановка байтов в 16-бит- |
|
|
Примечательно в этом примере то, что используется указа- |
|||||||||
|
ной переменной value: |
|
|
|
|
||||||||
|
|
|
|
тель для обращения к переменной, иначе сохранение значения |
|||||||||
|
|
|
|
|
|
||||||||
|
asm volatile(«mov __tmp_reg__, %A0» «\n\t» |
|
|
произошло бы только в коде эпилога, который будет добавлен пос- |
|||||||||
|
|
«mov %A0, %B0» |
«\n\t» |
|
|
ле команды разрешения прерывания, т.е. это уже нарушило бы |
|||||||
|
|
«mov %B0, __tmp_reg__» «\n\t» |
|
|
условие атомарности ассемблерной вставки. А с использованием |
||||||||
|
|
: «=r» (value) |
|
|
|
||||||||
|
|
|
|
|
указателя переменная обновляется до разрешения прерываний. |
||||||||
|
|
: «0» (value) |
|
|
|
||||||||
|
|
); |
|
|
|
|
Более правильным было бы в этом случае использовать |
||||||
|
|
|
|
|
|
__tmp_reg__ вместо r24: |
|
|
|
|
|||
|
Перестановка байтов 32-битного переменной value: |
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
asm volatile( |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
«cli» |
«\n\t» |
|
|
|
|
|
|
asm volatile(«mov __tmp_reg__, %A0» «\n\t» |
|
|
|
|
|
|
|
|
||||
|
|
|
|
«ld __tmp_reg__, %a0» |
«\n\t» |
|
|
|
|||||
|
|
«mov %A0, %D0» |
«\n\t» |
|
|
|
|
|
|
||||
|
|
|
|
|
«inc __tmp_reg__» |
|
«\n\t» |
|
|
|
|||
|
|
«mov %D0, __tmp_reg__» «\n\t» |
|
|
|
|
|
|
|
||||
|
|
|
|
|
«st %a0, __tmp_reg__» |
«\n\t» |
|
|
|
||||
|
|
«mov __tmp_reg__, %B0» «\n\t» |
|
|
|
|
|
|
|||||
|
|
|
|
|
«sei» |
«\n\t» |
|
|
|
|
|
||
|
|
«mov %B0, %C0» |
«\n\t» |
|
|
|
|
|
|
|
|
||
|
|
|
|
: |
|
|
|
|
|
|
|||
|
|
«mov %C0, __tmp_reg__» «\n\t» |
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
: «e» (ptr) |
|
|
|
|
|
|||
|
|
: «=r» (value) |
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
: «0» (value) |
|
|
|
); |
|
|
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Вышеприведенные примеры имеют одну неприятную особен- |
|||||||
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
||||||||
|
Для обозначения операнда, используемого и как источник, и |
|
ность: их нельзя использовать в участках программы, где преры- |
||||||||||
|
как приемник результата, разумно использовать модификатор «+»: |
|
вания уже запрещены, т.к. эти вставки принудительно разрешают |
||||||||||
|
|
|
|
|
|
прерывания. Казалось бы, эту проблему легко решить при помо- |
|||||||
|
|
|
|
|
|
||||||||
|
asm volatile(«mov __tmp_reg__, %A0» «\n\t» |
|
|
щи локальной переменной: |
|
|
|
|
|||||
|
|
«mov %A0, %D0» |
«\n\t» |
|
|
|
|
|
|
|
|
|
|
|
|
«mov %D0, __tmp_reg__» «\n\t» |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
«mov __tmp_reg__, %B0» «\n\t» |
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
uint8_t s; |
|
|
|
|
|
|
||
|
|
«mov %B0, %C0» |
«\n\t» |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
asm volatile( |
|
|
|
|
|
|||
|
|
«mov %C0, __tmp_reg__» «\n\t» |
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
«in %0, __SREG__» |
|
«\n\t» |
||||||
|
|
: «+r» (value) |
|
|
|
|
|
||||||
|
|
|
|
|
|
«cli» |
|
|
|
«\n\t» |
|||
|
|
); |
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
«ld __tmp_reg__, %a1» |
«\n\t» |
||||||
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
«inc __tmp_reg__» |
|
«\n\t» |
||||
|
Непредвиденная проблема может возникнуть еще и в случае, |
|
|
||||||||||
|
|
|
«st %a1, __tmp_reg__» |
«\n\t» |
|||||||||
|
если осуществляется работа с указателем, т.е. регистровой парой. |
|
|
«out __SREG__, %0» |
|
«\n\t» |
|||||||
|
|
|
: «=&r» (s) |
|
|
|
|
|
|||||
|
Предположим, программист использует следующее определения |
|
|
|
|
|
|
|
|||||
|
|
|
: «e» (ptr) |
|
|
|
|
|
|||||
|
операнда: |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
«e» (ptr) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
К сожалению, это не так, хотя и выглядит правильно. Ассемблер- |
||||||||
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
||||||||
|
Допустим, компилятор избрал для хранения этого операнда |
|
ный код модифицирует переменную, на которую указывает указа- |
||||||||||
|
|
тель ptr. Но загрузка значения указателя в регистровую пару осу- |
|||||||||||
|
регистровую пару Z, т.е. %A0 соответствует ZL (r30), а %B0 – ZH |
|
|||||||||||
|
(r31). Однако компилятор вызовет ошибку, если программист ис- |
|
ществляется в коде пролога, который выполняется еще до запре- |
||||||||||
|
|
щения прерываний, т.е. вполне значение указателя может быть из- |
|||||||||||
|
пользует обращение к этой паре так: |
|
|||||||||||
|
|
менено на этом этапе. Разумеется, в этом случае результат работы |
|||||||||||
|
|
|
|
|
|
||||||||
|
ld |
r24, Z |
|
|
|
ассемблерной вставки непредсказуем. Более того, при оптимиза- |
|||||||
|
|
|
|
ции значение указателя вообще может оказаться не в ОЗУ, а в ре- |
|||||||||
|
|
|
|
|
|
||||||||
|
Чтобы компилятор сгенерировал правильный код, необходи- |
|
гистрах. Самое меньшее, что можно в этом случае сделать, это |
||||||||||
|
|
применить специальный элемент списка зависимостей вставки |
|||||||||||
|
мо использовать только такую конструкцию: |
|
|||||||||||
|
|
memory: |
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
||
|
ld |
r24, %a0 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
5 8 |
|
|
|
|
|
|
41 На момент написания книги это соответствует версии GCC 4.3.хх |
||||||
|
|
|
|
|
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Радиолюбитель – 05/2011 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
СПРАВОЧНЫЙ МАТЕРИАЛ
{ |
|
|
|
|
|
|
«dec %1» |
«\n\t» |
|
|
|
|
|
|
|
|
«brne L_dl1%=» |
«\n\t» |
|
||
uint8_t s; |
|
|
|
|
|
|||||
|
|
|
|
: «=&w» (cnt) |
|
|
||||
asm volatile( |
|
|
|
|
|
|
||||
|
|
|
|
: «r» (ms), «r» (delay_count) |
|
|||||
«in %0, __SREG__» |
«\n\t» |
|
|
|
|
|||||
|
|
|
); |
|
|
|
||||
«cli» |
«\n\t» |
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
||||
«ld __tmp_reg__, %a1» |
«\n\t» |
|
|
|
} |
|
|
|
||
«inc __tmp_reg__» |
«\n\t» |
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
||||
«st %a1, __tmp_reg__» |
«\n\t» |
|
|
|
Пример демонстрирует реализацию функции задержки про- |
|||||
«out __SREG__, %0» |
«\n\t» |
|
|
|
||||||
|
|
|
граммным циклом на заданное количество миллисекунд. В функ- |
|||||||
: «=&r» (s) |
|
|
|
|
||||||
: «e» (ptr) |
|
|
|
|
ции используется глобальная переменная delay_count, которая |
|||||
: «memory» |
|
|
|
|
должна содержать значение тактовой частоты, деленное на 4000, |
|||||
); |
|
|
|
|
|
|
||||
|
|
|
|
|
|
причем это значение должно быть присвоено этой переменной до |
||||
} |
|
|
|
|
|
|
||||
|
|
|
|
|
|
обращений к функции. Как было показано ранее, функция исполь- |
||||
|
|
|
|
|
|
|
||||
Это укажет компилятору учесть при оптимизации тот факт, что |
|
зует локальную переменную для сохранения значения используе- |
||||||||
|
мых регистров. |
|
|
|||||||
ассемблерная вставка использует память, которую нельзя модифици- |
|
|
|
|||||||
|
Следующий пример показывает, как реализуется возврат зна- |
|||||||||
ровать. Но наиболее простой и удобный способ все-таки состоит в |
|
|||||||||
|
чения из ассемблерной функции: |
|||||||||
том, чтобы использовать volatile при определении указателя ptr: |
|
|||||||||
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
volatile uint_t *ptr; |
|
|
|
|
uint16_t inw(uint8_t port) |
|
|
|||
|
|
|
|
{ |
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint16_t result; |
|
|
|
Это обеспечит правильное поведение как самой ассемблерной |
|
|
|
|||||||
|
asm volatile ( |
|
|
|||||||
вставки, так и оптимизатора в ее отношении. |
|
|
|
«in %A0,%1» «\n\t» |
|
|
||||
|
|
|
«in %B0,(%1) + 1» |
|
|
|||||
Примечание. Случаи действительной необходимости в указании |
|
|
|
|||||||
|
: «=r» (result) |
|
|
|||||||
списка зависимостей достаточно редки. Почти всегда есть возмож- |
|
: «I» (_SFR_IO_ADDR(port)) |
|
|||||||
ность избежать их, обеспечив компилятору больше свободы в оптими- |
|
); |
|
|
|
|||||
|
return result; |
|
|
|||||||
зации кода. |
|
|
|
|
|
|
||||
|
|
|
|
} |
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
Макросы на ассемблере |
|
|
|
|
|
|
|
|||
|
|
|
Функция inw возвращает 16-разрядное число, считанное из |
|||||||
Для многократного использования ассемблерных вставок в раз- |
|
|||||||||
|
пары «смежных» портов ввода-вывода. port определяет «младший» |
|||||||||
личных проектах имеет смысл разместить их в заголовочном файле в |
|
|||||||||
|
порт. |
|
|
|||||||
виде макросов. AVR-LIBC содержит немалое количество таких фай- |
|
|
|
|||||||
|
|
|
|
|
||||||
лов, найти которые можно в директории avr/include. Однако такое ис- |
|
Соглашения об именах Си и ассемблера |
||||||||
пользование ассемблерных вставок может вызвать предупреждения |
|
По умолчанию GCC использует одинаковые имена переменных |
||||||||
компилятора для режима соответствия ANSII-стандарту. Чтобы избе- |
|
|||||||||
|
и функций для Си и для ассемблера. Однако программист может |
|||||||||
жать предупреждений, достаточно писать __asm__ вместо asm и |
|
|||||||||
|
изменить эту ситуацию при помощи особой формы оператора asm: |
|||||||||
__volatile__ вместо volatile – это полные синонимы. |
|
|||||||||
|
|
|
|
|
||||||
Более заметная проблема ассемблерных вставок в виде макро- |
|
unsigned long value asm(«clock») = 3686400; |
|
|||||||
сов связана с использованием меток. Макрос – это ведь просто под- |
|
|
|
|
|
|||||
|
|
|
|
|||||||
становка текста, поэтому в случае использования обычных меток в |
|
Это определение вынудит компилятор использовать имя clock |
||||||||
ассемблерныхвставкахвозможнопоявлениеодинаковыхметоквраз- |
|
вместо его значения. Такая «подмена» имеет смысл только для |
||||||||
личных участках программы, что недопустимо. Для избежания этого |
|
глобальных (статических) или внешних переменных, так как локаль- |
||||||||
следуетиспользоватьособыйсинтаксисметокдляассемблерныхвста- |
|
ные переменные не имеют символьных имен для ассемблера. Кро- |
||||||||
вок: в определении метки используется сочетание «%=», которое на |
|
ме того, локальные переменные могут быть помещены в регистры. |
||||||||
этапе компиляции заменяется на некий уникальный номер, таким об- |
|
Программист может назначить локальной переменной опреде- |
||||||||
разом, гарантируя уникальность всех меток. Вот, например, как реа- |
|
ленный регистр: |
|
|
||||||
лизован один из макросов в iomacros.h: |
|
|
|
|
|
|
|
|||
|
|
|
void Count(void) |
|
|
|||||
|
|
|
|
|
|
|
|
|
||
#define loop_until_bit_is_clear(port,bit) |
\ |
|
|
{ |
|
|
|
|||
|
|
register unsigned char counter asm(«r3»); |
|
|||||||
__asm__ __volatile__ ( |
\ |
|
|
|
||||||
|
|
|
|
|
|
|||||
«L_%=: « «sbic %0, %1» «\n\t» |
\ |
|
|
... какие то действия ... |
|
|||||
«rjmp L_%=» |
|
\ |
|
|
|
|||||
|
|
|
asm volatile(«clr r3»); |
|
|
|||||
: /* no outputs */ |
\ |
|
|
|
|
|||||
|
|
... какие то действия ... |
|
|||||||
: «I» (_SFR_IO_ADDR(port)), |
|
|
|
|||||||
|
|
|
|
|
|
|||||
«I» (bit) |
|
|
|
|
} |
|
|
|
||
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
В этом примере показано, как заставить компилятор поместить |
||||
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
||||
Метка L_%= будет заменена на что-то типа L_1234, при- |
|
локальную переменную counter в регистр r3. Однако, это не «веч- |
||||||||
чем это будет гарантированно уникальная метка. |
|
ное» закрепление: если оптимизатор компилятора сочтет, что зна- |
||||||||
|
|
|
|
|
|
|
чение переменной более не требуется сохранять, назначение реги- |
|||
Функции на ассемблере |
|
|
|
стра r3 может быть переопределено. Если программист закрепляет |
||||||
Макрос с ассемблерной вставкой будет приводить к появлению |
|
слишком много регистров за переменными, компилятор может ис- |
||||||||
одного и того же кода каждый раз при использовании макроса. Для |
|
черпать ресурсы регистров для компиляции остального кода. Кро- |
||||||||
многих критичных к размеру памяти приложений это неприемлемо. |
|
ме этого, принудительное назначение регистра переменной чаще |
||||||||
В этом случае логичнее реализовать С-функцию целиком на ас- |
|
приводит к ухудшению эффективности кода вместо ожидаемой его |
||||||||
семблере. |
|
|
|
|
оптимизации. |
|
|
|||
|
|
|
|
|
|
|
Существует и способ изменить имя функции: |
|||
void delay(uint8_t ms) |
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|||
{ |
|
|
|
|
|
|
extern long Calc(void) asm («CALCULATE»); |
|
||
uint16_t cnt; |
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|||
asm volatile ( |
|
|
|
|
|
|
|
|
||
|
|
|
|
Вышеприведенный пример заставит компилятор при обраще- |
||||||
«\n» |
|
|
|
|
||||||
«L_dl1%=:» |
«\n\t» |
|
|
|
нии к функции Calc() генерировать инструкцию вызова функции |
|||||
«mov %A0, %A2» |
«\n\t» |
|
|
|
||||||
|
|
|
CALCULATE. |
|
|
|||||
«mov %B0, %B2» |
«\n» |
|
|
|
|
|
||||
«L_dl2%=:» |
«\n\t» |
|
|
|
|
|
|
|
||
«sbiw %A0, 1»«\n\t» |
|
|
|
|
|
|
|
|
||
«brne L_dl2%=» |
«\n\t» |
|
|
|
|
|
Окончание следует. |
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 9 |
Радиолюбитель – 05/2011 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
