/*
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Chip type           : AT90S2313
Clock frequency     : 8,000000 MHz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/

#define F_CPU 8000000UL  // 8 MHz
    
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>

#define CLEARBITS(x,y) ((x) &= (~(y)))

volatile uint8_t secs = 0;
volatile uint8_t mins = 0;
volatile uint8_t hours = 0;
volatile uint8_t mode = 0;		//0 - clock, 1 - prog hours, 2 - prog minutes

//знакогенератор 
const uint8_t font[] PROGMEM = 
{
240,255,255,255,31,252,255,255,255,127,254,255,255,255,255,254,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,15,0,0,0,224,15,0,0,0,224,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,254,255,255,255,255,254,
255,255,255,255,252,255,255,255,127,240,255,255,255,31,0,0,0,0,0,0,0,0,0,
0,224,7,0,0,0,224,7,0,0,0,240,7,0,0,0,240,3,0,0,0,252,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,240,255,0,224,255,252,127,0,254,255,254,127,
0,255,255,254,63,192,255,255,255,63,224,255,255,255,31,240,255,255,255,31,
248,255,255,255,15,252,255,255,15,0,255,31,224,15,128,255,7,224,255,255,
255,131,255,255,255,255,129,255,255,255,127,128,255,255,255,63,192,255,
254,255,31,192,255,254,255,15,224,255,252,255,3,224,255,240,127,0,240,255,
240,255,0,255,31,252,127,0,255,127,254,127,0,254,255,254,63,0,254,255,255,
63,0,252,255,255,31,0,252,255,255,31,30,248,255,255,15,30,248,255,15,0,30,
0,224,15,0,63,0,224,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,254,255,251,255,255,254,255,243,255,255,252,
255,241,255,127,240,255,192,255,31,0,0,252,127,0,0,192,255,127,0,0,252,
255,127,0,192,255,255,127,0,252,255,255,127,0,255,255,255,120,0,255,255,7,
120,0,255,127,0,120,0,255,3,0,120,0,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,0,0,0,120,0,255,255,
15,255,31,255,255,15,254,127,255,255,15,254,255,255,255,15,252,255,255,
255,15,252,255,255,255,15,248,255,255,255,15,248,255,255,255,15,240,255,
15,192,0,0,224,15,224,0,0,224,15,224,255,255,255,15,240,255,255,255,15,
240,255,255,255,15,240,255,255,255,15,240,255,255,255,15,240,255,255,255,
15,224,255,255,127,15,192,255,255,31,240,255,255,255,31,252,255,255,255,
127,254,255,255,255,255,254,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,15,0,12,0,224,15,0,14,
0,224,255,7,254,255,255,255,15,255,255,255,255,15,255,255,255,255,31,255,
255,255,254,31,255,255,255,254,63,255,255,255,252,63,254,255,127,240,127,
252,255,31,255,31,0,0,0,255,31,0,0,0,255,15,0,0,0,255,15,0,0,248,255,7,0,
192,255,255,7,0,254,255,255,3,224,255,255,15,0,255,255,255,15,248,255,255,
255,143,255,255,255,255,255,255,255,255,127,255,255,255,255,3,255,255,255,
31,0,255,255,255,0,0,255,255,15,0,0,255,127,0,0,0,255,3,0,0,0,63,0,0,0,0,
240,255,192,255,31,252,255,241,255,127,254,255,243,255,255,254,255,251,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,15,0,30,0,224,15,0,30,0,224,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,254,255,251,255,255,254,
255,243,255,255,252,255,241,255,127,240,255,192,255,31,240,255,63,254,31,
252,255,127,252,127,254,255,255,252,255,254,255,255,248,255,255,255,255,
248,255,255,255,255,240,255,255,255,255,240,255,255,255,127,224,255,15,0,
112,0,224,15,0,48,0,224,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,254,255,255,255,255,254,255,255,255,255,
252,255,255,255,127,240,255,255,255,31
};

//button 0 proc
ISR(INT0_vect)
{
	cli();
	mode++;
	if(mode > 2) 
	{
		mode = 0;
	}
	if(mode > 0)
	{
		TIMSK = 0;
	}
	else
	{
		TIMSK = 0x40;
	}
	mark_mode();
	while(! (PORTD & _BV(PD2)))
	{
	}
	_delay_ms(30);
	sei();
}

//button 1 proc
ISR(INT1_vect)
{
	cli();
	if(mode == 1)
	{
		hours++;
		if(hours > 23)
		{
			hours = 0;
		}
		secs ^= 1;
	}
	if(mode == 2)
	{
		mins ++;
		if(mins > 59)
		{
			mins = 0;
		}
		secs ^= 1;
	}
	//боремся с дребезгом контактов
	while(! (PORTD & _BV(PD3)))
	{
	}
	_delay_ms(30);
	sei();
}

//1Hz timer proc
ISR(TIMER1_COMP1_vect)
{
	secs++;
	if(secs == 60)
	{
		secs = 0;
		mins++;
		if(mins == 60)
		{
			mins = 0;
			hours++;
			if(hours == 24)
			{
				hours = 0;
			}
		}
	}	
}

//reset lcd
void lcd_reset(void)
{
	_delay_ms(10);
	PORTB = 0b1010;			//clears PB2
	_delay_ms(10);
	PORTB = 0b1110;			//sets PB2
}

//send byte to lcd
void lcd_send_byte(uint8_t byte, uint8_t dc_data)
{
	
	cli();

	CLEARBITS(PORTB, _BV(PB0));

	//sets d/c state
	if(dc_data != 0)
	{
		PORTB |= _BV(PB3);
	}
	else
	{
		CLEARBITS(PORTB, _BV(PB3));
	}

	//sending byte loop
	for (uint8_t cnt = 0; cnt < 8; cnt++)
	{
		//cals bit state and sets Sdata
		if(byte & 0x80)
		{
			PORTB |= _BV(PB1);
		}	
		else
		{
			CLEARBITS(PORTB, _BV(PB1));
		}

		//sets Sclk to HIGH
		PORTB |= _BV(PB0);
		_delay_us(1);

		//sets Sclk to LOW
		CLEARBITS(PORTB, _BV(PB0));
		_delay_us(1);

		//shifts byte
		byte <<= 1;
	}

	sei();
}

//нижняя строка
void mark_mode(void)
{
	
	uint8_t cnt;
	uint8_t b;

	//horisontal addsessing
	lcd_send_byte(0b00100000, 0);

	//set position - x
	lcd_send_byte(0 | 0x80, 0);

	//set position - y
	lcd_send_byte(0b01000101, 0);
	
	for(cnt = 0; cnt < 82; cnt++)
	{
		b = 0;
		if(mode == 0)
		{
			if(cnt < 8 || cnt > 75 || (cnt > 10 && cnt < secs + 11))
			{
				b = 12;
			}
		}
		if(mode == 1 && cnt < 38)
		{
			b = 12;
		}
		if(mode == 2 && cnt > 44)
		{
			b = 12;
		}

		lcd_send_byte(b, 1);
	}

	//vertical addressing
	lcd_send_byte(0b00100010, 0);
}

//очистка экрана
void cls(void)
{
	// x = 0
	lcd_send_byte(0b10000000, 0);
	// y = 0
	lcd_send_byte(0b01000000, 0);
	
	//fills LCD RAM with zeroes
	for(uint8_t cnt = 0; cnt < 252; cnt++)
	{
		lcd_send_byte(0, 1);
		lcd_send_byte(0, 1);
	}

	// x = 0
	lcd_send_byte(0b10000000, 0);
	// y = 0
	lcd_send_byte(0b01000000, 0);
}

//вывод цифры
void lcd_digit(uint8_t d, uint8_t x)
{
	uint16_t pntr = d * 90;

	uint8_t xcnt;
	uint8_t ycnt;

	for(xcnt = 0; xcnt < 18; xcnt++)
	{
		//set position - x
		lcd_send_byte((x + xcnt) | 0x80, 0);
		//set position - y
		lcd_send_byte(0b01000000, 0);
		for(ycnt = 0; ycnt < 5; ycnt++)
		{
			lcd_send_byte(pgm_read_byte_near(&font[pntr]), 1);		
			pntr++;
		}
	}
}

//вывод двузначного числа
void out_decimal(uint8_t d, uint8_t x)
{
	lcd_digit(d / 10, x);
	lcd_digit(d % 10, x + 20);
}

//вывод секундных точек
void second_dots()
{
	uint8_t dots = (secs & 1) ? 0b11100111 : 0;
	
	//horisontal addsessing
	lcd_send_byte(0b00100000, 0);

	//set position - x
	lcd_send_byte(39 | 0x80, 0);
	//set position - y
	lcd_send_byte(0b01000010, 0);
	
	lcd_send_byte(dots, 1);
	lcd_send_byte(dots, 1);
	lcd_send_byte(dots, 1);
	lcd_send_byte(dots, 1);

	//vertical addressing
	lcd_send_byte(0b00100010, 0);
}

int main (void)
{
	// Инициализация портов
	// Port D
	PORTD=0x0C;
	DDRD=0x01;

	//PORT B 
	PORTB = 0x00;
	DDRB = 0x0F;


	lcd_reset();
	
	//initializing LCD
	lcd_send_byte(0b00100001, 0);

	//sets Vop
	lcd_send_byte(0b11100000, 0);

	//function set
	lcd_send_byte(0b00100010, 0);

	//screen control
	lcd_send_byte(0b00001100, 0);


	//send test pattern
	cls();

	//инициализация таймера 1 
	TCCR1A = 0x00;
	TCCR1B = 0x0C;

	//констата таймера на 1 герц = частота кварца / 256
	OCR1AH = 0x7A;
	OCR1AL = 0x12;

	TIMSK = 0x40;

	//инициализация прерываний
	GIMSK = 0b11000000;
	MCUCR=0x2A;

	/* Разрешение прерываний */
	sei();

	while(1)
	{
		PORTD ^= 1;
		uint8_t secs1;
		secs1 = secs;

		if(secs == 0 || mode > 0)
		{
			out_decimal(mins, 44);
			out_decimal(hours, 0);
		}

		second_dots();
		mark_mode();

		while(secs == secs1) 
		{
			asm("Sleep");
		};

	}
}
