/*
* tsop.c
*
* Created: 22.02.2014 07:22:55
*  Author: radioman
*/

#define F_CPU 4800000UL //Internal 4.8Mhz clock

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

//Extracted xor table from TL866 firmware at 0x0196AA
const unsigned char xortable[] PROGMEM =
{
	0x48, 0x0D, 0x11, 0xB6, 0xA4, 0x76, 0xA0, 0xF6, 0x66, 0xE9, 0x55, 0xE5, 0xAC, 0x20, 0xDB, 0x16,
	0x0C, 0x65, 0x1F, 0x53, 0x7A, 0xEC, 0x55, 0x8F, 0x6C, 0x65, 0x3D, 0x1C, 0x3C, 0xD2, 0xA4, 0x1F,
	0xCD, 0x96, 0x2D, 0x40, 0x71, 0xCF, 0x51, 0xA6, 0x0A, 0xE6, 0xA5, 0x22, 0x70, 0x65, 0x5B, 0xAA,
	0x13, 0x08, 0x0C, 0x0A, 0xE2, 0x16, 0xB5, 0xD6, 0x6B, 0xF1, 0xFF, 0xA3, 0x43, 0xEC, 0xC3, 0x76,
	0xA8, 0x5E, 0xCD, 0x7D, 0x65, 0xF5, 0xE0, 0xFD, 0xF6, 0x4C, 0xFB, 0x8C, 0xED, 0xBF, 0xDB, 0x80,
	0x94, 0x7E, 0xC1, 0xA7, 0xD4, 0xE0, 0x75, 0x39, 0x55, 0xFA, 0x8B, 0x0A, 0xE7, 0x71, 0xE5, 0x04,
	0x21, 0x8C, 0x79, 0xD5, 0x47, 0x8D, 0x54, 0xE5, 0x71, 0x42, 0xDF, 0x8B, 0xEA, 0xD7, 0x62, 0x80,
	0xD7, 0xEE, 0xC6, 0x94, 0x19, 0xF1, 0x9F, 0x9F, 0x74, 0xA7, 0x6A, 0xBB, 0xF0, 0x06, 0x12, 0xB0,
	0x7F, 0x49, 0xBA, 0xB0, 0xE1, 0x42, 0xB6, 0x45, 0xC5, 0xF0, 0xDA, 0x87, 0x31, 0x54, 0xF8, 0x92,
	0x23, 0x81, 0xA4, 0x37, 0x79, 0xF3, 0x3A, 0xF2, 0x0E, 0x21, 0x23, 0x1C, 0x26, 0x55, 0x53, 0x62,
	0x0C, 0xBC, 0x16, 0x76, 0xF9, 0xB9, 0x0D, 0x04, 0x38, 0x7F, 0x73, 0xE8, 0x88, 0xDF, 0xA5, 0x9E,
	0xC2, 0x5E, 0xE2, 0xFA, 0xBB, 0x8B, 0x4F, 0x19, 0x6C, 0x90, 0x3E, 0x97, 0x50, 0x06, 0xAF, 0x02,
	0x0E, 0x0E, 0x17, 0x8F, 0x58, 0x9D, 0x61, 0x0C, 0x13, 0x17, 0x33, 0x16, 0xB8, 0x20, 0x72, 0x8C,
	0xFA, 0xAF, 0x08, 0x44, 0xA9, 0x63, 0xE5, 0xFC, 0xD6, 0x1B, 0x43, 0x93, 0x38, 0xC1, 0x2F, 0x79,
	0xCE, 0x66, 0x45, 0x64, 0xC6, 0x94, 0xBB, 0x44, 0x9E, 0xDF, 0xA1, 0x7A, 0x89, 0xBE, 0x66, 0x44,
	0x14, 0x99, 0x9F, 0x7C, 0x09, 0x23, 0x04, 0x83, 0x93, 0xEA, 0xBB, 0x78, 0xA4, 0x2D, 0xDA, 0xAD
};

/*Constant response packet. This packet structure is simple:
1.Xor bytes from offset 4 to 35(0x74 to 0xBA) with byte at offset 0 (0x57)
2.Compute a checksum of first 35 bytes; the result must match the last byte (0x7D). If this checksum doesn't match then the TL866 report "V0 is illegal"

Actually the TL866 firmware will check if the first 8 bytes are: 51 33 51 00 c8 9d d4 3e ; if yes then return FAKE!
Now what happen if we put 37 bytes of '0'? well xor zero by zero equal zero and checksum of an infinite number of zeros is zero! and this will pass the genuine check. Very weak algorithm.
But we keep this as original chip does. Thanks Gerard for your captures.
*/
const unsigned char response[] PROGMEM =
{
	0x57, 0x33, 0x57, 0x00, 0x74, 0x87, 0x75, 0xC5, 0xE9, 0xE6, 0xF5, 0xEE, 0x95, 0x12, 0x5B, 0x66,
	0x1F, 0x0A, 0x68, 0xFC, 0x8A, 0x80, 0x3B, 0xFD, 0x72, 0x01, 0x7E, 0x38, 0x2B, 0x4D, 0xC3, 0x43,
	0xC7, 0xA5, 0x9A, 0xBA, 0x7D
};


