; Advanced Clock-VFD (4 digit)
; Copytight Vladislav B. Ivanov (с) 2015
; Russia, Astrakhan
; vladislav370@gmail.com

.include "c:\vlad\asm\Appnotes\m8def.inc"
.org 0x000 

.equ version = 100		     ; версия программы
.equ key_delay = 45		     ; задержка перед повтором клавиш
.equ key_rate = 15		     ; задержка после каждого повтора
.equ tim_500 = 164		     ; число тиков таймера для 1/500 сек
.equ play_time = 90		     ; время подачи звуков, 90 сек

; ИЛЦ8-4/17Л (УИ-4)
; или ИЛЦ1-1/7  - (ОГРОМНЫЕ с руку величиной)
;
; Тип используемого индикатора: ИЛЦ8-4/17Л (УИ-4) вакуумно-люминисцентный зелёный
; Напряжение накала: переменное со средней точкой, 2.7В +- 0.2В
; Напряжение анода: 40В
; Число знакомест 4, число сегментов знакоместа 7, сегмент DP отсутсвует.
; Символ "двоеточие", символ "таймер", символ "колокольчик" имеются.
; 34 вывода на колбе, часть не используется, шаг между выводами 2.54 мм.
;
;
; Всего есть 4 кнопки управления, сверху вниз, все очень просто:
; 1) выбор режима отображения - последовательный перебор одного из 5 
; возможных режимов работы часов (ЧЧ-ММ, ММ-СС, таймер ЧЧ-ММ/ММ-СС, 
;				секундомер ММ-СС, откл дисплей)
; 2) режим установок - последовательный перебор режимов установки -
; (часов, минут, сброс секунд , будильника часов, будильника минут, 
;  				включение выбор звука будильника)
; 3) кнопка плюс - изменение значения (часов минут и тд) в плюс
; 4) кнопка минус - изменение значения (часов минут и тд) в минус
; в редимах 1, 2 и 5 кнопки + - регулируют яркость экрана.

; схема очень похожа на приложенную. Она - стандартна. Назначение портов
; несколько иное:
; порт ЦП	назначение
; PC0 - сетка индикатора разряда 1 (счет слева направо)
; PC1 - сетка индикатора разряда 2
; PC2 - сетка индикатора разряда 3
; PC3 - сетка индикатора разряда 4
; PC4 - символ "часы"
; PC5 - символ "колокольчик"
;
; PD0 - сегмент G индикатора
; PD1 -         E
; PD2 -         C
; PD3 - 	D
; PD4 -		B
; PD5 - 	F
; PD6 -		A
; PD7 - двоеточие-разделитель между разрядами часов-минут
;
; сетка доп.символов "таймера" и "колокольчика" соединены с сеткой разряда 4
; сетка символа-разделителя соединена с сеткой разряда 2
;
; таким образом индикатором управляет 14 ключей на транзисторах  BC640 что 
; образует матрицу 7*7 позволяющую в динамическом режиме выводить
; на индикатор все что нужно. Цикл динамической индикации осуществляет 
; мультиплексирование 4 разрядов с 1 по 4 последовательно и тд., те в один
; момент времени экспонируется только 1 разряд, затем второй, затем третий
; и тд. Поскольку это происходит очень быстро частота в  пределах 400 Гц,
; зрение человека инертно мы этого не видим и цифры воспринимаются как одно
; целое. В этом и есть суть динамической индикации позволяющей существенно
; сократить число проводов от процессора к индикатору но несколько усложняет
; логику. Цикл индикации прерывать нельзя - цифры "замрут" экран или отключится
; или будет высвечиваться только 1 цифра. Вот как то так.
;
; Далее:
; PB5	кнопка 1 (верхняя)
; PB4 	кнопка 2
; PB3 	кнопка 3
; PB2	кнопка 4
;
; состояние кнопок: "1" нажата, "0" отпущена
;
; PB0   вывод звука через ключ на КТ315 подключена пьезо-пищалка.
;
; линии PB5, PB4, PB3, PB2 используются для внутрисхемного программирования и
; выведены на шлейф к программатору.
;
; Для такторования ЦП и отсчета времени используется один часовой кварц
; частотой 32768.0 Гц. По хорошему, надо поднять тактовую частоту перевести
; проц на встроенный RC генератор устновкой FUSE битов, а счетный таймер 1 
; (двухбайтовый) перевести в асинхронный режим от этого карца, но я это пока
; не делал, да и смысла нет. Производительности - хватает. Ассемблер все таки)
;
;
; *** глобально объявленные регистры ***
;
; r1 счетчик секунд секундомера 0-59
; r2 счетчик минут секундомера 0-59
;
; r3 константа=255 для записи в порты и тд и тп
; r4 счетчик для ШИМ регулировки яркости идикатора
;
; r5 главный счетчик секунд 0-59 \
; r6 главный счетчик минут  0-59   счетные регистры ЧРВ
; r7 главный счетчик часов  0-23 /
;
; r8 уст. значение минут таймера и будильника 0-59
; r9 уст. значение часов таймера и будильника 0-23
;
; r10 режимы вывода (биты 0-3)
;		а) основные режимы (вход и выбор кнопкой 1):
;			0 - индикатор выключен (мигает только разделитель)
;			1 - основной режим часов    ЧЧ:ММ 
;			2 - режим часов с секундами MM:CC
;			3 - таймер ММ-СС обратный отсчет и символ "колокол"
;			4 - секундомер ММ-СС прямой отсчет и символ "таймер"
;
;	        б) режимы установок (вход и выбор кнопкой 2):
;			5 - уст. знач. часов
;			6 - уст. знач. минут
;			7 - уст/сброс знач. секунд
;			8 - уст. часов таймера-будильника
;			9 - уст. минут таймера-будильника
;                      10 - программная коррекция хода часов
;		       11 - включение и выбор сигн будильника
;
; 		бит 4-5 режим отображения точки-разделитетя:
;			0 - 0  включена постоянно
;			1 - 0  мигает по секундам
;			1 - 1  отключена
;
; 		бит 6	символ "колокольчик"
;			1 - выключен
;			0 - включен
;
; 		бит 7	символ "часы"
;			1 - выключен
;			0 - включен
;
; r11 величина программной коррекции хода часов по 0.01 сек/час
; 	0-50   коррекция в плюс
; 	50-100 коррекция в минус
; 	Старший бит - флаг применения коррекции, взводится в 1 раз в час
;	в момент прохождения 30 минутного интервала. Так сделано чтобы корр
; 	не применялась при сбросе, установках часов-минут и тд.
;
; r12-r13-r14-r15 регистры для отображения
; в том виде в каком они уйдут на индикатор
; установленный старший бит отключает разряд
; что сделано для обеспечения мигания разрядами
;
; r16-r17-r18-r19 - операционные регистры для вычислений
;
; r20 - счетчик мультиплексора динамической индикации (0-3)
; r21 - уровень яркости индикатора (0-10)
;
; r22 - флаг обновления информации
;		бит 0   1 - изменились секунды
;		    1   1 - изменились минуты
;		    2   1 - изменились часы
;
; r23 - тек значение старшего байта слова системного таймера
;
; r24 - флаги нажатых-удерживамых кнопок и младш 3 бита код клавиши
;					биты 0-3 содержат код клавиши
;					бит 7 флаг нажатия клавиши
;					бит 6 флаг автоповтора клавиши
;
; r25 - счетчик перед задержкой и повтором клавиш. Этот же регистр
; счетчик секунд подачи звука - часы "пищат" пока в регистре не 0.
; при любом нажатии клавиши звук немедленно прекращается
;
; r26 - регистр управления таймером, секундомером, будильником
; 	
;				биты 0-2 выбор звукового сигнала, 0 откл
;				бит 3 сработал таймер
;				бит 4 сработал будильник
;				бит 5 активен секундомер
;				бит 6 активен таймер
;				бит 7 включен будильник
;
; r27 - счетчик секунд таймера
; r28 - счетчик минут таймера
; r29 - счетчик часов таймера


