/****************************************/
/* PocketStation Lib - by Orion_ [2013] */
/****************************************/
// Based on Miko Hoshina & Martin Korth work.
// If you use it, please give credits !
// v3.6

// Optional Functions, don't define this in your main .c file do reduce size
//#define	USE_SPRITE
//#define	USE_RANDOM
//#define	USE_SOUND

#ifndef	NULL
#define	NULL	((void*)0)
#endif

#ifndef	CLOCK_SPEED
#define	CLOCK_SPEED	CLOCK_4M
#endif

#ifndef	FPS
#define	FPS			64
#endif

#ifndef	SOUND_FREQ
#define	SOUND_FREQ	4000	// If 8000Hz, then use CLOCK_8M !
#endif

/**/

#define PAD_BUTTON	0x1
#define PAD_RIGHT	0x2
#define PAD_LEFT	0x4
#define PAD_DOWN	0x8
#define PAD_UP		0x10

#define	PAD_STATUS	((IRQ_STATUS_RAW) & 0x1F)

int		PadOnRelease, PadOnPress;

/**/

#define	LCD_ON		0x40
#define	LCD_OFF		0x00
#define	LCD_64HZ	0x10
#define	LCD_32HZ	0x20
#define	LCD_16HZ	0x30
#define	LCD_CPEN	0x08

#define LCD_MODE				*((volatile unsigned int *)0xD000000)
#define LCD_VRAM(_line_)		*((volatile unsigned int *)(0xD000100+(_line_ << 2)))
#define LCD_VRAM_ADRS			(volatile unsigned int *)0xD000100

/**/

#define CLOCK_62_5K				0x1
#define CLOCK_125K				0x2
#define CLOCK_250K				0x3
#define CLOCK_500K				0x4
#define CLOCK_1M				0x5
#define CLOCK_2M				0x6
#define CLOCK_4M				0x7
#define CLOCK_8M				0x8

#define TICK_PER_SEC			(15625 << CLOCK_SPEED)
#define TICK_PER_FRAME			(TICK_PER_SEC / FPS)

/**/

#define TIMER0_START()			*((volatile unsigned int *)(0xA800008)) |=  0x4
#define TIMER1_START()			*((volatile unsigned int *)(0xA800018)) |=  0x4
#define TIMER2_START()			*((volatile unsigned int *)(0xA800028)) |=  0x4
#define TIMER0_STOP()			*((volatile unsigned int *)(0xA800008)) &= ~0x4
#define TIMER1_STOP()			*((volatile unsigned int *)(0xA800018)) &= ~0x4
#define TIMER2_STOP()			*((volatile unsigned int *)(0xA800028)) &= ~0x4
#define TIMER0_SETCOUNT(_x_)	*((volatile unsigned int *)(0xA800000)) = (_x_)
#define TIMER1_SETCOUNT(_x_)	*((volatile unsigned int *)(0xA800010)) = (_x_)
#define TIMER2_SETCOUNT(_x_)	*((volatile unsigned int *)(0xA800020)) = (_x_)

/**/

#define FIQ_COMM			0x40
#define IRQ_TIMER0			0x80
#define IRQ_TIMER1			0x100
#define IRQ_RTC				0x200
#define IRQ_BATTERY			0x400
#define IRQ_PSCOMM			0x800
#define IRQ_INFRARED		0x1000
#define FIQ_TIMER2			0x2000

#define IRQ_STATUS			*((volatile unsigned int *)(0xA000000))
#define IRQ_STATUS_RAW		*((volatile unsigned int *)(0xA000004))
#define IRQ_ENABLE			*((volatile unsigned int *)(0xA000008))
#define IRQ_DISABLE			*((volatile unsigned int *)(0xA00000c))
#define IRQ_CLEAR			*((volatile unsigned int *)(0xA000010))

#define IRQ_DISABLE_ALL()	IRQ_DISABLE = 0x3FFF;

/**/

#define IOCTL_CONFIG			*((volatile unsigned int *)0xD800000)
#define IOCTL_OFF				*((volatile unsigned int *)0xD800004)
#define IOCTL_ON				*((volatile unsigned int *)0xD800008)
#define IOCTL_DAC				*((volatile unsigned int *)0xD800010)
#define IOCTL_DASOUND			*((volatile unsigned int *)0xD800014)
#define IOCTL_BATTERY			*((volatile unsigned int *)0xD800020)

#define IOCTL_LED				0x02
#define IOCTL_SPEAKER			0x20
#define IOCTL_IRDA				0x40


/****************************************/
// From header.S

// Bios Calls
void	*SetCallbacks(int index, void (*function));
int		SetCpuSpeed(int speed);
int		PrepareExecute(int flag, int dir_index, int param);
void	DoExecute(int snapshot_flag);
void	SetComOnOff(int flag);
int		GetDirIndex(void);

// IRQ/FIQ Asm
void	IntIRQ(void);
void	IntFIQ(void);

void	(*UserIRQ)(void);
int		(*UserFIQ)(void);	// Return 1 to Skip Library Sound Processing, else return 0


/****************************************/
// IRQ/Timer0 Handler (for VSync)

volatile int	VCount;

void IRQ_Handler(void)
{
	int	status;

	status = IRQ_STATUS;
	status &= IRQ_ENABLE;
	status &= ~(FIQ_TIMER2 | FIQ_COMM);	// Clear FIQ Bits

	if (status & IRQ_TIMER0)
		VCount++;

	if (UserIRQ)
		UserIRQ();

	IRQ_CLEAR = status;
}

/****************************************/
// FIQ/Timer2 Handler (for Sound)

void FIQ_Handler(void)
{
	IRQ_CLEAR = FIQ_TIMER2;
}

/****************************************/
// Sync Functions

void VSync(int count)
{
	register int	cnt = VCount;
	register int	PadState = PAD_STATUS;

	PadOnRelease = PadState;
	PadOnRelease ^= PadOnPress;
	PadOnRelease &= PadOnPress;
	PadOnPress = PadState;

	while (VCount < cnt + count);	// Wait For IRQ Timer0
}

/****************************************/
// Init, need to be called at first !

void PocketInit(void)
{
	SetCpuSpeed(CLOCK_SPEED);
	IRQ_DISABLE_ALL();

	UserIRQ = NULL;
	UserFIQ = NULL;

	VCount = 0;
	PadOnRelease = 0;
	PadOnPress = 0;

	SetCallbacks(1, IntIRQ);	// VSync Interrupt
	SetCallbacks(2, IntFIQ);	// Audio Interrupt

	IRQ_CLEAR = IRQ_TIMER0 | FIQ_TIMER2;
	IRQ_ENABLE = IRQ_TIMER0 | FIQ_TIMER2;

	// Start VSync Timer
	TIMER0_SETCOUNT(TICK_PER_FRAME);
	TIMER0_START();

	while (PAD_STATUS & PAD_BUTTON);	// Wait for Button Release (Button pressed when comming from Menu)
}


/****************************************/
// Exit, need to be called to exit app !

void PocketExit(void)
{
	SetComOnOff(0);
	TIMER0_STOP();
	TIMER2_STOP();
	IRQ_DISABLE_ALL();
	IRQ_ENABLE = IRQ_RTC | IRQ_PSCOMM;
	PrepareExecute(1, 0, GetDirIndex() | 0x30);	// Return to GUI on our Program Icon
	DoExecute(0);
}

/****************************************/
