/*	
 *	clipidle.c
 *
 *	アイドルスレッド
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2018 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()を使用してください。
 *	* Fri Jul 07 22:30:28 JST 2017 Naoyuki Sawa
 *	- idle_yield()を廃止しました。
 *	  idle_init()を呼び出すと、自動的にpceAppProcがフックされて、通常のyield()が、これまでのidle_yield()の動作を行うようになります。
 *	  今後は、アイドルスレッドの利用の有無に関係無く、通常のyield()を使用して大丈夫です。
 *	- idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。
 *	  追加した理由は、以下の通りです。
 *	  ゲームの思考ルーチン等をアイドル処理として実行した場合、アイドル処理が完了しないとゲームが進行しません。
 *	  しかし、描画処理も重い場合など、前面の処理が1フレームの時間を使い果たしてしまい、アイドル処理のためのタイムスライスが残らない恐れがあります。
 *	  そのような場合でも、アイドル処理がある程度は実行されるように、これまでは、アプリケーション側にて、以下のような処理を行っていました。
 *	  □変更前
 *	  │void schedule() {
 *	  │  〜略〜
 *	  │  apptm_wk = pceTimerGetCount();  //描画ルーチンの負荷が増えても、思考ルーチンに100%相当のタイムスライスを与えることにする。その分フレームレートが下がるが、思考完了待ちの間なので、さほど気にならないだろう。
 *	  │  idle_yield();
 *	  │  〜略〜
 *	  │}
 *	  しかし、上記のような処理は判りづらく、バグの原因になる可能性が高いと思いました。
 *	  そこで、clipidle.cモジュールの機能として、アイドル処理の最小実行時間を指定する事が出来るようにしました。
 *	  今後は、前述の処理は、以下のように指定する事が出来るようになりました。
 *	  □変更後
 *	  │void game_run() {
 *	  │  〜略〜
 *	  │  idle_init(idle_proc, NULL, 0, pceAppSetProcPeriod(INVALIDVAL));  //描画ルーチンの負荷が増えても、思考ルーチンに100%相当のタイムスライスを与えることにする。その分フレームレートが下がるが、思考完了待ちの間なので、さほど気にならないだろう。
 *	  │  〜略〜
 *	  │}
 *	  詳細は、clipidle.h内の、idle_init()関数のコメントの、引数minimum_timesliceの説明を参照して下さい。
 *	- idle_init()にアイドル処理の最小実行時間を指定する引数を追加した事に伴い、clippce.c内で定義していたapptm_wkは不要になったので削除しました。
 *	  残しておいても構わなかったのですが、今後、他の用途で使用する事も無いと思うし、残しておくと却って混乱を招きそうなので削除する事にしました。
 *	* Mon Jul 23 23:59:59 JST 2018 Naoyuki Sawa
 *	- 割り込みスタックとアイドルスレッドを併用するための処理を追加しました。
 *	  詳細はclippce.hの同日,及び,'Sun Jul 22 23:59:59 JST 2018 Naoyuki Sawa'のコメントを参照して下さい。
 *	* Thu Jul 26 23:59:59 JST 2018 Naoyuki Sawa
 *	- 割り込みスタックとアイドルスレッドを併用するための処理を修正しました。
 *	  前回(2018/07/23)に追加した処理では、不十分だった事がわかったためです。
 *	  詳細は、以下の通りです。
 *	- 前回に追加した処理で、一見、割り込みスタックとアイドルスレッドを併用出来るようになったように見えたのですが、よく調べてみると問題が残っていました。
 *	  タイマ処理(timer_start())とアイドルスレッドを併用すると、ユーザーコンテキスト中の割り込み処理は確かに割り込みスタックが使用されるのですが、システムコンテキスト中の割り込み処理は割り込みスタックが使用されずシステムスタックが使用されるのです。
 *	  具体的には、タイマ処理ルーチンがスタックを1KB消費したとすると、ユーザースタックには影響無いのですが、システムスタックが1KB多く消費されていました。
 *	  原因を調査した所、\usr\PIECE\sysdev\pcekn\timer.cのtimer_isr()関数内の、処理順序が原因である事が判りました。
 *
 *		□\usr\PIECE\sysdev\pcekn\timer.c
 *		│
 *		│void timer_isr(void)
 *		│{
 *		│    INT_BEGIN2;							┬
 *		│									│
 *		│    *(unsigned char *)0x40282 &= ~4;        // 割り込みクリア		│
 *		│									│割り込みスタック上で実行される区間
 *		│    if ( contextsw ) {						│
 *		│        asm( "xld.ub %r13,[snd_flg]" );     // クリティカル・フラグ	│
 *		│        asm( "ld.w %r12,%sp" );             // 現在のSP		│
 *		│        contextsw();							┴←この中でidle_context_switcherが呼び出され、システムスタックを返す。
 *		│        asm( "ld.w %sp,%r10" );             // 新しいSP		←コンテキストスイッチを行う場合、ここでシスタムスタックに切り替わる。
 *		│    }									┬
 *		│									│←システムスタック上で実行される区間
 *		│    timer_sub();							│←timer_start()で登録したタイマ処理ルーチンはこの中から呼び出されるので、システムスタック上で割り込みルーチンが実行されてしまう!!!
 *		│									│
 *		│    INT_END2;								┴
 *		│}
 *
 *	  timer_isr()関数内の処理順序が、「if ( contextsw ) {〜}」と「timer_sub();」の順序が逆だったならば問題無かったのですが、フラッシュROM上のP/ECEカーネルを変更する事は出来ないので、別の方法で解決する事にしました。
 *	  具体的には、以下の通りです。
 *	- timer_sub()を割り込みスタックのままで実行するために、idle_context_switcherはシステムスタックを返さずに、割り込みスタックのままで返すようにしました。
 *	  ただし、timer_isr()の終了時にシステムスタックへ切り替える必要が有るので、割り込みスタック上にもう一つスタックフレームを積み上げ、timer_isr()の終了時にシステムスタックへの切り替え処理(idle_context_switcher_ISP_bottom_half)が呼び出されるようにしました。
 *	  変更後の動作は、以下のようになります。
 *
 *		□\usr\PIECE\sysdev\pcekn\timer.c
 *		│
 *		│void timer_isr(void)
 *		│{
 *		│    INT_BEGIN2;							┬
 *		│									│
 *		│    *(unsigned char *)0x40282 &= ~4;        // 割り込みクリア		│
 *		│									│
 *		│    if ( contextsw ) {						│割り込みスタックで実行される区間
 *		│        asm( "xld.ub %r13,[snd_flg]" );     // クリティカル・フラグ	│
 *		│        asm( "ld.w %r12,%sp" );             // 現在のSP		│
 *		│        contextsw();							│←割り込みスタック上にもう一つスタックフレームを積み上げ、そのスタックフレームを返す。
 *		│        asm( "ld.w %sp,%r10" );             // 新しいSP		│←割り込みスタック上に積み上げた、もう一つのスタックフレームに切り替わる。
 *		│    }									│
 *		│									│
 *		│    timer_sub();							│←割り込みスタック上で実行される。
 *		│									│
 *		│    INT_END2;								┴←割り込みスタック上に積み上げた、もう一つのスタックフレームが消費される。
 *		│}									←idle_context_switcher_ISP_bottom_halfが呼び出され、システムスタックへの切り替え処理が行われる。
 *
 *	  上記の処理を行うのは、コンテキストスイッチが発生する場合のみであり、それ以外の場合はこれまでと同じです。
 *	  詳細は、idle_context_switcher_ISPとidle_context_switcher_ISP_bottom_halfの実装を参照して下さい。
 *	- 今回の修正によって、今度こそ、割り込みスタックとアイドルスレッドが併用出来るようになったと思います。
 *	  割り込みスタック有りの場合と、割り込みスタック無しの場合もテストして、問題無く動作する事を確認しました。
 *	  おそらく問題無いと思いますが、引き続き、実際のアプリケーションで動作検証を続けて行こうと思います。
 */
