/*	
 *	clipidle.c
 *
 *	アイドルスレッド
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2014 Naoyuki Sawa
 *
 *	* Fri Jun 19 01:44:03 JST 2009 Naoyuki Sawa
 *	- 1st リリース。
 *	* Sun Jun 21 21:42:18 JST 2009 Naoyuki Sawa
 *	- clippce.cにapptm_wk変数を追加したことに伴い、残りタイムスライスの算出にapptm_wkを参照するよう変更しました。
 *	* Sun Oct 11 21:57:48 JST 2009 Naoyuki Sawa
 *	- idle_stat()関数を追加しました。
 *	* Mon Oct 12 05:55:33 JST 2009 Naoyuki Sawa
 *	- idle_exit()関数を追加しました。
 *	- idle_exit()関数の追加に伴い、idle_stub()が不要になったため削除しました。
 *	* Thu Sep 11 21:21:57 JST 2014 Naoyuki Sawa
 *	- idle_init()に、スタックサイズを指定する引数を追加しました。
 *	  これまでは、アイドルスレッドのスタックメモリを静的配列で確保していましたが、今後は、動的に確保/開放します。
 *	- スタックメモリを確保するタイミングは、idle_init()が呼ばれた時です。
 *	- スタックメモリを開放するタイミングは、古いアイドルスレッドが終了する前にidle_init()が呼ばれた時と、アイドルスレッドが終了した時です。
 *	  アイドルスレッドが終了する契機は複数有りますが、全てidle_exitを経由するので、idle_exitにてスタックメモリを開放しています。
 *	* Fri Sep 12 22:07:53 JST 2014 Naoyuki Sawa
 *	- idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。
 */
#include "clip.h"

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

//{{2014/09/11変更:idle_init()に、スタックサイズを指定する引数を追加しました。
///* * Thu Jun 18 23:22:52 JST 2009 Naoyuki Sawa
// * - 簡単のために、アイドルスレッドのスタックサイズは固定とすることにしました。
// *   idle_init()でスタックサイズを指定することも考えましたが、アイドルスレッドはひとつしか走らないので、
// *   多少、スタックサイズに無駄があっても大きな影響は無いと考え、充分な大きさである8KBに固定としました。
// * - アプリケーションに本モジュールをリンクすると、アイドルスレッドを起動しているかどうかにかかわらず、
// *   固定でメモリを消費してしまうので、必要の無いアプリケーションにはリンクしないよう注意してください。
// */
//#define IDLE_STACK_SIZE 8192 /* 調整可 */
//↓2014/09/11変更:idle_init()に、スタックサイズを指定する引数を追加しました。
//↓・8KBでは多過ぎたので、既定のスタックサイズを4KBと定義することにしました。
//↓  4KBで過不足があれば、idle_init()の引数で適切なサイズを指定してください。
#define DEFAULT_STACK_SIZE	0x1000	/* 既定のスタックサイズ (調整可) */
//}}2014/09/11変更:idle_init()に、スタックサイズを指定する引数を追加しました。

//{{2014/09/11変更:idle_init()に、スタックサイズを指定する引数を追加しました。
//static int idle_stack[IDLE_STACK_SIZE / sizeof(int)];	/* アイドルスレッドのスタック */
//{{2014/09/11変更:idle_init()に、スタックサイズを指定する引数を追加しました。
static int* idle_stack/*[stack_size / sizeof(int)]*/;	/* アイドルスレッドのスタック */
//}}2014/09/11変更:idle_init()に、スタックサイズを指定する引数を追加しました。
static int idle_exit_code;				/* アイドルスレッドの終了コード */
static int idle_count;					/* アイドルスレッドの残りタイムスライス */
static int* idle_sp;					/* アイドルスレッドのスタックポインタ */
static int* main_sp __attribute__((unused));		/* メインスレッドのスタックポインタ */

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

