//***************************************************************************************** 
// Description: GPL951 A18 : 1 channel
//					   SPU Play ADPCM : 1 channel, SPU Play MIDI
//					   TFT : i80 I/F, 128 * 128
//					   PPU : 1 Text(blend), 4 sprite (zoom, rotate, moasic, blend)
//					   Halt Mode : IOB[7:0] Key wakeup 
//					   Deep Halt Mode : Alarm wakeup, Enter_DeepHaltMode(10), Input Paramter : 10 (Unit : Sec)
//					   ADC Line-In : IOE[0], ADC output data : IOC[11:0]
//					   Calendar function code
// Plateform: GPL951, unSP IDE 
// Edited and Created by Generalplus Technology Co., Ltd.

// TFT Module : A250_ST7735S (1.44", 128 * 128)
//    GPL951						Device
// IOA[4]    : TFT WE				TFT         : WR
// IOA[3]    : TFT CS				TFT         : CS
// IOA[2]    : TFT RS				TFT         : RS
// IOA[1]    : TFT OE				TFT         : RD
// IOA[0]    : TFT TE				TFT         : X

// IOB[7:0]  : Halt mode, Key change wakeup

// IOC[11:0] : ADC Output

// IOE[0]    : ADC Line-In
// IOE[1]    : TFT : RST			TFT 		: /RST	

// IOF[7:0]  : TFT Data				TFT 		: Data[7:0]
// IOF[8]    : A18 playback with auto mode
// IOF[9]    : Enter Halt Mode (Only IOB key wakeup)
// IOF[10]   : Enter Deep Halt Mode (RTC alarm wakeup)
// IOF[11]   : A18 Stop
// IOF[12]   : A18 Pause
// IOF[13]   : A18 Resume
// IOF[14]   : Next MIDI
// IOF[15]   : Stop MIDI

// FIQ		 : SPU, Play MIDI, ADPCM
// IRQ0		 : A18
// IRQ1		 : ADC
// IRQ5 	 : PPU
// IRQ7		 : RTC, 
//             TimeBaseA, for ADC convert

// Update History:
// 2016/12/26	V0.1 : Original 
// 2016/12/27	V0.2 : Modify Main.c : fnShow_Chr Paramter : 128, 160 to 128, 128
//									   Add TEXT blend effect	
//					   Modify RTC.c, Enter_DeepHaltMode(20) function, Add input paramter : Alarm (Unit : Sec)
//									 Enter_DeepHaltMode(20), alarm program bug fixed
//									 Enter_HaltMode function, IOB key wakeup, clear Key Change Interrupt flag
// 2017/01/19	V0.3 : I80 from Continuous mode to single mode
//					   Modify Main.c : Add LCD_I80_Align function, enable TMBB = 32Hz for update LCD
//					   Modify Resource_arrangement.asm : include file : Text1CellIdx.idx for TEXT index (G+Director V1.2.7.7 convert)
//					   Modify drv_l1_tft.c : Add LCD_I80_Align function
// 2017/01/26	V0.4 : 1. remove Hoffset & cosin(GPL951 not support) ram decalre in PPU driver
//					   2. modify TEXT index length declare in main.c
// 2017/02/08	V0.5 : 1. Modify A18 from 2ch to 1ch
//					   2. Add SPU play ADPCM, MIDI function
//					   3. Modify ADC INT from FIQ to IRQ1, FIQ for SPU
//					   4. Modify System.asm : 
//						- F_SACM_CHA_PlayInterrupt_Disable, F_SACM_CHB_PlayInterrupt_Disable, F_SACM_CHA_AudioOutput_Disable, F_SACM_CHB_AudioOutput_Disable
//						- Mask DAC AMP disable prrgram <= A18 stop, but SPU still playing	
//					   5. Modify Main.c : Add SPU play, pause, resume ADPCM and MIDI function code
// 2017/04/07	V0.6 : 1. Modify LCD_I80_Align, i80_write_cmd, i80_write_data, i80_read_data function
//						while ((*P_TFT_Status & 0x00F0) != 0x0)		//restart TFT, check status = idle, 0x705A[7:4]=4'h0
//							__asm ("nop");	
//						*P_TFT_Ctrl = 0x20xF;		// before 0x7050[0] write "1", check P_TFT_Status
// 2017/07/27 	V1.0 : Release
// 2019/02/18	V1.1 : Add register setting : Improve PPU performance  
//					   Update SPU Lib from V1.0.4 to V1.0.10
//					   Update Body header file for GPL951XXUB
//					   Update Calibration.asm for new SPI flash model
//==========================================================================
//**************************************************************************
// Header File Included Area
//**************************************************************************
#include "GPL951_Body.h"
#include "SACM.h"

