/*
Snake demo
eHaJo Blinkenlights Bausatz
www.eHaJo.de
*/

/*
 * snake.c
 *
 * Created: 14.09.2012
 *  Authors: CalM   -> calm@calm-solutuions.de
 *           /joggl -> info@ehajo.de
 */

#define F_CPU 8000000UL
#include <inttypes.h>
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdlib.h>

//Es sind 16 defines noetig
//Zeile 1
#define ZEILE1_PORT PORTB
#define ZEILE1_PIN PB4
//Zeile 2
#define ZEILE2_PORT PORTB
#define ZEILE2_PIN PB5
//Zeile 3
#define ZEILE3_PORT PORTB
#define ZEILE3_PIN PB6
//Zeile 4
#define ZEILE4_PORT PORTB
#define ZEILE4_PIN PB7
//Zeile 5
#define ZEILE5_PORT PORTC
#define ZEILE5_PIN PC7
//Zeile 6
#define ZEILE6_PORT PORTC
#define ZEILE6_PIN PC6
//Zeile 7
#define ZEILE7_PORT PORTC
#define ZEILE7_PIN PC5
//Zeile 8
#define ZEILE8_PORT PORTC
#define ZEILE8_PIN PC4

//Spalte 1
#define SPALTE1_PORT PORTD
#define SPALTE1_PIN PD4
//Spalte 2
#define SPALTE2_PORT PORTD
#define SPALTE2_PIN PD5
//Spalte 3
#define SPALTE3_PORT PORTD
#define SPALTE3_PIN PD6
//Spalte 4
#define SPALTE4_PORT PORTD
#define SPALTE4_PIN PD7
//Spalte 5
#define SPALTE5_PORT PORTB
#define SPALTE5_PIN PB0
//Spalte 6
#define SPALTE6_PORT PORTB
#define SPALTE6_PIN PB1
//Spalte 7
#define SPALTE7_PORT PORTB
#define SPALTE7_PIN PB2
//Spalte 8
#define SPALTE8_PORT PORTB
#define SPALTE8_PIN PB3


#define SPALTE1_AN SPALTE1_PORT &= ~(1<<SPALTE1_PIN)
#define SPALTE2_AN SPALTE2_PORT &= ~(1<<SPALTE2_PIN)
#define SPALTE3_AN SPALTE3_PORT &= ~(1<<SPALTE3_PIN)
#define SPALTE4_AN SPALTE4_PORT &= ~(1<<SPALTE4_PIN)
#define SPALTE5_AN SPALTE5_PORT &= ~(1<<SPALTE5_PIN)
#define SPALTE6_AN SPALTE6_PORT &= ~(1<<SPALTE6_PIN)
#define SPALTE7_AN SPALTE7_PORT &= ~(1<<SPALTE7_PIN)
#define SPALTE8_AN SPALTE8_PORT &= ~(1<<SPALTE8_PIN)

#define SPALTE1_AUS SPALTE1_PORT |= (1<<SPALTE1_PIN)
#define SPALTE2_AUS SPALTE2_PORT |= (1<<SPALTE2_PIN)
#define SPALTE3_AUS SPALTE3_PORT |= (1<<SPALTE3_PIN)
#define SPALTE4_AUS SPALTE4_PORT |= (1<<SPALTE4_PIN)
#define SPALTE5_AUS SPALTE5_PORT |= (1<<SPALTE5_PIN)
#define SPALTE6_AUS SPALTE6_PORT |= (1<<SPALTE6_PIN)
#define SPALTE7_AUS SPALTE7_PORT |= (1<<SPALTE7_PIN)
#define SPALTE8_AUS SPALTE8_PORT |= (1<<SPALTE8_PIN)


#define ZEILE1_AN ZEILE1_PORT |= (1<<ZEILE1_PIN)
#define ZEILE2_AN ZEILE2_PORT |= (1<<ZEILE2_PIN)
#define ZEILE3_AN ZEILE3_PORT |= (1<<ZEILE3_PIN)
#define ZEILE4_AN ZEILE4_PORT |= (1<<ZEILE4_PIN)
#define ZEILE5_AN ZEILE5_PORT |= (1<<ZEILE5_PIN)
#define ZEILE6_AN ZEILE6_PORT |= (1<<ZEILE6_PIN)
#define ZEILE7_AN ZEILE7_PORT |= (1<<ZEILE7_PIN)
#define ZEILE8_AN ZEILE8_PORT |= (1<<ZEILE8_PIN)

