Главная страница
Библиотека (скачать книги)
Скачать софт
Введение в программирование
Стандарты для C++
Уроки по C#
Уроки по Python
HTML
Веб-дизайн
Ассемблер в среде Windows
ActiveX
Javascript
Общее о Линукс
Линукс - подробно
Линукс - новое
Delphi
Паскаль для начинающих
Турбопаскаль
Новости
Партнеры
Наши предложения
Архив новостей





Подробности
Для всех флагов регистра SREG есть особые пары команд, устанавливающие или сбрасывающие их. С одной такой парой мы уже знакомы — это сброс или установка флага I командами sei и cli, разрешающей и запрещающей прерывания соответственно. Команды для остальных флагов формируются по тому же мнемоническому шаблону: например, установка флага переноса с осуществляется командой sec, а сброс — командой clc, установка и сброс флага Т — командами set и clt. Все эти команды — лишь синонимы пары команд bset, s и bclr, s (где s — номер бита в регистре SREG), позволяющих установить или сбросить любой флаг единообразным способом.

Специально для сравнения многобайтовых чисел существует команда срс, \читывающая флаг переноса с (ср. с командами adc и sbc в разделе "Команды арифметических операций" данной главы). Команда эта, как и команда ср, предназначена для сравнения с регистром, а не константой, хотя cpi также меняет флаг переноса и может использоваться в паре с командой срс. Листинг 6.1 иллюстрирует как, к примеру, можно проверить пару переменных AddrH:AddrL, обозначающих адреса во внешней памяти, на превышение числа 32 767 = S7FFF (что может потребоваться для проверки выхода за диапазон допустимых адресов, если объем памяти ограничен величиной 32 кбайта).

Листинг 6.1
cpi AddrL,OxFF ldi temp,0x7F cpc AddrH,temp brio continue_ ;если меньше, на продолжение clr AddrH ;иначе начинаем отсчет clr AddrL ;с нулевого адреса continue :
Попробуем посмотреть, как команды перехода работают на практике для организации циклов. Например, простейший цикл, в котором переменная temp последовательно принимает значения от 1 до 10, показан в листинге 6.2.

Листинг 6.2
clr temp /обнулить temp back_loop:
inc temp /увеличиваем temp на 1
<что-то делаем, необязательно с помощью temp>
cpi temp,10
brne back_loop

Обратите внимание— если требуется, чтобы temp начинала с нулевого значения, то фрагмент "что-то делаем" следует вставить до команды inc (цикл с постусловием), но тогда последним рабочим значением temp в цикле будет 9, а не 10 (а по выходу из процедуры — все равно 10). Иногда проще построить декрементный цикл — когда переменная уменьшается от заданного значения до нуля (листинг 6.3).

Листинг 6,3
ldi temp, 10 /загружаем 10 в temp - back_loop:
dec temp /уменьшаем temp на 1 <что-то делаем с помощью temp> brne back_loop

Как мы уже отмечали в предыдущей главе, при использовании команды dec вообще специальной команды сравнения не требуется, потому что она при достижении нуля сама установит флаг z (то же относится к команде tst, которая проверяет на условие "равно или меньше нуля"). И если даже при наличии dec задействовать регистр из первой половины регистрового файла (где команды загрузки непосредственного значения или сравнения с константой не работают), то лишняя команда понадобится не в каждом цикле, а только один раз — для загрузки предварительного значения:
ldi temp, 10 /загружаем 10 в temp
mov rl5,temp /загружаем 10 в г15 и далее, его используем
/в команде dec

Кроме этих команд перехода по равенству (неравенству), проверяющих значение флага z, есть команды, которые осуществляют переход по значениям других разрядов в регистре флагов SREG, устанавливаемых в зависимости от результатов операции ср или cpi. Например, команды brio ("перейти, если меньше") и brsh ("перейти, если больше или равно" — не следует путать ее с командой brhs, проверяющей флаг н на равенство 1) осуществляют переход в зависимости от состояния флага переноса с. Если в команде ср ri, г2 первый регистр окажется содержащим значение меньше, чем у второго, то флаг переноса будет установлен, а по команде brio произойдет переход. По команде brsh переход произойдет в противоположном случае — если флаг с оказался сброшен из-за того, что ri > г2. Команда brio при этом полностью эквивалентна команде brcs ("перейти, если флаг с установлен"), а команда brsh — уже встречавшейся нам в прошлой главе команде Ьгсс ("перейти, если флаг с сброшен"). Причем эти эквивалентные пары команд имеют также идентичные коды операций и по сути представляют собой синонимы одной команды, введенные для удобства мнемонического восприятия.

Мало того, если внимательно изучить коды операций для команд условного перехода (а их порядка 20), то окажется, что все они являются разными синонимами всего двух команд: brbs s, к и brbc s, к, где s — номер бита в регистре SREG (а к— адрес перехода). Как мы видим, эти команды более универсальны (например, с их помощью можно работать с шестым битом т, по состоянию которого отдельных команд перехода не предусмотрено).

Важная особенность команд типа brxx состоит в том, что они могут адресовать метку, отличающуюся от исходного адреса в последовательности команд не более чем на 63 позиции вперед или на 64 позиции назад, т. е. пригодны только для перехода "поблизости". Второй важный нюанс в работе всех команд перехода также заключается в том, что они могут занимать непредсказуемое количество циклов: один или два, в зависимости от того, выполняется условие или нет. В AVR используется конвейер команд, который "тупо" полагает, что следующей будет выполняться команда сразу после команды перехода. Естественно, если ветвление необходимо, конвейер останавливается на один лишний такт, в течение которого происходит выборка адреса перехода. Однако этот недостаток с лихвой компенсируется тем, что за счет конвейера почти все остальные команды выполняются за один такт.

Наконец, нужно учитывать, что регистр SREG не сохраняется при переходе к обработке прерывания, и если в прерывании встречаются команды, его модифицирующие, то его содержимое может быть испорчено. Поэтому если есть теоретическая возможность того, что прерывание "вклинится" между командой сравнения (или другой, устанавливающей биты SREG) И командой условного перехода, то SREG нужно сохранять и восстанавливать принудительно. Проще всего это делать через стек, командами push и pop в начале и конце обработчика прерывания (листинг 6.4).

Листинг 6.4 1
TIMER1: ;прерывание от таймера
push temp ;сохраняем в стеке рабочую переменную in temp,SREG
push temp /сохраняем в стеке SREG
<процедура прерывания>
pop_int:
pop temp /извлекаем SREG out SREG,temp
pop temp /извлекаем рабочую переменную reti /возврат из прерывания

Разумеется, сохранение заодно и рабочей переменной temp необязательно, и здесь приводится лишь в качестве примера. Просто в операциях сравнения нередко участвует и рабочая переменная, а она также может быть испорчена в процессе обработки "вклинившегося" прерывания.

Заметки на полях
Отметим, что во многих случаях такой прием с сохранением регистра SREG можно все же обойти, чтобы не загромождать код и не удлинять процедуры, хоть это в принципе и неграмотно (и я вам этого не говорил!). Все дело в вероятности наихудшего стечения обстоятельств, которую всегда можно прикинуть. Пусть, например, программа в основном цикле ожидает приема некой команды по UART, игнорируя любые другие пришедшие байты:
G_cykle:
rcall in_com /вызов процедуры приема байта см. главу 13 cpi temp,Command_A
/определяем, та ли команда breq ргос_А /если та, то на процедуру обработки команды rjmp G_cykle /иначе все сначала



     
 

Библиотека программиста. 2009.
Администратор: admin@programmer-lib.ru