#include "PPUsystem.h"                       
#include "PPUDrv.h"
#include "Pattern\SpriteSP.tab"
#include "drv_l1_tft.h"

#include  "SPU_Lib\SpuDrv.h"
//**************************************************************************
// Contant Defintion Area
//**************************************************************************
#define MaxSpeechNum		5		// Max. of speech in resource
#define MaxVolumeNum		16		// Max. of volume settings
#define MaxMidiNum			7		// Max. of Midi in resource

#define Foreground			0
#define Background			1
//#define ServiceType			Foreground
#define ServiceType			Background

// SPU section
#define MAX_SEQ 		7
//**************************************************************************
// Function Call Publication Area
//**************************************************************************


//*****************************************************************************************
// PPU Driver Area
//*****************************************************************************************
#define 	SP_MAX 				255		// Max Sprite Number 
#define 	TX1_INDEX_LENGTH	128		// Text1 Index Table Length
#define 	TX2_INDEX_LENGTH	1		// Text2 Index Table Length
#define 	TX3_INDEX_LENGTH	1		// Text3 Index Table Length
#define 	TX4_INDEX_LENGTH	1		// Text4 Index Table Length 
int	__attribute__((section(".iram")))	Text1IndexTable[TX1_INDEX_LENGTH];	//Text1 index table (number array)		
int __attribute__((section(".iram")))	Text2IndexTable[TX2_INDEX_LENGTH];	//Text2 index table (number array)	
int __attribute__((section(".iram")))	Text3IndexTable[TX3_INDEX_LENGTH];	//Text3 index table (number array)	
int __attribute__((section(".iram")))	Text4IndexTable[TX4_INDEX_LENGTH];	//Text4 index table (number array)	 		
int __attribute__((section(".iram")))	Text1AttrTable[TX1_INDEX_LENGTH>>1];	//Text1 attribute table (attribute array)	
int __attribute__((section(".iram")))	Text2AttrTable[TX2_INDEX_LENGTH>>1]; //Text2 attribute table (attribute array)
int __attribute__((section(".iram")))	Text3AttrTable[TX3_INDEX_LENGTH>>1]; //Text3 attribute table (attribute array)
int __attribute__((section(".iram")))	Text4AttrTable[TX4_INDEX_LENGTH>>1]; //Text4 attribute table (attribute array)
SPRITE			Sprite_Buff[SP_MAX];	//Sprite structure
SPRITE			Sprite_temp_Buff[2];	//Sprite structure
int __attribute__((section(".iram")))	SpriteBuf0[SP_MAX<<2];	//Sprite buffer 0
int	__attribute__((section(".iram")))	SpriteBuf1[SP_MAX<<2];	//Sprite buffer 1
static int tft_flag,disp_flag,gpio_flag,frame_rate_flag;

#define     MAX_SP_NUMBER   	255			// Maximum 256 sprites	
	
#define		INIT_SETTING		0			// Initial Setting
#define		GAME_LOOP			1			// Game Loop

#define systemclock 96000000
#define samplerate 8000
#define TimerPreloadSetup	0x10000-(systemclock/2/samplerate)
//**************************************************************************
// Global Variable Defintion Area
//**************************************************************************
unsigned Key;
unsigned SpeechIndex;
unsigned VolumeIndex;
unsigned BitRate;
unsigned ADC_Channel;
unsigned ADC_FIR_Type;
unsigned DAC_FIR_Type;
unsigned PauseFlag;
unsigned PlayCon;
unsigned Ext_Max_SpeechIndex;

// SPU Used
extern short* MIDI_Table;
extern short* ADPCM_Table;

//SACM section				
signed int vol = 100;

signed int iMidiIdx = 0;
signed int adpcmIdx = 0;

// ====================== PPU Used ======================
int Loop_State;
int flag2,count,zoom_level,rotate_level, Text_Blend_Flag, blend_level;
int flag,blend_level;
int mosaic_level;	
U32 addr0, addr1;

