/*---------------------------------------------------------------------------------

	Power control, keys, and HV clock registers

	Copyright (C) 2005
		Jason Rogers (dovoto)
		Dave Murphy (WinterMute)

	This software is provided 'as-is', without any express or implied
	warranty.  In no event will the authors be held liable for any
	damages arising from the use of this software.

	Permission is granted to anyone to use this software for any
	purpose, including commercial applications, and to alter it and
	redistribute it freely, subject to the following restrictions:

	1.	The origin of this software must not be misrepresented; you
		must not claim that you wrote the original software. If you use
		this software in a product, an acknowledgment in the product
		documentation would be appreciated but is not required.

	2.	Altered source versions must be plainly marked as such, and
		must not be misrepresented as being the original software.

	3.	This notice may not be removed or altered from any source
		distribution.

---------------------------------------------------------------------------------*/

/*!	\file system.h
	\brief NDS hardware definitions.
	These definitions are usually only touched during
	the initialization of the program.
*/

#ifndef NDS_SYSTEM_INCLUDE
#define NDS_SYSTEM_INCLUDE

#include "ndstypes.h"

//!	LCD status register.
#define	REG_DISPSTAT	(*(vu16*)0x04000004)

//! LCD Status register bitdefines
typedef enum
{
	DISP_IN_VBLANK   =  BIT(0), //!<	The display currently in a vertical blank.
	DISP_IN_HBLANK    = BIT(1), //!<	The display currently in a horizontal blank.
	DISP_YTRIGGERED   = BIT(2), //!<	Current scanline and %DISP_Y match.
	DISP_VBLANK_IRQ   = BIT(3), //!<	Interrupt on vertical blank.
	DISP_HBLANK_IRQ   = BIT(4), //!<	Interrupt on horizontal blank.
	DISP_YTRIGGER_IRQ = BIT(5)  //!<	Interrupt when current scanline and %DISP_Y match.
}DISP_BITS;

//!	Current display scanline.
#define	REG_VCOUNT		(*(vu16*)0x4000006)


//!	Halt control register.
/*!	Writing 0x40 to HALT_CR activates GBA mode.
	%HALT_CR can only be accessed via the BIOS.
*/
#define HALT_CR       (*(vu16*)0x04000300)

//!	Power control register.
/*!	This register controls what hardware should
	be turned on or off.
*/
#define	REG_POWERCNT	*(vu16*)0x4000304

#define REG_SCFG_ROM		*(vu16*)0x4004000

#ifdef ARM7
#define REG_SCFG_A9ROM		*(vu8*)0x4004000
#define REG_SCFG_A7ROM		*(vu8*)0x4004001  // ??
#endif

#define REG_SCFG_CLK		*(vu16*)0x4004004
#define REG_SCFG_RST		*(vu16*)0x4004006
#define REG_SCFG_EXT		*(vu32*)0x4004008
#define REG_SCFG_MC			*(vu16*)0x4004010

static inline
/*!
	\brief sets the Y trigger(?)

	\param Yvalue the value for the Y trigger.
*/
void SetYtrigger(int Yvalue) {
	REG_DISPSTAT = (REG_DISPSTAT & 0x007F ) | (Yvalue << 8) | (( Yvalue & 0x100 ) >> 1) ;
}

