/*********************************************
Project : PG 8x8 Midi Matrix 
Version : 0.1
Date    : 17.9.2002
Author  : Matti Leino
Company : Project Groove

PG 8x8 Midi Matrix uses MAXIMs MAX456 video crosspoint switch to route midi signals.
See Maxims datasheet for information: http://www.maxim-ic.com

If you have any questions, feel free to ask: mle@nic.fi

And of course, this code is GPL, so use it any way you want, but please keep these commanted lines in code.

(c) Matti Leino

Chip type           : AT90S2313
Clock frequency     : 4,000000 MHz
Memory model        : Tiny
Internal SRAM size  : 128
External SRAM size  : 0
Data Stack size     : 32         

------ss
---------------------------------------
IOs
PortB 0		LCD RS
PortB 1		LCD RD
PortB 2		LCD EN
PortB 3		free (output)
PortB 4		LCD DB4
PortB 5		LCD DB5
PortB 6		LCD DB6
PortB 7		LCD DB7

PortD 0		free (input)
PortD 1		Rotary Encoder Input A
PortD 2		Rotary Encoder Input B (Ext IRQ 0)
PortD 3		Exec-button (Ext IRQ 1)
PortD 4 	/LATCH for matrix
PortD 5 	DATA for matrix
PortD 6		WR/SCLK for matrix

---------------------------------------------

*********************************************/

#include <90s2313.h>

// Alphanumeric LCD Module functions
#asm
   .equ __lcd_port=0x18
#endasm
#include <lcd.h>
#include <delay.h>
#include <stdio.h>


/***************************************
*           Global constants           *
***************************************/
  
#define ENC_CH_A	!PIND.1
#define ENC_CH_B	!PIND.2
#define EXEC			!PIND.3
#define CP_LATCH	PIND.4
#define CP_DATA		PIND.5
#define CP_WR			PIND.6

typedef unsigned char byte;

/*************************************************
* There's two special characters defined.        *
* outOff indicates, as you propably can imagine, *
* if output is not selected for current input.   *
* outOn if output is selected                    *
*************************************************/

flash byte outOff[8]=
{
	0b0000000,
	0b0011111,
	0b0010001,
	0b0010001,
	0b0010001,
	0b0011111,
	0b0000000,
	0b0000000
};

flash byte outOn[8]=
{
	0b0000000,
	0b0011111,
	0b0011111,
	0b0011111,
	0b0011111,
	0b0011111,
	0b0000000,
	0b0000000
};

/******************************************
* This function is straightly copy/pasted *
* from CodeVisions manual                 *
*******************************************/

void define_char(byte flash *pc,byte char_code)
{
	byte i,a;
	a = (char_code<<3) | 0x40;
	for(i = 0; i < 8; i++) lcd_write_byte(a++,*pc++);
}


/***************************************
*           Global variables           *
***************************************/

eeprom byte savedOutArray[8] = { 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9 };
byte inputOpCodes[8] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 };
byte outputSel[8] = { 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9, 0x9 };
unsigned char execValue = 0;
unsigned char new_limit = 8;
unsigned char buff[9];
unsigned char work0 = 0;
unsigned char work1 = 0;
unsigned char exec_counter = 0;
unsigned char encValue = 0;
unsigned char inputValue = 0;
unsigned char i = 0; 
unsigned long int CP_data;

/***************************************
*           Global functions           *
***************************************/

void setupIO(void);
void initLCD(void);
void chooseInput(void); 
void chooseOutput(void);
void crunchData(void);
void setCP(void);
void save_e2(void);


/***************************************
* External Interrupt 0 service routine *
***************************************/

interrupt [EXT_INT0] void ext_int0_isr(void)
{	
	if(!ENC_CH_A)
	{
		if(encValue < new_limit)
		{
			encValue++;
			delay_ms(15);
		}	
		else
		{
			encValue = 0;
			delay_ms(15);
		}			
	}
	else
	{
		if(encValue != 0)
		{
			encValue--;
			delay_ms(15);
		}
		else
		{
			encValue = new_limit;
			delay_ms(15);
		}
	}
		
	delay_ms(55);
}

/***************************************
* External Interrupt 1 service routine *
***************************************/

interrupt [EXT_INT1] void ext_int1_isr(void)
{
	while(EXEC)
	{	
		exec_counter++;
		delay_ms(100);
	}
	
	if(exec_counter <= 35)
	{
		execValue = 1;	
		delay_ms(50);
	}
	else
		save_e2();
		
	exec_counter = 0;
}

/***************************************
*                 MAIN                 *
***************************************/

void main(void)
{
	setupIO();
	define_char(outOff,0);
	define_char(outOn,1);
	
	_lcd_ready();
	_lcd_write_data(0xe);
	lcd_gotoxy(0,0);
	sprintf(buff, "in:%-1uout:", encValue+1);
	lcd_puts(buff);
	for(i = 0; i < 8; i++)
		outputSel[i] = savedOutArray[i];
	initLCD();
	lcd_gotoxy(3,0);
	crunchData();
	setCP();	
	
/***************************************
*               Main loop              *
***************************************/	

	while (1)
	{
		if(encValue == 0) 
			lcd_gotoxy(3,0);
		else
			lcd_gotoxy(encValue-1,1);		
			
		if(encValue == 0 && execValue == 1)
		{
		 	chooseInput();
			delay_ms(250);
		 	encValue = 0;
		 	execValue = 0;
		}

		if(encValue != 0 && execValue == 1)
		{
			chooseOutput();
		}
  };
} 