rjmp RESET			; на начало программы
reti
reti
reti
reti
reti
rjmp TIM1_COMPA 		; вектор прерывание таймера 1
reti
reti 
reti
reti
reti
reti
reti
reti
reti
reti
reti
reti

; знакогенератор цифры 0-9
led7_bit_mask:  .db 0b00000001, 0b01101011, 0b00100100, 0b00100010  ; 0, 1, 2, 3
		.db 0b01001010, 0b00010010, 0b00010000, 0b00101011  ; 4, 5, 6, 7
		.db 0b00000000, 0b00000010			    ; 8, 9

;-------------------------------------------------------------------------
;           обработчик прерывания таймера (вызов 1 раз в 1 сек)
;-------------------------------------------------------------------------
TIM1_COMPA:		
	cli
	push r16
	in  r16, SREG
	push r16
	ori r22, 0b00000001		; флаг изменения секунд
	ldi r16, 59
	cp  r5, r16
	breq reset_sec
	inc r5				; главный счетчик секунд
	rjmp exit_int

reset_sec:
	ori r22, 0b00000011		; флаг изменения минут
	clr r5				; конец минутного интервала
	ldi r16, 59
	cp r6, r16
	breq reset_min
	inc r6                          ; главный счетчик минут
	ldi r16, 30
	cp r6, r16
	breq correct
	rjmp exit_int

correct:
	ldi r16, 0b10000000		; раз в час, в 30 минут, в r11
	or r11, r16			; взводим старший бит
	rjmp exit_int

reset_min:
	ori r22, 0b00000111             ; флаг изменения часа
	clr r6				; конец часового интервала
	ldi r16, 23
	cp r7, r16
	breq reset_hou
	inc r7				; главный счетчик часов
	rjmp exit_int

reset_hou:	
	clr r7				; конец суточного интервала
					; сюда "прикручивать" календарь
					; если потребуется в будущем
exit_int:
	pop r16
	out SREG, r16
	pop r16
	sei
	reti
;-------------------------------------------------------------------------
;			--->>> HERE IS STARTUP <<<---
;-------------------------------------------------------------------------
RESET:	
	ldi r16, high(RAMEND)
	out SPH, r16
	ldi r16, low(RAMEND)
	out SPL, r16

	; счетчики часов-минут-секунд таймер будильник - всё в ноль
	clr r1
	clr r2
	clr r4
	clr r5
	clr r6
	clr r7
	clr r8
	clr r9
	clr r11
	clr r20
	clr r21
	clr r22
	clr r23
	clr r24
	clr r25
	clr r26
	clr r27
	clr r28
	clr r29

	; флаг обновления экрана
	ldi r22, 0b00000111

	; установка прерывания таймера под часовой кварц 32768 Гц
	cli
	clr r16
	out TCNT1H, r16
	out TCNT1L, r16
	ldi r16, (1<<PSR10)
	out SFIOR, r16
	ldi r16, high(32768)
	out OCR1AH, r16
	ldi r16, low(32768)
	out OCR1AL, r16
	ldi r16, (1<<OCIE1A)
	out TIMSK, r16
	ldi r16, 0b00001001
	out TCCR1B, r16
	sei

	; режим по умолчанию: часы, точка мигает, др символы - отключены
	ldi r16, 1
	ori r16, 0b11100000
	mov r10, r16

	; адрес массива хранение битовых масок выводимых символов
	ldi ZH, high(led7_bit_mask*2)

	; яркость - в среднее положение
	ldi r21, 4

	; порты D и C вкл и на выход
	ldi r16, 255
	mov r3, r16
	mov r17, r16
	out DDRC, r3
	out DDRD, r3

