;**** Подпрограммы ввода/вывода для AVR ********************************
;*
;* Подпрограммы ввода/вывода для модуля STK200, демонстрирующие работу
;* с портами микроконтроллеров семейства AVR. При нажатии некоторой кнопки на
;* STK200 загораются светодиоды в соответствии со следующим шаблоном:
;* Кнопка PD0: Светящаяся "точка" перемещается слева направо
;* Кнопка PD1: Светащаяся "точка" перемещается справа налево
;* Кнопка PD2: Светящаяся "точка" перемещается туда-сюда
;* Кнопка PD3: Светящаяся "полоска" перемещается слева направо
;* Кнопка PD4: Светащаяся "полоска" перемещается справа налево
;* Кнопка PD5: Две светящиеся "точки" перемещаются слева направо
;* Кнопка PD6: Две светящиеся "точки" перемещаются справа налево
;* Кнопка PD7: Попеременно мерцают значения старшего и младшего полубайта  
;*             (режим, выбранный по умолчанию)
;*
;* Тактовая частота микроконтроллеров AVR: 4 МГц (стандарт STK200).
;*  
;*************************************************************************
.nolist
.include "8515def.inc"
.list

.def  Last = r14	; Предыдущее состояние кнопок
.def  Stat = r15	; Буфер для временного хранения содержимого SREG
.def  tmpi = r16	; Рабочий регистр прерывания от таймера
.def  ICnt = r17	; Счетчик прерываний
.def  Flg  = r18	; Флаги программы
.def  tmph = r19	; Рабочий регистр главной программы
.def  Mode = r20	; Режим индикации

.equ  Dwn  = 0	; Флаг 0 ("Нажата") = была нажата некоторая кнопка
.equ  Rpt  = 1	; Флаг 1 ("Повтор") = кнопка опять нажата
.equ  New  = 2	; Флаг 2 ("Новый") = смена режима индикации
.equ  Dir  = 3	; Флаг 3 = направление (0=влево, 1=вправо)

.equ  Time = 80000 / 1024

.cseg

rjmp  Initial	; После сброса – к главной программе
reti	; Внешнее прерывание 0 не используется
reti	; Внешнее прерывание 1 не используется
reti	; Прерывание T/C1 Capture не используется
reti	; Прерывание T/C1 Comp. A не используется
reti	; Прерывание T/C1 Comp. В не используется
reti	; Прерывание по переполнению T/C1 не используется 

Timer0:	; Прерывание по переполнению T0 (каждые 20 мс)
in    Stat,SREG	; Сохраняем флаги главной программы
ldi   tmpi,-Time
out   TCNT0,tmpi	; Перезагружаем T/C0
KeyScan:
in    tmpi,PinD	; Считываем состояние кнопок
sbrs  Flg,Dwn	; Пропускаем следующую команду, если при предыду-
	; щем прерывании была нажата какая-то кнопка
rjmp  TI1	; Переход, если ни одна кнопка не нажата
cpse  Last,tmpi	; Пропускаем следующую команду, если все еще нажата та же кнопка
rjmp  TI1	; Переход, если не совпадает состояние кнопок
sbrc  Flg,Rpt	; Пропускаем следующую команду, если флаг "Повтор" сброшен
rjmp  Display	; Переход, если флаг "Повтор" установлен
mov   Mode,tmpi	; Установка режима
sbr   Flg,1<<New | 1<<Rpt	; Установка флагов "Новый" и "Повтор"
rjmp  Display	; 
TI1:
cpi   tmpi,$FF	; Кнопка нажата?
brne  TI2	; Переход, если какая-то кнопка нажата
cbr   Flg,1<<Dwn | 1<<Rpt	; Сброс флагов "Нажата" и "Повтор"
rjmp  Display	; 
TI2:
mov   Last,tmpi	; Сохранение состояния кнопок
cbr   Flg,1<<Rpt	; Сброс флага "Повтор"
sbr   Flg,1<<Dwn	; Установка флага "Нажата"