#define PM_ARM9_DIRECT BIT(16)
//! Power Management control bits
typedef enum
{
	PM_SOUND_AMP		= BIT(0),		//!< Power the sound hardware (needed to hear stuff in GBA mode too).
	PM_SOUND_MUTE		= BIT(1),		//!< Mute the main speakers, headphone output will still work.
	PM_BACKLIGHT_BOTTOM	= BIT(2),		//!< Enable the bottom backlight if set.
	PM_BACKLIGHT_TOP	= BIT(3),		//!< Enable the top backlight if set.
	PM_SYSTEM_PWR		= BIT(6),		//!< Turn the power *off* if set.

	POWER_LCD		= PM_ARM9_DIRECT | BIT(0),		//!<	Controls the power for both LCD screens.
	POWER_2D_A		= PM_ARM9_DIRECT | BIT(1),		//!<	Controls the power for the main 2D core.
	POWER_MATRIX	= PM_ARM9_DIRECT | BIT(2),		//!<	Controls the power for the 3D matrix.
	POWER_3D_CORE	= PM_ARM9_DIRECT | BIT(3),		//!<	Controls the power for the main 3D core.
	POWER_2D_B		= PM_ARM9_DIRECT | BIT(9),		//!<	Controls the power for the sub 2D core.
	POWER_SWAP_LCDS	= PM_ARM9_DIRECT | BIT(15),		//!<	Controls which screen should use the main core.
	POWER_ALL_2D	= PM_ARM9_DIRECT | POWER_LCD | POWER_2D_A | POWER_2D_B,			//!< power just 2D hardware.
	POWER_ALL		= PM_ARM9_DIRECT | POWER_ALL_2D | POWER_3D_CORE | POWER_MATRIX	//!< power everything.
}PM_Bits;


/*!	\brief Causes the nds to go to sleep.
	The nds will be reawakened when the lid is opened.

	\note By default, this is automatically called when closing the lid.
*/
void systemSleep(void);

/*!	Set the LED blink mode
	\param bm What to power on.
*/
void ledBlink(int bm);

//!	Checks whether the application is running in DSi mode.
static inline bool isDSiMode() {
	extern bool __dsimode;
	return __dsimode;
}

//--------------------------------------------------------------
//    ARM9 section
//--------------------------------------------------------------
#ifdef ARM9

//!	Turns on specified hardware.
/*!	May be called from arm7 or arm9 (arm9 power bits will be ignored by arm7, arm7 power bits
	will be passed to the arm7 from the arm9).

	\param bits What to power on.
*/
void powerOn(int bits);

//!	Turns off specified hardware.
/*!	May be called from arm7 or arm9 (arm9 power bits will be ignored by arm7, arm7 power bits
	will be passed to the arm7 from the arm9).

	\param bits What to power on.
*/
void powerOff(int bits);

//internal fifo handlers
void systemMsgHandler(int bytes, void* user_data);
void systemValueHandler(u32 value, void* data);

//!	Switches the screens.
static inline void lcdSwap(void) { REG_POWERCNT ^= POWER_SWAP_LCDS; }

//!	Forces the main core to display on the top.
static inline void lcdMainOnTop(void) { REG_POWERCNT |= POWER_SWAP_LCDS; }

//!	Forces the main core to display on the bottom.
static inline void lcdMainOnBottom(void) { REG_POWERCNT &= ~POWER_SWAP_LCDS; }

//! Powers down the DS
static inline
void systemShutDown(void) {
	powerOn(PM_SYSTEM_PWR);
}

void readFirmware(u32 address, void *buffer, u32 length);
int writeFirmware(u32 address, void *buffer, u32 length);


//! gets the DS Battery level
u32 getBatteryLevel();

//!	Set the arm9 vector base
/*!	Arm9 only
	\param highVector high vector
*/
void setVectorBase(int highVector);


/*! \brief A struct with all the CPU exeption vectors.
	each member contains an ARM instuction that will be executed when an exeption occured.

	See gbatek for more information.
*/
typedef struct sysVectors_t {
	VoidFn	reset;			//!< CPU reset.
	VoidFn	undefined;		//!< undefined instruction.
	VoidFn	swi;			//!< software interrupt.
	VoidFn	prefetch_abort;	//!< prefetch abort.
	VoidFn	data_abort;		//!< data abort.
	VoidFn	fiq;			//!< fast interrupt.
} sysVectors;


extern sysVectors SystemVectors;
void setSDcallback(void(*callback)(int));

/*!
	\brief Sets the ARM9 clock speed, only possible in DSi mode
	\param speed CPU speed (false = 67.03MHz, true = 134.06MHz)
	\return The old CPU speed value
*/
bool setCpuClock(bool speed);

// Helper functions for heap size
//! returns current start of heap space
u8* getHeapStart();
//! returns current end of heap space
u8* getHeapEnd();
//! returns current heap limit
u8* getHeapLimit();