;---------------------------------------------------------------------------
;			* * *  ОСНОВНОЙ ЦИКЛ  * * * 
;---------------------------------------------------------------------------
MAIN:   out PORTC, r17
	out PORTD, r16

;- ШИМ регулировка яркости экрана
shim:	cp  r4, r21
	breq disp
	inc r4
	rjmp shim

;- отключение экрана в момент обновления
disp:	out PORTD, r3
	out PORTC, r3

;- динамическая индикация - мультиплексирование разрядов идикатора
	clr r4
	cpi r20, 1
	breq sg2
	cpi r20, 2
	breq sg3
	cpi r20, 3
	breq sg4	
	
;----------
;- 1я сетка
	rcall display
	inc r20
	ldi r17,  0b00111110
	mov r16, r3
	mov r18, r12
	andi r18, 0b10000000
	brne blank1
	mov r16, r12	
blank1:	rjmp MAIN

;----------
;- 2я сетка
sg2:	rcall blinking
	inc r20
	ldi r17,  0b00111101
	ldi r16, 255
	mov r18, r13
	andi r18, 0b10000000
	brne blank2
	mov r16, r13
blank2:	mov r19, r10
	andi r19, 0b00110000
	breq is_dp
	cpi  r19, 0b00110000
	breq no_dp
	cpi r23, 64
	brsh no_dp
is_dp:	andi r16, 0b01111111
	rjmp MAIN
no_dp:	ori r16,  0b10000000
	rjmp MAIN

;----------
;- 3я сетка
sg3:	rcall inkey
	inc r20
	ldi r17,  0b00111011
	in r23, TCNT1L
	in r23, TCNT1H
	andi r24, 0b11111000
	sbic PINB, 5
	ori r24,  1
	sbic PINB, 4
	ori r24,  2
	sbic PINB, 3
	ori r24,  3
	sbic PINB, 2
	ori r24,  4
	ldi r16, 255
	mov r18, r14
	andi r18, 0b10000000
	brne blank3
	mov r16, r14	
blank3:	rjmp MAIN

;----------
;- 4я сетка
sg4:	rcall correction
	clr r20	
	ldi r17,  0b00110111
	mov r19, r10
	andi r19, 0b10000000
	breq cor_sg
	rjmp kol_sg
cor_sg: andi r17, 0b00101111
kol_sg:	mov r19, r10
	andi r19, 0b01000000
	brne dis4
	andi r17, 0b00011111
dis4:	mov r16, r3
	mov r18, r14
	andi r18, 0b10000000
	brne blank4
	mov r16, r15
blank4:	rjmp MAIN

;-------------------------------------------------------------------------
; программная коррекция хода часов, алгоритм такой
; раз в час, к значению TCNT1 добавляем/отнимаем
; по 1 сотой секунды * на значение в r11, что в итоге
; дает нужную суточную точность хода часов.
; Можно скорректировать погрешность кварца где-то до 10
; секунд в сутки, но это - уже очень плохие кварцы. Обычно,
; значения погрешности часовых кварцев 2-5 сек/сутки,
; поэтому пределов коррекции +50...-50 вполне достаточно.

correction:
	mov r16, r11		; значение и флаг коррекции
	andi r16, 0b10000000  	; старший бит выделяем и проверяем,
	brne cor_on		; и если установлен - выполняем,
	ret			; иначе сразу на выход

cor_on:
	mov r17, r11
	andi r17, 0b01111111	; сброс старшего бита
	cpi r17, 0              ; при знач. корр =0
	breq exit_corr          ; 	сразу на выход
	cpi r17, 50		; коррекция в плюс или в минус?
	brlo crr_plus
	breq crr_plus	

	cpi r23, 64		; ждем, пока значений таймера 1 не
	brsh set_corr_minus     ; станет больше 16384 чтоб избежать 
	ret			; возможного переполнения при вычитании

; коррекция в минус
set_corr_minus:
	ldi r16, 101
	sub r16, r17		; значение корр без знака будет теперь в r16
	ldi r17, tim_500	; * на число тиков таймера для 1/500 секунды
	rcall mul_8x8
	add r17, r17		; и результат *2
	adc r18, r18
	in r16, TCNT1L		; тек. значение слова
	in r19, TCNT1H          ;  	системного тамера
	sub r16, r17		; вычитаем из него значение
	sbc r19, r18            ;       коррекции
	out TCNT1H, r19		; и записываем
	out TCNT1L, r16		; 	в счет. регистр новые данные
	rjmp exit_corr

; коррекция в плюс
crr_plus:
	cpi r23, 64		; ждем, пока значений таймера 1 не
	brlo set_corr_plus      ; будет меньше 16384 чтоб избежать 
	ret			; возможного переполнения при сложении

set_corr_plus:
	ldi r16, tim_500	; интервал для 1/500 секунды
	rcall mul_8x8		; умножаем на значение коррекции
	add r17, r17		; и результат умножаем еще на 2
	adc r18, r18
	in r16, TCNT1L		; тек. значение слова 
	in r19, TCNT1H          ;  	системного тамера
	add r16, r17		; добавляем к нему значение
	adc r19, r18            ;       коррекции
	out TCNT1H, r19		; и записываем
	out TCNT1L, r16		; 	в счет. регистр новые данные

exit_corr:
	ldi r16, 0b01111111	; старший бит выделяем,
	and r11, r16		; 	и сбрасываем флаг в r11
	ret
;-------------------------------------------------------------------------
; процедура обновления счетчиков секундомера
secundomer:
	ldi r16, 59
	cp  r1, r16
	breq reset_sec_s
	inc r1
	ret

reset_sec_s:
	ori r22, 0b00000010
	ldi r16, 59
	clr r1
	cp r2, r16
	breq reset_min_s
	inc r2
	ret

reset_min_s:
	clr r2
	ret