/**************************************************************************************/
/*****************                      FUNCTIONS                       ***************/
/**************************************************************************************/

void setupIO(void)
{
	// Declare your local variables here

	// Input/Output Ports initialization
	// Port B
	PORTB=0x00;
	DDRB=0xFF;

	// Port D
	PORTD=0x1F;
	DDRD=0x70;

	// Timer/Counter 0 initialization
	// Clock source: System Clock
	// Clock value: Timer 0 Stopped
	// Mode: Output Compare
	// OC0 output: Disconnected
	TCCR0=0x00;
	TCNT0=0x00;

	// Timer/Counter 1 initialization
	// Clock source: System Clock
	// Clock value: Timer 1 Stopped
	// Mode: Output Compare
	// OC1 output: Discon.
	// Noise Canceler: Off
	// Input Capture on Falling Edge

	// These has been disabled cause of the lack of the code memory...=)
	/*
	TCCR1A=0x00;
	TCCR1B=0x00;
	TCNT1H=0x00;
	TCNT1L=0x00;
	OCR1H=0x00;
	OCR1L=0x00;
  */
  
	// External Interrupt(s) initialization
	// INT0: On
	// INT0 Mode: Low level
	// INT1: On
	// INT1 Mode: Low level
	GIMSK=0xC0;
	MCUCR=0x00;
	GIFR=0xC0;

	// Timer(s)/Counter(s) Interrupt(s) initialization
	TIMSK=0x00;

	// Analog Comparator initialization
	// Analog Comparator: Off
	// Analog Comparator Input Capture by Timer/Counter 1: Off
	ACSR=0x80;

	// LCD module initialization
	lcd_init(8);

	// Global enable interrupts
	#asm("sei")
}

/*******************************************************************************************************/

void initLCD(void)
{
	for(i = 0; i < 8; i++)
	{
		lcd_gotoxy(i,1);
		if( outputSel[i] == inputOpCodes[inputValue-1] )		//(outputSel[i] != 0xa)
			lcd_putchar(1);
		else
			lcd_putchar(0);
	}
}

/*******************************************************************************************************/

void chooseInput(void)
{
	new_limit = 7;
	execValue = 0;
	while( execValue == 0 )
	{
		inputValue = encValue+1;
		lcd_gotoxy(3,0);
		sprintf(buff, "%-1u", inputValue);
		lcd_puts(buff);
		initLCD();			
		lcd_gotoxy(0,0);
		delay_ms(50);
	};
	new_limit = 8;
} 

/*******************************************************************************************************/

void chooseOutput(void)
{
	lcd_gotoxy(encValue-1,1);
	execValue = 0;
	if(outputSel[encValue-1] != 0x9)
	{
		lcd_putchar(0);
		outputSel[encValue-1] = 0x9;
	}
	else
	{
		lcd_putchar(1);
		outputSel[encValue-1] = inputOpCodes[inputValue-1];
	}
	lcd_gotoxy(encValue-1,1);
	crunchData();
	setCP();
}

/*******************************************************************************************************/

void crunchData(void)
{
	for(i = 0; i < 8; i+=2)
	{
		work0 = 0;
		work1 = 0;
		work0 = outputSel[i];	
		work1 = outputSel[i+1];
		work0 <<= 4; // Shiftataan 4 bittiä vasuriin...
		work0 &= 0xF0;
		work0 |= work1; // Oorataan siihen work1 -> saadaan tavu, jossa4 ylintä on work0, ja 4 alinta work1
		CP_data |= work0; // Oorataan se CP_dataan.
		if( i < 6 )
			CP_data <<= 8; // Shiftataan 8 vasuriin.
	}
		/*
		sprintf(buff,"%-lu",CP_data);
		lcd_gotoxy(0,1);
		lcd_puts(buff);
		delay_ms(5000);
		initLCD();
		*/
}

/*******************************************************************************************************/

void save_e2(void)
{
	for(i = 0; i < 8; i++)
		savedOutArray[i] = outputSel[i];
	lcd_gotoxy(0,1);
	lcd_putsf("Saved!  ");
	delay_ms(3000);
	initLCD();
} 

/*******************************************************************************************************/

void setCP(void)
{
	CP_LATCH = 0;
	for(i = 0; i < 32; i++)
	{
		#asm
			bst r23,7
 		  in  r0,0x10
 		  bld r0,5
			out 0x10,r0
		#endasm
		CP_data <<= 1;	
		CP_WR = 1;						// Kellotetaan Crosspointille.
		CP_WR = 0;
	}
	delay_us(5);
	CP_LATCH = 1;	
}

// End Of File

/*	for(i = 32; i > 0; i--)
	{
		CP_DATA = (CP_data << i) & 0x1;  
		CP_WR = 1;						// Kellotetaan Crosspointille.
		CP_WR = 0;
	}*/
	
		/*
				CP_LATCH = 0;
				delay_ms(500);
				CP_LATCH = 1;
				CP_DATA = 0;
				delay_ms(500);
				CP_DATA = 1;
				CP_WR = 0;
				delay_ms(500);
				CP_WR = 1;
				CP_DATA = 0;
				delay_ms(500);
				CP_DATA = 1; 
	*/ 