#endif //ARM9

//--------------------------------------------------------------
//    ARM7 section
//--------------------------------------------------------------
#ifdef ARM7

#define REG_CONSOLEID	(*(vu64*)0x04004D00)

//!	Power-controlled hardware devices accessable to the ARM7.
/*!	Note that these should only be used when programming for
	the ARM7.  Trying to boot up these hardware devices via
	the ARM9 would lead to unexpected results.
	ARM7 only.
*/
typedef enum {
	POWER_SOUND = BIT(0),			//!<	Controls the power for the sound controller.

	PM_CONTROL_REG		= 0,		//!<	Selects the PM control register
	PM_BATTERY_REG		= 1,		//!<	Selects the PM battery register
	PM_AMPLIFIER_REG	= 2,		//!<	Selects the PM amplifier register
	PM_READ_REGISTER	= (1<<7),	//!<	Selects the PM read register
	PM_AMP_OFFSET		= 2,		//!<	Selects the PM amp register
	PM_GAIN_OFFSET		= 3,		//!<	Selects the PM gain register
	PM_BACKLIGHT_LEVEL	= 4, 		//!<	Selects the DS Lite backlight register
	PM_GAIN_20			= 0,		//!<	Sets the mic gain to 20db
	PM_GAIN_40			= 1,		//!<	Sets the mic gain to 40db
	PM_GAIN_80			= 2,		//!<	Sets the mic gain to 80db
	PM_GAIN_160			= 3,		//!<	Sets the mic gain to 160db
	PM_AMP_ON			= 1,		//!<	Turns the sound amp on
	PM_AMP_OFF			= 0			//!<	Turns the sound amp off
} ARM7_power;

//!< PM control register bits - LED control
#define PM_LED_CONTROL(m)  ((m)<<4)

//install the fifo power handler
void installSystemFIFO(void);

//cause the ds to enter low power mode
void systemSleep(void);
//internal can check if sleep mode is enabled
int sleepEnabled(void);

// Warning: These functions use the SPI chain, and are thus 'critical'
// sections, make sure to disable interrupts during the call if you've
// got a VBlank IRQ polling the touch screen, etc...

// Read/write a power management register
int writePowerManagement(int reg, int command);

static inline
int readPowerManagement(int reg) {
	return writePowerManagement((reg)|PM_READ_REGISTER, 0);
}

static inline
void powerOn(int bits) {
	REG_POWERCNT |= bits;
}

static inline
void powerOff(PM_Bits bits) {
	REG_POWERCNT &= ~bits;
}

void readUserSettings();
void systemShutDown();

#endif /* ARM7 */


/*!	\brief Backlight level settings.
	Note, these are only available on DS Lite.
*/
typedef enum {
	BACKLIGHT_LOW,	//!< low backlight setting.
	BACKLIGHT_MED,	//!< medium backlight setting.
	BACKLIGHT_HIGH,	//!< high backlight setting.
	BACKLIGHT_MAX	//!< max backlight setting.
} BACKLIGHT_LEVELS;


// Common functions