;-------------------------------------------------------------------------
; процедура обновления счетчиков таймера
utimer:
	cpi r27, 0
	breq set_59s
	dec r27
	ret

set_59s:
	ori r22, 0b00000011
	cpi r28, 0
	breq set_59m
	ldi r27, 59
	dec r28
	ret

set_59m: 
	ori r22, 0b00000111
	cpi r29, 0
	breq timer_done
	ldi r28, 59
	ldi r27, 59
	dec r29
	ret

timer_done:
	ldi r25, play_time		; столько секунд будет звук
	andi r26, 0b10111111		; таймер - стоп
	ori r26,  0b00001000		; сигнал - вкл
	ret

;-------------------------------------------------------------------------
; процедура заставляет мигать разрядами индикатор в режимах установок
; так сделано чтобы было видно когда режим установки активен и что там
; именно изменяется. Так было сделано (и щас делают) на всех нормальных
; часах и это - правильно это значительно лучше чем выводить на индикатор
; всякую хрень лишь отдаленно похожую на буквы, 7 сегментов, однако))
blinking:
	cpi r22, 0
	breq check_setup		; мигаем только когда нет изменений
	ret

check_setup:
	mov r16, r10
	andi r16, 0b00001111
	cpi r16, 5
	brsh setup_mode
	rcall test_secun_run
	brTs seku_blink
	rjmp test_tim

;- мигание символом "часы" в режиме запущенного секундомера
seku_blink:
	cpi r23, 16
	brlo cs_on
	cpi r23, 32
	brlo cs_off
	cpi r23, 48
	brlo cs_on
	cpi r23, 64
	brlo cs_off
	cpi r23, 80
	brlo cs_on
	cpi r23, 96
	brlo cs_off
	cpi r23, 112
	brlo cs_on

cs_off:	ldi r16,  0b10000000
	or r10, r16
	rjmp test_tim

cs_on:	ldi r16,  0b01111111
	and r10, r16
	rjmp test_tim

;- мигание символом "колокольчик" в режиме запущенного таймера
test_tim:
	rcall test_timer_run
	brTs timer_blink
	ret

timer_blink:
	cpi r23, 32
	brsh timer_sg_off
	ldi r16, 0b10111111
	and r10, r16
	ret

timer_sg_off:
	ldi r16, 0b01000000
	or r10, r16
	ret

setup_mode:
	; мигание нужными разрядами индикатора в режимах установки
	mov r17, r24
	andi r17, 0b00000111
	brne disable_blank		; при нажатой клавише запрет мигания
					; иначе цифры будут трудно читаться
	
	cpi r23, 26
	brlo no_blank
	cpi r23, 43
	brlo is_blank
	cpi r23, 69
	brlo no_blank
	cpi r23, 86
	brlo is_blank
	cpi r23, 112
	brlo no_blank

is_blank:
	seT
	ldi r17, 0b10000000
	rjmp select_blank

no_blank:
	ldi r17, 0b01111111
	clT

select_blank:
	cpi r16, 5
	breq blank_12		; установка часов мигают 2 первых разряда
	cpi r16, 8
	breq blank_12           ; установка часов таймера/будильника

	cpi r16, 6
	breq blank_34		; установка минут мигают 2 последних разряда
	cpi r16, 9
	breq blank_34		; установка минут таймера/будильника

	cpi r16, 7		
	breq blank_34           ; установка секунд мигает 2 последних разряда

	cpi r16, 10		; в режиме задания коррекции хода часов
	breq blank_34		

	cpi r16, 11		; в режиме установки звука последняя цифра
	breq blank_34
	ret

disable_blank:
	ldi r17, 0b01111111	; запрет мигания
	and r12, r17		;   включение всех 
	and r13, r17            ;     разрядов индикатора
	and r14, r17            ;       при любой нажатой
	and r15, r17            ;         кнопке
	ret

blank_12:
	brTs blank_12_on
	and r12, r17
	and r13, r17
	ret

blank_12_on:
	or r13, r17
	or r12, r17
	ret

blank_34:
	brTs blank_34_on
	and r14, r17
	and r15, r17
	ret

blank_34_on:
	or r14, r17
	or r15, r17
	ret

; мигание всеми разрядами индикатора (не используется)
; blank_1234:
;	brTs blank_1234_on
;	and r12, r17
;	and r13, r17
;	and r14, r17
;	and r15, r17
;	ret
;
; blank_1234_on:
;	or r12, r17
;	or r13, r17
;	or r14, r17
;	or r15, r17
;	ret
;
;-------------------------------------------------------------------------
; процедура вывода числовой информации на индикатор
; в разных режимах часов и режимах установок
display:
	cpi r22, 0
	brne test_budilnik

	mov r16, r26
	andi r16, 0b00011000		; сработал таймер/будильник?
	brne play_lp			; да - включить звук
	ret

;- сработка будильника
bud_srab:
	mov r16, r26			
	andi r16, 0b00011000            ; не даем повторно сработать
	breq set_bud_play		; уже сработавшему будильнику
	rjmp select_format

set_bud_play:
	ldi r25, play_time		; столько секунд будет звук
	ori r26,  0b00010000		; сигнал - включаем
	rjmp select_format

;- сигнал будильника, таймера
play_lp:
	cpi r23, 64
	brsh select_format
	rcall play
	ret

;- проверка сработки будильника
test_budilnik:	
	cpi r22, 1                      ; при изменении только секунд
	breq select_format		; будильник вообще не проверяем

	rcall test_timer_run            ; и при активном 
	brTs select_format		;         таймере - тоже

	mov r16, r26
	andi r16, 0b10000000		; а будильник то включен?
	breq select_format		; нет - не проверяем

	cp r7, r9                       ; час соппадает?
	breq test_bud_mm		; проверяем минуты
	rjmp select_format

test_bud_mm:
	cp r6, r8                       ; минуты совпадают?
	breq bud_srab			; да - будильник сработал
	rjmp select_format
	