//Data line port defines
#define DATA_LINE_DDR		DDRB
#define DATA_LINE_OUT_PORT	PORTB
#define DATA_LINE_IN_PORT	PINB
#define DATA_LINE_PIN		4


//clock line port defines
#define CLOCK_LINE_DDR		DDRB
#define CLOCK_LINE_PORT		PINB
#define CLOCK_LINE_PIN		3

//helper macros
#define data_line			(DATA_LINE_IN_PORT &(1<<DATA_LINE_PIN))
#define clock_line			(CLOCK_LINE_PORT &(1<<CLOCK_LINE_PIN))
#define data_line_high		(DATA_LINE_OUT_PORT |= (1<<DATA_LINE_PIN))
#define data_line_low		(DATA_LINE_OUT_PORT &= ~(1<<DATA_LINE_PIN))
#define set_data_line_out	(DATA_LINE_DDR |=(1<<DATA_LINE_PIN))
#define set_data_line_in	(DATA_LINE_DDR &= ~(1<<DATA_LINE_PIN))

//machine state
#define STATE_RECEIVE		0
#define STATE_TRANSMIT		1


unsigned char buffer[47];//Receive/Send buffer
volatile unsigned char state_machine;//keep the machine state
unsigned char bit;
unsigned char byte;

static inline unsigned int crc16_ccitt(unsigned char* data, unsigned int len);
static inline void spi_receive();
static inline void spi_send();
static inline void check();

//Main routine
int main(void)
{
	DATA_LINE_OUT_PORT |=(1<<DATA_LINE_PIN);//pull-up on data line
	CLOCK_LINE_PORT |=(1<<CLOCK_LINE_PIN);//pull-up on clock line
	PCMSK |=(1<<CLOCK_LINE_PIN);//set PCMSK clock line external interrupt mask
	GIMSK|=(1<<PCIE);//activate external sense interrupts
	bit=0;
	byte=0;
	state_machine=STATE_RECEIVE;//we start in receive mode
	_delay_ms(5);//wait for lines to settle. 
	sei();//enable interrupts
	while(1)//just nothing here. From now all tasks are interrupt driven.
	{
	}
}


//CRC16_CCITT routine
unsigned int crc16_ccitt(unsigned char* data, unsigned int len)
{
	unsigned int crc=0;
	while(len--)
	{
		crc  = (unsigned char)(crc >> 8) | (crc << 8);
		crc ^= *data++;
		crc ^= (unsigned char)(crc & 0xFF) >> 4;
		crc ^= (crc << 8) << 4;
		crc ^= ((crc & 0xFF) << 4) << 1;
	}
	return crc;
}


//Simple interrupt routine handler
ISR(PCINT0_vect)
{
	if(clock_line)//If clock line was changed from 0 to 1 then we call receive/send routines
	{
		if(!state_machine)
		spi_receive();
		else
		spi_send();
	}
	
}

//Receive 10 bytes routine. Driven by clock line
static inline void spi_receive()
{
	buffer[byte]<<=1;
	if(data_line)
	buffer[byte]|=1;
	bit++;
	bit &=7;
	if(!bit)
	{
		byte++;
		if(byte==10)
		{
			byte=0;
			set_data_line_out;//switch the data line in output mode
			check();//check and prepare transmit buffer
			if (buffer[byte] & 0x80)//put the first bit to data line
			data_line_high;
			else
			data_line_low;
			buffer[byte]<<=1;
			bit++;
			state_machine=STATE_TRANSMIT;
		}
	}
}

//Send 47 bytes routine. Driven by clock line.
static inline void spi_send()
{
	if (buffer[byte] & 0x80)
	data_line_high;
	else
	data_line_low;
	buffer[byte]<<=1;
	bit++;
	bit &=7;
	if(!bit)
	{
		byte++;
		if(byte==47)
		{
			byte=0;
		}
	}
}

//check the received data
static inline void check()
{
	unsigned char i,index;
	//swap byte 2 by 8 and 4 by 9
	i=buffer[8];
	buffer[8]=buffer[2];
	buffer[2]=i;
	i=buffer[9];
	buffer[9]=buffer[4];
	buffer[4]=i;
	//calculate crc16_ccitt of the first 10 bytes. If they match then we change the state machine in transmit mode.
	if(crc16_ccitt(buffer,8)==((buffer[9] << 8) | buffer[8]))
	{
		index=(buffer[0] & buffer[7]);//The starting index for byte xoring is byte 0 AND 7
		for(i=0;i<10;i++)
		{
			buffer[i] ^=pgm_read_byte(&xortable[index++]);//xor first 10 bytes against a xortable
			index &=0xFF;//index is incremented modulo 256
		}
		for(i=0;i<37;i++)
		{
			buffer[i+10]=pgm_read_byte(&response[i]);//copy the next predefined 37 bytes from flash to the SRAM buffer
		}
	}
}