//=========================================================================================	
//*****************************************************************************************
// Function Call Definition Area
//***************************************************************************************** 


int main()
{
	unsigned int *p;		
	
	// ====================== A18 Variable Initial =======================			
	Key = 0;
	SpeechIndex = 0;
	VolumeIndex = 9;
	PauseFlag = 0;
	DAC_FIR_Type = C_DAC_FIR_Type2;
	ADC_FIR_Type = C_ADC_FIR_Type0;
	PlayCon = 0;
	
	Loop_State = INIT_SETTING;
		
	// ====================== System Initial =======================
	System_Initial();
	
	// ============= Only for GPL95100UB =============            
    *P_SPI_Improve |= C_SPI_Improve2_En;			//Improve SPI Flash 
	*P_SYS_Ctrl |= C_SPI_Improve1_En;				//0x7803 bit5 default(0)  spif_bug2_fix_en, 1 enable      
	*P_PLL_Sel |= 0x0100;							//Operating Mode : Real 32K (External 32768 X'tal)
	// =============================================== 
	
	// ====================== RTC Initial =======================	
	RTC_Initial();	
	
	// ====================== ADC Initial =======================
//-------------	set ADC
	*P_ADC_LineCH_En=0x01;	//line in0 channel enable
	*P_ADC_SH_Wait=0x00;
	*P_ADC_Setup=(C_ADC_AlwaysOn + C_ADC_AutoSample_TimerA + C_ADC_SysDiv1024);
	*P_MICADC_Setup |=C_MICADC_Vref_En;		//build ref voltage	
	*P_MADC_Ctrl=(C_ADC_ReadyFlag+C_ADC_ReadyINT+C_ADC_ErrFlag1+C_ADC_ErrFlag2+C_ADC_Manual_LineIn0);

	*P_TimerA_Preload=TimerPreloadSetup;
	*P_TimerA_Ctrl=0x8060;
	
//----------------- Timebase for line in sample rate	
	*P_TimeBaseA_Ctrl=(C_TimeBaseAFlag+C_TimeBaseAIntEn+C_TimeBaseAEn+C_TimeBaseA_1Hz);
		
//	*P_INT_Priority1|=C_ADC_FIQ;			//setup SAR ADC and MICin as FIQ mode
//	*P_INT_Priority2|=C_MicFifoFull_FIQ;
	
	// ====================== A18 Initial =======================
	SACM_A1800_Initial();		// A1800 initial
	USER_A1800_Volume(VolumeIndex);
	SACM_A1800_Play(SpeechIndex, DAC1, Ramp_Up + Ramp_Dn);	// playback with auto mode			
	
	// ====================== TFT Initial =======================	
	// Set TMBB for update LCD
	*P_TimeBaseB_Ctrl = C_TimeBaseBFlag | C_TimeBaseBEn | C_TimeBaseB_32Hz;
	
	//Enable IOA as TFT control I/F and IOF as TFT data	
	*P_IOA_Mux |= C_IOA_TFT_En;
	*P_IOF_Mux |= C_IOF_TFT_En;		
	
	// ====================== SPU Initial =======================
	// Set SPU beat event IRQ as FIQ
	*P_INT_Priority3 =0x0004;
	asm("fiq on");

	// Initial SPU
	InitSPU();	
	
	SetMidiChannelMask(0x0000,0xFFFF); // Enable SPU channel 0 ~ 15 for midi playback	
	SetMidiVolume(vol); // Set volume = 127	
	
	adpcmIdx=0;
	p = (int *)&ADPCM_Table + adpcmIdx*2; 
	TM_PlayPCM_NoEnv_FixCH(15,*(p+1),*p,63,100);	
	
//	iMidiIdx = 0;
//	p = (int *)&MIDI_Table + iMidiIdx*2; 
////	PlayMidi(*(p+1), *p, SS_PLAYMIDI_INFINITY); // Play midi infinitely
//	PlayMidi(*(p+1), *p, SS_PLAYMIDI_ONCE); // Play midi infinitely
//==================================================================		
	
	while(1)
	{
		Key = SP_GetCh();
		switch(Key)
		{	
			case 0x0000:
				break;

			case 0x0100:	// IOF8 + Vcc
				if(++SpeechIndex >= MaxSpeechNum)			// next speech
					SpeechIndex = 0;
				SACM_A1800_Play(SpeechIndex, DAC1 + DAC2, Ramp_Up + Ramp_Dn);		// play next speech
				break;

			case 0x0200:	// IOF9 + Vcc
				__asm ("INT OFF");
				Before_HaltMode();
				Enter_HaltMode();
				HaltMode_Wakeup_Initial();

				// SPU play ADPCM (drm)
				adpcmIdx=0;
				p = (int *)&ADPCM_Table + adpcmIdx*2; 
				TM_PlayPCM_NoEnv_FixCH(15,*(p+1),*p,63,100);	
				
				// Play A18
				SACM_A1800_Play(SpeechIndex, DAC1, Ramp_Up + Ramp_Dn);	// playback with auto mode	
				break;

			case 0x0400:	// IOF10 + Vcc
				// Only Power_On key or RTC (0.5s, 1s, Alarm) can wakeup GPL951
				// V33_REG, V18_REG shutdown, SPI flash do not work, only V18_RTC have power and RTC function can work correctly
				Enter_DeepHaltMode(20);						//Input Paramter = Alarm Timer (sec)

				break;

			case 0x0800:	// IOF11 + Vcc
				PlayCon = 0;
				SACM_A1800_Stop();
				break;
			
			case 0x1000:	// IOF12 + Vcc
				SACM_A1800_Pause();						// playback pause
//				PauseMidi();
//				TM_PausePCM(15);
				break;

			case 0x2000:	// IOF13 + Vcc
				SACM_A1800_Resume();					// playback resuem
//				ResumeMidi();
//				TM_ResumePCM(15);
				break;

			case 0x4000:	// IOF14 + Vcc			
				// When A18 and SPU stop, program will disable DAC AMP for save power, if we need use SPU, we should set DAC AMP Enable
				if ((*P_CHA_Ctrl & (C_AUDIO_AMPNegative_En+C_AUDIO_AMPPositive_En)) == 0)
					*P_CHA_Ctrl |= (C_AUDIO_AMPNegative_En+C_AUDIO_AMPPositive_En);

				if(++iMidiIdx >= MaxMidiNum)			// next Midi
					iMidiIdx = 0;
					
				p = (int *)&MIDI_Table + iMidiIdx*2; 
				PlayMidi(*(p+1), *p, SS_PLAYMIDI_ONCE); 
				break;

			case 0x8000:	// IOF15 + Vcc
				StopMidi();
				break;

			default:
				break;
		} // end of switch
		
		switch(Loop_State)
		{
			case INIT_SETTING:
				// Initial Picture + PPU setting
				Loop_State = INIT_SETTING;					
						
	    		// Initial PPU Drier
				InitPPUDrv(TX1_INDEX_LENGTH, TX2_INDEX_LENGTH, TX3_INDEX_LENGTH, SP_MAX);
					
				// Reset PPU register & SRAM					 
				ClearAllSetting();								
													
				// Initial PPU Module					 				
				InitPPU(PPU_DISABLE, TEXT_RELATED, TEXT_BOT2TOP, VGA_DISABLE, VGA_NON_INTERLACE, FREE_ENABLE, LINE_BASE, RGB565,0, BPP_16);
					
				// Set Palette Type				 
				SetPaletteType(PALETTE16_SEPARATE);						
								
				// Show Text1 for character mode
               	addr0 = GetLongAddress(6);
   	         	addr1 = GetLongAddress(3);
               	fnShow_Chr(TEXT1, addr0, 0, addr1, H_SIZE32, V_SIZE32, 128, 128, TEXT_512x256, COLOR256, TextDepth0, TextPalettePage0,PaletteBank0);						
                    					
				Sprite_Initial_Setting();													

				tft_start(A116_T180LA);

//             	// Enable PPU
               	ppu_on();
                    
			   	// Enable vertical blanking interrupt					 
			   	ClearVblankIRQ();
			   	VblankIrqON();	
		       	LCD_I80_Align();																										 								
							
				Loop_State = GAME_LOOP;
				break;
					
			case GAME_LOOP:	
			
				if ((*P_TimeBaseB_Ctrl & C_TimeBaseBFlag) != 0)
				{
						
					if(Text_Blend_Flag)
					{
						SetTextEffect(TEXT1, BLEND, ENABLE);
						SetTxBlendMode(TEXT1, 1);
						SetTxBlendLevel64(TEXT1, blend_level);
					}		
					Sprite_Display();
						
//					System_ServiceLoop();
																							
					//Show Sprite to the screen					
					Paint_Sprite();	
					LCD_I80_Align();									
					*P_TimeBaseB_Ctrl |= C_TimeBaseBFlag;
				}	
				break;							
		}		
		
				
		if(PlayCon)
		{
			if(SACM_A1800_Check_Con() == -1)
			{
				if(++SpeechIndex >= MaxSpeechNum)		// next speech
					SpeechIndex = 0;
				USER_A1800_SetStartAddr_Con(SpeechIndex);
				SACM_A1800_Play_Con(Manual_Mode_Index, DAC1 + DAC2, Ramp_Dn);
			}
		}		
		
		
		if(ServiceType == Foreground)
			SACM_A1800_ServiceLoop();
		
		System_ServiceLoop();
		Calendar_Function();

//		// When A18 and SPU stop, disable DAC AMP for save power
//		if (GetSPUStatus() == 0)
//		{
//			if (SACM_A1800_GetStatus() == 0)
//			{
//				if ((*P_CHA_Ctrl & (C_AUDIO_AMPNegative_En+C_AUDIO_AMPPositive_En)) != 0)		
//				{
//					*P_CHA_Ctrl &=~(C_AUDIO_AMPNegative_En+C_AUDIO_AMPPositive_En);
////					SACM_A1800_Play(SpeechIndex, DAC1, Ramp_Up + Ramp_Dn);	// playback with auto mode	
////					PlayMidi(*(p+1), *p, SS_PLAYMIDI_ONCE); // Play midi infinitely
//				}				
//			}
//		}
		
	} // end of while
   	return 0;	
}