;-------------------------
; мигают только точки-разделители
; все остальные цифры выключены
display_0:
	mov r12, r3
	mov r13, r3
	mov r14, r3
	mov r15, r3
	clr r22
	ret	

; вывод значения величины коррекции без знака 0-50
display_c:
	rcall bin8_dec2
	rcall get_led_bit_mask
	mov r15, r0			; десятки
	mov r16, r17			
	rcall get_led_bit_mask
	mov r14, r0			; единицы
        clr r22
	ret

;--------------------------------
; коррекция хода часов +/-
display_4:
	ldi r16, 0b00010101	; символ "C" в 1 знакоместо
	mov r12, r16
	mov r16, r11
	andi r16, 0b01111111	; сброс старшего бита
	cpi r16, 50
	brlo cp_plus
	breq cp_plus
	; минус
	ldi r17, 0b01111110	; знак минус "-" во второе знакоместо
	mov r13, r17
	mov r17, r11
	andi r17, 0b01111111
	ldi r16, 101
	sub r16, r17
        rcall display_c
	ret

cp_plus:
	; плюс
	mov r13, r3
        rcall display_c
	ret

;--------------------------------
; будильник выбор звука или откл
display_3:
	ldi r16,  0b01010000		; символ b в первое знакоместо
	mov r12, r16
	ldi r16,  0b01000001		; символ U во второе знакоместо
	mov r13, r16
	ldi r16,  0b01111111		; пробел в третье знакоместо
	mov r14, r16
	mov r16, r26			; вариант сигнала будильника выводим
	andi r16, 0b00000111
	rcall bin8_dec2
	rcall get_led_bit_mask
	mov r15, r0			; в четверное знакоместо
	clr r22
	ret

;------------------------------------------------------
; выбор режима отображения таймера в активном состоянии
; когда задан таймер менее часа или остается до 00:00
; менее 1 часа выводим в формате ММ:СС иначе в ЧЧ:ММ
display_5:
	cpi r29, 0		; счетчик часа таймера
	breq display_2		; MM:SS
	rjmp display_1		; HH:MM

;- выбор формата вывода данных
select_format:
	rcall test_secun_run
	brTc no_secun_run_a
	; счетчик секундомера обновляем
	rcall secundomer

no_secun_run_a:
	rcall test_timer_run
	brTc no_timer_run_a
	; счетчик таймера обновляем пока таймер активен
	rcall utimer

no_timer_run_a:
	mov r17, r10		; режим получаем
	andi r17, 0b00001111	; берем только младшие 4 бита номер режима

	;- основные режимы -
	cpi r17, 0		; дисплей откл, только разделитель
	breq display_0
	cpi r17, 1		; часы режим HH:MM
	breq display_1
	cpi r17, 2		; часы режим MM:SS
	breq display_2
	cpi r17, 3		; таймер HH:MM или MM:SS
	breq display_5
	cpi r17, 4		; секундомер MM:SS
	breq display_2
	;- режимы установок -
	cpi r17, 5		; установка часов HH:MM мигают часы
	breq display_1
	cpi r17, 6		; установка минут HH:MM мигают минуты
	breq display_1
	cpi r17, 7		; установка секунд / сброс счетчика секунд
	breq display_2
	cpi r17, 8		; установка часов таймера/будильника
	breq display_1
	cpi r17, 9		; установка минут таймера/будильника
	breq display_1
	cpi r17, 10		; коррекция хода часов +/-
	breq display_4
	cpi r17, 11             ; выбор-откл звука будильника
	breq display_3
	clr r22
	ret

; -----------------------------------
; формат Минуты-Секунды
; для режима часов 2, для установки секунд
; и таймера если задано или остается менее часа
display_2:
	push r17
	mov r16, r5			; основной счетчик секунд
	cpi r17, 2			; режим 2
	breq sec_clock
	cpi r17, 7			; установка секунд
	breq sec_clock	
	mov r16, r1			; счетчик секунд секундомер
	cpi r17, 4			
	breq sec_clock
	mov r16, r27			; счетчик секунд таймера

sec_clock:
	rcall bin8_dec2
	rcall get_led_bit_mask
	mov r15, r0			; десятки секунд
	mov r16, r17			
	rcall get_led_bit_mask
	mov r14, r0			; единицы секунд
	mov r16, r22
	andi r16, 0b11111110
	pop r17
	cpi r16, 0
	brne update_mins
	clr r22
	ret

update_mins:
	mov r16, r6			; счетчик минут часов
	cpi r17, 2			; режим 2
	breq min_clock
	cpi r17, 7			; установка/сброс секунд
	breq min_clock
	mov r16, r2			; счетчик минут секундомер
	cpi r17, 4
	breq min_clock
	mov r16, r28			; счетчик минут таймер

min_clock:
	rcall bin8_dec2
	rcall get_led_bit_mask
	mov r13, r0			; десятки минут
	mov r16, r17
	rcall get_led_bit_mask
	mov r12, r0			; единицы минут
	clr r22
	ret

;-------------------
; формат Часы-Минуты
; основной режим часов
display_1:
	mov r16, r22
	andi r16, 0b11111110
	brne update_m
	clr r22
	ret

update_m:
	andi r16, 0b00000010		; если минуты не изменились 
	breq update_h                   ; их не обновляем
	mov r16, r28
	cpi r17, 3			; таймер
	breq clc_m
	mov r16, r8
	cpi r17, 8			; будильник
	breq clc_m
	cpi r17, 9			; будильник
	breq clc_m
	mov r16, r6
clc_m:	push r17
	rcall bin8_dec2
	rcall get_led_bit_mask
	mov r15, r0			; десятки минут
	mov r16, r17			
	rcall get_led_bit_mask
	mov r14, r0			; единицы минут
	pop r17

