/*
 *	app.c
 *
 *	Tcl速度計測
 *
 *	* Sun Oct 26 21:56:40 JST 2014 Naoyuki Sawa
 *	- 1st リリース。
 */
#include "app.h"

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

#ifdef USE_SOUND
const void* sound_table[SOUND_COUNT];
#endif /*USE_SOUND*/

#ifdef USE_TEXTURE
TEXTURE texture_table[TEXTURE_COUNT];
#endif /*USE_TEXTURE*/

#ifdef USE_SPRITE
#define SPRITE_ SPRITE_INIT2
const SPRITE sprite_table[SPRITE_COUNT] = {
#include "sprite.h"
};
#undef SPRITE_
#endif /*USE_SPRITE*/

#define extern
#include "work.h"
#undef extern

/****************************************************************************
 *	main
 ****************************************************************************/

int
main()
{
	/* 初期化。 */
	app_init();

	/* メインループ。 */
	app_main();

	return 0;
}

/****************************************************************************
 *	app_init
 ****************************************************************************/

#ifdef  USE_SOUND_MANAGER
static void sound_play_stub(int iCh, int iData, int iLoop) {
	if((unsigned)iData >= SOUND_COUNT) { DIE(); }
	sound_play(iCh, sound_table[iData], iLoop);
}
#endif/*USE_SOUND_MANAGER*/

#ifdef  USE_SOUND_MIXER
static void SndMxr_Update() {
	int i;
	unsigned char AttSum[32];
	SndMxr_GetSndChAttAll(pSndMxr,-1,AttSum); //全ｻｳﾝﾄﾞﾁｬﾈﾙの減衰量を取得する｡
	for(i=0;i<SOUND_MAXCH;i++) {sound_att(i,AttSum[i]);} //全ｻｳﾝﾄﾞﾁｬﾈﾙの減衰量を設定する｡
}
static void SndMxr_TimerProc(void* context) { //Sound Mixer用ﾀｲﾏ割込処理
	if(sound_stat(1)) {SndMxr_SetMxrChAtt(pSndMxr,4,64, 0,0);} //ｻｳﾝﾄﾞﾁｬﾈﾙ1で演出BGMが再生中ならば､ｽﾃｰｼﾞBGMを即消音する｡          ┐
	else              {SndMxr_SetMxrChAtt(pSndMxr,4, 0,64,5);} //ｻｳﾝﾄﾞﾁｬﾈﾙ1で演出BGMが停止中ならば､ｽﾃｰｼﾞBGMを0.5秒で復帰する｡     ├{{TODO:ｱﾌﾟﾘｹｰｼｮﾝ調整}}
	if(sound_stat(3)) {SndMxr_SetMxrChAtt(pSndMxr,5,64, 0,0);} //ｻｳﾝﾄﾞﾁｬﾈﾙ3でｴﾗｰ音が再生中ならば､ｽﾃｰｼﾞBGM,演出BGM,SEを即消音する｡ │
	else              {SndMxr_SetMxrChAtt(pSndMxr,5, 0, 0,0);} //ｻｳﾝﾄﾞﾁｬﾈﾙ3でｴﾗｰ音が停止中ならば､ｽﾃｰｼﾞBGM,演出BGM,SEを即復帰する｡ ┘
	SndMxr_Exec(pSndMxr); //Sound Mixerを実行する｡
	SndMxr_Update(); //全ｻｳﾝﾄﾞﾁｬﾈﾙの減衰量を設定する｡
}
#endif/*USE_SOUND_MIXER*/

#ifdef  USE_SOUND_SEQUENCER
static int SndSeq_fnLen(int iPhr) {
	return ppd_playback_time(sound_table[iPhr]);
}
static int SndSeq_fnNext(int iPhr) {
	return (iPhr == SND_BGM_1G) || (iPhr == SND_BGM_ST) ? iPhr : -1;
}
static int SndSeq_fnPrev(int iPhr) {
	return (iPhr == SND_BGM_1G) || (iPhr == SND_BGM_ST) ? iPhr : -1;
}
static void SndSeq_fnPan(int iTrk, int iPan) {
	//P/ECEにはステレオ再生は無いので、本関数は使用しない。
}
static void SndSeq_fnVol(int iTrk, int iVol) {
	SndMxr_SetMxrChAtt(pSndMxr, iTrk, 64-(iVol*64/SndVol_Max), 0, 0);
	SndMxr_Update();
}
static void SndSeq_fnStop(int iTrk) {
	sound_stop(iTrk);
}
static void SndSeq_fnPlay(int iTrk, int iPhr, int iVol, int iPan) {
	sound_play(iTrk, sound_table[iPhr], -1);
	SndMxr_SetMxrChAtt(pSndMxr, iTrk, 64-(iVol*64/SndVol_Max), 0, 0);
	SndMxr_Update();
}
static int SndSeq_fnTime() {
	return pceTimerGetCount(); //現在時刻を[ms]単位で返せ。
}
#endif/*USE_SOUND_SEQUENCER*/

