#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>

/*
 RESET:D0
 MOSI :D1
 MISO :A1
 SCK  :A0
 LED  :D2
 TRIG :A1
 */

#define DDR_RESET DDRD
#define DDR_MOSI  DDRD
#define DDR_MISO  DDRA
#define DDR_SCK   DDRA
#define DDR_LED   DDRD
#define DDR_TRI_T DDRA

#define PORT_RESET PORTD
#define PORT_MOSI  PORTD
#define PORT_MISO  PORTA
#define PORT_SCK   PORTA
#define PORT_LED   PORTD
#define PORT_TRI_T PORTA
#define PORT_TRI_R PORTB

#define IO_RESET 0
#define IO_MOSI  1
#define IO_MISO  1
#define IO_SCK   0
#define IO_LED   2
#define IO_TRI_T 1
#define IO_TRI_R 6

#define PIN_MISO  PINA
#define PIN_TRI_R PINB

#define RESET_ENABLE (DDR_RESET |= (1<<IO_RESET))
#define  MOSI_ENABLE (DDR_MOSI  |= (1<<IO_MOSI ))
#define   SCK_ENABLE (DDR_SCK   |= (1<<IO_SCK  ))
#define   LED_ENABLE (DDR_LED   |= (1<<IO_LED  ))
#define TRI_T_ENABLE (DDR_TRI_T |= (1<<IO_TRI_T))

#define RESET_DISABLE (DDR_RESET &= ~(1<<IO_RESET))
#define  MOSI_DISABLE (DDR_MOSI  &= ~(1<<IO_MOSI ))
#define   SCK_DISABLE (DDR_SCK   &= ~(1<<IO_SCK  ))
#define   LED_DISABLE (DDR_LED   &= ~(1<<IO_LED  ))
#define TRI_T_DISABLE (DDR_TRI_T &= ~(1<<IO_TRI_T))

#define RESET_H (PORT_RESET |= (1<<IO_RESET))
#define  MOSI_H (PORT_MOSI  |= (1<<IO_MOSI ))
#define   SCK_H (PORT_SCK   |= (1<<IO_SCK  ))
#define   LED_H (PORT_LED   |= (1<<IO_LED  ))
#define TRI_T_H (PORT_TRI_T |= (1<<IO_TRI_T))

#define RESET_L (PORT_RESET &= ~(1<<IO_RESET))
#define  MOSI_L (PORT_MOSI  &= ~(1<<IO_MOSI ))
#define   SCK_L (PORT_SCK   &= ~(1<<IO_SCK  ))
#define   LED_L (PORT_LED   &= ~(1<<IO_LED  ))
#define TRI_T_L (PORT_TRI_T &= ~(1<<IO_TRI_T))

#define  MISO_H ((PIN_MISO &(1<<IO_MISO)) != 0)
#define TRI_R_H ((PIN_TRI_R&(1<<IO_TRI_R)) != 0)

#define TRI_R_PULLUP (PORT_TRI_R |=  (1<<IO_TRI_R))
#define TRI_R_HIZ    (PORT_TRI_R &= ~(1<<IO_TRI_R))

#define NOP asm volatile("nop")

#define WAIT_SCK   NOP;NOP;NOP;NOP
#define WAIT_FLASH _delay_ms(10);