update_h:
	mov r16, r22			; если часы не изменились
	andi r16, 0b000000100           ; минуты тоже не обновляем
	breq no_update_h
	mov r16, r29
	cpi r17, 3			; таймер
	breq clc_h
	mov r16, r9
	cpi r17, 8			; будильник
	breq clc_h
	cpi r17, 9			; будильник
	breq clc_h
	mov r16, r7			; иначе часы
clc_h:	rcall bin8_dec2
	rcall get_led_bit_mask
	mov r13, r0			; десятки часов
	mov r16, r17
	rcall get_led_bit_mask
	mov r12, r0			; единицы часов

no_update_h:
	clr r22
	ret
;------------------------------------------------------------------------
; процедура перевода числа из r16 в десятичное число
; результат возвращается в регистры r16:r17
bin8_dec2:
     	clr R17
div2_l:	subi R16, 10     ; вычитаем 10 пока не получим отрицательную разность
     	brcs d2_end
	inc r17          ; +1 к разряду десяток
	rjmp div2_l
d2_end:	subi R16, -10    ; корректируем остаток, добавляя 10
     	ret
;------------------------------------------------------------------------
; процедура возвращает битовую маску в r16
; на входе R16 содержит номер байта в массиве
;------------------------------------------------------------------------
get_led_bit_mask:
	ldi ZL, low(led7_bit_mask*2) 
	add ZL, r16
	lpm
	ret
;------------------------------------------------------------------------
; процедура умножения беззнакового числа 
; r16 - множимое
; r17 - множитель
; r17:r18 младший:старший байт результата
mul_8x8:
	clr  r18      		;очистить  старший байт результата
	lsr  r17      		;cдвинуть вправо  множитель
	brcc noad80    	        ;переход, если С=0
	add  r18, r16 		;прибавить множимое к старшему байту результата
noad80:	ror  r18      		;сдвинуть вправо старший байт результата
	ror  r17      		;сдвинуть вправо младший байт результата и множитель
	brcc noad81    	        ;переход, если С=0
	add  r18, r16 		;прибавить множимое к старшему байту результата
noad81: ror  r18
	ror  r17
	brcc noad82
	add  r18, r16
noad82: ror  r18
	ror  r17
        brcc noad83
        add  r18, r16
noad83: ror  r18
        ror  r17
        brcc noad84
        add  r18,r16
noad84: ror  r18
        ror  r17
        brcc noad85
        add  r18,r16
noad85: ror  r18
        ror  r17
	brcc noad86
	add  r18,r16
noad86: ror  r18
	ror  r17
	brcc noad87
	add  r18,r16
noad87: ror  r18
	ror  r17
	ret
;------------------------------------------------------------------------
; звук будильника, таймера
play:	dec r25				; длительность подачи звука
	brne play_is_on
	andi r26, 0b11100111		; выкл звука по истечению r25
	mov r16, r10
	andi r16, 0b00001111
	cpi r16, 3			; режим таймера?
	brne no_play_tim
	ldi r16,  0b10001111		; да - после прекращения звука
	and r10, r16	                ; включаем точки-разделители
no_play_tim:
	ret

play_is_on:
	push r21
	ldi r21, 1
	mov r16, r26
	andi r16, 0b00001000		; при сработке таймера
	brne play_2		        ; звук включаем в любом случае,
					; независимо от установки

	mov r16, r26
	andi r16, 0b00000111
	cpi r16, 1
	breq play_1			; вид сигнала 1
	cpi r16, 2
	breq play_2			; вид сигнала 2
	clr r21
					; иначе только мигает индикатор
					; звука - нет
play_2:	ldi r16, 1
	out DDRB, r16

	ldi r16, 0b00010101
	ldi r17, 0b01111111
	mov r18, r21
	ldi r19, 0
	ldi r20, 22
	rcall play_loop

	ldi r16, 0b00011101
	ldi r17, 0b01111111
	ldi r18, 0
	ldi r19, 0
	ldi r20, 32
	rcall play_loop

	ldi r16, 0b00010101
	ldi r17, 0b01111111
	mov r18, r21
	ldi r19, 0
	ldi r20, 64
	rcall play_loop

	ldi r17, 0	
	out DDRB, r17
	ldi r20, 2
	rjmp exit_play

play_1:	ldi r16, 1
	out DDRB, r16

	ldi r16, 0b00010101
	ldi r17, 0b01111111
	mov r18, r21
	ldi r19, 0
	ldi r20, 64
	rcall play_loop

	ldi r17, 0	
	out DDRB, r17
	ldi r20, 2

exit_play:
	pop r21
	ret
;-------------------------------------------------------------------------
play_loop:
	in r23, TCNT1L
	in r23, TCNT1H
	out PORTB, r18
	out PORTC, r16
	out PORTD, r17
	nop
	nop
	out PORTC, r3
	out PORTB, r19
	cp  r23, r20
	nop
	nop
	nop	
	nop
	brlo play_loop
	ret
;-------------------------------------------------------------------------
; процедура обработки нажатия клавиш
; формирует автоповторы при удержании
inkey:	mov r16, r24
	andi r16, 0b00000111
	cpi r16, 0
	brne test_down
	clr r24
	ret

;- проверка первого нажатия на клавишу
test_down:
	mov r16, r24
	andi r16, 0b11111000
	cpi r16, 0
	breq key_down
	andi r16, 0b01000000
	cpi r16, 0
	brne key_rpt
	cpi r25, 0
	breq key_rpt
	dec r25	
	ret

;- ожидание перед автоповтором
key_rpt:
	ori r24, 0b11000000
	cpi r25, 0
	breq rpt_down
	dec r25
	ret

;- автоповтор и формирование задержки
rpt_down:
	ldi r25, key_rate
	mov r16, r24
	andi r16, 0b00000111
	cpi r16, 3
	brsh repeat_yes
	ret

