В качестве переменных var_1 и var_2 возможны любые регистры, при этом действия внутри процедуры всегда будут совершаться с заданными регистрами var_loc. Обратите внимание, что когда таких переменных несколько, важно соблюдать правильный порядок их помещения в стек и извлечения оттуда, согласно принципу «первым вошел — последним вышел» (в программах на языках высокого уровня за порядком переменных в стеке следят специальные форматы вызова функций типа stdcall и подобные).
Аналогично происходит обработка прерываний, только специальной команды, как вы знаете, там нет, вызов производится обычным переходом rjmp или jmp, но поскольку он осуществляется с определенного адреса (там, где стоит вектор прерывания), то контроллер делает то же самое: сохраняет в стеке адрес командного счетчика, на котором выполнение основной программы было грубо нарушено, начинает выполнять прерывание и ожидает команды возврата, здесь она записывается как reti (return interrupt).
Еще одна важнейшая группа команд ветвления программы — команды перехода по состоянию отдельного бита в указанном регистре (sbrs, sbic и т. п.). Они очень удобны для организации процедур, аналогичных оператору выбора CASE в языках высокого уровня, но, к сожалению, обладают непривычной логикой: «пропустить следующую команду, если условие выполняется» и для новичка могут показаться слишком заумными. В качестве примера приведу довольно сложную по логике работы, но характерную для микроконтроллерной техники процедуру, в которой задача формулируется так: при наступлении некоторого условия мигать попеременно зеленым и красным светодиодами (СД).
Предположим, что условие мигания задается состоянием бита 3 в некоем рабочем регистре, который назовем регистром флагов — Flag (не путать с «официальным» регистром флагов SREG, о котором далее). Если бит 3 регистра Flag равен единице (установлен), надо мигать, если нет (сброшен) — оба СД погашены.
Красный СД подсоединен к выходу порта В номер 5, а зеленый — к выходу порта С номер 7 (разумеется, это могут быть любые другие выводы других портов).
Текущее состояние СД задается битом 4 в том же регистре флагов Flag.
Алгоритм работы такой программы на языке Pascal (листинг 13.3) описывается типичным вложенным оператором выбора.
case <бит 3 per. Flag> of
0: <погасить оба СД>
1: case <бит 4 per. Flag> of
1: <устанавливаем Port С, вых. 7>; //горим зеленым <сбрасываем
Port В, вых. 5>; //гасим красный
<сбрасываем бит 4 Flag>; {
0: устанавливаем Port В, вых. 5>; //горим красным
<сбрасываем Port С, вых. 7>; //гасим зеленый
<устанавливаем бит 4 Flag>; //
end;
end;
Разобравшийся в алгоритме читатель уже, несомненно, задает вопрос — а как обеспечить цикличность? Для этого подобный код включают в обработчик события по таймеру с секундным, например, интервалом. Причем, что характерно, и в микроконтроллере, и в операционной системе Windows это происходит абсолютно одинаково: инициализируется системный таймер (в МК для этого надо разрешить соответствующее прерывание), задается интервал его срабатывания (в Windows это одна команда, в МК их несколько больше) и — вперед! Но с таймерами мы будем разбираться далее по ходу дела, а пока посмотрим, как тот же алгоритм реализовывается в МК (листинг 13.4).
sbrs Flag,3 ;если флаг 3 стоит, будем мигать
rjmp dark ;иначе будем гасить
sbrs Flag,4 ;если флаг 4 стоит, будем гореть зеленым
rjmp set4 ;иначе красным
cbr Flag, 0Ь00010000 ;следующий раз горим красным
cbi PortB,5 ;гасим зеленый
sbi PortC,7 ;горим зеленым
rjmp continue ;все готово
set4: ;если флаг 4 не стоит, будем гореть красным
sbr Flag, 0Ь00010000 ;следующий раз горим зеленым
cbi PortC,7 ;гасим красный
sbi PortB,5 ;горим красным
rjmp continue ;все готово
dark:
cbi PortC,7 ;гасим оба
cbi PortB,5
continue:
…