Один важный нюанс в работе всех команд перехода заключается в том, что они могут занимать непредсказуемое количество циклов (1 или 2), в зависимости от того, выполняется условие или нет. В AVR реализован конвейер команд, который «тупо» полагает, что следующей будет выполняться команда сразу после команды перехода. Естественно, если ветвление необходимо (в большинстве случаев), конвейер останавливается на один лишний такт, в течение которого происходит выборка адреса перехода. Однако этот недостаток с лихвой компенсируется тем, что за счет конвейера почти все остальные команды выполняются за один такт— недостижимая мечта для многих других типов контроллеров (например, в популярном до сих пор семействе 8051, выпущенном еще в начале 80-х, команда выполняется как минимум за 12 тактов, хотя некоторые современные клоны этого семейства могут работать и быстрее).

Арифметика и логика в интерпретации AVR

Арифметические операции для AVR на первый взгляд могут показаться реализованными довольно странно для пользователя, привыкшего к бытовому представлению об арифметике, но на самом деле получается очень стройная система.

Не вызывают никаких возражений только очевидные операции: add R1,R2 (сложить два регистра, записать результат в первый) и sub ri,r2 (вычесть второй из первого, записать результат в первый). Но если вдуматься, то вопросов возникает множество: а что будет, если сумма превышает 255? Или разность меньше нуля? Куда девать остатки? Оказывается, все продумано: для учета переноса есть специальные команды adc и sbc соответственно. Корректная операция сложения двух 16-разрядных чисел будет занимать две команды:

add RL1,RL2

adc RH1,RH2

Здесь RL1 и RL2 содержат младшие (low) байты слагаемых, a RH1 и RH2 — старшие (high). Если при первой операции результат превысит 255, то перенос запишется в специальный флаг переноса (в регистре флагов sreg обозначается буквой С) и учтется при второй операции. Аналогично выглядит операция вычитания.

Постойте, но мы же вовсе не хотели складывать 16-разрядные числа! Мы хотели всего лишь сделать так, чтобы в результате сложения 8-разрядных чисел получился правильный результат, пусть он займет два байта. На что нам тогда старший байт второго слагаемого, если его вообще в природе не существует? Конечно, можно сделать его фиктивным, загрузив в некий регистр нулевое значение, но это только кажется, что регистров у AVR много (аж 32 штуки), на самом деле они довольно быстро расходуются под переменные и разные другие надобности, и занимать целый регистр под фиктивную операцию, пусть даже на один раз, как-то некрасиво. Потому «экономная» операция сложения 8-разрядных чисел будет выглядеть таким образом:

add RL1,R2

brcc add_8

inc RH1

add_8:

Исходные слагаемые находятся в R1 и R2, а результат будет в RH1:RH2. Отметим, что в старшем разряде (RH1) в результате может оказаться только либо 0, либо 1, т. к. сумма двух восьмиразрядных чисел не может превысить число 510 (255 + 255), именно потому флаг переноса С представляет собой один-единственный бит в регистре флагов. В этой процедуре команда brcc представляет собой операцию условного перехода на метку add_8 по условию, что флаг переноса равен нулю (BRanch if Carry Cleared). Таким образом, если флаг не равен нулю, выполнится операция увеличения значения регистра RH1 на единицу inc RH1, в противном случае она будет пропущена.

Внимательный читатель, несомненно, уже заметил ошибку в программе: а чему равно значение RH1 до выполнения нашей процедуры? Вдруг оно совсем не ноль, и тогда нельзя говорить о корректном результате. Поэтому правильней было бы дополнить нашу процедуру еще одним оператором, который расположить раньше всех остальных: cir RH1 (т. е. очистить RH1).

Заметим, что во всех случаях процедура разрастается во времени: было две команды, стало четыре, причем тут имеется еще и неявное замедление, поскольку все представленные арифметические команды выполняются за один такт, а команда ветвления brcc может занимать такт (если С = 1), а может и два (если С = 0). Итого мы выиграли один регистр, а потеряли два или три такта. И так всегда: есть процедуры, оптимизированные по времени, есть — по количеству команд, а могут быть и по количеству занимаемых регистров. Идеала, как и везде в жизни, тут не добиться, приходится идти на компромисс.

Перейти на страницу:

Поиск

Похожие книги