С используемыми здесь командами установки и сброса отдельных бит (sbi, sbr и т. п.) мы плотнее познакомимся чуть позже, а сейчас задержимся на ключевой команде всего алгоритма — sbrs, что расшифровывается, как Skip if Bit in Register is Set («пропустить, если бит в регистре установлен»). Имеется в виду, что по состоянию бита (если он установлен в единицу) пропустить нужно следующую команду. В качестве последней обычно также выступает одна из команд ветвления, как здесь, но далеко не всегда. Удобно, например, организовывать выход из прерывания или подпрограммы по какому-то условию, если поставить следующей после sbrs команду reti или, соответственно, ret (примеры мы еще встретим).

Противоположная по логике процедура записывается, как sbrc (Skip if Bit in Register is Cleared — «пропустить, если бит в регистре очищен», т. е. равен нулю). Наконец, есть еще пара аналогичных команд — sbis и sbic, которые применяются, когда нужно отследить состояние бита в регистре ввода/вывода (I/O), а не в регистре общего назначения. Все эти команды мы будем активно применять в дальнейшем.

Наконец, в самой обширной группе команд ветвления имена начинаются с букв Ьг (от слова branch — «ветка»). Это команды условного перехода, которые считаются одними из самых главных в любой системе программирования, т. к. позволяют организовывать циклы — базовое понятие, программистских наук. По смыслу все эти команды сводятся к банальному if… then («если… то»). Мы будем пользоваться лишь частью этих команд, потому что они во многом взаимозаменяемы, и здесь подробно разберем только одну пару команд. Смысл остальных вам будет понятен по ходу дела.

Это наиболее часто употребляемая пара brne (Branch if Not Equal, «перейти, если не равно») и breq (Branch if Equal, «перейти, если равно»). Уже из смысла этих команд понятно, как они пишутся: после команды следует метка, на которую нужно перейти. Вопрос только такой: откуда здесь берется собственно условие? Для этого все команды ветвления обязательно употребляют в паре с одной из команд, устанавливающих флаг нуля Z в регистре флагов SREG. Обычно (и это наиболее наглядно) для этой цели служит команда ср (от compare— «сравнить»), которая сравнивает регистры, или cpi («сравнить с непосредственным значением»). Например, вот так будет вы глядеть простейший цикл, в котором переменная temp последовательно принимает значения от 1 до 10:

     clr temp  ;обнулить temp

back_loop:

     inc temp  ;увеличиваем temp на 1

     <что-то делаем, необязательно с помощью temp>

     cpi temp,10

     brne back_loop

Обратите внимание: если надо, чтобы temp начинала с нулевого значения, то фрагмент «что-то делаем» следует вставить до команды inc, но тогда последним рабочим значением temp в цикле будет 9, а не 10, а после выхода из процедуры — все равно 10.

Другой нюанс заключается в том, что удобная команда сравнения с непосредственным числом cpi работает только для регистров с номерами от 16 до 31 (как и многие другие команды работы с непосредственными значениями, например, ldi). По этой причине рабочие переменные (temp, счетчики) всегда желательно выбирать из этой половины регистрового файла (в примерах в этой книге, как и в «аппнотах», кстати, обычно temp — это r16, хотя и не всегда). Положение осложняется тем, что регистры из старшей половины наиболее дефицитны: последние шесть из них объединены в пары для работы с памятью (см. далее) и некоторых других операций, r24 и r25 задействованы в команде adiw и т. п. Если переменных не хватает, то регистр из первой половины регистрового файла (допустим, это r15) в аналогичном цикле приходится использовать с парой команд:

ldi   temp,10

ср  r15,temp

Иногда проще построить декрементный цикл, в котором переменная уменьшается от заданного значения до нуля:

     ldi temp,10  ;загружаем 10 в temp

back_loop:

     dec temp  ;уменьшаем temp на 1

     <что-то делаем с помощью temp>

     brne back_loop

Как видите, здесь вообще ничего сравнивать не требуется, потому что команда dec при достижении нуля сама установит флаг Z (то же относится к команде tst temp, которая эквивалентна команде cpi temp, 0). И если даже выбрать регистр из первой половины, то лишняя команда понадобится не в каждом цикле, а только один раз для загрузки предварительного значения:

ldi temp,10  ;загружаем 10 в temp

mov r15,temp  ;загружаем 10 в r15 и далее его используем

;в команде dec

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

Поиск

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