#ifdef  USE_EVENT_TIMER
static void EvtTmr_onEvent(int iEvtTmrCh, int iEvent) { //ｲﾍﾞﾝﾄﾀｲﾏ用ｲﾍﾞﾝﾄ処理関数
	TRACE("%d:%d:%d\n", now / FPS, iEvtTmrCh, iEvent);
}
#endif/*USE_EVENT_TIMER*/

#ifdef  USE_LOG_BUFFER
static int LogBuf_fnDraw(int x, int y, const char* Str) { //ｱﾌﾟﾘｹｰｼｮﾝ定義の文字列描画関数
	render_reduced_string(&render, x, y, Str, 3);
	return 10;
}
#endif/*USE_LOG_BUFFER*/

void
app_init()
{
	/* 一般的な初期化。 */
	pceAppSetProcPeriod(1000 / FPS);
#ifdef V_SCREEN
	pceLCDSetBuffer(_def_vbuff);
#else /*V_SCREEN*/
	pceLCDSetBuffer(vbuff);
#endif /*V_SCREEN*/
	pceLCDDispStart();
#ifdef USE_REPEAT
	pad_set_repeat(REPEAT_PAD, REPEAT_DELAY, REPEAT_INTERVAL);
#endif /*USE_REPEAT*/

	/* 描画環境初期化。 */
	surface.w = DISP_X;
	surface.h = DISP_Y;
	surface.vbuff = vbuff;
#ifdef USE_3D
	surface.zbuff = zbuff;
	render_init_3d(&render, &surface, 0, 0, 0, 0, FRONT, BACK, FOVY);
#else /*USE_3D*/
	render_init_2d(&render, &surface, 0, 0, 0, 0);
#endif /*USE_3D*/
#ifdef USE_RENDF
	surface.zbuff = zbuff;
	rendf_init(&rendf, &surface, 0, 0, 0, 0, FRONT, BACK, FOVY);
#endif /*USE_RENDF*/

#if defined(TRACE_ON) && (TRACE_ON)
	/* 標準エラー出力を開きます。 */
	stderr_open(9/*標準エラー出力モニタ接続待ち時間(調整可)*/);
#endif /*TRACE_ON*/

#ifdef USE_UFE
	/* USBファイルシステムエミュレーションを開始します。 */
	ufe_setup(9/*PC側ファイルサーバ接続待ち時間(調整可)*/);
#endif /*USE_UFE*/

#ifdef USE_RESOURCE
	/* pexファイル添付リソースを取得します。 */
	resource = resource_address(PRESCAN_STRINGIFY(PROJECT) ".pex");
#endif /*USE_RESOURCE*/

#ifdef TURBO
	/* クロック・メモリウェイトを切替えます。 */
	turbo(TURBO);
#endif /*TURBO*/

#ifdef USE_DBG
	/* DBGユニットを設定します。 */
	dbg_init();
#endif /*USE_DBG*/

#ifdef GC_LEVEL
	/* ガーベージコレクターを初期化します。 */
	gc_init(GC_LEVEL);
#endif /*GC_LEVEL*/

#ifdef LDIRECT_INTERVAL
	/* 高速切替・16階調表示を初期化します。 */
	ldirect_init(LDIRECT_INTERVAL);
#endif /*LDIRECT_INTERVAL*/

#ifdef USE_MUSIC
	/* 音楽ライブラリを初期化します。 */
#ifdef USE_INSTDEF2
	{ extern int loadInst();/*→instdef2.c*/ loadInst(); } /* 「Randomism Zone(http://www8.plala.or.jp/randomism/)」さんの『ドラム音色分離キットver.2.10』を使用 */
#endif /*USE_INSTDEF2*/
	music_init();
	music_filter_install(music_filter_loudness);//{{TODO:P/ECE標準音楽ﾗｲﾌﾞﾗﾘの音量増幅を行わない場合は削除してください｡}}
#if defined(USE_2BIT_ADPCM) || defined(USE_TRUESPEECH)
	/* - 2-bit ADPCM Decoder,又は,DSP Group TrueSpeech Decoderと、音楽ライブラリを併用する場合は、音楽ライブラリ側の使用チャネルをずらすことにしました。
	 *   この対策を行わないと、両方がチャネル0を使用しようとして、ハングアップしてしまいます。
	 * - 2-bit ADPCM Decoder,又は,DSP Group TrueSpeech Decoderに限らず、stream_play()を利用する機能と、音楽ライブラリと併用する場合は、同様の対策が必要です。
	 * - music_wchを変更する処理は、InitMusic()よりも後で行う必要があります。
	 *   InitMusic()が、「music_wch = 0;」を行っているからです。InitMusic()よりも先に変更すると、0に戻ってしまいます。
	 * - 尚、music_loudness()の機能もmusic_wchを参照していますが、
	 *   随時、値を取得しているので、music_loudness()を呼び出した後にmusic_wchを変更しても、問題はありません。
	 */
	music_wch = 3; /* チャネル0=2-bit ADPCM Decoder,又は,DSP Group TrueSpeech Decoder、チャネル1-2=アプリケーション使用、チャネル3=音楽ライブラリとする */
#endif /*defined(USE_2BIT_ADPCM) || defined(USE_TRUESPEECH)*/
#endif /*USE_MUSIC*/

	/* クリーンアップ関数を登録。 */
	atexit(app_exit);

#ifdef USE_2BIT_ADPCM
	/* 2-bit ADPCM Decoderを起動します。 */
	adpcm_init(SOUND_MAXCH);
#endif /*USE_2BIT_ADPCM*/

#ifdef USE_TRUESPEECH
	/* DSP Group TrueSpeech Decoderを起動します。 */
	TrueSpeech_Init(SOUND_MAXCH);
#endif /*USE_TRUESPEECH*/

	/* サウンドを初期化します。 */
#ifdef USE_SOUND
	{
#define SOUND_(name) { extern const unsigned char name[]; sound_table[SND_##name] = name; }
#define SOUND_RESOURCE_(name,fname) { sound_table[SND_##name] = fpk_address(resource, fname, NULL); }
#include "sound.h"
#undef SOUND_
#undef SOUND_RESOURCE_
	}
#endif /*USE_SOUND*/

	/* Sound Managerを初期化します。 */
#ifdef  USE_SOUND_MANAGER
	{
		static const ST_SndMgrInfo stSndMgrInfo = { sound_play_stub, sound_stop, sound_stat, sound_att };
		pSndMgrBgm = SndMgr_Init(&stSndMgrInfo, SOUND_MANAGER_BGM_MINCH, SOUND_MANAGER_BGM_MAXCH); /* BGM Sound Manager */
		pSndMgrSe1 = SndMgr_Init(&stSndMgrInfo, SOUND_MANAGER_SE1_MINCH, SOUND_MANAGER_SE1_MAXCH); /* SE1 Sound Manager */
		pSndMgrSe2 = SndMgr_Init(&stSndMgrInfo, SOUND_MANAGER_SE2_MINCH, SOUND_MANAGER_SE2_MAXCH); /* SE2 Sound Manager */
	}
#endif/*USE_SOUND_MANAGER*/

	/* Sound Mixerを初期化します。 */
#ifdef  USE_SOUND_MIXER
	{
		static const int TBL_SndMxr_SndChMask[] = { //ｻｳﾝﾄﾞﾁｬﾈﾙﾏｽｸ配列
			(1<<0),               //Sound Mixerﾁｬﾈﾙ0:ﾄﾗｯｸ0(ｻｳﾝﾄﾞｼｰｹﾝｻによるﾎﾞﾘｭｰﾑ指定)         ┐
			(1<<1),               //Sound Mixerﾁｬﾈﾙ1:ﾄﾗｯｸ1(ｻｳﾝﾄﾞｼｰｹﾝｻによるﾎﾞﾘｭｰﾑ指定)         │
			(1<<2),               //Sound Mixerﾁｬﾈﾙ2:ﾄﾗｯｸ2(ｻｳﾝﾄﾞｼｰｹﾝｻによるﾎﾞﾘｭｰﾑ指定)         │
			(1<<3),               //Sound Mixerﾁｬﾈﾙ3:ﾄﾗｯｸ3(ｻｳﾝﾄﾞｼｰｹﾝｻによるﾎﾞﾘｭｰﾑ指定)         ├{{TODO:ｱﾌﾟﾘｹｰｼｮﾝ調整}}
			(1<<0),               //Sound Mixerﾁｬﾈﾙ4:演出BGM再生時減音対象:ｽﾃｰｼﾞBGM            │
			(1<<0)|(1<<1)|(1<<2), //Sound Mixerﾁｬﾈﾙ5:ｴﾗｰ音再生時減音対象  :ｽﾃｰｼﾞBGM,演出BGM,SE │
			-1};                  //Sound Mixerﾁｬﾈﾙ6:ﾄｰﾀﾙﾎﾞﾘｭｰﾑ           :全ｻｳﾝﾄﾞﾁｬﾈﾙ         ┘
		pSndMxr = SndMxr_Init(ARRAY_SIZE(TBL_SndMxr_SndChMask), TBL_SndMxr_SndChMask); //Sound Mixerを作成する｡
		timer_start(100, SndMxr_TimerProc, NULL); //Sound Mixer用ﾀｲﾏ割込処理を開始する｡
	}
#endif/*USE_SOUND_MIXER*/

	/* サウンドシーケンサを初期化します。 */
#ifdef  USE_SOUND_SEQUENCER
	{
		static const ST_SndSeqInfo stSndSeqInfo = { &SndSeq_fnTime, &SndSeq_fnPlay, &SndSeq_fnStop, &SndSeq_fnVol, &SndSeq_fnPan, &SndSeq_fnLen, &SndSeq_fnNext, &SndSeq_fnPrev };
		SndSeq_Init(&stSndSeqInfo);
		timer_start(10, SndSeq_Exec, NULL); //ｻｳﾝﾄﾞｼｰｹﾝｻ用ﾀｲﾏ割込処理を開始する｡
	}
#endif/*USE_SOUND_SEQUENCER*/

	/* イベントタイマを初期化します。 */
#ifdef  USE_EVENT_TIMER
	{
		static const uint16_t TBL_EvtTmr_Data0[]={//ｲﾍﾞﾝﾄﾀｲﾏﾁｬﾈﾙ#0のﾃﾞｰﾀ
			SEC(0),10, //開始直後に､ｺﾏﾝﾄﾞ番号(10)を実行する｡
			SEC(2),11, // 2秒待って､ｺﾏﾝﾄﾞ番号(11)を実行する｡
			SEC(1),12, // 1秒待って､ｺﾏﾝﾄﾞ番号(12)を実行する｡
			SEC(0),13, //その後すぐ､ｺﾏﾝﾄﾞ番号(13)を実行する｡
			SEC(3),-1};// 3秒待って､先頭へﾙｰﾌﾟする｡
		static const uint16_t TBL_EvtTmr_Data1[]={//ｲﾍﾞﾝﾄﾀｲﾏﾁｬﾈﾙ#1のﾃﾞｰﾀ
			SEC(9),20, // 9秒待って､ｺﾏﾝﾄﾞ番号(20)を実行する｡
			SEC(0), 0};//その後すぐ､終了する｡
		static const uint16_t* const TBL_EvtTmr_Data[]={//各ｲﾍﾞﾝﾄﾀｲﾏﾁｬﾈﾙのﾃﾞｰﾀ
			TBL_EvtTmr_Data0, //ｲﾍﾞﾝﾄﾀｲﾏﾁｬﾈﾙ#0のﾃﾞｰﾀ
			TBL_EvtTmr_Data1};//ｲﾍﾞﾝﾄﾀｲﾏﾁｬﾈﾙ#1のﾃﾞｰﾀ
		pEvtTmr=EvtTmr_Init(ARRAY_SIZE(TBL_EvtTmr_Data),TBL_EvtTmr_Data,EvtTmr_onEvent);//ｲﾍﾞﾝﾄﾀｲﾏを初期化する｡
	}
#endif/*USE_EVENT_TIMER*/

	/* 汎用カウンタ管理を初期化します。 */
#ifdef  USE_COUNTER_MANAGER
	pCntMgr = CntMgr_Init(COUNTER_MAXCH);
#endif/*USE_COUNTER_MANAGER*/

	/* ログバッファを初期化します。 */
#ifdef  USE_LOG_BUFFER
	pLogBuf = LogBuf_Init((DISP_Y+9)/10, LogBuf_fnDraw);
#endif/*USE_LOG_BUFFER*/

	/* テクスチャを初期化します。 */
#ifdef USE_TEXTURE
	{
#define TEXTURE_(name) extern const unsigned char name[];
#define TEXTURE_RESOURCE_(name,fname) const void* const name = fpk_address(resource, fname, NULL);
#include "texture.h"
#undef TEXTURE_
#undef TEXTURE_RESOURCE_
		texture_table_init(texture_table, TEXTURE_COUNT,
#define TEXTURE_(name) name,
#define TEXTURE_RESOURCE_(name,fname) name,
#include "texture.h"
#undef TEXTURE_
#undef TEXTURE_RESOURCE_
		NULL/*最後の','を吸収するためのダミー。texture_table_init()に対しては無意味です。*/);
	}
#endif /*USE_TEXTURE*/

#ifdef USE_SPRITE
	/* 簡易スプライトドライバを初期化します。 */
	sprite_init(&render, sprite_table, SPRITE_COUNT, texture_table, TEXTURE_COUNT);
#endif /*USE_SPRITE*/

#ifdef USE_TCPIP
	/* TCP/IPプロトコルスイートを開始します。 */
	tcpip_start(0/*Ethernetドライバ送信バッファサイズ(調整可)*/);
#endif /*USE_TCPIP*/

#ifdef USE_VIDREC
	/* 画面出力を初期化します。 */
	yield(); /* runで実行した場合の実行確認に応答するため、USB機能の切り替えを僅かに遅らせる */
#ifdef LDIRECT_INTERVAL
	cdcacm_start(0, sizeof vbuff / 2, NULL, NULL);
#else /*LDIRECT_INTERVAL*/
	cdcacm_start(0, sizeof vbuff / 4, NULL, NULL);
#endif /*LDIRECT_INTERVAL*/
#endif /*USE_VIDREC*/

#ifdef  USE_TASK
	/* タスクスケジューラを初期化します。
	 * - アイドルスレッドと併用する場合は、idle_proc()の中で初期化してください。
	 *   アイドルスレッドと併用する方法は、task_yield()の[note]を参照してください。 */
	task_init(INT_MIN);//{{TODO:ｼｽﾃﾑｱｲﾄﾞﾙﾀｽｸの実行優先度はｱﾌﾟﾘｹｰｼｮﾝ調整}}
#endif/*USE_TASK*/

/*{{アプリケーション特有の初期化*/
	srand(pceTimerGetCount());
/*}}アプリケーション特有の初期化*/
}