#define ZEILE1_AUS ZEILE1_PORT &= ~(1<<ZEILE1_PIN)
#define ZEILE2_AUS ZEILE2_PORT &= ~(1<<ZEILE2_PIN)
#define ZEILE3_AUS ZEILE3_PORT &= ~(1<<ZEILE3_PIN)
#define ZEILE4_AUS ZEILE4_PORT &= ~(1<<ZEILE4_PIN)
#define ZEILE5_AUS ZEILE5_PORT &= ~(1<<ZEILE5_PIN)
#define ZEILE6_AUS ZEILE6_PORT &= ~(1<<ZEILE6_PIN)
#define ZEILE7_AUS ZEILE7_PORT &= ~(1<<ZEILE7_PIN)
#define ZEILE8_AUS ZEILE8_PORT &= ~(1<<ZEILE8_PIN)


#define ALLE_ZEILEN_AN {ZEILE1_AN; ZEILE2_AN; ZEILE3_AN; ZEILE4_AN;ZEILE5_AN; ZEILE6_AN; ZEILE7_AN; ZEILE8_AN;}
#define ALLE_ZEILEN_AUS {ZEILE1_AUS; ZEILE2_AUS; ZEILE3_AUS; ZEILE4_AUS;ZEILE5_AUS; ZEILE6_AUS; ZEILE7_AUS; ZEILE8_AUS;}


#define ALLE_SPALTEN_AN {SPALTE1_AN; SPALTE2_AN; SPALTE3_AN; SPALTE4_AN;SPALTE5_AN; SPALTE6_AN; SPALTE7_AN; SPALTE8_AN;}
#define ALLE_SPALTEN_AUS {SPALTE1_AUS; SPALTE2_AUS; SPALTE3_AUS;SPALTE4_AUS; SPALTE5_AUS; SPALTE6_AUS; SPALTE7_AUS; SPALTE8_AUS;}

//Nicht schön tut aber seinen dienst
#define ZEILE_AUS(zeile) {	switch(zeile){case 0: ZEILE1_AUS; break; case 1: ZEILE2_AUS; break; case 2: ZEILE3_AUS; break; case 3: ZEILE4_AUS; break; case 4: ZEILE5_AUS; break; case 5: ZEILE6_AUS; break; case 6: ZEILE7_AUS; break; case 7: ZEILE8_AUS; break; default: break; }}
#define ZEILE_AN(zeile) {switch(zeile) {	case 0:	ZEILE1_AN;	break;	case 1:	ZEILE2_AN;	break;	case 2:	ZEILE3_AN;	break;	case 3:	ZEILE4_AN;	break;	case 4:	ZEILE5_AN;	break;	case 5:	ZEILE6_AN;	break;	case 6:	ZEILE7_AN;	break;	case 7:	ZEILE8_AN;	break;	default:	break;}}

#define SPALTE_AUS(zeile) {	switch(zeile){case 0: SPALTE1_AUS; break; case 1: SPALTE2_AUS; break; case 2: SPALTE3_AUS; break; case 3: SPALTE4_AUS; break; case 4: SPALTE5_AUS; break; case 5: SPALTE6_AUS; break; case 6: SPALTE7_AUS; break; case 7: SPALTE8_AUS; break; default: break; }}
#define SPALTE_AN(zeile) {switch(zeile) {	case 0:	SPALTE1_AN;	break;	case 1:	SPALTE2_AN;	break;	case 2:	SPALTE3_AN;	break;	case 3:	SPALTE4_AN;	break;	case 4:	SPALTE5_AN;	break;	case 5:	SPALTE6_AN;	break;	case 6:	SPALTE7_AN;	break;	case 7:	SPALTE8_AN;	break;	default:	break;}}

volatile uint8_t Bildspeicher[8][8] =
{
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0}
};//Der Bildspeicher des Systems als 2D-Array

void snake();
void bild_clear();

volatile uint8_t x = 0, y = 0;		// aktuelle koordinaten der "schlange"
volatile int8_t xvec = 1; yvec = 0;	// vectoren fuer die bewegung
volatile uint8_t apple_x = 4, apple_y = 4;  // position vom apfel
volatile uint8_t erstmals = 1;


volatile uint8_t akt_zeile=0;

ISR(INT0_vect)
{ // links
	if(erstmals)
	{
		srand(TCNT0);
		erstmals = 0;
	}			
	xvec = -1;
	yvec = 0;
}

ISR(INT1_vect)
{ // oben
	if(erstmals)
	{
		srand(TCNT0);
		erstmals = 0;
	}		
	xvec = 0;
	yvec = 1;
}

ISR(INT2_vect)
{ // rechts
	if(erstmals)
	{
		srand(TCNT0);
		erstmals = 0;
	}		
	xvec = 1;
	yvec = 0;
}