void InitFish(void)
{	
	Sprite_Buff[0] = Sprite0001_Sp[0];
	Sprite_Buff[0].x = 120;
	Sprite_Buff[0].y = 0;	
	
	Sprite_Buff[1] = Sprite0002_Sp[0];
	Sprite_Buff[1].x = 30;
	Sprite_Buff[1].y = 30;
	
	Sprite_Buff[2] = Sprite0003_Sp[0];
	Sprite_Buff[2].x = 0;
	Sprite_Buff[2].y = 60;
	
	Sprite_Buff[3] = Sprite0004_Sp[0];
	Sprite_Buff[3].x = 30;
	Sprite_Buff[3].y = 80;
	
	Sprite_Buff[4] = Sprite0005_Sp[0];
	Sprite_Buff[4].x = 120;	
	Sprite_Buff[4].y = 80;	

}

void Sprite_Initial_Setting(void)
{
	// Set Sprite Control					 
	SetSpriteCtrl(SP_ENABLE, SP_COORD1, SP_NOROUND, SP_RELATED, MAX_SP_NUMBER);
	// Initial Text Palette	 
	addr0 = GetLongAddress(0);
	PaletteInitial(PALETTE_BUFF, TEXT_PALETTE0, addr0);
	addr0 = GetLongAddress(1);
	PaletteInitial(PALETTE_BUFF, TEXT_PALETTE1, addr0);								
									
	// Initial Sprite Palette					 
	addr0 = GetLongAddress(4);
	PaletteInitial(PALETTE_BUFF, SPRITE_PALETTE0, addr0);
								
	// Show Sprite
	InitFish();
    addr0 = GetLongAddress(5);					
	Init_Sprite(addr0);		
	
	// Enable Sprite Rotate & Zoom					 
	SetSpriteEffect(SP_ROTATE, ENABLE);
	SetSpriteEffect(SP_ZOOM, ENABLE);			
						
	//Sprite Blend setup
	SetSpBlendMode(BLEND64);
	SetSpBlend(0, ENABLE);		
						
	// Enable Sprite MOSAIC					 						 
	SetSpriteEffect(SP_MOSAIC, ENABLE);				 				
											 
	count=-1;
	flag2 = 0;	
	rotate_level = 0;
	zoom_level = 32;	
	mosaic_level=0;	
	blend_level=32;
	Text_Blend_Flag = 1;
}	