/****************************************************************************
 *	app_exit
 ****************************************************************************/

void
app_exit()
{
/*{{アプリケーション特有のクリーンアップ*/
	/** no job **/
/*}}アプリケーション特有のクリーンアップ*/

#ifdef USE_VIDREC
	/* 画面出力を終了します。 */
	cdcacm_stop();
#endif /*USE_VIDREC*/

#ifdef USE_TCPIP
	/* TCP/IPプロトコルスイートを終了します。 */
	tcpip_stop();
#endif /*USE_TCPIP*/

#ifdef USE_MUSIC
	/* 音楽ライブラリを終了します。 */
	music_free();
#endif /*USE_MUSIC*/

#ifdef LDIRECT_INTERVAL
	/* 高速切替・16階調表示を解除します。 */
	ldirect_free();
#endif /*LDIRECT_INTERVAL*/

#ifdef GC_LEVEL
	/* ガーベージコレクターを解除します。 */
	gc_free();
#endif /*GC_LEVEL*/

#ifdef USE_DBG
	/* DBGユニットの設定を解除します。 */
	dbg_free();
#endif /*USE_DBG*/

#ifdef USE_UFE
	/* USBファイルシステムエミュレーションを終了します。 */
	ufe_stop();
#endif /*USE_UFE*/
}