#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));		/* メインスレッドのスタックポインタ */
//{{2017/07/07追加:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。
static int idle_minimum_timeslice;			//アイドル処理の最小実行時間。(ミリ秒)
static void (*old_pceAppProc)(int cnt);			//フックする前のpceHeapAlloc。(NULL=まだフックしていない)
static void   new_pceAppProc( int cnt);			//ローカル関数宣言。(idle_init()にて前方参照しているので)
//}}2017/07/07追加:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。

/*--------------------------------------------------------------------------*/
//{{2018/07/23追加:割り込みスタックとアイドルスレッドを併用するための処理を追加しました。
static void* idle_save_sp;	//アイドルスレッド中断時のstack_info.save_spを保存する。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//{{2018/07/26変更:割り込みスタックとアイドルスレッドを併用するための処理を修正しました。
////アイドルスレッド⇒メインスレッド
//static void __attribute__((unused))/*asmブロックから参照*/ idle_context_switcher_ISP() {
//	//割り込みスタック有りならば…
//	if(stack_info.isp) {
//ENTER_CS;
//		//アイドルスレッド中断時のstack_info.save_spを保存する。
//		idle_save_sp = stack_info.save_sp;
//		//アイドルスレッドが消費した割り込みスタックよりも低位アドレスに、メインスレッド用の割り込みスタックを設定する。
//		// - もしidle_context_switcherが処理を返す前に多重割り込みが発生して、アイドルスレッドのままメインスレッド用の割り込みスタックが使用されても問題無い。
//		stack_info.isp = get_sp();
//		//メインスレッドでの割り込みスタック切り替えを許可する。
//		stack_info.save_sp = NULL;
//LEAVE_CS;
//	}
//}
//↓2018/07/26変更:割り込みスタックとアイドルスレッドを併用するための処理を修正しました。
//アイドルスレッド⇒メインスレッド
/*static*/ void* idle_context_switcher_ISP(void* main_sp);	//戻り値は%r10ではなく%r12で返す。
asm("
idle_context_switcher_ISP:
		;//%r12      := newsp = main_sp
		;//[%sp+  0] := retp ─ idle_context_switcherへの戻りポインタ
		;//[%sp+  4] := retp ─ usr/PIECE/sysdev/pcekn/timer.c:timer_isr()への戻りポインタ
		;//[%sp+  8] := %alr ┬ pceTimerSetContextSwitcher()「スタックフレームの構成」仕様
		;//[%sp+ 12] := %ahr │
		;//[%sp+ 16] := %r0  │
		;//[%sp+ 20] := %r1  │
		;//[%sp+ 24] := %r2  │
		;//[%sp+ 28] := %r3  │
		;//[%sp+ 32] := %r4  │
		;//[%sp+ 36] := %r5  │
		;//[%sp+ 40] := %r6  │
		;//[%sp+ 44] := %r7  │
		;//[%sp+ 48] := %r8  │
		;//[%sp+ 52] := %r9  │
		;//[%sp+ 56] := %r10 │
		;//[%sp+ 60] := %r11 │
		;//[%sp+ 64] := %r12 │
		;//[%sp+ 68] := %r13 │
		;//[%sp+ 72] := %r14 │
		;//[%sp+ 76] := %r15 │
		;//[%sp+ 80] := %psr │
		;//[%sp+ 84] := %pc  ┴
		xld.w		%r4, [stack_info+40]				;//%r4  := isp = stack_info.isp
		cmp		%r4, 0						;//if(!isp) { goto RET }
		jreq		idle_context_switcher_ISP_RET			;//
		xcall		EnterCS						;//%r10 := cs = EnterCS()
		xld.w		%r4, [%sp+ 0]					;//%r4  := idle_context_switcherへの戻りポインタ
		xld.w		%r5, [%sp+ 4]					;//%r5  := usr/PIECE/sysdev/pcekn/timer.c:timer_isr()への戻りポインタ
		xld.w		%r9, idle_context_switcher_ISP_bottom_half	;//%r9  := idle_context_switcher_ISP_bottom_half
		xsub		%sp, %sp, 80					;//%sp  -= 80
		xld.w		[%sp+ 0], %r4					;//[%sp+ 0] := idle_context_switcherへの戻りポインタ
		xld.w		[%sp+ 4], %r5					;//[%sp+ 4] := usr/PIECE/sysdev/pcekn/timer.c:timer_isr()への戻りポインタ
		xld.w		[%sp+48], %r8					;//[%sp+48] := 0
		xld.w		[%sp+80], %r8					;//[%sp+80] := 0
		xld.w		[%sp+84], %r9					;//[%sp+84] := idle_context_switcher_ISP_bottom_half
		xcall.d		LeaveCS						;//LeaveCS(cs)
		ld.w		%r12, %r10					;//%r12 := cs								*delay*
		xadd		%r12, %sp, 8					;//%r12 := %sp+8
		;//%r12      := newsp = %sp+8	────────────────────────────────────────────────┐
		;//[%sp+  0] := retp ─ idle_context_switcherへの戻りポインタ									│
		;//[%sp+  4] := retp ─ usr/PIECE/sysdev/pcekn/timer.c:timer_isr()への戻りポインタ						│
		;//[%sp+  8] := %alr ┬ pceTimerSetContextSwitcher()「スタックフレームの構成」仕様	timer_isr()のretiが消費する。	←───┘
		;//[%sp+ 12] := %ahr │ ＼
		;//[%sp+ 16] := %r0  │  │
		;//[%sp+ 20] := %r1  │  │
		;//[%sp+ 24] := %r2  │  │
		;//[%sp+ 28] := %r3  │  │
		;//[%sp+ 32] := %r4  │  │
		;//[%sp+ 36] := %r5  │  │
		;//[%sp+ 40] := %r6  │  │
		;//[%sp+ 44] := %r7  │  ├ %alr〜%r15のうち、%r8=0。それ以外は不定値。
		;//[%sp+ 48] := %r8  │  │
		;//[%sp+ 52] := %r9  │  │
		;//[%sp+ 56] := %r10 │  │
		;//[%sp+ 60] := %r11 │  │
		;//[%sp+ 64] := %r12 │  │
		;//[%sp+ 68] := %r13 │  │
		;//[%sp+ 72] := %r14 │  │
		;//[%sp+ 76] := %r15 │ ／ 
		;//[%sp+ 80] := %psr │ IE=0								idle_context_switcher_ISP_bottom_halfをIE=0で呼ぶため。
		;//[%sp+ 84] := %pc  ┴ idle_context_switcher_ISP_bottom_half
		;//[%sp+ 88] := %alr ┬ pceTimerSetContextSwitcher()「スタックフレームの構成」仕様	idle_context_switcher_ISP_bottom_halfのretiが消費する。
		;//[%sp+ 92] := %ahr │
		;//[%sp+ 96] := %r0  │
		;//[%sp+100] := %r1  │
		;//[%sp+104] := %r2  │
		;//[%sp+108] := %r3  │
		;//[%sp+112] := %r4  │
		;//[%sp+116] := %r5  │
		;//[%sp+120] := %r6  │
		;//[%sp+124] := %r7  │
		;//[%sp+128] := %r8  │
		;//[%sp+132] := %r9  │
		;//[%sp+136] := %r10 │
		;//[%sp+140] := %r11 │
		;//[%sp+144] := %r12 │
		;//[%sp+148] := %r13 │
		;//[%sp+152] := %r14 │
		;//[%sp+156] := %r15 │
		;//[%sp+160] := %psr │
		;//[%sp+164] := %pc  ┴
idle_context_switcher_ISP_RET:
		ret								;//%r12 := newsp
		;//-------------------------------------------------------------;//
idle_context_switcher_ISP_bottom_half:
		;//[%sp+  0] := retp ─ idle_context_switcherへの戻りポインタ
		;//[%sp+  4] := retp ─ usr/PIECE/sysdev/pcekn/timer.c:timer_isr()への戻りポインタ
		;//[%sp+  8] := %alr ┬ pceTimerSetContextSwitcher()「スタックフレームの構成」仕様
		;//[%sp+ 12] := %ahr │
		;//[%sp+ 16] := %r0  │
		;//[%sp+ 20] := %r1  │
		;//[%sp+ 24] := %r2  │
		;//[%sp+ 28] := %r3  │
		;//[%sp+ 32] := %r4  │
		;//[%sp+ 36] := %r5  │
		;//[%sp+ 40] := %r6  │
		;//[%sp+ 44] := %r7  │
		;//[%sp+ 48] := %r8  │
		;//[%sp+ 52] := %r9  │
		;//[%sp+ 56] := %r10 │
		;//[%sp+ 60] := %r11 │
		;//[%sp+ 64] := %r12 │
		;//[%sp+ 68] := %r13 │
		;//[%sp+ 72] := %r14 │
		;//[%sp+ 76] := %r15 │
		;//[%sp+ 80] := %psr │
		;//[%sp+ 84] := %pc  ┴
		;//アイドルスレッド中断時のstack_info.save_spを保存する。
		xld.w		%r0, [stack_info+44]				;//%r0                := save_sp = stack_info.save_sp
	;//	xld.w		[idle_save_sp], %r0				;//idle_save_sp       := save_sp	────────────┐
		;//アイドルスレッドが消費した割り込みスタックよりも低位アドレスに、メインスレッド用の割り込みスタックを設定する。		│
		ld.w		%r1, %sp					;//%r1                := sp = get_sp()				│
		xld.w		[idle_save_sp], %r0				;//idle_save_sp       := save_sp	←───────────┘	*anti-interlock*
		xld.w		[stack_info+40], %r1				;//stack_info.isp     := sp
		;//メインスレッドでの割り込みスタック切り替えを許可する。
	;//	xld.w		[stack_info+44], %r8				;//stack_info.save_sp := NULL		────────────┐
		;//メインスレッドのコンテキストを復元する。											│
		xld.w		%r0, [main_sp]					;//%r0                := main_sp				│
		xld.w		[stack_info+44], %r8				;//stack_info.save_sp := NULL		←───────────┘	*anti-interlock*
		ld.w		%sp, %r0					;//%sp                := main_sp
		popn		%r1
		ld.w		%ahr, %r1
		ld.w		%alr, %r0
		popn		%r15
		reti								;//これで割り込み許可になる。
");
//}}2018/07/26変更:割り込みスタックとアイドルスレッドを併用するための処理を修正しました。
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//メインスレッド⇒アイドルスレッド
static void __attribute__((unused))/*asmブロックから参照*/ idle_exec_ISP() {
	//割り込みスタック有りならば…
	if(stack_info.isp) {
ENTER_CS;
		//割り込みスタックのアドレスを初期設定に戻す。
		stack_info.isp = stack_info.intstack_end;
		//アイドルスレッド中断時のstack_info.save_spを復元する。(初回ならばNULL)
		stack_info.save_sp = idle_save_sp;
LEAVE_CS;
	}
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//アイドルスレッド終了
static void idle_free_ISP() {
	//割り込みスタック有りならば…
	if(stack_info.isp) {
ENTER_CS;
		//割り込みスタックのアドレスを初期設定に戻す。
		stack_info.isp = stack_info.intstack_end;
		//メインスレッドでの割り込みスタック切り替えを許可する。
		stack_info.save_sp = NULL;
LEAVE_CS;
	}
}
//}}2018/07/23追加:割り込みスタックとアイドルスレッドを併用するための処理を追加しました。
/*--------------------------------------------------------------------------*/

/* * 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
//{{2018/07/26変更:割り込みスタックとアイドルスレッドを併用するための処理を修正しました。
////{{2018/07/23追加:割り込みスタックとアイドルスレッドを併用するための処理を追加しました。
//		xcall		idle_context_switcher_ISP	;//割り込みスタックとアイドルスレッドを併用するための処理を呼び出す。
////}}2018/07/23追加:割り込みスタックとアイドルスレッドを併用するための処理を追加しました。
//		xld.w		%r12, [main_sp]			;//       %r12    := main_sp
//↓2018/07/26変更:割り込みスタックとアイドルスレッドを併用するための処理を修正しました。
		xld.w		%r12, [main_sp]			;//       %r12    := main_sp
		xcall		idle_context_switcher_ISP	;//       %r12    := newsp = idle_context_switcher_ISP(main_sp)
//}}2018/07/26変更:割り込みスタックとアイドルスレッドを併用するための処理を修正しました。
idle_context_switcher_RET:					;// } } }
		ret.d						;// return  newsp
		ld.w		%r10, %r12			;// %r10 := newsp	*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
//{{2018/07/23追加:割り込みスタックとアイドルスレッドを併用するための処理を追加しました。
		xcall		idle_exec_ISP			;//割り込みスタックとアイドルスレッドを併用するための処理を呼び出す。
//}}2018/07/23追加:割り込みスタックとアイドルスレッドを併用するための処理を追加しました。
		;// アイドルスレッドのコンテキストを復元します。
		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:
//{{2017/07/07コメント変更:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。
//		;// idle_yield()から呼ばれた場合、これ以降プリエンプトが発生しないよう、割り込み禁止。
//↓2017/07/07コメント変更:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。
		;// new_pceAppProc()から呼ばれた場合、これ以降プリエンプトが発生しないよう、割り込み禁止。
//}}2017/07/07コメント変更:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。
		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
//{{2017/07/07コメント変更:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。
//		reti					;// idle_yield()から呼ばれた場合は、割り込み禁止のまま帰る。
//↓2017/07/07コメント変更:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。
		reti					;// new_pceAppProc()から呼ばれた場合は、割り込み禁止のまま帰る。
//}}2017/07/07コメント変更:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。
							;// 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)
//↓2017/07/07変更:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。
void
idle_init(int (*fn)(void* param), void* param, int stack_size, int minimum_timeslice)
//}}2017/07/07変更:idle_yield()を廃止しました。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()を使用してください。
//{{2017/07/07追加:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。
		//まだpceAppProcをフックしていなければ、pceAppProcをフックする。
		// - このフックは、idle_free()やアプリケーション終了時にも、解除する必要はありません。
		//   RAM上にロードされたアプリケーションヘッダを書き換えているだけであり、アプリケーション終了時に破棄されるからです。
		if(!old_pceAppProc) {
			extern const int __START_DEFAULT_CODE[];
			/*const*/ pceAPPHEAD* apphead = (/*const*/ pceAPPHEAD*)__START_DEFAULT_CODE;
			old_pceAppProc = apphead->periodic_proc;
			                 apphead->periodic_proc = new_pceAppProc;
		}
		//アイドル処理の最小実行時間を格納する。
		idle_minimum_timeslice = minimum_timeslice;
//}}2017/07/07追加:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。
//{{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だった場合は、この処理はダミーとなる。
//{{2018/07/23追加:割り込みスタックとアイドルスレッドを併用するための処理を追加しました。
	idle_free_ISP();	//割り込みスタックとアイドルスレッドを併用するための処理を呼び出す。
//}}2018/07/23追加:割り込みスタックとアイドルスレッドを併用するための処理を追加しました。
}
//}}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();								/* システム処理を実行 */
//}
//↓2017/07/07変更:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。
static void new_pceAppProc(int cnt) {
	int t = pceTimerGetCount();						//今回のpceAppProc()の開始時刻を取得する。
	(*old_pceAppProc)(cnt);							//今回のpceAppProc()を実行する。
	if(idle_sp) {								//アイドルスレッドが未完了ならば…
		t -= pceTimerGetCount();					//┬残りタイムスライスを求める。	「t=pceAppSetProcPeriod(INVALIDVAL)-(pceTimerGetCount()-t)」と同じです。生成されるコードの効率が良くなるように式を変形しました。
		t += pceAppSetProcPeriod(INVALIDVAL);				//┘
		if(t < idle_minimum_timeslice) { t = idle_minimum_timeslice; }	//残りタイムスライスが、アイドル処理の最小実行時間未満ならば、アイドル処理の最小実行時間とする。
		if(t > 0) {							//残りタイムスライスが有れば…
			idle_count = t;						//残りタイムスライスを格納する。
ENTER_CS;
			pceTimerSetContextSwitcher(idle_context_switcher);	//プリエンプトを設定する。
			idle_exec();						//アイドルスレッドに切り替える。
			pceTimerSetContextSwitcher(NULL);			//プリエンプトを解除する。
LEAVE_CS;
		}
	}
}
//}}2017/07/07変更:idle_yield()を廃止しました。idle_init()にアイドル処理の最小実行時間を指定する引数を追加しました。

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

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

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

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

