/*


*/

#ifndef F_CPU
#define F_CPU              7372800UL
#endif

#define BAUDRATE           115200
#define BAUDREG            ((unsigned int)((F_CPU * 10) / (16UL * BAUDRATE) - 5) / 10)

#define Pages				112

#include <avr/io.h>
#include <avr/boot.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>

#include <util/delay.h>

//void (*reset)(void)=0x0000; 
static void (*jump_to_app)(void) = 0x0000;

unsigned char PageBuffer[SPM_PAGESIZE];

void sendchar(unsigned char c)			//rs232 write
{
	UDR0 = c;							// prepare transmission
	while (!(UCSR0A & (1 << TXC0)));	// wait until byte sendt
	UCSR0A |= (1 << TXC0);				// delete TXCflag
}

unsigned char recchar(void)				//rs232 read
{
	while(!(UCSR0A & (1 << RXC0)));		// wait for data
	return UDR0;
}

void recvBuffer(unsigned char size)
{
	unsigned char cnt ;
	unsigned char *tmp =  PageBuffer;
	
	for(cnt = 0 ; cnt < SPM_PAGESIZE ; cnt ++)
	{
		*tmp++ = (cnt < size) ? recchar() : 0xff;
	}
}

unsigned char writeFlashPage(unsigned int addr,unsigned char size )
{
	uint32_t pagestart = (uint32_t)addr<<1;
	uint32_t baddr = pagestart;
	uint16_t data;
	uint8_t *tmp = PageBuffer;
	
	do 
	{
		data = *tmp ++;
		data |= *tmp++ << 8;
		boot_page_fill(baddr,data);
		baddr += 2;
		size -= 2;
	} while (size);
	
	boot_page_write(pagestart);
	boot_spm_busy_wait();
	boot_rww_enable();
	
	return baddr>>1;
}

void eraseFlash(void)
{
	// erase only main section (bootloader protection)
	uint32_t addr = 0;
	while (112 > addr) {
		boot_page_erase(addr);		// Perform page erase
		boot_spm_busy_wait();		// Wait until the memory is erased.
		//addr += SPM_PAGESIZE;
		addr += 112;
	}
	boot_rww_enable();	
}

int main(void)
{
	unsigned char get_data = 0;
	unsigned int time_out = 0;
	unsigned int address = 0;
	
	PORTD	= 0x04;
	DDRD	= 0x0e;
	
	cli();
	//RS232 
	UBRR0H = BAUDREG/256;
	UBRR0L = BAUDREG%256;
	UCSR0B = ( 1 << RXEN0 ) | ( 1 << TXEN0 );
	UCSR0C = ( 1 << UCSZ00 ) | ( 1 << UCSZ01 );

	sendchar('B');
	sendchar('o');
	sendchar('o');
	sendchar('t');

	sendchar(0x0d);	//==	sendchar('\n')
	sendchar(0x0a);	//==	sendchar('\r');
	
start:
	//rs232 read data
	while(!(UCSR0A & (1 << RXC0)))	// wait for data
	{
		if(time_out == 2000)
		{	
			time_out = 0;
			sendchar('@');
			jump_to_app();
		}
		
		_delay_ms(1);
		time_out ++;		
	}
	
	//get a command 
	get_data = UDR0;
	time_out = 0;
	switch(get_data)
	{
		case 'a':
			address = recchar() << 8;
			address |= recchar();			
			sendchar('a');
		break;
		case 'w':	//write flash
		{
			unsigned int size;
			
			size = recchar() << 8;
			size |= recchar();
			sendchar('g');	
			PORTD	^=	(1 <<PD2);
			recvBuffer(size);
			//PORTD	^=	(1 <<PD2);
			writeFlashPage(address,size);
			PORTD	^=	(1 <<PD2);
			sendchar('!');
		}
		break;
		case 'e':
			eraseFlash();
			sendchar('e');
		break;
		default:
			sendchar('?');
		break;
	}
	goto start;
	
	return 0;
}