/****************************************************************************
 *	update
 ****************************************************************************/

/* * Mon Mar 10 13:29:56 JST 2008 Naoyuki Sawa
 * - update()から、画面更新処理を分離し、高速化しました。
 */
#ifdef V_SCREEN
/* vbuff(縦画面)→_def_vbuff(横画面)変換 */
//static void rotate_screen() {
//	unsigned char *src = &vbuff[88 - 1];	/* 転送先の横画面は左上から走査開始します */
//	unsigned char *dst = &_def_vbuff[0];	/* 転送元の縦画面は右上から走査開始します */
//	int x = 88;
//	do {
//		int y = 128;
//		do {
//			*dst++ = *src;		/* 横画面を右方向に走査します */
//			src += 88;		/* 縦画面を下方向に走査します */
//		} while(--y);
//						/* 横画面は既に次の行へ移動しています */
//		src -= 88 * 128 + 1;		/* 縦画面を左の列の一番上へ移動します */
//	} while(--x);
//}
//↓高速化 (C版 = 9.2ms/1call → ASM版 = 3.2ms/1call)
/*static*/ void rotate_screen();
asm("
	.code
	.align	1
rotate_screen:
	xld.w	%r12, 0x0e00			; %r12 := FRAM4_START
	xld.w	%r13, rotate_screen_DOX		; %r13 := DOY
	xld.w	%r14, rotate_screen_END		; %r14 := END
	xcall.d	memcpy				; %r10 := FRAM4_START = memcpy(FRAM4_START, DOX, END - DOX)
	sub	%r14, %r13			; %r14 := END - DOY			*delay*
	xld.w	%r4, vbuff+87			; %r4  := src = &vbuff[88 - 1]
	xld.w	%r5, _def_vbuff			; %r5  := dst = &_def_vbuff[0]
	xld.w	%r6, 88				; %r6  := 88
	xld.w	%r7, 128			; %r7  := 128
	xld.w	%r11, 11265			; %r11 := 88 * 128 + 1
	ld.w	%r12, %r6			; %r12 := x = 88
	jp	%r10				; goto FRAM4_START			(jp.dは不可!!)
;//{{ここからFRAM4領域へ転送
rotate_screen_DOX:				; do {
	ld.w	%r13, %r7			;   %r13 := y = 128
rotate_screen_DOY:				;   do {
	ld.ub	%r9, [%r4]			;     %r9  := c = *src
	sub	%r13, 1				;     %r13 := --y			*anti-interlock*
	ld.b	[%r5]+, %r9			;     %r5  := *dst++ = c
	jrne.d	rotate_screen_DOY		;   } while(--y)
	add	%r4, %r6			;     %r4  := src += 88			*delay*
	sub	%r12, 1				;   %r12 := --x
	jrne.d	rotate_screen_DOX		; } while(--x)
	sub	%r4, %r11			;   %r4  := src -= 88 * 128 + 1		*delay*
	ret
rotate_screen_END:
;//}}ここまでFRAM4領域へ転送
");
#endif /*V_SCREEN*/

/* * Mon Nov 06 17:37:17 JST 2006 Naoyuki Sawa
 * - schedule()から、画面更新処理だけを分離しました。
 *   フレーム待ち無し画面更新だけを行う場合は、この関数を呼び出してください。
 */
void
update()
{
#ifdef V_SCREEN
	rotate_screen();
#endif /*V_SCREEN*/

	/* 画面転送 */
	pceLCDTrans();
}

/****************************************************************************
 *	vidrec
 ****************************************************************************/

#ifdef USE_VIDREC
static void
vidrec()
{
#ifdef LDIRECT_INTERVAL
	static const unsigned char hdr[4] = { DISP_X,DISP_Y,4/*[bpp]*/,FPS };
#else /*LDIRECT_INTERVAL*/
	static const unsigned char hdr[4] = { DISP_X,DISP_Y,2/*[bpp]*/,FPS };
#endif /*LDIRECT_INTERVAL*/
	static int active;
	//
	unsigned char buf[128/*調整可*/]; /* (sizeof vbuff)/Nのサイズとすること */
	const unsigned char* src;
	unsigned char* dst;

	if(cdcacm_recv(buf, 1) > 0) {
		switch(buf[0]) {
		case 0:
			active = 0; /* 画面出力停止 */
			break;
		case 1:
			active = 1; /* 画面出力開始 */
			src = hdr;
			do {
				if(pcePadGetDirect() & PAD_SELECT) {
					exit(0); /* 送信待ちに割り込んで強制終了 */
				}
				src += cdcacm_send(src, &hdr[sizeof hdr] - src);
			} while(src < &hdr[sizeof hdr]);
			break;
		}
	}
	if(active) {
		src = vbuff;
		do {
			dst = buf;
			do {
#ifdef LDIRECT_INTERVAL
				*dst++ = src[0]|(src[1]<<4); 
				src += 2;
#else /*LDIRECT_INTERVAL*/
				*dst++ = src[0]|(src[1]<<2)|(src[2]<<4)|(src[3]<<6);
				src += 4;
#endif /*LDIRECT_INTERVAL*/
			} while(dst < &buf[sizeof buf]);
			dst = buf;
			do {
				if(pcePadGetDirect() & PAD_SELECT) {
					exit(0); /* 送信待ちに割り込んで強制終了 */
				}
				dst += cdcacm_send(dst, &buf[sizeof buf] - dst);
			} while(dst < &buf[sizeof buf]);
		} while(src < &vbuff[sizeof vbuff]);
	}
}
#endif /*USE_VIDREC*/

/****************************************************************************
 *	schedule
 ****************************************************************************/

/* * Fri Dec 15 00:49:15 JST 2006 Naoyuki Sawa
 * - schedule()関数に、サウンドポーズ対応コードを追加しました。
 *   少し試した限りでは上手く動作しているようですが、スリープからの復帰時に問題が起きないか未確認です。
 * - もし、ポーズ状態のまま、すなわち、サウンドポーズ状態のままでアプリケーションを終了してしまった場合にも、
 *   CLiPライブラリ内のpceAppExit()でpceWaveStop(1)を呼び出しており、サウンド状態がリセットされるので、大丈夫です。
 */

void
schedule()
{
#ifdef  USE_EVENT_TIMER
	/* イベントタイマ処理。 */
	EvtTmr_Exec(pEvtTmr);
#endif/*USE_EVENT_TIMER*/

#ifdef  USE_COUNTER_MANAGER
	/* 汎用カウンタ管理処理。 */
	CntMgr_Exec(pCntMgr);
#endif/*USE_COUNTER_MANAGER*/

	/* ログバッファ描画。 */
#ifdef  USE_LOG_BUFFER
	LogBuf_Draw(pLogBuf, 0, 0);
#endif/*USE_LOG_BUFFER*/

#ifdef USE_PAUSE
	{
		/* PAUSEメッセージ領域のイメージ退避用バッファは、324バイト必要です。
		 * スタック上に取るのは危険ですし、staticに取るのもメモリの無駄です。
		 * そこで、PAUSE時にのみヒープから確保することにします。
		 */
		//unsigned char save[PAUSE_H][PAUSE_W];
		unsigned char (*save)[PAUSE_W] = NULL/*警告抑制*/;
		int pause = 0;
		/*{{2006/12/15:サウンドポーズ対応*/
		int sound_pause = 0/*警告抑制*/;
		/*}}2006/12/15:サウンドポーズ対応*/
		int x, y;
		for(;;) {
#endif /*USE_PAUSE*/

			/* 画面更新。 */
			update();

#ifdef USE_VIDREC
			/* 画面出力。 */
			vidrec();
#endif /*USE_VIDREC*/

#ifdef  USE_SOUND_MANAGER
			/* Sound Manager処理。 */
			SndMgr_Exec(pSndMgrBgm); /* BGM Sound Manager */
			SndMgr_Exec(pSndMgrSe1); /* SE1 Sound Manager */
			SndMgr_Exec(pSndMgrSe2); /* SE2 Sound Manager */
#endif/*USE_SOUND_MANAGER*/

#ifdef USE_IDLE
			/* アイドル処理+システム処理。 */
			idle_yield();
#else /*USE_IDLE*/
			/* システム処理。 */
			yield();
#endif /*USE_IDLE*/

			/* パッド状態更新。 */
			joy = pad_get();

#ifdef V_SCREEN
			joy = (joy & ~(PAD_LF | PAD_RI | PAD_UP | PAD_DN | /* 縦画面用入力変換 */
				       TRG_LF | TRG_RI | TRG_UP | TRG_DN))
				| (joy & PAD_LF ? PAD_UP : 0)
				| (joy & PAD_RI ? PAD_DN : 0)
				| (joy & PAD_UP ? PAD_RI : 0)
				| (joy & PAD_DN ? PAD_LF : 0)
				| (joy & TRG_LF ? TRG_UP : 0)
				| (joy & TRG_RI ? TRG_DN : 0)
				| (joy & TRG_UP ? TRG_RI : 0)
				| (joy & TRG_DN ? TRG_LF : 0);
#endif /*V_SCREEN*/

			/* SELECTボタンが押されたら終了する。 */ /* アプリケーションによって不要ならば、この2行を削除してください。 */
			if(joy & TRG_SELECT) exit(EXIT_SUCCESS); /* アプリケーションによって不要ならば、この2行を削除してください。 */

#ifdef USE_PAUSE
			/* PAUSE処理。 */
			if(joy & TRG_START) {
				pause = !pause;	/* PAUSE状態切り替え */
				if(pause) {	/* PAUSE IN */
					/*{{2006/12/15:サウンドポーズ対応*/
					ENTER_CS;
					sound_pause = fINT_EDMA.EHDM1;
					if(sound_pause) {
						fINT_EDMA.EHDM1 = 0;
					}
					LEAVE_CS;
					/*}}2006/12/15:サウンドポーズ対応*/
					/* PAUSEメッセージ表示部分のイメージを退避。 */
					save = (unsigned char (*)[PAUSE_W])malloc(PAUSE_W * PAUSE_H); /* イメージ退避用バッファ確保 */
					if(save == NULL) DIE();
					for(y = 0; y < PAUSE_H; y++) {
						for(x = 0; x < PAUSE_W; x++) {
							save[y][x] = vbuff[DISP_X * (PAUSE_Y + y) + (PAUSE_X + x)];
						}
					}
				} else {	/* PAUSE OUT */
					/*{{2006/12/15:サウンドポーズ対応*/
					ENTER_CS;
					if(sound_pause) {
						fINT_EDMA.EHDM1 = 1;
					}
					LEAVE_CS;
					/*}}2006/12/15:サウンドポーズ対応*/
					/* PAUSEメッセージ表示部分のイメージを復帰。 */
					for(y = 0; y < PAUSE_H; y++) {
						for(x = 0; x < PAUSE_W; x++) {
							vbuff[DISP_X * (PAUSE_Y + y) + (PAUSE_X + x)] = save[y][x];
						}
					}
					free(save); /* イメージ退避用バッファ開放 */
				}
			}
			if(!pause) break;
			/* PAUSEメッセージ点滅表示。 */
			if(pceTimerGetCount() % 1000 < 750) {
				render_string_framed(&render, PAUSE_X, PAUSE_Y, PAUSE_STR, PAUSE_FONT, PAUSE_COLOR);
			} else {
				for(y = 0; y < PAUSE_H; y++) {
					for(x = 0; x < PAUSE_W; x++) {
						vbuff[DISP_X * (PAUSE_Y + y) + (PAUSE_X + x)] = save[y][x];
					}
				}
			}
		}
	}
#endif /*USE_PAUSE*/

	now++;	/* グローバルタイマ更新 */
}

/****************************************************************************
 *	app_main
 ****************************************************************************/

/*--- C言語版(int) ---*/
static int is_prime1(int n) {
	int i;
	for(i = 2; i < n; i++) {
		if(n % i == 0) {
			return 0;
		}
	}
	return 1;
}
static int c_prog1(int N) {
	int c = 0;
	int i;
	for(i = 2; i <= N; i++) {
		if(is_prime1(i)) {
			c = c + 1;
		}
	}
	return c;
}

/*--- C言語版(float) ---*/
static float is_prime2(float n) {
	float i;
	for(i = 2; i < n; i++) {
		if(fmodf(n, i) == 0) {
			return 0;
		}
	}
	return 1;
}
static float c_prog2(int N) {
	float c = 0;
	float i;
	for(i = 2; i <= N; i++) {
		if(is_prime2(i)) {
			c = c + 1;
		}
	}
	return c;
}

/*--- C言語版(double) ---*/
static double is_prime3(double n) {
	double i;
	for(i = 2; i < n; i++) {
		if(fmod(n, i) == 0) {
			return 0;
		}
	}
	return 1;
}
static double c_prog3(int N) {
	double c = 0;
	double i;
	for(i = 2; i <= N; i++) {
		if(is_prime3(i)) {
			c = c + 1;
		}
	}
	return c;
}

/*--- Lua版 ---*/
static lua_State* L;
static int panic(lua_State* L) {
	DIE();
}
static const char l_prog[] = "
-- nが素数ならばtrue、素数でなければfalseを返します。
function is_prime(n)
	local i
	for i = 2, (n - 1) do
		if (n % i) == 0 then
			return false
		end
	end
	return true
end
-- 2〜Nの範囲にある素数の数を、変数cに格納します。
c = 0
for i = 2, N do
	if is_prime(i) then
		c = c + 1
	end
end
";

/*--- Tcl版 ---*/
static Tcl_Interp* iPtr;
static const char t_prog[] = "
# nが素数ならば1、素数でなければ0を返します。
proc is_prime {n} {
	for {set i 2} {$i < $n} {incr i} {
		if {$n % $i == 0} then {
			return 0
		}
	}
	return 1
}
# 2〜Nの範囲にある素数の数を、変数cに格納します。
set c 0
for {set i 2} {$i <= $N} {incr i} {
	if {[is_prime $i]} then {
		incr c
	}
}
";

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

void
app_main()
{
	int N = 300;	//2〜Nの範囲にある素数の数を求める。
	int c, t, y;

	surface_clear(&surface, 0);	//  01234567890123456789012345
	y = 0;
	render_printf(&render, 0, y, 0, 3, "       結果[個]  時間[ms]"); y += 10;
	render_printf(&render, 0, y, 0, 3, "-------------------------"); y += 10;
	render_printf(&render, 0, y, 0, 3, "C(int)                   "); y += 10;
	render_printf(&render, 0, y, 0, 3, "C(flt)                   "); y += 10;
	render_printf(&render, 0, y, 0, 3, "C(dbl)                   "); y += 10;
	render_printf(&render, 0, y, 0, 3, "Lua                      "); y += 10;
	render_printf(&render, 0, y, 0, 3, "Tcl                      "); y += 10;
	render_printf(&render, 0, y, 0, 3, "-------------------------"); y += 10;
	schedule();
	y = 20;

	/*--- C言語版(int) ---*/
	/* 実行時間を計測します。 */
	t = pceTimerGetCount();
	c = c_prog1(N);
	t = pceTimerGetCount() - t;
	/* 実行時間と結果を表示します。 */
	render_printf(&render, 35, y, 0, 3, "%8d %9d", c, t); y += 10;
	schedule();

	/*--- C言語版(float) ---*/
	/* 実行時間を計測します。 */
	t = pceTimerGetCount();
	c = c_prog2(N);
	t = pceTimerGetCount() - t;
	/* 実行時間と結果を表示します。 */
	render_printf(&render, 35, y, 0, 3, "%8d %9d", c, t); y += 10;
	schedule();

	/*--- C言語版(double) ---*/
	/* 実行時間を計測します。 */
	t = pceTimerGetCount();
	c = c_prog3(N);
	t = pceTimerGetCount() - t;
	/* 実行時間と結果を表示します。 */
	render_printf(&render, 35, y, 0, 3, "%8d %9d", c, t); y += 10;
	schedule();

	/*--- Lua版 ---*/
	/* Luaを初期化します。 */
	L = luaL_newstate();
	lua_atpanic(L, panic);
	luaL_openlibs(L);
	lua_pushnumber(L, N);							//┬Luaでｸﾞﾛｰﾊﾞﾙ変数を設定する方法
	lua_setglobal(L, "N");							//┘
	/* Luaスクリプトをロードします。 */
	luaL_loadstring(L, l_prog);
	/* 実行時間を計測します。 */
	t = pceTimerGetCount();
	lua_call(L, 0, 0);
	t = pceTimerGetCount() - t;
	/* 結果を取得します。 */
	lua_getglobal(L, "c");							//┬Luaでｸﾞﾛｰﾊﾞﾙ変数を取得する方法
	c = lua_tointeger(L, -1);						//┘
	/* Luaを終了します。 */
	lua_close(L);
	/* 実行時間と結果を表示します。 */
	render_printf(&render, 35, y, 0, 3, "%8d %9d", c, t); y += 10;
	schedule();

	/*--- Tcl版 ---*/
	/* Tclを初期化します。 */
	iPtr = Tcl_CreateInterp();
	Tcl_SetVar(iPtr, "N", strdup_printf("%d", N));				//─Tclでｸﾞﾛｰﾊﾞﾙ変数を設定する方法
	/* 実行時間を計測します。 */
	t = pceTimerGetCount();
	if(Tcl_Eval(iPtr, t_prog, NULL, 0)) { DIE(); }
	t = pceTimerGetCount() - t;
	/* 結果を取得します。 */
	if(Tcl_GetInt(iPtr, Tcl_GetVar(iPtr, "c") ? : "", &c)) { DIE(); }	//─Tclでｸﾞﾛｰﾊﾞﾙ変数を取得する方法
	/* 実行時間と結果を表示します。 */
	render_printf(&render, 35, y, 0, 3, "%8d %9d", c, t); y += 10;
	schedule();

	y = DISP_Y - 10;
	render_printf(&render, 0, y, 0, 3, "何かｷｰを押すと終了します");
	for(;;) {
		schedule();
		if(joy & TRG_MASK) {
			break;
		}
	}
}