void Sprite_Display(void)
{
    count++;	
				
	// Zoom    
    if ((count%1) == 0) {
   		//Sprite Rotate level
		if (++rotate_level > 63)
		rotate_level = 0;
		//Sprite Zoom level						
		if (flag2 == 0) {
			if (++zoom_level > 48) {
 				zoom_level = 48;
	 			flag2 = 1;
			}
	}
	else {
		if (--zoom_level < 1) {
		 	zoom_level = 1;
		 	flag2 = 0;
		 }
		 count=1;
		}
   }	 
	SetSpRotate(4, rotate_level);					 
	SetSpZoom(4, zoom_level); 	
				    
	// Blend
	if ((count%1) == 0) {
		if (flag == 0) {
			blend_level+=2;
			if (blend_level > 63) {
			 	blend_level = 63;
			 	flag = 1;
			 	count=0;
			}
		}
	else {
		blend_level-=2;
			if (blend_level < 0) {
			 	blend_level = 0;
			 	flag = 0;
			 	count=0;
			 }
		}	 							 					 
		SetSpBlend64(0,  blend_level);
	}	 	 		
					
	// Mosaic
	  	if ((count%20) == 0) {
			if (++mosaic_level > 3)
				mosaic_level = 0;
			}	
		SetSpMosaic(1, mosaic_level);	
						
	// Flip								 					
	if(count%24==0)
	{
		//sprite vhflip	
		SetSpriteFlip(3,HV_FLIP);
		count=1;				 
	}	
	else if(count%16==0)
	{
	//sprite hflip	
	SetSpriteFlip(3, V_FLIP);		 
	}
	else if(count%8==0)
	{
	 //sprite vflip	
	SetSpriteFlip(3, H_FLIP);
	}						
				
	// Set Sprite Position				
	SetSpritePosition(0,-2,0,ENABLE);
	SetSpritePosition(1,-1,0,ENABLE);
	SetSpritePosition(2,-4,0,ENABLE);
	SetSpritePosition(3,1,0,ENABLE);
//	SetSpritePosition(4,1,0,ENABLE);
}

