/*
 *
 *	シンプルライブラリなしで、逐次処理的なプログラムを書くサンプル
 *
 */
#include <piece.h>

/****************************************************************************
 *	アプリケーションごとに変更する必要のない部分
 ****************************************************************************/

void app_proc();
void app_exit();
void yield();
int main();

typedef struct _CONTEXT {
	int r0, r1, r2, r3;
	int* sp;
} CONTEXT;
static CONTEXT system_context;	/* システムコンテキスト保存用 */
static CONTEXT user_context;	/* ユーザーコンテキスト保存用 */

void
pceAppInit()
{
	/* ユーザーコンテキストを初期化します。 */
	user_context.sp = (int*)0x1C00;		/* 1000〜1C00:ユーザースタック */
						/* 1C00〜2000:システムスタック */
	*--user_context.sp = (int)app_exit;	/* main()が制御を返した時に呼ばれる関数 */
	*--user_context.sp = (int)main;		/* アプリケーションのエントリポイント */
}

void
pceAppProc(int count)
{
	/* yield()とスタックフレームの構成を合わせる必要があるので、
	 * 引数なしの関数を経由します。
	 */
	app_proc();
}

void
pceAppExit()
{
	/* 何もしません。 */
}

void
app_proc()
{
	asm("
	; システムコンテキストを退避します。
	xld.w %r7, system_context
	ld.w [%r7]+, %r0
	ld.w [%r7]+, %r1
	ld.w [%r7]+, %r2
	ld.w [%r7]+, %r3
	ld.w %r4, %sp
	ld.w [%r7]+, %r4

	; ユーザーコンテキストを復元します。
	xld.w %r7, user_context
	ld.w %r0, [%r7]+
	ld.w %r1, [%r7]+
	ld.w %r2, [%r7]+
	ld.w %r3, [%r7]+
	ld.w %r4, [%r7]+
	ld.w %sp, %r4
	");
}

void
app_exit()
{
	asm("
	; システムコンテキストを復元します。
	xld.w %r7, system_context
	ld.w %r0, [%r7]+
	ld.w %r1, [%r7]+
	ld.w %r2, [%r7]+
	ld.w %r3, [%r7]+
	ld.w %r4, [%r7]+
	ld.w %sp, %r4

	; 終了要求を発行します。
	ld.w %r12, %r10
	xcall pceAppReqExit
	");
}

void
yield()
{
	asm("
	; ユーザーコンテキストを退避します。
	xld.w %r7, user_context
	ld.w [%r7]+, %r0
	ld.w [%r7]+, %r1
	ld.w [%r7]+, %r2
	ld.w [%r7]+, %r3
	ld.w %r4, %sp
	ld.w [%r7]+, %r4

	; システムコンテキストを復元します。
	xld.w %r7, system_context
	ld.w %r0, [%r7]+
	ld.w %r1, [%r7]+
	ld.w %r2, [%r7]+
	ld.w %r3, [%r7]+
	ld.w %r4, [%r7]+
	ld.w %sp, %r4
	");
}

/****************************************************************************
 *	アプリケーションごとに変更する部分(サンプル)
 ****************************************************************************/

unsigned char vbuff[DISP_X * DISP_Y];	/* 仮想VRAM */

int
main()
{
	int x, y;

	/* 初期化を行います。 */
	pceLCDDispStop();
	pceLCDSetBuffer(vbuff);
	pceAppSetProcPeriod(1000 / 30);
	pceLCDDispStart();

	/* キャラクタの初期位置。 */
	x = y = 0;

	/* 左上から右上へ移動。 */
	while(x < DISP_X - 5) {
		/* キャラクタ表示。 */
		memset(vbuff, 0, sizeof vbuff);
		pceFontPut(x, y, 'A');
		x += 2;
		pceLCDTrans();
		/* システム処理。 */
		yield();
	}
	x = DISP_X - 5;

	/* 右上から右下へ移動。 */
	while(y < DISP_Y - 10) {
		/* キャラクタ表示。 */
		memset(vbuff, 0, sizeof vbuff);
		pceFontPut(x, y, 'A');
		y += 2;
		pceLCDTrans();
		/* システム処理。 */
		yield();
	}
	y = DISP_Y - 10;

	/* 右下から左下へ移動。 */
	while(x > 0) {
		/* キャラクタ表示。 */
		memset(vbuff, 0, sizeof vbuff);
		pceFontPut(x, y, 'A');
		x -= 2;
		pceLCDTrans();
		/* システム処理。 */
		yield();
	}
	x = 0;

	/* 左下から左上へ移動。 */
	while(y > 0) {
		/* キャラクタ表示。 */
		memset(vbuff, 0, sizeof vbuff);
		pceFontPut(x, y, 'A');
		y -= 2;
		pceLCDTrans();
		/* システム処理。 */
		yield();
	}
	y = 0;

	return 0;
}