/* * Thu Jun 18 20:15:58 JST 2009 Naoyuki Sawa
 * - idle_context_switcher()の中で、多重割り込み中はプリエンプトさせない理由について、以下に説明します。
 * - P/ECEカーネルの中で、多重割り込みを使っているのは、usr/PIECE/sysdev/pcekn/snd.c:sound_isr3()です。
 *   多重割り込み中の不安定な状態のまま、別のスレッドに長時間コンテキストスイッチすることは危険です。
 *   pceTimerSetContextSwitcher()には、PCE_TSCSF_INCRITICALフラグを用いて、これを防ぐ仕組みが有ります。
 * - CLiPライブラリの中で、多重割り込みを使っているのは、clipldir.c:_ldirect_isr()です。(2006/06/18現在)
 *   (ちなみに多重割り込みを使っている理由は、_ldirect_isr()から呼び出すldirect_trans()が時間がかかり、
 *    割り込み禁止のままだとサウンド割り込みが長時間ブロックされて、ノイズが発生するのを防ぐためです。)
 * - _ldirect_isr()が多重割り込み中であることを示すために、PCE_TSCSF_INCRITICALフラグに相当するような
 *   グローバルフラグを用いて、idle_context_switcher()に知らせることもできますが、もしそうすると、
 *   clipldir.cモジュールと本モジュールの間に依存性が生じてしまい、あまり良い設計とは言えません。
 *   そこで、clipldir.cモジュールには手を加えず、idle_context_switcher()自身で判断する方法を考えました。
 * - 通常の割り込み許可状態は、%psr[IL]=0、かつ、%psr[IE]=1です。
 *   割り込みが発生したとき、%psr[IL]=発生した割り込みレベル、かつ、%psr[IE]=0となります。
 *   その割り込みの中で、多重割り込みを受け付ける場合は、%psr[IL]=受け付ける割り込みレベル-1、かつ、
 *   %psr[IE]=1とします。このとき、%psr[IL]=0とすることはありえません。
 *   なぜなら、そうすると割り込み許可状態と同じになってしまい、多重割り込みの意味が無くなるからです。
 * - 以上より、idle_context_switcher()が呼ばれたときに、多重割り込みかどうかを判断するには、
 *   スタックに保存されている%psrの、ILが0か否かを判断し、0ならば通常の割り込み許可状態からの割り込み、
 *   0以外ならば、多重割り込み受け付け状態からの多重割り込みと判断できます。
 * - なお、%psr[IL]を見て多重割り込みかどうかを判断する方法で、サウンドの多重割り込みも判断できます。
 *   PCE_TSCSF_INCRITICALを検査しなくても、%psr[IL]を見る方法だけでも、結果的には同じになります。
 *   しかし、PCE_TSCSF_INCRITICALを検査することは、pceTimerSetContextSwitcher()の仕様ですので、
 *   形式上、明示的にPCE_TSCSF_INCRITICALを検査して判断するコードも残しておくことにしました。
 */
