Что такое циклы и какого вида они бывают
Цикл – это повторение однообразной последовательности действий некоторое количество раз. В программировании цикл – это выполнение одной команды или их множества, некоторое количество раз. Цикл – это основа основ. На их применении организована почти вся компьютерная техника. Как ни странно циклы бывают конечные и бесконечные.
Бесконечный цикл – это повторение однообразной последовательности действий постоянно. К бесконечному циклу можно отнести движение планеты вокруг своей оси, зависшую программу, и саму жизнь в каком-то смысле. Планета может быть когда-то, остановиться, но пока этого не произошло, пусть она будет примером. Организовать бесконечный цикл программно не составляет никакого труда. Вы с этим уже сталкивались, но может быть, не предали значения:
; простейший бесконечный цикл
main:
goto main ; постоянно переходить на main
Конечный цикл – это повторение однообразной последовательности действий, до наступления какого-то условия. Человек, бегущий по кругу, двигатель автомобиля, бьющееся сердце – всё это конечные циклы. Чтобы организовать конечный цикл программно, следует придумать условие, по которому он будет завершён. Пусть условием завершения будет окончание элементов массива.
; конечный цикл
size EQU 0x20 ; размер массива
.
.
main:
movlw D‘10’ ;
movwf size ; size = 10
A2: decfsz size ; пока не 0 -
goto A2 ; переходить на А2
; если 0 –
goto main ; перейти на main
Как видите также ничего сложного и команда decfsz пригодилась.
Также проверку цикла на окончание можно организовать, используя команду btfss. Казалось бы, если исключить из команды decfsz вычитание 1, то остальная её часть идентична btfss, но это не так.
Команды btfss и btfsc, направлены на работу с битами, поэтому за 1 машинный цикл способны произвести проверку 1 бита, указанного в регистре. В целом если бы не было одной хитрой особенности, заложенной проектировщиками МК, тогда проверку всего регистра на 0 результат пришлось бы организовывать не совсем удачным, но рабочим способом – проверяя каждый из 8 битов, на не единичное значение. Пример:
; проверка регистр на 0 результат, используя команду btfss
movlw B‘11001100’ ; W = 11001100
movwf value ; value = 11001100
D2: decf value,f ; value = value - 1
; начать проверку на 0 результат
D3: btfss value,7 ; 7 бит:
goto D4 ; 0 – проверить 6 бит
goto D2 ; 1 – назад к D2
D4: btfss value,6 ; 6 бит:
goto D5 ; 0 – проверить 5 бит
goto D2 ; 1 – назад к D2
.
.
D9: btfss value,0 ; 0 - бит
goto D10 ; 0 - выход
goto D2 ; 1 – назад к D2
D10: goto exit ; - проверка окончена, value = 0
Наберите код и посмотрите в отладчике, с помощью окна Watch, что происходит с переменной value. Хотя код занимает огромное количество машинных циклов, увеличивает программу в несколько раз, и вообще эстетически не приемлем – он справляется с возложенной на него задачей. Такое решение проблемы, наверное, имело смысл, если бы не существовало альтернативы. Но к счастью она есть.
В официальной документации, о работе с битами сказано: «При изменении любого бита регистра, из памяти данных, читается целый байт, содержащий необходимый бит, изменяет его(бит), после чего данные обратно записываются в память». Когда вы знакомились с регистрами общего и специального назначения, то должны были запомнить, что условно их следует разделить на регистры контроля и управления. В целом чёткой границы не существует и практически всегда один регистр отвечает как за контроль, так и за управление. Следует вспомнить о регистре STATUS, который предназначен для контроля и управления МК. Чтобы не отправлять вас в путешествие к предыдущим лабораторным работам, автор нужную информацию о регистре STATUS разместил чуть ниже, на рис.5.1.
Рис.5.1 – регистр STATUS
Обратите внимание на 2 бит: Z (zero) – флаг нулевого результата, который имеет два параметра: 1 = нулевой результат выполнения арифметической или логической операции, 0 = не нулевой результат выполнения арифметической или логической операции. Вдаваться в подробности реализации, как это происходит на машинном уровне, мы не станем, а лишь отметим тот факт, что когда какой-либо из регистров, после проведения операции, получает 0 результат, 2 бит регистра STATUS это обязательно зарегистрирует и выставит 1. Благодаря этому предыдущий код проверки каждого бита, можно отложить в долгий ящик на случай безвыходных ситуаций, и применить другой подход – проверки 2 бита регистра STATUS на не 0 результат, который является оптимальным во всех отношениях. Текущий пример является переработанной версией предыдущего:
; проверка регистр на 0 результат, используя команду btfss и регистр STATUS
movlw B‘11001100’ ; W = 11001100
movwf value ; value = 11001100
D2: decf value,f ; value = value - 1
; начать проверку на 0 результат
D3: btfss STATUS,2 ; проверить флаг Z на не 0:
goto D4 ; результат 0 – value = 0
goto D3 ; результат 1 – обратно к D2
D4: exit ; проверка завершена
Думаю, разница ощутима, а также следует запомнить тот факт, что решений существует огромное множество, но следует всегда находить «золотую середину» и выбирать самое лучшее, при котором и программист не «перетруждает» себя работой, и процессор не загружен выполнением лишних циклов бесполезных команд.
Вернёмся к началу разговора и отметим, что команда decfsz при получении 0 результата, не устанавливает 2 бит регистра STATUS в 1. Будьте внимательны, когда занимаетесь разработкой алгоритма, иначе потом придётся искать ошибки, которых нет.
И чтобы завершить раздел о циклах, решим, наконец, задачу и оптимизируем программу со снятием значений порта, используя все рассмотренные выше инструменты.
; вариант 2 – конечная версия программы
size EQU 0x20 ; размер массива
array_data EUQ 0x21 ; адрес начала массиа для хранения 60 значений
.
.
C2: movlw D‘60’ ; размер массива
movwf size ; инициализировать значение
movlw array_data ; взятие адреса массива
movwf FSR ; загрузка адреса в FSR
C3:
; задержка на 1с
movfw PORT ; считываем данные порта
movwf INDF ; запись данных за 1 секунду по адресу 0х20
incf FSR,f ; следующий адрес, FSR = FSR + 1;
decf size,f ; уменьшение кол-ва элементов
btfss STATUS,2 ; элементы массива кончились?
goto C3 ; нет, продолжаем запись
goto C4 ; да, начинаем расчёт
C4: call Calc_Data ; запустить функцию расчёт данных с порта
Последняя версия программы значительно лучше, чем предыдущие попытки. Вполне может быть, что и это не самый оптимальный вариант решения поставленной задачи, но во всяком случае он является простым и понятным.
5.1.3.1 *Команда BNZ
Существует иная интерпретация команды btfss:
btfss STATUS,2 ;
Она проще, понятнее, но к сожалению не входит в систему команд, описанную в официальной документации МК 16 серии. Автору предыдущий вариант показался громоздким, и он попытался его заменить чем-то иным. В системе команд МК 18 серии, эта же команда, проверки 2 бита регистра STATUS, имеет название bnz, а в качестве параметра принимает только метку, на которую требуется перейти, если результат не 0, иначе она пропускается, и выполняется следующая команда. Общий вид записи и пример.
Общая форма записи:
bnz label ;
Пример:
A2: decf value,f ; результат 0 ?
bnz A2 ; нет – вернуться на А2
Компилятор воспринимает эту команду, как проверку бита Z регистра STATUS, на 0 результат. Однако будьте внимательны. Хотя применение команды bnz кажется удобнее, если после загрузки кода в МК, программа будет вести себя не совсем корректно, то вина за это будет лежать только на вас. Автор показал вам хитрость, но это не значит, что её нужно использовать!