const uint8_t flash_quine[] PROGMEM = {
	0xA4,0xC1,0xBC,0xC1,0xBB,0xC1,0xBA,0xC1,0xB9,0xC1,0xB8,0xC1,0xB7,0xC1,0xB6,0xC1,
	0xB5,0xC1,0xB4,0xC1,0xB3,0xC1,0xB2,0xC1,0xB1,0xC1,0xB0,0xC1,0xAF,0xC1,0xAE,0xC1,
	0xAD,0xC1,0xAC,0xC1,0xAB,0xC1,
	0xFF,0xFF,
	0x11,0x24,0x1F,0xBE,0xCF,0xED,
	0xCD,0xBF,0x10,0xE0,0xA0,0xE6,0xB0,0xE0,0xE6,0xE4,0xF6,0xE0,0x02,0xC0,0x05,0x90,
	0x0D,0x92,0xA0,0x36,0xB1,0x07,0xD9,0xF7,0x10,0xE0,0xA0,0xE6,0xB0,0xE0,0x01,0xC0,
	0x1D,0x92,0xA0,0x36,0xB1,0x07,0xE1,0xF7,0x13,0xD1,0x63,0xC1,0x41,0xCE,0x27,0xE0,
	0x30,0xE0,0x90,0xE0,0x48,0x2F,0x50,0xE0,0xD8,0x98,0xBA,0x01,0x02,0x2E,0x02,0xC0,
	0x75,0x95,0x67,0x95,0x0A,0x94,0xE2,0xF7,0x60,0xFF,0x02,0xC0,0x91,0x9A,0x01,0xC0,
	0x91,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xD8,0x9A,0x99,0x0F,0xC9,0x99,
	0x91,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x50,0x30,0x40,0x8F,0xEF,
	0x2F,0x3F,0x38,0x07,0x09,0xF7,0xD8,0x98,0x89,0x2F,0x08,0x95,0xCF,0x93,0xDF,0x93,
	0xCD,0xB7,0xDE,0xB7,0xC3,0x50,0xCD,0xBF,0x2B,0x83,0x4A,0x83,0x69,0x83,0xCF,0xDF,
	0x69,0x81,0x86,0x2F,0xCC,0xDF,0x4A,0x81,0x84,0x2F,0xC9,0xDF,0x2B,0x81,0x82,0x2F,
	0xC6,0xDF,0xCD,0x5F,0xCD,0xBF,0xDF,0x91,0xCF,0x91,0x08,0x95,0xCF,0x93,0x8C,0xEA,
	0xBE,0xDF,0x83,0xE5,0xBC,0xDF,0x80,0xE0,0xBA,0xDF,0xC8,0x2F,0x80,0xE0,0xB7,0xDF,
	0x81,0xE0,0xC3,0x35,0x09,0xF0,0x80,0xE0,0xCF,0x91,0x08,0x95,0x8C,0xEA,0x60,0xE8,
	0x40,0xE0,0x20,0xE0,0xD3,0xDF,0x08,0x95,0x98,0x2F,0x24,0x2F,0x66,0x23,0x11,0xF0,
	0x88,0xE4,0x01,0xC0,0x80,0xE4,0x60,0xE0,0x49,0x2F,0xC8,0xDF,0x08,0x95,0x68,0x2F,
	0x62,0x95,0x63,0x70,0x48,0x2F,0x50,0xE0,0x84,0xE0,0x44,0x0F,0x55,0x1F,0x8A,0x95,
	0xE1,0xF7,0x8C,0xE4,0x20,0xE0,0xBA,0xDF,0x83,0xEC,0x99,0xE0,0x01,0x97,0xF1,0xF7,
	0x00,0xC0,0x00,0x00,0x08,0x95,0xCF,0x92,0xDF,0x92,0xEF,0x92,0xFF,0x92,0x0F,0x93,
	0x1F,0x93,0xCF,0x93,0xDF,0x93,0x6B,0x01,0xE3,0xEC,0xF9,0xE0,0x31,0x97,0xF1,0xF7,
	0x00,0xC0,0x00,0x00,0x7C,0x01,0xC0,0xE0,0xD0,0xE0,0x00,0xE0,0x10,0xE0,0x1E,0xC0,
	0xF7,0x01,0x44,0x91,0x8C,0x2F,0x60,0xE0,0xC7,0xDF,0xF7,0x01,0x31,0x96,0x44,0x91,
	0x8C,0x2F,0x61,0xE0,0xC1,0xDF,0xCF,0x5F,0xC0,0x31,0x31,0xF0,0x98,0x01,0x2E,0x5F,
	0x3F,0x4F,0x2C,0x15,0x3D,0x05,0x20,0xF0,0x8D,0x2F,0xC1,0xDF,0xDF,0x5F,0xC0,0xE0,
	0x0E,0x5F,0x1F,0x4F,0x82,0xE0,0x90,0xE0,0xE8,0x0E,0xF9,0x1E,0x0C,0x15,0x1D,0x05,
	0xF8,0xF2,0xDF,0x91,0xCF,0x91,0x1F,0x91,0x0F,0x91,0xFF,0x90,0xEF,0x90,0xDF,0x90,
	0xCF,0x90,0x08,0x95,0x8F,0x92,0x9F,0x92,0xAF,0x92,0xBF,0x92,0xCF,0x92,0xDF,0x92,
	0xEF,0x92,0xFF,0x92,0x0F,0x93,0x1F,0x93,0xCF,0x93,0xDF,0x93,0x5C,0x01,0x4B,0x01,
	0x83,0xEC,0x99,0xE0,0x01,0x97,0xF1,0xF7,0x00,0xC0,0x00,0x00,0x00,0xE0,0x10,0xE0,
	0xFF,0x24,0xCC,0x24,0xDD,0x24,0x01,0xC0,0x01,0xE0,0xC0,0xE0,0xD0,0xE0,0x06,0xC0,
	0xE6,0x01,0x00,0xE0,0x03,0xC0,0xE6,0x01,0x00,0xE0,0x10,0xE0,0xF5,0x01,0xEC,0x0F,
	0xFD,0x1F,0x44,0x91,0xFE,0x01,0x31,0x96,0xEA,0x0D,0xFB,0x1D,0xE4,0x90,0x4F,0x3F,
	0x51,0xF4,0x9F,0xEF,0xE9,0x16,0x39,0xF4,0x00,0x23,0x31,0xF3,0x92,0xE0,0xC9,0x2E,
	0xD1,0x2C,0xCC,0x0E,0xDD,0x1E,0x81,0x2F,0x60,0xE0,0x66,0xDF,0x81,0x2F,0x61,0xE0,
	0x4E,0x2D,0x62,0xDF,0x22,0x96,0x1F,0x5F,0x10,0x31,0x29,0xF0,0xC8,0x15,0xD9,0x05,
	0xE8,0xF2,0x00,0x23,0xA9,0xF6,0x8F,0x2D,0x62,0xDF,0xF3,0x94,0xC8,0x15,0xD9,0x05,
	0xA0,0xF2,0x01,0x30,0x81,0xF2,0xDF,0x91,0xCF,0x91,0x1F,0x91,0x0F,0x91,0xFF,0x90,
	0xEF,0x90,0xDF,0x90,0xCF,0x90,0xBF,0x90,0xAF,0x90,0x9F,0x90,0x8F,0x90,0x08,0x95,
	0xC6,0x9A,0x8F,0xE9,0x96,0xE8,0xA1,0xE0,0x81,0x50,0x90,0x40,0xA0,0x40,0xE1,0xF7,
	0x00,0xC0,0x00,0x00,0x80,0xE0,0xB6,0x99,0xFD,0xCF,0x8F,0x5F,0x8A,0x30,0xD8,0xF3,
	0xC6,0x98,0xD0,0x9A,0x89,0x9A,0x88,0x9A,0x8F,0xE3,0x9D,0xE0,0xA3,0xE0,0x81,0x50,
	0x90,0x40,0xA0,0x40,0xE1,0xF7,0x00,0xC0,0x00,0x00,0x10,0xDF,0x1F,0xDF,0x86,0xE2,
	0x90,0xE0,0x64,0xE2,0x73,0xE0,0x7E,0xDF,0xD0,0x98,0x89,0x98,0x88,0x98,0x8A,0x9A,
	0x92,0x9A,0x8F,0xE9,0x96,0xE8,0xA1,0xE0,0x81,0x50,0x90,0x40,0xA0,0x40,0xE1,0xF7,
	0x00,0xC0,0x00,0x00,0x92,0x98,0x8A,0x98,0x8F,0xE9,0x96,0xE8,0xA1,0xE0,0x81,0x50,
	0x90,0x40,0xA0,0x40,0xE1,0xF7,0x00,0xC0,0x00,0x00,0x8F,0xE9,0x96,0xE8,0xA1,0xE0,
	0x81,0x50,0x90,0x40,0xA0,0x40,0xE1,0xF7,0x00,0xC0,0x00,0x00,0xD9,0x98,0xD1,0x9A,
	0xA7,0xEA,0xB1,0xE6,0x11,0x97,0xF1,0xF7,0x00,0xC0,0x00,0x00,0xD1,0x98,0x00,0x00,
	0xFE,0xCF,0xF8,0x94,0xFF,0xCF
};