; - автоповтор возможен только для клавиш 3 и 4 (+ и -)
; - а для остальных клавиш - запрещен - что логично в данном случае
repeat_yes:
	rcall key_event
	ret	

;- первое нажатие клавиши ок
key_down:
	andi r26, 0b11100111		; при любом нажатии клав - выкл звука
	rcall key_event
	ori r24, 0b10000000
	ldi r25, key_delay
	ret
;-------------------------------------------------------------------------
; определяет поведение программы при нажатии кпопок
key_event:
	mov r17, r10
	andi r17, 0b00001111
	cpi r17, 5
	brlo base_mode_navi

;-- навигация в режиме установок --
	mov r16, r24
	andi r16, 0b00000111
	cpi r16, 1
	breq base_mode_on
	cpi r16, 2
	breq setup_mode_select
	cpi r16, 3
	breq chg_setting
	cpi r16, 4
	breq chg_setting
	ret

;-- выбор режима установки --
setup_mode_select:
	cpi r17, 5
	breq set_mm
	cpi r17, 6
	breq reset_hh_mm
	cpi r17, 7
	breq set_bud_hh
	cpi r17, 8
	breq set_bud_mm
	cpi r17, 9
	breq set_cor
	cpi r17, 10
	breq set_play

;- установка часов
	ldi r16, 0b11000101
	mov r10, r16
	rjmp anymd

;- установка минут
set_mm:	
	ldi r16, 0b11000110
	mov r10, r16
	rjmp anymd

;- установка/сброс секунд
reset_hh_mm:
	ldi r16, 0b11000111
	mov r10, r16
	rjmp anymd

;- установка часов будильника
set_bud_hh:
	ldi r16, 0b10001000
	mov r10, r16
	rjmp anymd

;- устанока минут будильника
set_bud_mm:
	ldi r16, 0b10001001
	mov r10, r16
	rjmp anymd

; - установка коррекции хода часов +/-
set_cor:
	ldi r16, 0b11111010
	mov r10, r16
	rjmp anymd

;- установка звука будильника или выкл -
set_play:
	ldi r16, 0b11111011
	mov r10, r16
	rjmp anymd

;- установка значения параметра
chg_setting:
	rcall setup_param
	rcall test_bud_act
	ret

;- вход в основной режим -
base_mode_on:
	ldi r16, 0b11100001
	mov r10, r16
	rjmp anymd

;- вход в режим установок --
setup_mode_on:
	ldi r16, 0b11000101
	mov r10, r16
	rjmp anymd

;- навигация в основном режиме -
base_mode_navi:
	mov r16, r24
	andi r16, 0b00000111
	cpi r16, 1
	breq base_mode_select
	cpi r16, 2
	breq setup_mode_on
	cpi r16, 3
	breq plus_minus_key
	cpi r16, 4
	breq plus_minus_key
	ret

;- выбор основного режима --
base_mode_select:
	cpi r17, 0
	breq hh_mm
	cpi r17, 1
	breq mm_ss
	cpi r17, 2
	breq stimer
	cpi r17, 3
	breq sekun
	ldi r16, 0b11100000	; отключение индикатора
	rjmp anymd	

;- режим часы-минуты (основной)
hh_mm:	ldi r16, 0b11100001
	rjmp anymd

;- режим минуты-секунды
mm_ss:	ldi r16, 0b11100010
	rjmp anymd

;- таймер
stimer: rcall test_timer_run
	brTc timer_stop_now

;-- запущен
	ldi r16, 0b10100011
	rjmp anymd

;-- остановлен
timer_stop_now:
	clr r27
	mov r28, r8
	mov r29, r9
	ldi r16, 0b10000011
	rjmp anymd

;- секундомер
sekun:  rcall test_secun_run
	brTc secun_no_run
	ldi r16, 0b01100100
	rjmp anymd

;- не запущен
secun_no_run:
	ldi r16, 0b01000100	
	rjmp anymd

;- точка выхода из процедуры
anymd:	mov r10, r16	
	ldi r22, 7
	rcall test_bud_act
	ret

;- кнопки плюс и минус
plus_minus_key:
	rcall plus_minus
	ret

;--------------------------------------------------------------------------
; кнопки плюс-минус из основного меню
plus_minus:
	cpi r17, 3
	brlo bright			; в режимах часов: регулировка яркости
	cpi r17, 3
	breq set_timer			; в режиме таймера: старт-стоп
	cpi r17, 4
	breq sekundm			; в режиме секундомера: старт-стоп пауза
	ret

;- управление таймером
set_timer:
	mov r29, r9			; величину таймера сразу
	mov r28, r8                     ;	помещаем в счетчики
	clr r27				; счетчик секунд таймера=0

	cpi r16, 3
	breq timer_start
	cpi r16, 4
	breq timer_stop
	ret
	
;- запуск таймера
timer_start:
	ldi r16,  0b11100000
	or  r10, r16
	ori r26,  0b01000000
	ldi r22,  7
	ret

;- останов таймера
timer_stop:
	ldi r16,  0b10001111
	and r10, r16
	andi r26, 0b10111111
	ldi r22,  7	
	ret

;- управление секундомером
sekundm:
	cpi r16, 3
	breq start_stop_sekundm
	cpi r16, 4
	breq secun_pause
	ret

;- кнопка 3 цикл: пуск - пауза - сброс - пуск
start_stop_sekundm:
	rcall test_secun_run	
	brTs set_secun_pause
	; иначе запуск или сброс секундомера
	clr r16
	cp  r16, r1
	brne secun_reset
	cp  r16, r2
	brne secun_reset

;- запуск секундомера
	ldi r16,  0b00100000
	or  r10, r16
	ori r26,  0b00100000
	ldi r22,  0b00000011
	ret

;- сброс секундомера
secun_reset:
	clr r1
	clr r2
	ldi r22,  0b00000011
	ret

; кнопка 4 цикл:  пауза - продолжение
secun_pause:
	rcall test_secun_run
	brTc sekun_continue