/*static*/ unsigned long idle_context_switcher(unsigned long nowsp, int flag);
asm("
		.code
		.align	1
idle_context_switcher:
		;// %r12     := nowsp
		;// %r13     := flag
		;// [%sp+ 0] := retp ─ usr/PIECE/sysdev/pcekn/timer.c:timer_isr()への戻りポインタ
		;// [%sp+ 4] := %alr ┬ pceTimerSetContextSwitcher()「スタックフレームの構成」仕様
		;// [%sp+ 8] := %ahr │
		;// [%sp+12] := %r0  │
		;// [%sp+16] := %r1  │
		;// [%sp+20] := %r2  │
		;// [%sp+24] := %r3  │
		;// [%sp+28] := %r4  │
		;// [%sp+32] := %r5  │
		;// [%sp+36] := %r6  │
		;// [%sp+40] := %r7  │
		;// [%sp+44] := %r8  │
		;// [%sp+48] := %r9  │
		;// [%sp+52] := %r10 │
		;// [%sp+56] := %r11 │
		;// [%sp+60] := %r12 │
		;// [%sp+64] := %r13 │
		;// [%sp+68] := %r14 │
		;// [%sp+72] := %r15 │
		;// [%sp+76] := %psr │
		;// [%sp+80] := %pc  ┴
		xld.w		%r4, [idle_count]		;// idle_count--
		sub		%r4, 1
		xld.w		[idle_count], %r4
		jrgt		idle_context_switcher_RET	;// if(idle_count <= 0) {
		and		%r13, 1				;//   if(!(flag & PCE_TSCSF_INCRITICAL)) {
		jrne		idle_context_switcher_RET
		xld.w		%r4, [%sp+76]			;//     %r4  := %psr
		xand		%r4, %r4, 0x00000F00		;//     if(%psr[IL] == 0) { /* 多重割り込み中でないこと */
		jrne		idle_context_switcher_RET
		xld.w		[idle_sp], %r12			;//       idle_sp := nowsp
		xld.w		%r12, [main_sp]			;//       nowsp   := main_sp
idle_context_switcher_RET:					;// } } }
		ret.d						;// return  nowsp
		ld.w		%r10, %r12			;// %r10 := nowsp	*delay*
");

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

//{{2009/10/12:idle_exit()追加に伴い、idle_stub()を削除
///*static*/ void idle_stub() __attribute__((noreturn));
//asm("
//		.code
//		.align		1
//idle_stub:
//		;// アイドル処理の関数を呼び出します。
//		popn		%r1			;// %r0  := fn,  %r1  :=    param
//		call.d		%r0			;// %r10 := exit_code := fn(param)
//		ld.w		%r12, %r1		;// %r12 :=                 param	*delay*
//		;// idle_yield()から呼ばれた場合、これ以降プリエンプトが発生しないよう、割り込み禁止。
//		ld.w		%psr, %r8		;// %psr[IE] := 0
//		;// アイドルスレッドの終了コードを格納し、アイドルスレッドのコンテキストを破棄します。
//		xld.w		[idle_exit_code], %r10	;// idle_exit_code := exit_code
//		xld.w		[idle_sp], %r8		;// idle_sp        := NULL
//		;// メインスレッドのコンテキストを復元します。
//		xld.w		%r0, [main_sp]		;// %r0 := main_sp
//		ld.w		%sp, %r0		;// %sp := main_sp
//		;// [%sp+ 0] := %alr ┬ pceTimerSetContextSwitcher()「スタックフレームの構成」仕様
//		;// [%sp+ 4] := %ahr │
//		;// [%sp+ 8] := %r0  │
//		;// [%sp+12] := %r1  │
//		;// [%sp+16] := %r2  │
//		;// [%sp+20] := %r3  │
//		;// [%sp+24] := %r4  │
//		;// [%sp+28] := %r5  │
//		;// [%sp+32] := %r6  │
//		;// [%sp+36] := %r7  │
//		;// [%sp+40] := %r8  │
//		;// [%sp+44] := %r9  │
//		;// [%sp+48] := %r10 │
//		;// [%sp+52] := %r11 │
//		;// [%sp+56] := %r12 │
//		;// [%sp+60] := %r13 │
//		;// [%sp+64] := %r14 │
//		;// [%sp+68] := %r15 │
//		;// [%sp+72] := %psr │
//		;// [%sp+76] := %pc  ┴
//		popn		%r1
//		ld.w		%ahr, %r1
//		ld.w		%alr, %r0
//		popn		%r15
//		reti					;// idle_yield()から呼ばれた場合は、割り込み禁止のまま帰る。
//							;// idle_wait() から呼ばれた場合は、元の割り込み状態に戻る。
//");
//}}2009/10/12:idle_exit()追加に伴い、idle_stub()を削除
//→ * Mon Oct 12 05:55:33 JST 2009 Naoyuki Sawa
//→ - アイドル関数の終了処理をidle_exit()に分けたため、idle_stub()は不要になりました。

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

/*static*/ void idle_exec();
asm("
		.code
		.align	1
idle_exec:
		;// メインスレッドのコンテキストを退避します。
		xsub		%sp, %sp, 4
		pushn		%r15
		ld.w		%r0, %alr
		ld.w		%r1, %ahr
		pushn		%r1
		ld.w		%r0, %psr
		xld.w		[%sp+72], %r0
		;// [%sp+ 0] := %alr ┬ pceTimerSetContextSwitcher()「スタックフレームの構成」仕様
		;// [%sp+ 4] := %ahr │
		;// [%sp+ 8] := %r0  │
		;// [%sp+12] := %r1  │
		;// [%sp+16] := %r2  │
		;// [%sp+20] := %r3  │
		;// [%sp+24] := %r4  │
		;// [%sp+28] := %r5  │
		;// [%sp+32] := %r6  │
		;// [%sp+36] := %r7  │
		;// [%sp+40] := %r8  │
		;// [%sp+44] := %r9  │
		;// [%sp+48] := %r10 │
		;// [%sp+52] := %r11 │
		;// [%sp+56] := %r12 │
		;// [%sp+60] := %r13 │
		;// [%sp+64] := %r14 │
		;// [%sp+68] := %r15 │
		;// [%sp+72] := %psr │
		;// [%sp+76] := %pc  ┴
		ld.w		%r0, %sp
		xld.w		[main_sp], %r0
		;// アイドルスレッドのコンテキストを復元します。
		xld.w		%r0, [idle_sp]
		ld.w		%sp, %r0
		;// [%sp+ 0] := %alr ┬ pceTimerSetContextSwitcher()「スタックフレームの構成」仕様
		;// [%sp+ 4] := %ahr │
		;// [%sp+ 8] := %r0  │
		;// [%sp+12] := %r1  │
		;// [%sp+16] := %r2  │
		;// [%sp+20] := %r3  │
		;// [%sp+24] := %r4  │
		;// [%sp+28] := %r5  │
		;// [%sp+32] := %r6  │
		;// [%sp+36] := %r7  │
		;// [%sp+40] := %r8  │
		;// [%sp+44] := %r9  │
		;// [%sp+48] := %r10 │
		;// [%sp+52] := %r11 │
		;// [%sp+56] := %r12 │
		;// [%sp+60] := %r13 │
		;// [%sp+64] := %r14 │
		;// [%sp+68] := %r15 │
		;// [%sp+72] := %psr │
		;// [%sp+76] := %pc  ┴
		popn		%r1
		ld.w		%ahr, %r1
		ld.w		%alr, %r0
		popn		%r15
		reti				;// これで割り込み許可になる。
");

/*****************************************************************************
 *	グローバル関数
 *****************************************************************************/

/*static*/ void _idle_ret(int exit_code) __attribute__((noreturn));
           void idle_exit(int exit_code) __attribute__((noreturn));
asm("
		.code
		.align		1
		.global		idle_exit
_idle_ret:
		ld.w		%r12, %r10		;// %r12 := exit_code
idle_exit:
		;// idle_yield()から呼ばれた場合、これ以降プリエンプトが発生しないよう、割り込み禁止。
		ld.w		%psr, %r8		;// %psr[IE] := 0
;//{{2014/09/12変更:idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。
;//		;// アイドルスレッドの終了コードを格納し、アイドルスレッドのコンテキストを破棄します。
;//		xld.w		[idle_exit_code], %r12	;// idle_exit_code := exit_code
;//		xld.w		[idle_sp], %r8		;// idle_sp        := NULL
;//		;// メインスレッドのコンテキストを復元します。
;//		xld.w		%r0, [main_sp]		;// %r0 := main_sp
;//		ld.w		%sp, %r0		;// %sp := main_sp
;//;//{{2014/09/11追加:idle_init()に、スタックサイズを指定する引数を追加しました。
;//		;// アイドルスレッドのスタックメモリを開放します。			┓「idle_sp := NULL」のすぐ下で、アイドルスレッドのスタックメモリを開放してはいけません。
;//		xld.w		%r12, [idle_stack]	;// free(idle_stack)		┃「%sp := main_sp」を行った*後*で、アイドルスレッドのスタックメモリを開放することに注意してください。
;//		xld.w		[idle_stack], %r8	;//      idle_stack := NULL	┃前者のタイミングでは、%spがまだアイドルスレッドのスタックメモリ内を指しており、free()の戻りアドレスが格納されているスタックメモリを開放することになるからです。
;//		xcall		free			;//				┛実際には、割り込み禁止中なので問題になる事は無いと思いますが、間違いであることにかわりはありません。
;//;//}}2014/09/11追加:idle_init()に、スタックサイズを指定する引数を追加しました。
;//↓2014/09/12変更:idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。
		;// アイドルスレッドの終了コードを格納します。
		xld.w		[idle_exit_code], %r12	;// idle_exit_code := exit_code
		;// メインスレッドのコンテキストを復元します。
		xld.w		%r0, [main_sp]		;// %r0 := main_sp
		ld.w		%sp, %r0		;// %sp := main_sp
		;// アイドルスレッドのスタックメモリを開放します。
		;// ┃メインスレッドのコンテキストを復元する前に、アイドルスレッドのスタックメモリを開放してはいけません。
		;// ┃メインスレッドのコンテキストを復元した後で、アイドルスレッドのスタックメモリを開放することに注意してください。
		;// ┃もし、前者のタイミングで解放しようとすると、%spがまだアイドルスレッドのスタックメモリ内を指しており、idle_free()の戻りアドレスが格納されているスタックメモリを開放することになってしまうからです。
		;// ┃実際には、割り込み禁止中なので問題になる事は無いと思いますが、間違いであることにかわりはありません。
		xcall		idle_free		;// idle_free()			
;//}}2014/09/12変更:idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。
		;// [%sp+ 0] := %alr ┬ pceTimerSetContextSwitcher()「スタックフレームの構成」仕様
		;// [%sp+ 4] := %ahr │
		;// [%sp+ 8] := %r0  │
		;// [%sp+12] := %r1  │
		;// [%sp+16] := %r2  │
		;// [%sp+20] := %r3  │
		;// [%sp+24] := %r4  │
		;// [%sp+28] := %r5  │
		;// [%sp+32] := %r6  │
		;// [%sp+36] := %r7  │
		;// [%sp+40] := %r8  │
		;// [%sp+44] := %r9  │
		;// [%sp+48] := %r10 │
		;// [%sp+52] := %r11 │
		;// [%sp+56] := %r12 │
		;// [%sp+60] := %r13 │
		;// [%sp+64] := %r14 │
		;// [%sp+68] := %r15 │
		;// [%sp+72] := %psr │
		;// [%sp+76] := %pc  ┴
		popn		%r1
		ld.w		%ahr, %r1
		ld.w		%alr, %r0
		popn		%r15
		reti					;// idle_yield()から呼ばれた場合は、割り込み禁止のまま帰る。
							;// idle_wait() から呼ばれた場合は、元の割り込み状態に戻る。
");

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

//{{2009/10/12:idle_exit()追加とidle_stub()廃止に伴い変更
//void
//idle_init(int (*fn)(void* param), void* param)
//{
//	if(fn) {
//		/* アイドルスレッドのコンテキストを作成。 */
//		idle_sp = &idle_stack[IDLE_STACK_SIZE / sizeof(int) - 22];
//	//	idle_sp[ 0] = /*don't care*/;	// %alr := ?         ┬ pceTimerSetContextSwitcher()「スタックフレームの構成」仕様
//	//	idle_sp[ 1] = /*don't care*/;	// %ahr := ?         │
//	//	idle_sp[ 2] = /*don't care*/;	// %r0  := ?         │
//	//	idle_sp[ 3] = /*don't care*/;	// %r1  := ?         │
//	//	idle_sp[ 4] = /*don't care*/;	// %r2  := ?         │
//	//	idle_sp[ 5] = /*don't care*/;	// %r3  := ?         │
//	//	idle_sp[ 6] = /*don't care*/;	// %r4  := ?         │
//	//	idle_sp[ 7] = /*don't care*/;	// %r5  := ?         │
//	//	idle_sp[ 8] = /*don't care*/;	// %r6  := ?         │
//	//	idle_sp[ 9] = /*don't care*/;	// %r7  := ?         │
//		idle_sp[10] = 0;		// %r8  := bp        │
//	//	idle_sp[11] = /*don't care*/;	// %r9  := ?         │
//	//	idle_sp[12] = /*don't care*/;	// %r10 := ?         │
//	//	idle_sp[13] = /*don't care*/;	// %r11 := ?         │
//	//	idle_sp[14] = /*don't care*/;	// %r12 := param     │
//	//	idle_sp[15] = /*don't care*/;	// %r13 := ?         │
//	//	idle_sp[16] = /*don't care*/;	// %r14 := ?         │
//	//	idle_sp[17] = /*don't care*/;	// %r15 := ?         │
//		idle_sp[18] = 0x00000010;	// %psr := IL=0,IE=1 │
//		idle_sp[19] = (int)idle_stub;	// %pc  := idle_stub ┴
//		idle_sp[20] = (int)fn;		// idle_stub()で使用
//		idle_sp[21] = param;		// idle_stub()で使用
//	} else {
//		/* アイドルスレッドのコンテキストを破棄。 */
//		idle_sp = NULL;
//	}
//}
//}}2009/10/12:idle_exit()追加とidle_stub()削除に伴い変更
//↓ * Mon Oct 12 05:55:33 JST 2009 Naoyuki Sawa
//↓ - アイドル関数の起動のためにidle_stub()を経由する必要が無くなり、初期スタックの構成が変りました。
//{{2014/09/11変更:idle_init()に、スタックサイズを指定する引数を追加しました。
//void
//idle_init(int (*fn)(void* param), void* param)
//↓2014/09/11変更:idle_init()に、スタックサイズを指定する引数を追加しました。
void
idle_init(int (*fn)(void* param), void* param, int stack_size)
//}}2014/09/11変更:idle_init()に、スタックサイズを指定する引数を追加しました。
{
//{{2014/09/12変更:idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。
////{{2014/09/11追加:idle_init()に、スタックサイズを指定する引数を追加しました。
//	/* 古いアイドルスレッドが終了する前に本関数を呼び出した場合のために、古いアイドルスレッドのコンテキストを確実に破棄します。 */
//	idle_sp = NULL;		//古いアイドルスレッドのコンテキストを破棄
//	free(idle_stack);	//古いアイドルスレッドのスタックメモリを破棄。(古いアイドルスレッドが無かった場合も、free(NULL)は安全)
//	     idle_stack = NULL;
////}}2014/09/11追加:idle_init()に、スタックサイズを指定する引数を追加しました。
//↓2014/09/12変更:idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。
	/* 古いアイドルスレッドが終了する前に本関数を呼び出した場合のために、古いアイドルスレッドのコンテキストを確実に破棄します。 */
	idle_free();
//}}2014/09/12変更:idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。
//{{2014/09/12削除:idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。
//	if(fn) {
//}}2014/09/12削除:idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。
//{{2014/09/11変更:idle_init()に、スタックサイズを指定する引数を追加しました。
//		/* アイドルスレッドのコンテキストを作成。 */
//		idle_sp = &idle_stack[IDLE_STACK_SIZE / sizeof(int) - 21];
//↓2014/09/11変更:idle_init()に、スタックサイズを指定する引数を追加しました。
		/* アイドルスレッドのコンテキストを作成。 */
		if(!stack_size) { stack_size = DEFAULT_STACK_SIZE; }
		stack_size = (stack_size + 3) & ~3;	//スタックサイズをsizeof(int)の倍数に切り上げる
		idle_stack = malloc(stack_size);	//新しいアイドルスレッドのスタックメモリを確保
		if(!idle_stack) { DIE(); }
		idle_sp = &idle_stack[stack_size / sizeof(int) - 21];
//}}2014/09/11変更:idle_init()に、スタックサイズを指定する引数を追加しました。
	//	idle_sp[ 0] = /*don't care*/;	// %alr  := ?         ┬ pceTimerSetContextSwitcher()「スタックフレームの構成」仕様
	//	idle_sp[ 1] = /*don't care*/;	// %ahr  := ?         │
	//	idle_sp[ 2] = /*don't care*/;	// %r0   := ?         │
	//	idle_sp[ 3] = /*don't care*/;	// %r1   := ?         │
	//	idle_sp[ 4] = /*don't care*/;	// %r2   := ?         │
	//	idle_sp[ 5] = /*don't care*/;	// %r3   := ?         │
	//	idle_sp[ 6] = /*don't care*/;	// %r4   := ?         │
	//	idle_sp[ 7] = /*don't care*/;	// %r5   := ?         │
	//	idle_sp[ 8] = /*don't care*/;	// %r6   := ?         │
	//	idle_sp[ 9] = /*don't care*/;	// %r7   := ?         │
		idle_sp[10] = 0;		// %r8   := bp        │
	//	idle_sp[11] = /*don't care*/;	// %r9   := ?         │
	//	idle_sp[12] = /*don't care*/;	// %r10  := ?         │
	//	idle_sp[13] = /*don't care*/;	// %r11  := ?         │
		idle_sp[14] = (int)param;	// %r12  := param     │
	//	idle_sp[15] = /*don't care*/;	// %r13  := ?         │
	//	idle_sp[16] = /*don't care*/;	// %r14  := ?         │
	//	idle_sp[17] = /*don't care*/;	// %r15  := ?         │
		idle_sp[18] = 0x00000010;	// %psr  := IL=0,IE=1 │
		idle_sp[19] = (int)fn;		// %pc   := fn        ┴
		idle_sp[20] = (int)_idle_ret;	// %retp := _idle_ret
//{{2014/09/11削除:idle_init()に、スタックサイズを指定する引数を追加しました。
//	} else {
//		/* アイドルスレッドのコンテキストを破棄。 */
//		idle_sp = NULL;
//}}2014/09/11削除:idle_init()に、スタックサイズを指定する引数を追加しました。
//{{2014/09/12削除:idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。
//	}
//}}2014/09/12削除:idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。
}

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

//{{2014/09/12追加:idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。
void
idle_free()
{
	     idle_sp    = NULL;	//古いアイドルスレッドのコンテキストを破棄する。既にidle_sp=NULLだった場合は、この処理はダミーとなる。
	free(idle_stack);	//古いアイドルスレッドのスタックメモリを破棄。古いアイドルスレッドが無かった場合も、free(NULL)は安全。
	     idle_stack = NULL;	//既にidle_stack=NULLだった場合は、この処理はダミーとなる。
}
//}}2014/09/12追加:idle_init()の引数fnにNULLを指定してアイドル処理を開放することはできなくなりました。今後は代りにidle_free()を使用してください。

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

//{{2009/06/21:残りタイムスライスの算出にapptm_wkを参照するよう変更
//void
//idle_yield()
//{
//	static int t0;								/* ユーザー処理の開始時刻 */
//	       int t1 = pceTimerGetCount();					/* ユーザー処理の終了時刻を取得 */
//	       int dt = t1 - t0;						/* ユーザー処理の経過時間 */
//	   idle_count = pceAppSetProcPeriod(INVALIDVAL) - dt;			/* 残りタイムスライス */
//	if(idle_count > 0) {							/* タイムスライスが残っていたら */
//		if(idle_sp) {							/* アイドルスレッドが未完了なら */
//ENTER_CS;
//			pceTimerSetContextSwitcher(idle_context_switcher);	/* プリエンプトを設定 */
//			idle_exec();						/* アイドルスレッドに切り替える */
//			pceTimerSetContextSwitcher(NULL);			/* プリエンプトを解除 */
//LEAVE_CS;
//		}
//	}
//	yield();								/* システム処理を実行 */
//	t0 = pceTimerGetCount();						/* ユーザー処理の開始時刻を格納 */
//}
//}}2009/06/21:残りタイムスライスの算出にapptm_wkを参照するよう変更
//↓ * Sun Jun 21 21:42:18 JST 2009 Naoyuki Sawa
//↓ - clippce.cにapptm_wk変数を追加したことに伴い、残りタイムスライスの算出にapptm_wkを参照するよう変更しました。
void
idle_yield()
{
	int dt = pceTimerGetCount() - apptm_wk;					/* ユーザー処理の経過時間 */
	idle_count = pceAppSetProcPeriod(INVALIDVAL) - dt;			/* 残りタイムスライス */
	if(idle_count > 0) {							/* タイムスライスが残っていたら */
		if(idle_sp) {							/* アイドルスレッドが未完了なら */
ENTER_CS;
			pceTimerSetContextSwitcher(idle_context_switcher);	/* プリエンプトを設定 */
			idle_exec();						/* アイドルスレッドに切り替える */
			pceTimerSetContextSwitcher(NULL);			/* プリエンプトを解除 */
LEAVE_CS;
		}
	}
	yield();								/* システム処理を実行 */
}

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

int
idle_wait()
{
	if(idle_sp) {		/* アイドルスレッドが未完了なら */
		idle_exec();	/* プリエンプトを設定せずに、アイドルスレッドに切り替える */
	}
	return idle_exit_code;	/* アイドルスレッドの終了コード */
}

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

int
idle_stat()
{
	return (int)idle_sp;
}