uint8_t SPI_send_byte(uint8_t data) {
	uint8_t out = 0;
	uint8_t i;
	for(i = 0; i != 8; i++) {
		SCK_L;
		if(data&(1<<(7-i)))
			MOSI_H;
		else
			MOSI_L;
		WAIT_SCK;
		SCK_H;
		out <<= 1;
		if(MISO_H)
			out |= 1;
		WAIT_SCK;
	}
	SCK_L;
	return out;
}

void SPI_send_bytes(uint8_t data1, uint8_t data2, uint8_t data3, uint8_t data4) {
	SPI_send_byte(data1);
	SPI_send_byte(data2);
	SPI_send_byte(data3);
	SPI_send_byte(data4);
}

uint8_t SPI_begin_programming(void) {
	uint8_t out;
	SPI_send_byte(0b10101100);
	SPI_send_byte(0b01010011);
	out = SPI_send_byte(0);
	SPI_send_byte(0);
	
	if(out == 0b01010011)
		return 1;
	return 0;
}

void SPI_erase(void) {
	SPI_send_bytes(0b10101100, 0b10000000, 0, 0);
}

void SPI_flash_page_write(uint8_t address, uint8_t hl, uint8_t data) {
	SPI_send_bytes(hl != 0 ? 0b01001000 : 0b01000000, 0, address, data);
}

