#ifndef __BOOTLOADER_H__
#define __BOOTLOADER_H__

#define DDR(x) (*(&x - 1))      /* address of data direction register of port x */
#define PIN(x) (*(&x - 2))      /* address of input register of port x          */

#ifdef HWB_PORT
#define HWB_DDR DDR(HWB_PORT)
#define HWB_PIN PIN(HWB_PORT)
#else
#define HWB_PORT PORTD
#define HWB_DDR DDRD
#define HWB_PIN PIND
#define HWB_MASK (1 << 7)
#endif


#if FLASHEND == (0x7FFF)
#define BOOT_SECTION_SIZE 4*1024
#elif FLASHEND == (0x3FFF)
#define BOOT_SECTION_SIZE 4*1024
#elif FLASHEND == (0x1FFF)
#define BOOT_SECTION_SIZE 2*1024
#endif

#define BOOTLOADER_START_ADDRESS (FLASHEND+1) - (BOOT_SECTION_SIZE)



#define BOOT_KEY 0xAA55AA55
uint32_t boot_key __attribute__ ((section (".noinit")));


#define STRINGIFY(x) #x
#define EXPAND(x) STRINGIFY(x)

static inline void enter_bootloader(void) { 
	__asm__ __volatile__("jmp " EXPAND(BOOTLOADER_START_ADDRESS)); 
}


static inline void hardware_reset(void) {
	cli();
	#define Wdt_change_enable()      (WDTCSR |= (1<<WDCE) | (1<<WDE))
	#define Wdt_enable_16ms()        (WDTCSR =  (1<<WDE))
	Wdt_change_enable();
	Wdt_enable_16ms();
	while(1);
}

static inline void boot_loader(void) {
	boot_key = BOOT_KEY;
	hardware_reset();
}


#define is_wdt_reset()  ((MCUSR&(1<<WDRF)) ? (1==1):(0==1))
static inline void check_bootloader(void) {
	if ((is_wdt_reset()) && (boot_key == BOOT_KEY)) {
		boot_key = 0;
		enter_bootloader();
	} 
}


static inline void check_hwb(void) {
	if ( !(HWB_PIN & HWB_MASK) ) enter_bootloader(); 	// low/zero = active
}

#endif