/*
 *	app.c
 *
 *	P/ECEアプリケーションの雛型（CLiPライブラリ版）
 *
 *	* Tue Jul 23 21:01:00 JST 2002 Naoyuki Sawa
 *	- 作成開始。
 */
#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()
{
	/* 捕捉されなかった例外を捕えるための、例外フレームを初期化します。 */
	SEH_init();

	/* アプリケーションを初期化します。 */
	app_init();

	/* メインループを実行します。 */
	for(;;) {
		if(demo_main() < 0) { break; }
		game_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 iLop, int iVol, int iPan) {
	sound_play(iTrk, sound_table[iPhr], iLop);
	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*/

#ifdef  USE_TCL
//srand seed
static int Tcl_SrandCmd(Tcl_Interp* iPtr) {
	if((iPtr->argc < 1) || (iPtr->argc > 2)) { DIE(); }
	srand((iPtr->argc == 2) ? atoi(iPtr->argv[1]) : time(NULL));
	return TCL_OK;
}
//rand
static int Tcl_RandCmd(Tcl_Interp* iPtr) {
	if(iPtr->argc != 1) { DIE(); }
	iPtr->result = strdup_printf("%d", rand());
	return TCL_OK;
}
//log string
static int Tcl_LogCmd(Tcl_Interp* iPtr) {
	if(iPtr->argc != 2) { DIE(); }
#ifdef  USE_LOG_BUFFER
	LogBuf_Print(pLogBuf, iPtr->argv[1]);
	update();
#else //USE_LOG_BUFFER
	TRACE("%s\n", iPtr->argv[1]);
#endif//USE_LOG_BUFFER
	return TCL_OK;
}
//schedule
static int Tcl_ScheduleCmd(Tcl_Interp* iPtr) {
	if(iPtr->argc != 1) { DIE(); }
	schedule();
	return TCL_OK;
}
#endif//USE_TCL

void
app_init()
{
#ifdef  USE_USBSUSPEND_PROBLEMWORKAROUND
	//「USBがサスペンドした時にP/ECEがハングアップする事が有る問題」を回避する処理を設定します。
	Intel915G_WindowsXp_UsbSuspend_ProblemWorkaround_init();
#endif//USE_USBSUSPEND_PROBLEMWORKAROUND

	/* 一般的な初期化。 */
	pceAppSetProcPeriod(1000 / FPS);
#ifdef V_SCREEN
	pceLCDSetBuffer(_def_vbuff);
#else /*V_SCREEN*/
	pceLCDSetBuffer(vbuff);
#endif /*V_SCREEN*/
//不要	pceLCDDispStart();	//{{2014/12/31削除:cstart.cのpceAppInit00()で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 BITMAP_GC_UNIT
	/* Bitmap GCを初期化します。 */
	BitmapGC_Init(BITMAP_GC_UNIT);
#endif /*BITMAP_GC_UNIT*/

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

#ifdef USE_MUSIC
	/* 音楽ライブラリを初期化します。 */
	InitMusic();
#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()を利用する機能と、音楽ライブラリと併用する場合は、同様の対策が必要です。
	 * //{{2015/01/15コメント削除:muslib.c版のInitMusic()は「music_wch = 0;」を行わないようにしたので、どのタイミングでmusic_wchを変更しても構いません。
	 * //- music_wchを変更する処理は、InitMusic()よりも後で行う必要があります。
	 * //  InitMusic()が、「music_wch = 0;」を行っているからです。InitMusic()よりも先に変更すると、0に戻ってしまいます。
	 * //- 尚、music_loudness()の機能もmusic_wchを参照していますが、
	 * //  随時、値を取得しているので、music_loudness()を呼び出した後にmusic_wchを変更しても、問題はありません。
	 * //}}2015/01/15コメント削除:muslib.c版のInitMusic()は「music_wch = 0;」を行わないようにしたので、どのタイミングで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*/

	/* LEDシーケンサを初期化します。 */
#ifdef  USE_LED
	pLedSeq = LedSeq_Init(LedCh_Max + 1, LedSeqCh_Max + 1);
#endif//USE_LED

	/* イベントタイマを初期化します。 */
#ifdef  USE_EVENT_TIMER
	{
		static ST_EvtTmrFunc stEvtTmrFunc = { EvtTmr_onEvent };
		pEvtTmr = EvtTmr_Init(EvtTmrNo_Max+1);	//ｲﾍﾞﾝﾄﾀｲﾏを初期化する｡
		EvtTmr_AddFunc(pEvtTmr, &stEvtTmrFunc);	//ｲﾍﾞﾝﾄ処理関数を登録する｡
	}
#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_TCL
	//Tclインタプリタを高速化するために、定期的にガーベージコレクションを行う。
#if     defined(GC_LEVEL)
/*必須*/timer_start(500/*調整可*/, gc_run, NULL);//cliptcl.cのコメント参照。通常のGCでTclインタプリタを使用する場合は、この行を実行しないと極端に遅くなる。
#elif   defined(BITMAP_GC_UNIT)
//任意	timer_start(500/*調整可*/, gc_run, NULL);//cliptcl.cのコメント参照。Bitmap GCを使用する場合は極端に遅くはないが、この行を実行する方が高速ではある。
#else //defined(GC_LEVEL)||defined(BITMAP_GC_UNIT)
	#error Tclインタプリタはガーベージコレクターの併用が必須です。
#endif//defined(GC_LEVEL)||defined(BITMAP_GC_UNIT)
	//Tclインタプリタを作成する。
	iPtr = Tcl_CreateInterp();
	//汎用的なアプリケーション定義のコマンドを登録する。
	Tcl_CreateCommand(iPtr, "srand",    Tcl_SrandCmd,    NULL);
	Tcl_CreateCommand(iPtr, "rand",     Tcl_RandCmd,     NULL);
	Tcl_CreateCommand(iPtr, "log",      Tcl_LogCmd,      NULL);
	Tcl_CreateCommand(iPtr, "schedule", Tcl_ScheduleCmd, NULL);
#endif//USE_TCL

	/* テクスチャを初期化します。 */
#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 LDIRECT_INTERVAL
	/* 高速切替・16階調表示を解除します。 */
	ldirect_free();
#endif /*LDIRECT_INTERVAL*/

#ifdef BITMAP_GC_UNIT
	/* Bitmap GCを解除します。 */
	BitmapGC_Free();
#endif /*BITMAP_GC_UNIT*/

#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*/

#ifdef  USE_USBSUSPEND_PROBLEMWORKAROUND
	//「USBがサスペンドした時にP/ECEがハングアップする事が有る問題」を回避する処理を解除します。
	Intel915G_WindowsXp_UsbSuspend_ProblemWorkaround_free();
#endif//USE_USBSUSPEND_PROBLEMWORKAROUND
}

/****************************************************************************
 *	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()から、画面更新処理だけを分離しました。
 *   フレーム待ち無し画面更新だけを行う場合は、この関数を呼び出してください。
 * * Thu Dec 21 21:17:56 JST 2014 Naoyuki Sawa
 * - ログバッファ描画を、これまではschedule()で行っていましたが、update()で行うように変更しました。
 *   それに加えて、ログバッファ描画前後で、仮想VRAMを保存するようにしました。
 *   この変更によって、アプリケーションは、ログバッファ描画の影響を無視して、オーバーレイと見なせるようになりました。
 *   以下のような処理が可能となります。
 *   □例1
 *   │void app_main(){
 *   │  surface_clear(&surface,0);
 *   │  render_ellipse(&render,0,0,DISP_X,DISP_Y,3);//円を描画する｡
 *   │  for(;;){
 *   │    schedule();//円を破壊せずにﾛｸﾞﾊﾞｯﾌｧ描画のみをｽｸﾛｰﾙできる｡
 *   │    LogBuf_Printf(pLogBuf,"%d",now);
 *   │} }
 *   □例2
 *   │void test(){
 *   │  //〜何か処理〜
 *   │  LogBuf_Printf(pLogBuf,"some message");
 *   │  update();//ﾛｸﾞﾊﾞｯﾌｧ描画のみを更新する｡
 *   │  //〜何か処理〜
 *   │} }
 * - 今回の変更に伴い、ログバッファ描画が、録画と、PCからの画面キャプチャに含まれなくなったことに、注意してください。
 *   録画はschedule()の中のvidrec()で，キャプチャはschedule()の中のyield()内で行われる処理です。
 *   vidrec()やyield()が呼ばれる時点では、仮想VRAMは復元されており、ログバッファ描画は残っていないからです。
 *   ログバッファ描画は、主にテストのための表示ですので、録画やキャプチャに含まれていなくても問題が無いと思いますが、
 *   もし必要である場合は、当関数の最後の、仮想VRAMを復元する処理を削除すれば、変更前と同様に含まれるようにできます。
 */
void
update()
{
#ifdef  USE_LOG_BUFFER
	/* ログバッファ描画前に仮想VRAMを退避する。 */
    #ifdef  V_SCREEN
	static unsigned char tmpbuf[sizeof vbuff];	//ログバッファと縦画面を併用する場合は、仮想VRAMを退避する一時的なバッファとして_def_vbuffを使えない。rotate_screen()が_def_vbuffを破壊するからである。tmpbuf[]の分メモリ使用量が増えることに注意せよ。
	memcpy(tmpbuf, vbuff, sizeof vbuff);
    #else //V_SCREEN
	memcpy(_def_vbuff, vbuff, sizeof vbuff);	//ログバッファと縦画面を併用しない場合は、仮想VRAMを退避する一時的なバッファとして_def_vbuffを使う。
    #endif//V_SCREEN
	/* ログバッファ描画。 */
	LogBuf_Draw(pLogBuf, 0, 0);
#endif/*USE_LOG_BUFFER*/

#ifdef V_SCREEN
	rotate_screen();
#endif /*V_SCREEN*/

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

#ifdef  USE_LOG_BUFFER
	/* ログバッファ描画前に退避した仮想VRAMを復元する。 */
    #ifdef  V_SCREEN
	memcpy(vbuff, tmpbuf, sizeof vbuff);		//ログバッファと縦画面を併用する場合
    #else //V_SCREEN
	memcpy(vbuff, _def_vbuff, sizeof vbuff);	//ログバッファと縦画面を併用しない場合
    #endif//V_SCREEN
#endif/*USE_LOG_BUFFER*/
}

/****************************************************************************
 *	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_LED
	/* LEDシーケンサ処理。 */
	{
		unsigned char Buffer[LedCh_Max + 1];
		memset(Buffer, 1/*消灯*/, sizeof Buffer);
		LedSeq_Exec(pLedSeq, Buffer);
		//{{TODO:ここでBuffer[]を画面に描画してください｡}}
	}
#endif//USE_LED

#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++;	/* グローバルタイマ更新 */
}

/****************************************************************************
 *	delay
 ****************************************************************************/

void
delay(int frame)
{
	while(frame > 0) {
		schedule();
		frame--;
	}
}

/****************************************************************************
 *	demo_main
 ****************************************************************************/

#ifdef USE_IDLE
static int idle_proc(void* param) {
	/* {{TODO:ここで時間のかかる処理を行う}} */
	return 0;
}
#endif /*USE_IDLE*/

#ifdef  USE_TASK
static int task_proc(void* param) {
	int* pval = param;
	for(;;) { (*pval)++; task_yield(); }
	return 0;
}
#endif/*USE_TASK*/

#ifdef  USE_LOT
static int fnRnd(int* rndval) {
	return (*rndval)++;
}
#endif//USE_LOT

#ifdef  USE_DIR
static void fnAct_Lcd(int DirLyr, int ActNo) {
	const char* s;
	switch(ActNo) {
	default: DIE(); break;
	case 0: s = ""; break;
	//--- LcdUi ---
	case LcdUi_TjUi:    s = "TjUi";   break;//通常UI
	case LcdUi_BoUi:    s = "BoUi";   break;//ﾎﾞｰﾅｽUI
	//--- LcdCmd ---
	case LcdCmd_TjBgSc: s = "TjBgSc"; break;//通常BGｽｸﾛｰﾙ
	case LcdCmd_TjBgTe: s = "TjBgTe"; break;//通常BG停止
	case LcdCmd_TjAcAl: s = "TjAcAl"; break;//通常ｱｸﾀｰ歩く
	case LcdCmd_TjAcKz: s = "TjAcKz"; break;//通常ｱｸﾀｰ気付く
	case LcdCmd_TjAcHz: s = "TjAcHz"; break;//通常ｱｸﾀｰﾊｽﾞﾚ
	case LcdCmd_TjAcBo: s = "TjAcBo"; break;//通常ｱｸﾀｰﾎﾞｰﾅｽ
	case LcdCmd_TjAcRp: s = "TjAcRp"; break;//通常ｱｸﾀｰﾘﾌﾟﾚｲ
	case LcdCmd_TjAcBe: s = "TjAcBe"; break;//通常ｱｸﾀｰﾍﾞﾙ
	case LcdCmd_TjAcSk: s = "TjAcSk"; break;//通常ｱｸﾀｰｽｲｶ
	case LcdCmd_TjAcCh: s = "TjAcCh"; break;//通常ｱｸﾀｰﾁｪﾘｰ
	case LcdCmd_Bonus:  s = "Bonus";  break;//ﾎﾞｰﾅｽBG
	case LcdCmd_BoEnd:  s = "BoEnd";  break;//ﾎﾞｰﾅｽ終了
	case LcdCmd_BoGet:  s = "BoGet";  break;//ﾎﾞｰﾅｽGet
	case LcdCmd_BoAcTe: s = "BoAcTe"; break;//ﾎﾞｰﾅｽｱｸﾀｰ停止
	case LcdCmd_BoAcKt: s = "BoAcKt"; break;//ﾎﾞｰﾅｽｱｸﾀｰ回転
	}
	render_rectangle_fill(&render, 0,10*DirLyr, DISP_X,10, 0);
	render_string(&render, 0,10*DirLyr, s, 0,3);
}
static void fnAct_Lmp(int DirLyr, int ActNo) {
	const char* s;
	switch(ActNo) {
	default: DIE(); break;
	case 0: s = ""; break;
	//--- LedCmd ---
	case LedCmd_TjAcKz: s = "TjAcKz"; break;//通常ｱｸﾀｰ気付く
	case LedCmd_TjAcHz: s = "TjAcHz"; break;//通常ｱｸﾀｰﾊｽﾞﾚ
	case LedCmd_TjAcBo: s = "TjAcBo"; break;//通常ｱｸﾀｰﾎﾞｰﾅｽ
	case LedCmd_TjAcRp: s = "TjAcRp"; break;//通常ｱｸﾀｰﾘﾌﾟﾚｲ
	case LedCmd_TjAcBe: s = "TjAcBe"; break;//通常ｱｸﾀｰﾍﾞﾙ
	case LedCmd_TjAcSk: s = "TjAcSk"; break;//通常ｱｸﾀｰｽｲｶ
	case LedCmd_TjAcCh: s = "TjAcCh"; break;//通常ｱｸﾀｰﾁｪﾘｰ
	case LedCmd_Bonus:  s = "Bonus";  break;//ﾎﾞｰﾅｽBG
	case LedCmd_BoEnd:  s = "BoEnd";  break;//ﾎﾞｰﾅｽ終了
	case LedCmd_BoGet:  s = "BoGet";  break;//ﾎﾞｰﾅｽGet
	}
	render_rectangle_fill(&render, 0,10*DirLyr, DISP_X,10, 0);
	render_string(&render, 0,10*DirLyr, s, 0,3);
}
static void fnAct_Snd(int DirLyr, int ActNo) {
	const char* s;
	switch(ActNo) {
	default: DIE(); break;
	case 0: s = ""; break;
	//--- SndBgm ---
	case SndBgm_Bonus: s = "Bonus";  break;//ﾎﾞｰﾅｽBGM
	case SndBgm_BoEnd: s = "BoEnd";  break;//ﾎﾞｰﾅｽ終了
	//--- SndSe ---
	case SndSe_TjAcKz: s = "TjAcKz"; break;//通常ｱｸﾀｰ気付く
	case SndSe_TjAcHz: s = "TjAcHz"; break;//通常ｱｸﾀｰﾊｽﾞﾚ
	case SndSe_TjAcBo: s = "TjAcBo"; break;//通常ｱｸﾀｰﾎﾞｰﾅｽ
	case SndSe_TjAcRp: s = "TjAcRp"; break;//通常ｱｸﾀｰﾘﾌﾟﾚｲ
	case SndSe_TjAcBe: s = "TjAcBe"; break;//通常ｱｸﾀｰﾍﾞﾙ
	case SndSe_TjAcSk: s = "TjAcSk"; break;//通常ｱｸﾀｰｽｲｶ
	case SndSe_TjAcCh: s = "TjAcCh"; break;//通常ｱｸﾀｰﾁｪﾘｰ
	case SndSe_BoAcKt: s = "BoAcKt"; break;//ﾎﾞｰﾅｽｱｸﾀｰ回転
	case SndSe_BoGet:  s = "BoGet";  break;//ﾎﾞｰﾅｽGet
	}
	render_rectangle_fill(&render, 0,10*DirLyr, DISP_X,10, 0);
	render_string(&render, 0,10*DirLyr, s, 0,3);
}
static void fnAct_Prg(int DirLyr, int ActNo) {
	const char* s;
	switch(ActNo) {
	default: DIE(); break;
	case 0: s = ""; break;
	}
	render_rectangle_fill(&render, 0,10*DirLyr, DISP_X,10, 0);
	render_string(&render, 0,10*DirLyr, s, 0,3);
}
static void fnAct(int ActNo/*[31]=オーバーフロー,[30:24]=未定義,[23:0]=演出番号*/) {
	static void (*const TBL_fnAct[])(int DirLyr/*演出レイヤ*/, int ActNo/*演出番号*/) = {
		fnAct_Lcd,//0 液晶(UI)
		fnAct_Lcd,//1 液晶(後)
		fnAct_Lcd,//2 液晶(前)
		fnAct_Lmp,//3 ﾗﾝﾌﾟ(後)
		fnAct_Lmp,//4 ﾗﾝﾌﾟ(前)
		fnAct_Snd,//5 ｻｳﾝﾄﾞ(BGM)
		fnAct_Snd,//6 ｻｳﾝﾄﾞ(SE)
		fnAct_Prg,//7 ﾌﾟﾛｸﾞﾗﾑ
	};
	int DirLyr = (unsigned char)(ActNo >> 16);
	if(ActNo < 0) { return; }//オーバーフロー時に送信されたコマンドは処理しない事にした。この判断はアフリケーション次第であり処理しても構わない。ただし演出シーケンサの関数を再帰的に呼び出してはいけない。
	if((unsigned)DirLyr >= ARRAY_SIZE(TBL_fnAct)) { DIE(); }
	(*TBL_fnAct[DirLyr])(DirLyr, (unsigned short)ActNo);
}
#endif//USE_DIR

#ifdef  USE_FSM
typedef struct _ST_FsmTest {
	int x,y;
	ST_FsmObj stFsmObj;
} ST_FsmTest;
static ST_FsmTest stFsmTest;
void FsmFunc_Map1_State1_enter(struct _ST_FsmObj* pFsmObj) { TRACE("FsmFunc_Map1_State1_enter\n"); }
void FsmFunc_Map1_State1_leave(struct _ST_FsmObj* pFsmObj) { TRACE("FsmFunc_Map1_State1_leave\n"); }
int FsmFunc_Map1_State1_Trans1_guard(struct _ST_FsmObj* pFsmObj,int iTrans,void* pParam) {
	ST_FsmTest* const pFsmTest = CONTAINING_RECORD(pFsmObj,ST_FsmTest,stFsmObj);
	TRACE("FsmFunc_Map1_State1_Trans1_guard\n"); return pFsmTest->x == 0; }
int FsmFunc_Map1_State2_Trans2_guard(struct _ST_FsmObj* pFsmObj,int iTrans,void* pParam) {
	ST_FsmTest* const pFsmTest = CONTAINING_RECORD(pFsmObj,ST_FsmTest,stFsmObj);
	TRACE("FsmFunc_Map1_State2_Trans2_guard\n"); return pFsmTest->y != 0; }
void FsmFunc_Map1_State1_Trans1_act(struct _ST_FsmObj* pFsmObj,int iTrans,void* pParam) { TRACE("FsmFunc_Map1_State1_Trans1_act\n"); }
void FsmFunc_Map1_State2_Trans2_act(struct _ST_FsmObj* pFsmObj,int iTrans,void* pParam) { TRACE("FsmFunc_Map1_State2_Trans2_act\n"); }
void FsmFunc_Map1_default_act(struct _ST_FsmObj* pFsmObj,int iTrans,void* pParam) {
	TRACE("FsmFunc_Map1_default_act\n"); FsmObj_SetState(pFsmObj,FsmState_State1); }
#endif//USE_FSM

int
demo_main()
{
#ifdef USE_IDLE
	idle_init(idle_proc, NULL, 0);
#endif /*USE_IDLE*/

#ifdef  USE_TASK
	{
		int val = 0;
		TASK* task = task_create(INT_MIN, task_proc, &val, 0);
		for(;;) {
			schedule();
			if(joy & TRG_A) { break; }
			surface_clear(&surface, 0);
			render_printf(&render, 0,0, 0,3, "%d", val);
			task_yield();
		}
		task_kill(task);
	}
#endif/*USE_TASK*/

#ifdef USE_MUSIC
	{
		/* SAMPLE */
		extern /*const*/ unsigned char C350[];
		PlayMusic(C350);
	}
#endif /*USE_MUSIC*/

#ifdef  USE_BISON_FLEX
	{
		/* SAMPLE */
		FILE* fp = fopen("program.txt", "r");
#if     (YYDEBUG!=0)
	//	yydebug = 1; /* nonzero means print parse trace */
#endif/*(YYDEBUG!=0)*/
		yylex_init(fp);
		if((yyparse() != 0) || (yynerrs != 0)) DIE();
		fclose(fp);
	}
#endif/*USE_BISON_FLEX*/

#ifdef  USE_LOT
	{
		/* SAMPLE */
		int hitcnt[(1+4)/*eHazure,eReplay,eBell,eCherry,eBonus*/]={0};
		int rndval=0;
		LotRom_Init(TBL_LotTblNo, fnRnd, &rndval);//抽選表ROM管理を開始する｡
	    #if     defined(TRACE_ON) && (TRACE_ON)
		LotRom_BeginLog();//ﾛｸﾞを開始する｡
	    #endif//defined(TRACE_ON) && (TRACE_ON)
		while(rndval <= USHRT_MAX) {
			int hitval = LotRom_draw_l(LotTblNo_Nor/*通常時*/, 2/*ｽﾍﾟｯｸV*/,6/*設定6*/,-1/*終端*/);
			if(hitval == -1) { hitval = eHazure; }
			if((unsigned)hitval >= ARRAY_SIZE(hitcnt)) { DIE(); }
			hitcnt[hitval]++;
		}
		surface_clear(&surface, 0);
		render_printf(&render, 0,10*0, 0,3, "eHazure %5d\n", hitcnt[0]);
		render_printf(&render, 0,10*1, 0,3, "eReplay %5d\n", hitcnt[1]);
		render_printf(&render, 0,10*2, 0,3, "eBell   %5d\n", hitcnt[2]);
		render_printf(&render, 0,10*3, 0,3, "eCherry %5d\n", hitcnt[3]);
		render_printf(&render, 0,10*4, 0,3, "eBonus  %5d\n", hitcnt[4]);
	    #if     defined(TRACE_ON) && (TRACE_ON)
		LotRom_DumpLog();//ﾛｸﾞを出力する｡
		LotRom_EndLog();//ﾛｸﾞを終了する｡LotRom_Free()の中で自動的に行われるので､省略可能です｡
	    #endif//defined(TRACE_ON) && (TRACE_ON)
		LotRom_Free();//抽選表ROM管理を終了する｡ｱﾌﾟﾘｹｰｼｮﾝ終了と同時ならば､この処理は不要です｡
		for(;;) { schedule(); if(joy & TRG_A) { break; } }
	}
#endif/*USE_LOT*/

#ifdef  USE_DIR
	{
		/* SAMPLE */
		const unsigned char* const TBL_DirSeq = (const unsigned char*)&TBL_DirTblNo[TBL_DirTblNo[0]];
		int selDirTblNo = 0, selTrig = 1;
		DirSeq_Init(fnAct);
		surface_clear(&surface, 0);
		for(;;) {
			schedule();
			DirSeq_Exec();
			if((joy & PAD_A) && (joy & PAD_B)) { break; }
			if(joy & TRG_UP) { if(--selDirTblNo < 0) { selDirTblNo = DirTblNo_Max; } }//演出ﾃｰﾌﾞﾙ番号選択(-)
			if(joy & TRG_DN) { if(++selDirTblNo > DirTblNo_Max) { selDirTblNo = 0; } }//演出ﾃｰﾌﾞﾙ番号選択(+)
			if(joy & TRG_LF) { if(--selTrig < 1) { selTrig = 30; } }//ﾄﾘｶﾞ選択(-)
			if(joy & TRG_RI) { if(++selTrig > 30) { selTrig = 1; } }//ﾄﾘｶﾞ選択(+)
			if(joy & TRG_A) { DirSeqCh_SetTbl(0, selDirTblNo ? &TBL_DirSeq[TBL_DirTblNo[selDirTblNo]] : NULL); selTrig = 1; }//演出ﾃｰﾌﾞﾙｾｯﾄｱｯﾌﾟ
			if(joy & TRG_B) { DirSeq_SendTrig(selTrig); if(++selTrig > 30) { selTrig = 30; } }//ﾄﾘｶﾞ送信
			render_rectangle_fill(&render, 0,DISP_Y-10, DISP_X,10, 0);
			render_printf(&render, 0,DISP_Y-10, 0,3, "DirTblNo %2d / Trigger %2d", selDirTblNo, selTrig);
		}
	}
#endif//USE_DIR

#ifdef  USE_SOUND_MIXER
	{
		SndMxr_SetMxrChAtt(pSndMxr, 6, 3, 0, 0); //ﾄｰﾀﾙﾎﾞﾘｭｰﾑを設定する｡(3dB減衰)
		sound_play(0, sound_table[SND_BGM_ST], -1); //ｽﾃｰｼﾞBGMを無限ﾙｰﾌﾟ再生する｡
		for(;;) {
			schedule();
			if(joy & TRG_A ) { sound_play(1, sound_table[SND_BGM_1G], 2); } //演出BGMを2回再生する｡この間､ｽﾃｰｼﾞBGMは消音する｡
			if(joy & TRG_B ) { sound_play(2, sound_table[SND_SE_BTN], 1); } //SEを1回再生する｡これは他のどの音にも影響しない｡
			if(joy & TRG_UP) { sound_play(3, sound_table[SND_SE_ERR], 3); } //ｴﾗｰ音を3回再生する｡この間､他の音を全て消音する｡
			if(joy & TRG_DN) { break; }
		}
	}
#endif/*USE_SOUND_MIXER*/

#ifdef  USE_EVENT_TIMER
	{
		for(;;) {
			schedule();
			if(joy & TRG_UP) { EvtTmr_StartNo(pEvtTmr,EvtTmrNo_Sample1,TBL_EvtTmrNo,EvtTmrNo_Sample1); }	//ｲﾍﾞﾝﾄﾀｲﾏﾁｬﾈﾙ#1を開始する｡
			if(joy & TRG_DN) { EvtTmr_Stop(pEvtTmr,EvtTmrNo_Sample1); }					//ｲﾍﾞﾝﾄﾀｲﾏﾁｬﾈﾙ#1を停止する｡
			if(joy & TRG_LF) { EvtTmr_StartNo(pEvtTmr,EvtTmrNo_Sample2,TBL_EvtTmrNo,EvtTmrNo_Sample2); }	//ｲﾍﾞﾝﾄﾀｲﾏﾁｬﾈﾙ#2を開始する｡
			if(joy & TRG_RI) { EvtTmr_Stop(pEvtTmr,EvtTmrNo_Sample2); }					//ｲﾍﾞﾝﾄﾀｲﾏﾁｬﾈﾙ#2を停止する｡
			if(joy & TRG_A ) { break; }
		}
	}
#endif/*USE_EVENT_TIMER*/

#ifdef  USE_COUNTER_MANAGER
	{
		//初期値設定
		CntMgr_SetVal(pCntMgr, 0,    0, 0, 0);//初期値=   0
		CntMgr_SetVal(pCntMgr, 1,    0, 0, 0);//初期値=   0
		CntMgr_SetVal(pCntMgr, 2, 1000, 0, 0);//初期値=1000
		CntMgr_SetVal(pCntMgr, 3, 1000, 0, 0);//初期値=1000
		//目標値設定
		CntMgr_SetVal(pCntMgr, 0, 1000, 1000, SEC( 5));// 5秒で0⇒1000に変化
		CntMgr_SetVal(pCntMgr, 1, 1000, 1000, SEC(10));//10秒で0⇒1000に変化
		CntMgr_SetVal(pCntMgr, 2,    0, 1000, SEC( 5));// 5秒で1000⇒0に変化
		CntMgr_SetVal(pCntMgr, 3,    0, 1000, SEC(10));//10秒で1000⇒0に変化
		for(;;) {
		    //	uint16_t ValNow[COUNTER_MAXCH];
			schedule();
			surface_clear(&surface, 0);
		    //	CntMgr_GetValAll(pCntMgr, ValNow);
			render_printf(&render, 0, 0, 0,3, "%d", CntMgr_GetVal(pCntMgr, 0)/*ValNow[0]*/);
			render_printf(&render, 0,10, 0,3, "%d", CntMgr_GetVal(pCntMgr, 1)/*ValNow[1]*/);
			render_printf(&render, 0,20, 0,3, "%d", CntMgr_GetVal(pCntMgr, 2)/*ValNow[2]*/);
			render_printf(&render, 0,30, 0,3, "%d", CntMgr_GetVal(pCntMgr, 3)/*ValNow[3]*/);
		}
	}
#endif/*USE_COUNTER_MANAGER*/

#ifdef  USE_FSM
	{
		FsmObj_Init(&stFsmTest.stFsmObj,REG_open_key_l(TBL_RegTbl,RegKey_FsmMap,-1),FsmMap_Map1,FsmState_State1);	//⇒State1へ
		FsmObj_ApplyTrans(&stFsmTest.stFsmObj,FsmTrans_Trans1,NULL);	//⇒State1のTrans1遷移によりState2へ
		FsmObj_ApplyTrans(&stFsmTest.stFsmObj,FsmTrans_Trans2,NULL);	//⇒State2のTrans2のGuard条件によりState2のまま
		FsmObj_ApplyTrans(&stFsmTest.stFsmObj,FsmTrans_Trans1,NULL);	//⇒Map1のdefault遷移によりState1へ
	}
#endif//USE_FSM

	for(;;) {
		schedule();
		surface_clear(&surface, 1);

#ifdef USE_TCPIP
		{
			/* SAMPLE */
			if(joy & TRG_A) tcpip_config();
		}
#endif /*USE_TCPIP*/

#ifdef USE_SPRITE
		{
			/* SAMPLE */
			sprite_draw(0, 0, SPR4F/*O*/, 0);
			sprite_draw(8, 0, SPR4B/*K*/, 0);
		}
#endif /*USE_SPRITE*/

#ifdef USE_3D
		{
			/* SAMPLE */
			extern const MESH MESH_CUBE; /* mqo/cube.pmx */
			extern const WIRE WIRE_CONE; /* mqo/cone.pwx */
			render_save(&render);
				mlookat_(render.context->matrix,
					fld(0.0), fld(0.0), fld(0.0),
					fld(0.0), fld(0.0), fld(1.0),
					fld(0.0));
				render_save(&render);
#ifdef USE_TEXTURE
					render.context->texture = &texture_table[TEX_CUBE];
#endif /*USE_TEXTURE*/
					mxlate_(render.context->matrix,
						fld(-1.0), fld(0.0), fld(5.0));
					mrotq_(render.context->matrix, qslerp(
						qrot(fld(1.0), fld(0.0), fld(0.0), fld(0.0)),
						qrot(fld(1.0), fld(0.0), fld(0.0), PI),
						fidiv(fld(now % SEC(5)), SEC(5))));
					render_mesh(&render, &MESH_CUBE);
				render_restore(&render);
				render_save(&render);
					mxlate_(render.context->matrix,
						fld(1.5), fld(-1.0), fld(5.0));
					mrotq_(render.context->matrix, qslerp(
						qrot(fld(0.0), fld(1.0), fld(0.0), fld(0.0)),
						qrot(fld(0.0), fld(1.0), fld(0.0), PI),
						fidiv(fld(now % SEC(5)), SEC(5))));
					render_wire(&render, &WIRE_CONE, 3);
				render_restore(&render);
			render_restore(&render);
		}
#endif /*USE_3D*/

#ifdef  USE_LOG_BUFFER
		{
			LogBuf_Printf(pLogBuf, "now=%u", now);
		}
#endif/*USE_LOG_BUFFER*/
	}

#ifdef USE_IDLE
	idle_wait();
#endif /*USE_IDLE*/

	return 0;
}

/****************************************************************************
 *	game_main
 ****************************************************************************/

void
game_main()
{
	for(;;) {
		schedule();
	}
}