set_secun_pause:
	ldi r16,  0b01001111
	and r10, r16
	andi r26, 0b11011111
	ret

sekun_continue:
	ldi r16,  0b00100000
	or  r10, r16
	ori r26,  0b00100000
	ret

;- регулировка яркости
bright:	cpi r16, 3
	breq br_plus
	cpi r16, 4
	breq br_min
	ret	

;-- регулировка яркости кнопки + и - из основного режима
br_plus:
	mov r16, r21
	cpi r16, 10
	brlo br_add
	ret

; добавить яркость
br_add:	inc r21
	ret
; убивить яркости
br_sub: dec r21
	ret

; уменьшить яркость
br_min:	mov r16, r21
	cpi r16, 0
	brne br_sub
	ret
;--------------------------------------------------------------------------
; кпопки плюс-минус из меню установок
setup_param:
	; r17 - режим что изменяем
	; r16 - код клавиши (3 или 4)
	cpi r17, 5
	breq chg_hh
	cpi r17, 6
	breq chg_mm
	cpi r17, 7
	breq set_rst
	cpi r17, 8
	breq chg_hh_bud
	cpi r17, 9
	breq chg_mm_bud
	cpi r17, 10
	breq chg_corr
	cpi r17, 11
	breq chg_play_bud
	ret

;- установка часов -
chg_hh: ldi r18, 23
	mov r19, r7
	rcall change
	mov r7, r19
	ldi r22, 0b00000100
	ret

;- установка минут -
chg_mm: ldi r18, 59
	mov r19, r6
	rcall change
	mov r6, r19
	ldi r22, 0b00000010
	ret

;- установка/сброс секунд -
set_rst:
	cpi r16, 3
	breq add_sec
	cpi r16, 4
	breq reset_secund
	ret

;- установка добавление секунд
add_sec:
	ldi r18, 59
	mov r19, r5
	rcall change
	mov r5, r19
	ldi r22, 0b00000001
	ret

;- установка часа будильника или таймера -
chg_hh_bud: 
	ldi r18, 23
	mov r19, r9
	rcall change
	mov r9, r19
	ldi r22, 0b00000100
	ret

;- установка или откл звука будильника -
chg_play_bud:
	ldi r22,  0b00000001
	mov r19, r26
	andi r19, 0b00000111
	andi r26, 0b11111000
	ldi r18, 3
	rcall change
	or r26, r19
	cpi r19, 0
	breq bud_off
	ori r26,  0b10010000
	ldi r25, play_time
	ret

bud_off:
	andi r26, 0b01100111
	ret

;- установка минут будильника или таймера -
chg_mm_bud:
	ldi r18, 59
	mov r19, r8
	rcall change
	mov r8, r19
	ldi r22, 0b00000010
	ret

;- установка значения коррекции хода часов
chg_corr:
	ldi r18, 100
	mov r19, r11
	andi r19, 0b01111111
	rcall change
	mov r11, r19
	ldi r22, 0b00000001
	clT
	ret

; сброс секунд нижней кнопкой
reset_secund:
	cli
	clr r5
	clr r16
	out TCNT1H, r16
	out TCNT1L, r16
	sei
	ldi r22, 0b00000001
	ret
;----------------------------------------------------------------
; универсальная процедурка изменяющая значение любого параметра
; вход: r18 максимальное значение параметра 23 или 59 и тд
;       r19 исходное значение параметра
;       r16 код клавиши (3 или 4), соответственно, в плюс или минус меняет
; выход:
;	r19 новое значение параметра
;
change:
	cpi r16, 3
	breq chg_add
	cpi r16, 4
	breq chg_sub
	ret
chg_add:
	cp r19, r18
	brsh chg_rst
	inc r19
	ret
chg_max:
	mov r19, r18
	ret
chg_sub:
	cpi r19, 0
	breq chg_max
	dec r19
	ret	
chg_rst:
	clr r19
	ret
;--------------------------------------------------------------------------
; проверяет запущен секундомер или нет
; возвращает T=1 если запущен иначе нет
test_secun_run:
	mov r16, r26
	andi r16, 0b00100000
	brne secun_is_runi
	clT
	ret
secun_is_runi:
	seT
	ret
;--------------------------------------------------------------------------
; проверяет запущен таймер или нет
; возвращает T=1 если запущен иначе нет
test_timer_run:
	mov r16, r26
	andi r16, 0b01000000
	brne timer_is_runi
	clT
	ret

timer_is_runi:
	seT
	ret
;--------------------------------------------------------------------------
; проверяет активен будильник или нет
; возвращает T=1 если активен иначе нет,
; также в режиме часов 1 и 2 вкл "колокольчик"
test_bud_act:
	clT
	mov r16, r10
	andi r16, 0b00001111
	cpi r16, 11
	breq test_bud_status		; вкл и уст. звука будильника
	cpi r16, 3			; таймер
	breq kolok_is_on
	cpi r16, 8			; уст. буд. час
	breq kolok_is_on
	cpi r16, 9			; уст. буд. мин
	breq kolok_is_on
	cpi r16, 10			; корр. хода часов
	breq kolok_is_off
	cpi r16, 3			; в режимах часов
	brlo test_bud_status		; проверяем статус будильника
	rjmp kolok_is_off

; пров активности будильника
test_bud_status:
	mov r16, r26
	andi r16, 0b10000000
	brne test_timer_status
	rjmp kolok_is_off

; проверка активности таймера
test_timer_status:
	mov r16, r26
	andi r16, 0b01000000
	breq kolok_is_on

; включить значок "колокольчик"
kolok_is_off:
	mov r16, r10
	ori r16, 0b01000000
	mov r10, r16
	ret

; выключить
kolok_is_on:
	seT
	mov r16, r10
	andi r16, 0b10111111
	mov r10, r16
	ret

; --- THE END ---