void SPI_flash_page(uint8_t page) {
	SPI_send_bytes(0b01001100, ((page>>4)&0b11), (page&0xF)<<4, 0);
	WAIT_FLASH;
}

void SPI_flash_write(const uint8_t data[], uint16_t length) {
	uint16_t i;
	uint8_t page = 0, address = 0;
	WAIT_FLASH;
	for(i = 0; i < length; i += 2) {
		SPI_flash_page_write(address, 0, pgm_read_byte(&data[i]));
		SPI_flash_page_write(address, 1, pgm_read_byte(&data[i+1]));
		address++;
		if(address == 16 || i+2 >= length) {
			SPI_flash_page(page);
			address = 0;
			page++;
		}
	}
}

void SPI_flash_write_quine(const uint8_t data[], uint16_t length) {
	uint16_t i = 0;
	uint16_t offset_after = 0;
	uint8_t page = 0, address = 0;
	uint8_t rewrite = 0;
	WAIT_FLASH;
	
	while(1) {
		uint8_t data1 = pgm_read_byte(&data[i]);
		uint8_t data2 = pgm_read_byte(&data[i+1]);
		if(data1 == 0xFF && data2 == 0xFF) {
			if(rewrite == 0) {
				i = 0;
				rewrite = 1;
				continue;
			} else {
				offset_after = i+2;
			}
		}
		SPI_flash_page_write(address, 0, data1);
		SPI_flash_page_write(address, 1, data2);
		i += 2;
		address++;
		if(address == 16 || (i >= length && rewrite == 0)) {
			SPI_flash_page(page);
			address = 0;
			page++;
		}
		if(i >= length) {
			if(rewrite == 1) {
				rewrite = 0;
				i = offset_after;
				continue;
			} else {
				break;
			}
		}
	}
}

int main(void) {
	
	//wait
	TRI_R_PULLUP;
	_delay_ms(500);
	{
		uint8_t counter = 0;
		while(1) {
			if(!TRI_R_H) {
				counter++;
				if(counter >= 10)
					break;
			} else {
				counter = 0;
			}
		}
		TRI_R_HIZ;
	}
	
	//enter
	SCK_ENABLE;
	MOSI_ENABLE;
	RESET_ENABLE;
	_delay_ms(1000);
	
	//write setup
	SPI_begin_programming();
	SPI_erase();
	SPI_flash_write_quine(flash_quine, sizeof(flash_quine));
	
	//leave
	SCK_DISABLE;
	MOSI_DISABLE;
	RESET_DISABLE;
	
	//leave
	LED_ENABLE;
	LED_H;
	_delay_ms(500);
	LED_L;
	LED_DISABLE;
	_delay_ms(500);
	
	_delay_ms(500);
	
	//trigger
	TRI_T_L;
	TRI_T_ENABLE;
	_delay_ms(100);
	TRI_T_DISABLE;
	
	while(1)
		NOP;
}