void Write_CMD_Delay(void)
{				
	int i;
	for (i=0; i<6; i++)
		__asm ("nop");
}

void Before_HaltMode(void)
{				
	__asm ("INT OFF");
	
	// A18 Stop
	PlayCon = 0;
	SACM_A1800_Stop();	
	*P_CHA_Ctrl &= ~(C_AUDIO_CHA_En+C_AUDIO_AMPNegative_En+C_AUDIO_AMPPositive_En+0x0078);
	*P_CHB_Ctrl &= ~C_AUDIO_CHB_En;
	
	// Disable TFT and PPU
	ppu_off();
//	gpio_write_io(IOE_PIN_1,1);					// CS Pin = High
	*P_TFT_Ctrl = 0x0;							// TFT Disable
	*P_PPU_IRQ_Status |= 0x01;					// Clear TFT FP INT, or it will wakeup, when enter halt mode
	
	// Disable ADC, Power for ADC and IOE Power
	*P_SYS_Ctrl &= ~(C_ADC_Power_En + C_ADC_LDO_En);
	
	*P_MADC_Ctrl &= ~C_ADC_ReadyINT;
	*P_MICADC_Setup &= ~C_MICADC_Vref_En;		//disable ref voltage	
	*P_TimeBaseA_Ctrl &= ~C_TimeBaseAIntEn;	
}	

void HaltMode_Wakeup_Initial(void)
{
	// Enable ADC Power for ADC and IOE Power
	*P_SYS_Ctrl |= C_ADC_Power_En + C_ADC_LDO_En;	
	*P_MADC_Ctrl |= C_ADC_ReadyINT;
	*P_MICADC_Setup |= C_MICADC_Vref_En;		//disable ref voltage	
	*P_TimeBaseA_Ctrl |= C_TimeBaseAIntEn;	
	
	// Enable DAC
	*P_CHA_Ctrl |= C_AUDIO_CHA_En+C_AUDIO_AMPNegative_En+C_AUDIO_AMPPositive_En+ 0x0068;
	*P_CHB_Ctrl |= C_AUDIO_CHB_En;
	
	// Enable TFT and PPU
	Loop_State = INIT_SETTING;	
}