Display:
dec   ICnt	; Счетчик - 1
breq  D1	; Переход, если равно нулю
rjmp  IntEnd	; Завершение обработки прерывания
D1:
ldi   ICnt,12	; Новая инициализация счетчика
in    tmpi,PortB	; Считывание состояния светоиндикаторов
sbrs  Mode,0	; Пропускаем следующую команду,если Бит0=1
rjmp  Mode0
sbrs  Mode,1	; Пропускаем следующую команду,если Бит1=1
rjmp  Mode1
sbrs  Mode,2	; Пропускаем следующую команду,если Бит2=1
rjmp  Mode2
sbrs  Mode,3	; Пропускаем следующую команду,если Бит3=1
rjmp  Mode3
sbrs  Mode,4	; Пропускаем следующую команду,если Бит4=1
rjmp  Mode4
sbrs  Mode,5	; Пропускаем следующую команду,если Бит5=1
rjmp  Mode5
sbrs  Mode,6	; Пропускаем следующую команду,если Бит6=1
rjmp  Mode6
Mode7:
sbrs  Flg,New	; Если флаг "Новый"=1, то – новый режим
rjmp  M7
ldi   tmpi,$F0	; Инициализация шаблона индикации
cbr   Flg,1<<New	; Сброс флага "Новый"
M7:
com   tmpi	; Дополнение до 1
out   PortB,tmpi	; Включаем светодиоды
rjmp  IntEnd	; Готово
Mode6:
sbrs  Flg,New	; Если флаг "Новый"=1, то – новый режим
rjmp  M6
ldi   tmpi,$33	; Инициализация шаблона индикации
cbr   Flg,1<<New	; Сброс флага "Новый"
M6:
lsl   tmpi	; Сдвиг влево
brcc  M6A	; Переход, если не "столкновение"
ori   tmpi,$01
M6A:
out   PortB,tmpi	; Включаем светодиоды
rjmp  IntEnd	; Готово
Mode5:
sbrs  Flg,New	; Если флаг "Новый"=1, то – новый режим
rjmp  M5
ldi   tmpi,$CC	; Инициализация шаблона индикации
cbr   Flg,1<<New	; Сброс флага "Новый"
M5:
lsr   tmpi	; Сдвиг вправо
brcc  M5A	; Переход, если не "столкновение"
ori  tmpi,$80
M5A:
out   PortB,tmpi	; Включаем светодиоды
rjmp  IntEnd	; Готово
Mode4:
sbrs  Flg,New	; Если флаг "Новый"=1, то – новый режим
rjmp  M4
ser   tmpi	; Инициализация шаблона индикации
cbr   Flg,1<<New	; Сброс флага "Новый"
M4:
lsl   tmpi	; Сдвиг влево
brcs  M4A	; Переход, если не "столкновение"
ori  tmpi,$01
M4A:
out   PortB,tmpi	; Включаем светодиоды
rjmp  IntEnd	; Готово
Mode3:
sbrs  Flg,New	; Если флаг "Новый"=1, то – новый режим
rjmp  M3
ser   tmpi	; Инициализация шаблона индикации
cbr   Flg,1<<New	; Сброс флага "Новый"
M3:
lsr   tmpi	; Сдвиг вправо
brcs  M3A	; Переход, если не "столкновение"
ori  tmpi,$80
M3A:
out   PortB,tmpi	; Включаем светодиоды
rjmp  IntEnd	; Готово
Mode2:
sbrs  Flg,New	; Если флаг "Новый"=1, то – новый режим
rjmp  M2
ldi   tmpi,$F7	; Инициализация шаблона индикации
cbr   Flg,1<<New	; Сброс флага "Новый"
M2:
sec	; Устанавливаем флаг переноса
ror   tmpi	; Сдвиг вправо
sbrc  Flg,Dir	; Если флаг Dir=0, то сдвиг влево
rjmp  M2B
rol   tmpi	; Меняем направление на сдвиг влево
sec	; Устанавливаем флаг переноса
rol   tmpi	; Сдвиг влево
brcs  M2A	; Переход, если не "столкновение"
sbr   Flg,1<<Dir	; Установка флага Dir: сдвиг вправо
ror   tmpi	; Меняем направление на сдвиг вправо
sec	; Устанавливаем флаг переноса
ror   tmpi	; Сдвиг вправо
M2A:
out   PortB,tmpi	; Включаем светодиоды
rjmp  IntEnd	; Готово
M2B:
brcs  M2C	; Переход, если не "столкновение"
cbr   Flg,1<<Dir	; Сброс флага Dir: сдвиг влево
rol   tmpi	; Меняем направление на сдвиг влево
sec	; Устанавливаем флаг переноса
rol   tmpi	; Сдвиг влево
M2C:
out   PortB,tmpi	; Включаем светодиоды
rjmp  IntEnd	; Готово
Mode1:
sbrs  Flg,New	; Если флаг "Новый"=1, то – новый режим
rjmp  M1
ldi   tmpi,$F7	; Инициализация шаблона индикации
cbr   Flg,1<<New	; Сброс флага "Новый"
M1:
sec	; Устанавливаем флаг переноса
rol   tmpi	; Сдвиг влево
brcs  M1A	; Переход, если флаг переноса С=1
ldi   tmpi,$FE	; Начинаем сначала
M1A:
out   PortB,tmpi	; Включаем светодиоды
rjmp  IntEnd	; Завершение обработки прерывания
Mode0:
sbrs  Flg,New	; Если флаг "Новый"=1, то – новый режим
rjmp  M0
ldi   tmpi,$7F	; Инициализация шаблона индикации
cbr   Flg,1<<New	; Сброс флага "Новый"
M0:
sec	; Устанавливаем флаг переноса
ror   tmpi	; Сдвиг вправо
brcs  M0A	; Переход, если флаг переноса С=1
ldi   tmpi,$7F	; Начинаем сначала
M0A:
out   PortB,tmpi	; Включаем светодиоды
IntEnd:
out   SREG,Stat	; Восстанавливаем регистр состояния
reti

Initial:
ldi   tmph,High(RamEnd)
out   sph,tmph
ldi   tmph,Low(RamEnd)
out   spl,tmph	; Инициализация стека
ldi   tmph,$F0	; Включаем светодиоды 0..3
out   PortB,tmph	
ldi   tmph,$FF	
out   DDRB,tmph	; Все разряды порта В - выход
clr   tmph	; Эти команды – не обязательны, поскольку
out   DDRD,tmph	;   порт D является входом по умолчанию
clr   Flg	; Сбрасываем все флаги
ldi   Mode,$7F	; По умолчанию выбран шаблон индикации 7
ldi   ICnt,12	; Инициализация счетчика прерываний
ldi   tmph,$02	; С помощью разряда TOIE0 разрешаем
out   TIMSK,tmph	;   прерывание по переполнению T/C0
ldi   tmph,-Time	; Передаем начальное значение для 
out   TCNT0,tmph	;   таймера 0
ldi   tmph,$05	; Устанавливаем коэффициент деления 
out   TCCR0,tmph	;   и тем самым запускаем T/C0
sei	; Общее разрешение прерываний
Wait:
ldi   tmph,$20	; Выбираем ждущий режим 
out   MCUCR,tmph	;   и разрешаем переход в "спящий" режим 
sleep	; Ожидаем прерывания от T/C0
nop
rjmp  Wait