/*!
	\brief User's DS settings.
	Defines the structure the DS firmware uses for transfer
	of the user's settings to the booted program.

	Theme/Color values:
	- 0 = Gray
	- 1 = Brown
	- 2 = Red
	- 3 = Pink
	- 4 = Orange
	- 5 = Yellow
	- 6 = Yellow/Green-ish
	- 7 = Green
	- 8 = Dark Green
	- 9 = Green/Blue-ish
	- 10 = Light Blue
	- 11 = Blue
	- 12 = Dark Blue
	- 13 = Dark Purple
	- 14 = Purple
	- 15 = Purple/Red-ish

	Language values:
	- 0 = Japanese
	- 1 = English
	- 2 = French
	- 3 = German
	- 4 = Italian
	- 5 = Spanish
	- 6 = Chinese(?)
	- 7 = Unknown/Reserved
*/
typedef struct tPERSONAL_DATA
{
	u8 RESERVED0[2];			//	??? (0x05 0x00). (version according to gbatek)

	u8 theme;					//!<	The user's theme color (0-15).
	u8 birthMonth;				//!<	The user's birth month (1-12).
	u8 birthDay;				//!<	The user's birth day (1-31).

	u8 RESERVED1[1];			//	???

	s16 name[10];				//!<	The user's name in UTF-16 format.
	u16 nameLen;				//!<	The length of the user's name in characters.

	s16 message[26];			//!<	The user's message.
	u16 messageLen;				//!<	The length of the user's message in characters.

	u8 alarmHour;				//!<	What hour the alarm clock is set to (0-23).
	u8 alarmMinute;				//!<	What minute the alarm clock is set to (0-59).
	//0x02FFFCD3  alarm minute

	u8 RESERVED2[4];			//	??? 0x02FFFCD4  ??


	u16 calX1;				//!<	Touchscreen calibration: first X touch
	u16 calY1;				//!<	Touchscreen calibration: first Y touch
	u8 calX1px;				//!<	Touchscreen calibration: first X touch pixel
	u8 calY1px;				//!<	Touchscreen calibration: first X touch pixel

	u16 calX2;				//!<	Touchscreen calibration: second X touch
	u16 calY2;				//!<	Touchscreen calibration: second Y touch
	u8 calX2px;				//!<	Touchscreen calibration: second X touch pixel
	u8 calY2px;				//!<	Touchscreen calibration: second Y touch pixel

	struct {
		unsigned int language			: 3;	//!<	User's language.
		unsigned int gbaScreen			: 1;	//!<	GBA screen selection (lower screen if set, otherwise upper screen).
		unsigned int defaultBrightness	: 2;	//!<	Brightness level at power on, dslite.
		unsigned int autoMode			: 1;	//!<	The DS should boot from the DS cart or GBA cart automatically if one is inserted.
		unsigned int RESERVED5			: 2;	//	???
		unsigned int settingsLost		: 1;	//!<	User Settings Lost (0=Normal, 1=Prompt/Settings Lost)
		unsigned int RESERVED6			: 6;	//	???
	} PACKED;

	u16	RESERVED3;			//	???
	u32	rtcOffset;			//!<	Real Time Clock offset.
	u32	RESERVED4;			//	???
} PACKED PERSONAL_DATA ;

//!	Default location for the user's personal data (see %PERSONAL_DATA).
#define PersonalData ((PERSONAL_DATA*)0x2FFFC80)


//! struct containing time and day of the real time clock.
typedef	struct {
	u8 year;	//!< add 2000 to get 4 digit year
	u8 month;	//!< 1 to 12
	u8 day;		//!< 1 to (days in month)

	u8 weekday;	//!< day of week
	u8 hours;	//!< 0 to 11 for AM, 52 to 63 for PM
	u8 minutes;	//!< 0 to 59
	u8 seconds;	//!< 0 to 59
} RTCtime;



// argv struct magic number
#define ARGV_MAGIC 0x5f617267

//structure used to set up argc/argv on the DS
struct __argv {
	int argvMagic;		// argv magic number, set to 0x5f617267 ('_arg') if valid
	char *commandLine;	// base address of command line, set of null terminated strings
	int length;			// total length of command line
	int argc;			// internal use, number of arguments
	char **argv;		// internal use, argv pointer
	int dummy;			// internal use
	u32 host;			// internal use, host ip for dslink 
};

#define __system_argv		((struct __argv *)0x02FFFE70)

#define BOOTSIG	0x62757473746F6F62ULL	// 'bootstub'

struct __bootstub {
	u64	bootsig;
	VoidFn arm9reboot;
	VoidFn arm7reboot;
	u32 bootsize;
};


#ifdef ARM9
/*!
	\brief returns a cached mirror of an address.
	\param address an address.
	\return a pointer to the cached mirror of that address.
*/
void *memCached(void *address);

/*!
	\brief returns an uncached mirror of an address.
	\param address an address.
	\return a pointer to the uncached mirror of that address.
*/
void *memUncached(void *address);

void resetARM7(u32 address);
#endif

#ifdef ARM7
void resetARM9(u32 address);
#endif

#endif