ISR(INT3_vect)
{  // unten
	if(erstmals)
	{
		srand(TCNT0);
		erstmals = 0;
	}	
	xvec = 0;
	yvec = -1;
}

ISR(TIMER0_COMPA_vect)
{
	cli();
	ALLE_SPALTEN_AUS;
	ZEILE_AUS(akt_zeile);
	
	akt_zeile++;
	akt_zeile &= 0x07; // maximal 8 zeilen
	
	//Zeile 1
	if(Bildspeicher[akt_zeile][0])
		SPALTE1_AN;
		
	//Zeile 2
	if(Bildspeicher[akt_zeile][1])
		SPALTE2_AN;
		
	//Zeile 3
	if(Bildspeicher[akt_zeile][2])
		SPALTE3_AN;
		
	//Zeile 4
	if(Bildspeicher[akt_zeile][3])
		SPALTE4_AN;
		
	//Zeile 5
	if(Bildspeicher[akt_zeile][4])
		SPALTE5_AN;
		
	//Zeile 6
	if(Bildspeicher[akt_zeile][5])
		SPALTE6_AN;
		
	//Zeile 7
	if(Bildspeicher[akt_zeile][6])
		SPALTE7_AN;
		
	//Zeile 8
	if(Bildspeicher[akt_zeile][7])
		SPALTE8_AN;
	
	ZEILE_AN(akt_zeile);
	sei();
}

int main(void)
{
	//Setup der IO
	PORTB=0;
	PORTC=0;
	PORTD=0b00001111; // die vier Tasten sind Eingänge
	DDRB=0xFF; //Alles auf Ausgang, alles low
	DDRC=0xFF; //Alles auf Ausgang, alles low
	DDRD=0b11110000; // Pullups der Eingänge einschalten
	
	PORTD |= (1<<PD3) | (1<<PD2) | (1<<PD1) | (1<<PD0);
	MCUCR &= ~(1<<PUD);
		
	//Direkt aus dem Takt der CPU den Timer versorgen
	CLKSEL0 =  (1<<EXTE); // externe Taktquelle einschalten
	CLKPR = (1<<CLKPCE);  // Prescaler-Change aktivieren
	CLKPR = 0;			  // Prescaler Teilefaktor 1

	TCCR0A |= (1<<WGM01); // Timer im CTC-Mode
	TCCR0B |= (1<<FOC0A) | (1<<CS01) | (1<<CS00); // Compare output A aktivieren, 64er Teiler
	OCR0A = 200; // Hier gibts nen Compare-Match Interrupt
	TIMSK0 |= (1<<OCIE0A); // Capture Interrupt enable Timer 0 Capture A
	SREG |= (0b10000000); // Global Interrupt enable
	
	EICRA |= (1<<ISC01) | (1<<ISC11) | (1<<ISC21) | (1<<ISC31); // Externe Interrupts, fallende Flanke
	EIMSK |= (1<<INT0) | (1<<INT1) | (1<<INT2) | (1<<INT3);     // Externe Interrupts aktivieren
	sei(); // Globales Interrupt enable
	
	snake();
}


void snake()
{
	uint8_t zaehler = 0;
	
	while(1)
	{
		x += xvec;
		x %= 8; //&= 0b00000111;	// Bei ueberlauf wieder von vorne anfangen
		y += yvec;
		y %= 7;	// Bei ueberlauf wieder von vorne anfangen

		if(Bildspeicher[x][y]) // Apfel an der Stelle
		{
			apple_x = rand() % 8;
			apple_y = rand() % 7;
			zaehler++;
		}
		bild_clear();		// Letzte Schlange loeschen
		
		// Den Punktestand als binaer in der obersten Zeile anzeigen
		if(zaehler & 0b00000001)
			Bildspeicher[7][7] = 1;
		if(zaehler & 0b00000010)
			Bildspeicher[6][7] = 1;
		if(zaehler & 0b00000100)
			Bildspeicher[5][7] = 1;
		if(zaehler & 0b00001000)
			Bildspeicher[4][7] = 1;
		if(zaehler & 0b00010000)
			Bildspeicher[3][7] = 1;
		if(zaehler & 0b00100000)
			Bildspeicher[2][7] = 1;
		if(zaehler & 0b01000000)
			Bildspeicher[1][7] = 1;
		if(zaehler & 0b10000000)
			Bildspeicher[0][7] = 1;
		
		Bildspeicher[x][y] = 1;
		Bildspeicher[apple_x][apple_y] = 1;
		_delay_ms(400);
	}	
	
}

void bild_clear()
{	// das array leeren
	for(uint8_t n = 0; n < 8; n++)
	{
		for(uint8_t m = 0; m<8; m++)
		{
			Bildspeicher[n][m] = 0;
		}
	}
}