/*	
 *	cliplibc.c
 *
 *	標準Ｃライブラリの補助
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2015-2018 Naoyuki Sawa
 *
 *	* Wed Jan 07 21:18:08 JST 2015 Naoyuki Sawa
 *	- 新規作成。
 *	- errnoを、clippce.c⇒cliplibc.cへ移動しました。
 *	- 「下位レベル関数/ダミー関数 : 標準Ｃライブラリ」の実装を、clippce.c⇒cliplibc.cへ移動しました。
 *	- 「EPSONライブラリの置き換え」の実装を、clippce.c⇒cliplibc.cへ移動しました。
 *	- 「標準Ｃライブラリの補助」の実装を、clippce.c⇒cliplibc.cへ移動しました。具体的には、clippce.hの2015/01/07の下記コメントに相当します:
 *	  │ - strdup(),strcasecmp(),strncasecmp()の宣言をclippce.h⇒include/string.hへ，実装をclippce.c⇒cliplibc.cへ移動しました。
 *	  │   strcasecmp(),strncasecmp()は本当はstrings.hで宣言すべきなのですが、P/ECEにはstrings.hが無いのでinclude/string.hで宣言することにしました。
 *	  │ - strnlen(),strndup()の宣言をclippce.h⇒include/string.hへ，実装をclippce.c⇒cliplibc.cへ移動しました。
 *	  │ - ismbblead(),ismbbtrail(),iscsym(),iscsymf(),isblank()の宣言をclippce.h⇒include/ctype.hへ，実装をclippce.c⇒cliplibc.cへ移動しました。
 *	  │   ismbblead(),ismbbtrail()は本当はmbctype.hで宣言すべきなのですが、P/ECEにはmbctype.hが無いのでinclude/ctype.hで宣言することにしました。
 *	  │ - strupr(),strlwr()の宣言をclippce.h⇒include/string.hへ，実装をclippce.c⇒cliplibc.cへ移動しました。
 *	  │ - splitpath(),makepath()の宣言をclippce.h⇒include/stdlib.hへ，実装をclippce.c⇒cliplibc.cへ移動しました。
 *	  │ - ffs()の宣言をclippce.h⇒include/string.hへ，実装をclippce.c⇒cliplibc.cへ移動しました。
 *	  │   ffs()は本当はstrings.hで宣言すべきなのですが、P/ECEにはstrings.hが無いのでinclude/string.hで宣言することにしました。
 *	  │ - fileno(),isatty()の宣言を、clippce.h⇒include/stdio.hへ移動しました。実装はclipio.cにあります。
 *	  │   isatty()は本当はio.hかunistd.hで宣言すべきなのですが、P/ECEにはio.hもunistd.hも無いのでinclude/stdio.hで宣言することにしました。
 *	* Wed Jan 07 21:29:51 JST 2015 Naoyuki Sawa
 *	- Visual C++ 6.0でビルドする時にエラーになる箇所を、条件コンパイルディレクティブで除外するようにしました。
 *	  今回の変更は、P/ECE開発環境でビルドする場合には、影響有りません。
 *	* Wed Mar 18 22:26:03 JST 2015 Naoyuki Sawa
 *	- ダミーのread()の実装を削除しました。
 *	  ダミーのread()はEPSONライブラリのsscanf()のために定義していた物で、だいぶん前にsscanf()を独自実装した時点で不要になっていた事にこれまで気付かず無駄にコード容量を消費していました。
 *	  ダミーのread()を削除した事により、コード容量が12バイト少なくなりました。
 *	* Tue Mar 24 21:38:35 JST 2015 Naoyuki Sawa
 *	- EPSONライブラリのbsearch()を置き換えました。
 *	  EPSONライブラリのbsearch()のままでも問題無いのですが、C言語で実装されていて生成されるコードに少し無駄が有ります。
 *	  アセンブラ化する事によって、68バイト⇒60バイトになり、8バイトのサイズ節約になりました。
 *	  アセンブラ化による高速化の効果は、比較関数の処理時間を含めなければ、約15%の高速化です。
 *	* Wed Apr 29 14:30:43 JST 2015 Naoyuki Sawa
 *	- memccpy(),mempcpy(),strncpy(),stpncpy(),strcpy(),stpcpy()を追加しました。
 *	  宣言は、include/string.hに追加しました。
 *	- strncpy()とstrcpy()は、P/ECE開発環境のEPSONライブラリにも含まれていますが、自分で再実装しました。
 *	  stpncpy()とstpcpy()と共通化できる部分が多く、まとめて実装する方がコードサイズ節約できるからです。
 *	* Sun May 31 13:54:55 JST 2015 Naoyuki Sawa
 *	- strcpy()とstrcat()はVisual C++ 6.0でReleaseビルドすると「error C2169: 組み込み関数は定義できません。」になるので除外しました。
 *	* Sat Jul 25 17:20:54 JST 2015 Naoyuki Sawa
 *	- log2()を追加しました。宣言は、include/math.h に有ります。log2()は、C99で追加された関数です。「Lua 5.3.1」が、使用しています。
 *	  EPSONライブラリにも、Visual C++ 6.0にも、log2()は含まれていません。
 *	* Sat Oct 10 21:38:51 JST 2015 Naoyuki Sawa
 *	- cliplibc.cの中でsetjmp(),longjmp()を実装して、EPSONライブラリの同名の関数を置き換えました。
 *	* Wed Oct 14 21:50:46 JST 2015 Naoyuki Sawa
 *	- dirname(),basename()を追加しました。宣言は、include/libgen.h に有ります。
 *	* Tue Oct 20 21:18:14 JST 2015 Naoyuki Sawa
 *	- a64l(),l64a(),l64a_r()を追加しました。宣言は、include/stdlib.h に有ります。
 *	* Sat Oct 24 14:03:11 JST 2015 Naoyuki Sawa
 *	- program_invocation_name,program_invocation_short_nameの変数定義を追加しました。変数宣言はerrno.hに有ります。
 *	  詳細は、/clip/include/errno.h の同日のコメントを参照して下さい。
 *	* Mon Nov 02 21:32:47 JST 2015 Naoyuki Sawa
 *	- Win32プロジェクトでは、main()が呼び出される前に、program_invocation_name,program_invocation_short_nameが初期化されるように変更しました。
 *	* Sat Nov 21 21:27:06 JST 2015 Naoyuki Sawa
 *	- strchrnul()を追加しました。
 *	* Mon Nov 23 21:30:44 JST 2015 Naoyuki Sawa
 *	- strtok_r()を追加しました。この変更に伴いWin32プロジェクトでも自作のstrtok()を有効にしました。
 *	  strtok_r()の宣言は、include/string.h に有ります。
 *	* Tue Nov 24 21:41:25 JST 2015 Naoyuki Sawa
 *	- sleep(),usleep()を追加しました。宣言はunistd.hに有ります。
 *	* Sat Feb 13 21:29:21 JST 2016 Naoyuki Sawa
 *	- rawmemchr()を追加しました。宣言は、include/string.hに追加しました。
 *	  □「Linux Programmer's Manual - MEMCHR」(http://linuxjm.osdn.jp/html/LDP_man-pages/man3/memchr.3.html)より引用:
 *	  │rawmemchr()関数はmemchr()と同様だが、cがsが指す場所から始まるメモリー領域のどこかにあることを前提にして(つまり、プログラマはそのことを知っている)、最適化した方法でcの検索を実行する(すなわち、検索範囲の上限を示す引き数も使用されない)。
 *	  │rawmemchr()関数はマッチするバイトが見つかった場合はマッチしたバイトへのポインターを返す。マッチするバイトが見つからなかった場合、結果は不定である。
 *	  │rawmemchr()関数はGNU拡張であり、glibc2.1から使用可能である。
 *	- 上記の説明には'最適化した方法でcの検索を実行する'と書いてありますが、当実装では普通のmemchr()を呼び出すだけす。
 *	  呼び出し側が、検索範囲の上限を示す引数を省略出来る、利便性のためだけに用意しました。
 *	* Thu Mar 24 21:04:15 JST 2016 Naoyuki Sawa
 *	- signbit(),copysign(),copysignf()を追加しました。
 *	  これらはC99で追加された符号操作用のマクロ,及び,関数で、あまり使う事は無いと思いますが、覚えておくために実装しておく事にしました。
 *	- round(),roundf(),nearbyint(),nearbyintf(),rint(),rintf(),lround(),lroundf(),lrint(),lrintf()を追加しました。
 *	  これらはC99で追加された丸め関数で、特に、lround()が便利だと思います。
 *	  いままでは'(int)((x>=0.0)?(x+0.5):(x-0.5))'のように書いていた処理が、'lround(x)'だけで済むようになりました。
 *	- 宣言は全て、include/math.hに有ります。
 *	  倍精度バージョンの関数、copysign(),lround(),lrint(),round(),nearbyint(),rint(),trunc()の実装は、cliplibc.cに有ります。
 *	  単精度バージョンの関数、copysignf(),lroundf(),lrintf(),roundf(),nearbyintf(),rintf(),truncf()の実装は、framflt2.cに有ります。
 *	  実装を分けた理由は、単精度演算ライブラリが(アプリケーションが使用していないのに)無駄にリンクされて、サイズが増える事を防ぐためです。
 *	* Mon Mar 28 21:40:28 JST 2016 Naoyuki Sawa
 *	- fdim(),fdimf(),fmin(),fminf(),fmax(),fmaxf(),fma(),fmaf()を追加しました。
 *	  これらはC99で追加された関数です。
 *	  fdim()は、何に使うのか良く判らないのですが、覚えておくために実装しておく事にしました。
 *	  fmin(),fmax()は、min(),max()マクロよりも型を明示出来るし副作用が無いので、便利に使用出来ると思います。
 *	  fma()は、積和演算を短く・判り易く書けるので、(使う場面は少ないと思いますが)使い道は有ると思います。
 *	- 宣言は全て、include/math.hに有ります。
 *	  倍精度バージョンの関数、fdim(),fmin(),fmax(),fma()の実装は、cliplibc.cに有ります。
 *	  単精度バージョンの関数、fdimf(),fminf(),fmaxf(),fmaf()の実装は、framflt2.cに有ります。
 *	  実装を分けた理由は、単精度演算ライブラリが(アプリケーションが使用していないのに)無駄にリンクされて、サイズが増える事を防ぐためです。
 *	* Mon Mar 28 22:52:58 JST 2016 Naoyuki Sawa
 *	- fdim(),fmin(),fmax(),fma()をアセンブラ化しました。
 *	  fdim(),fmin(),fmax()の共通処理を工夫して、上手くコードサイズを削減出来たと思います。
 *	  尚、__fcmpdが%r4～%r7を破壊しない事に依存しています。
 *	  もし今後、__fcmpdの実装を変更する場合は、注意して下さい。
 *	* Fri Apr 29 21:34:57 JST 2016 Naoyuki Sawa
 *	- strdupa(),strndupa()用のヘルパ関数__strndupa_helper()を追加しました。
 *	  strdupa(),strndupa()のマクロ定義は、include/string.hに有ります。
 *	  詳細は、include/string.h の同日のコメントを参照して下さい。
 *	* Sun May 01 23:11:51 JST 2016 Naoyuki Sawa
 *	- mbsspn(),mbscspn()を追加しました。
 *	  宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
 *	- mbsspn(),mbscspn()を追加した理由は、clipyaml.cにて以下のような問題が生じ、strcspn()ではなくmbscspn()を使う必要が生じたからです。
 *	  │【誤】strcspn("キー[12]", "[,") ⇒ 3 … 誤り。'ー'(\x81\x5B)の2バイト目が誤って'['(\x5B)に一致してしまう。
 *	  │【正】mbscspn("キー[12]", "[,") ⇒ 4 … 正しい。
 *	- 今回はたまたまmbscspn()で問題が顕在化しましたが、同様の問題はstrchr()でも起こり得ます。
 *	  近い内に、mbschr()も追加しようと思います。	⇒{{2016/05/20コメント追記:mbschr(),mbsstr(),mbspbrk()を追加しました。}}
 *	* Mon May 02 00:16:14 JST 2016 Naoyuki Sawa
 *	- mbsspn(),mbscspn()をアセンブラ化しました。
 *	  コードサイズが120バイトも小さくなりました。
 *	* Tue May 03 21:44:23 JST 2016 Naoyuki Sawa
 *	- iscsym(),iscsymf(),isblank(),ismbblead(),ismbbtrail()をcliplibc.c⇒ctype.cへ移動しました。
 *	  詳細は、/clip/ctype.c の同日のコメントを参照して下さい。
 *	* Wed May 04 21:28:21 JST 2016 Naoyuki Sawa
 *	- bsearch_r()を追加し、bsearch()はbsearch_r()への呼び出しに変更しました。宣言はinclude/stdlib.hに有ります。
 *	  標準Cライブラリではbsearch_r()は定義されていないのですが、必要な事が多いので追加する事にしました。
 *	- 標準Cライブラリでで、qsort()に対するqsort_r()は定義されているのに、bsearch()に対するbsearch_r()が定義されていないのが不思議です。
 *	  Webで検索すると同様の疑問を持つ人は少なくないようなのですが、なぜこうなっているのかの理由は見付かりませんでした。
 *	* Wed May 11 21:35:57 JST 2016 Naoyuki Sawa
 *	- mbsnextc(),mbsinc(),mbsninc()を追加しました。
 *	  宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
 *	- mbsnextc(),mbsinc()を追加した理由は、これまでは個別にismbblead(),ismbbtrail()を呼び出してシフトJIS文字列をパースしていた処理を、
 *	  mbsnextc(),mbsinc()を使う方が呼び出し側の条件分岐が減らせてメモリ節約になると思ったからです。
 *	  mbsninc()は、現在の所使う予定は有りませんが、mbsinc()と合わせて実装しておく事にしました。
 *	- mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。
 *	  結果は変わりません。
 *	* Wed May 18 21:26:51 JST 2016 Naoyuki Sawa
 *	- makepath()のext引数に空文字を渡した時に'.'を補ってしまっていたバグを修正しました。
 *	  makepath()が'.'を補うのはext引数が非NULL,かつ,空文字でない時のみとするのが正解でした。
 *	  本物の_makepath()もそうなっています。
 *	* Thu May 19 22:52:12 JST 2016 Naoyuki Sawa
 *	- mbsdec()を追加しました。
 *	  宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
 *	- 現在の所、mbsdec()はまだ使っていません。
 *	  VC++6.0のオリジナル版ライブラリでは_makepath()等で使われているようです。
 *	  当ライブラリのmakepath()の実装はまだマルチバイト対応していないのですが、もし今後マルチバイト対応する(?)時にmbsdec()を使う予定です。	⇒{{2016/05/20コメント追記:makepath()をマルチバイト対応しました。}}
 *	* Fri May 20 21:59:17 JST 2016 Naoyuki Sawa
 *	- splitpath()をマルチバイト対応しました。
 *	  これまでは、ファイル名や拡張子に二バイト目が'\'の文字が含まれていた時に、誤ってその位置までディレクトリと見なしてしまうバグが有りました。
 *	  修正後は、上記の問題は無くなりました。
 *	- makepath()をマルチバイト対応しました。
 *	  これまでは、ディレクトリ名の最後の文字が二バイト目が'\'の文字だった時に、ディレクトリ名とファイル名の間に'\'を補われないバグが有りました。
 *	  修正後は、上記の問題は無くなりました。
 *	- 今回の修正によって、自作のsplitpath()とmakepath()は、VC++6.0のオリジナル版の_splitpath()と_makepath()と、ほとんど同じになったと思います。
 *	  あらゆる場面で、VC++6.0のオリジナル版の_splitpath()と_makepath()の代わりに、自作のsplitpath()とmakepath()を使用しても、安全になりました。
 *	* Fri May 20 23:37:14 JST 2016 Naoyuki Sawa
 *	- mbschr(),mbsstr(),mbspbrk()を追加しました。
 *	  宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
 *	* Sun May 29 21:54:56 JST 2016 Naoyuki Sawa
 *	- Visual C++ .NETで、main()が呼び出される前にプロセスが終了してしまう問題を修正しました。
 *	  詳細は、以下の通りです。
 *	- これまでVC++6.0では問題無く動作していた上記の仕組みが、VC++2015でコンパイルすると、動作しなくなりました。
 *	  VC++2015で、main()にブレークポイントを設定してデバッグ実行すると、main()が呼び出されずにハングアップしてしまいました。
 *	  調査した所、VC++2015(厳密には、VC++6.0以降VC++2015までのVisual C++ .NETのどれかのバージョンから)では、main()が呼び出される前に実行する関数が、voidではなく0を返さなければいけない事が判りました。
 *	  VC++6.0では、_initterm()(C:\Program Files (x86)\Microsoft Visual Studio\VC98\CRT\SRC\CRT0DAT.C)は、main()が呼び出される前に実行する関数の戻り値を検査せず、それらが失敗する事は想定していません。
 *	  それに対して、VC++2015では、_initterm_e()(C:\Program Files (x86)\Windows Kits\10\Source\10.0.10240.0\ucrt\startup\initterm.cpp)は、関数が0以外の値を返すと処理を中断し、
 *	  _initterm_e()の呼び出し元である__scrt_common_main_seh()(C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\exe_common.inl)は、main()を呼び出さずに終了してしまいます。
 *	  VC++2015のデバッグ実行がなぜ終了しないのかが不思議なのですが、おそらくデバッガがプロセスにアタッチする前に終了してしまい、VC++2015がデバッグを終了するトリガが無いためではないかと思います。
 *	- 修正方法は、VC++.NETならば、main()が呼び出される前に実行する関数が、0を返すように修正するだけです。
 *	  尚、VC++6.0とVC++.NETを区別する必要は無く、VC++6.0でも、main()が呼び出される前に実行する関数が、0を返すように修正してしまっても構いません。(_initterm()は戻り値を無視するから)
 *	  しかし、VC++6.0では戻り値がvoidで構わない事を明示するために、あえて場合分けして、VC++6.0の場合はこれまで通り戻り値をvoidのままとする事にしました。
 *	* Mon Jun 06 21:45:00 JST 2016 Naoyuki Sawa
 *	- アセンブラmemmove()の高速バージョンを作成しました。
 *	  これまでは、memcpy()が使用出来ない時は、単純に1バイトづつコピーしていました。
 *	  高速バージョンでは、ksvec200.sのmemcpyと同様に、コピー元アドレスとコピー先アドレスのメモリアライメント,及び,残り転送バイト数によって、ワード単位,及び,ハーフワード単位でもコピーするようにしました。
 *	  memcpy()が使用出来ない時の速度が、最大3.2倍程度早くなりました。
 *	- ただし、この変更によって、コードサイズが64バイト大きくなりました。
 *	  これまで敢えて高速化していなかった理由は、memmove()は使用頻度が低いので全体の性能に影響するケースは少なく、高速化よりもコードサイズ低減を優先していたからです。
 *	  しかし、本日clipmemc.cにMemCpt_Resize()を追加し、MemCpt_Resize()はmemmove()を頻繁に使用するので、memmove()の速度が大きな影響を持つケースが生じました。
 *	  今後、clipmemc.cモジュールの使用頻度が高くなるかどうかがまだ判らないのですが、コードサイズ64バイト程度の増加ならば許容範囲だと思って、この機会にmemmove()を高速化しておく事にしました。
 *	- シンボルUSE_SPEED_OPTIMIZED_MEMMOVEを定義するかしないかによって、これまでのコードサイズが小さいバージョンと、本日作成した高速バージョンを選択出来ます。
 *	  今後は、本日作成した高速バージョンを使って行く予定ですが、もしコードサイズを減らす必要が生じたり、高速バージョンの不具合が生じた場合は、USE_SPEED_OPTIMIZED_MEMMOVEをコメントアウトして古いバージョンに戻してみてください。
 *	* Tue Jun 14 20:57:03 JST 2016 Naoyuki Sawa
 *	- strnextc(),strinc(),strninc(),strdec()を追加しました。
 *	  宣言はinclude/string.hに追加しました。(VC++環境ではtchar.hですがP/ECE環境ではstring.hが適切だと思います。)
 *	- これらはVC++特有の関数です。
 *	  mbsnextc(),mbsinc(),mbsninc(),mbsdec()と違って、strnextc(),strinc(),strninc(),strdec()が必要な事はほとんど無いと思いますが、mbsnextc(),mbsinc(),mbsninc(),mbsdec()との対称性のために実装しておく事にしました。
 *	  使用例は、cliplibc.cモジュール内の、ローカル関数strupr_strlwr_mbsupr_mbslwr()を参照して下さい。
 *	* Tue Jun 14 21:51:27 JST 2016 Naoyuki Sawa
 *	- 既存のstrupr(),strlwr()を、新しいstrupr(),strlwr()とmbsupr(),mbslwr()に分離しました。
 *	  詳細は、以下の通りです。
 *	- 既存のstrupr(),strlwr()は、下記のような経緯で、シフトJIS対応となっていました。
 *	  │① Mon Jul 06 23:33:17 JST 2009 : シングルバイト版のstrupr(),strlwr()を、作成しました。
 *	  │② Sat Dec 25 12:48:38 JST 2010 : strupr(),strlwr()を、シフトJIS対応に変更しました。
 *	  しかし、strupr(),strlwr()が常にシフトJIS対応として動作すると、(無いとは思いますが)UTF-8文字列に対してstrupr(),strlwr()を使う時に問題が生じます。
 *	  そこで、VC++のライブラリと同様に、シングルバイト版はstrupr(),strlwr()、シフトJIS対応は新規のmbsupr(),mbslwr()として、分けて実装する事にしました。
 *	- 分けて実装する方法として、上記の①と②のコードを単純にそのまま使う方法も有ったのですが、今回は、改めて再実装する事にしました。
 *	  そうした理由は、strupr(),strlwr(),mbsupr(),mbslwr()は共通処理が多く、まとめて実装する事で、コードサイズが節約出来るからです。
 *	  詳細は、strupr_strlwr_mbsupr_mbslwr()の実装を参照して下さい。
 *	* Thu Jun 16 20:40:56 JST 2016 Naoyuki Sawa
 *	- 本日ksvec200.sのmemcpyを(dest==src)の場合に対して最適化した恩恵で、memmoveも(dest==src)の場合に最適化されるようになりました。
 *	  memmove自体のコードは変更していません。
 *	  詳細は、cliplibc.c内のmemmoveの実装内の'{{2016/06/16コメント追記:～'のコメント、及び、ksvec200.s内の同日のコメントを参照して下さい。
 *	- cliplibc.cのアセンブラ版memmoveの実装の下に、「memcpy,memmove,memsetのテストプログラム」を追記しました。
 *	  本日(2016/06/16)時点で、VC++6.0とP/ECEで結果が一致する事を確認しました。
 *	  もし今後memcpy,memmove,memsetを変更する場合は、このテストプログラムを使って動作確認を行って下さい。
 *	* Sun Jun 26 15:24:18 JST 2016 Naoyuki Sawa
 *	- mbstok(),mbstok_r()を追加しました。
 *	  既存のstrtok(),strtok_r()とも、処理を共通化しました。
 *	  宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
 *	- さらに、strtok(),strtok_r(),及び,mbstok(),mbstok_r()をアセンブラ化しました。
 *	  アセンブラ化した事によって、mbstok(),mbstok_r()を追加する前(strtok(),strtok_r()のみのC言語版)のコードサイズに比べて、12バイトしか増えていません。	←←←注目!!
 *	- アセンブラ版は、コードサイズ低減を優先したため、かなりトリッキーな実装になってしまいました。
 *	  コードサイズ低減のために、特に工夫した点は、以下の通りです。
 *	  1. インラインパラメータを使用して、アドレスを直接レジスタに代入するよりも、少ない命令数でアドレスを代入しました。
 *	     インラインパラメータを使用すると、常に命令数が少なくなる訳ではありませんが、当実装においてはレジスタの使い回しと合わせて、命令数を減らせました。
 *	  2. 空文字列定数を、下位8ビットが0である命令に重複させて、空文字列定数を削減しました。
 *	     下記の実装の、'call.d %r0'の部分を参照して下さい。
 *	  3. pushnとpopnの数をわざと非対称にして、途中でスタックに積んだ値を、popnで破壊しても良い%r4にダミーとして復帰させて、破棄しました。
 *	- 以上のように、かなりトリッキーで、読みづらいアセンブラプログラムになってしまいました。
 *	  ここまでコードサイズ低減する必要も無かったのですが、上記のテクニックの覚え書きも兼ねて、このように実装しておく事にしました。
 *	  読みづらくはありますが、全体のコード量は少ないので、レジスタとスタックの値を追っていけば、後で読み返した時にも理解出来ると思います。
 *	* Sat Sep 03 14:13:50 JST 2016 Naoyuki Sawa
 *	- malloc_usable_size()が、(int)でアドレス演算を行っていたのを、(char*)で行うように修正しました。
 *	  64ビット対応のための準備ですが、32ビットでも(char*)で行う方が本当は正しいからです。
 *	  とは言え、32ビットではどちらでも同じ結果になるので、既存の32ビットコードには影響有りません。
 *	* Wed Sep 07 21:52:10 JST 2016 Naoyuki Sawa
 *	- 64ビット版では、「#pragma section(".CRT$XIU", long, read)」を追加しました。
 *	  詳細は/clip/test.win/keep/開発メモ.txtの'Thu Aug 11 23:59:25 JST 2016'のコメントを参照して下さい。
 *	* Thu Sep 08 22:20:43 JST 2016 Naoyuki Sawa
 *	- init_program_invocation_name()が確実に実行されるように、「#pragma comment(linker, "/INCLUDE:_pinit_program_invocation_name")」及び「#pragma comment(linker, "/INCLUDE:pinit_program_invocation_name")」を追加しました。
 *	  VS2015のReleaseビルド対応のためです。詳細は、以下の通りです。
 *	- これまで(VC++6.0では)、".CRT$XIU"に配置した変数に対して、明示的な「#pragma comment(linker, "/INCLUDE:～")」を指定していたのは、clipmisc.hの「#pragma comment(linker, "/INCLUDE:_pmerge_env_option")」だけでした。
 *	  アプリケーションがclipmisc.cモジュール内の関数や変数を使用しない場合でも、常にmerge_env_option()がmain()よりも先にargcとargvを調整する必要が有ったからです。
 *	  それ以外の".CRT$XIU"に配置した変数に対しては、「#pragma comment(linker, "/INCLUDE:～")」を指定していませんでした。
 *	  アプリケーションが同一モジュール内の変数や関数を使用しない場合は、".CRT$XIU"による初期化も不要だったからです。
 *	- しかし、VS2015では状況が変わりました。
 *	  VS2015のReleaseビルドでは、「/Gy(関数レベルでリンクする)」及び「/OPT:REF(参照されない関数とデータを削除する)」がデフォルトで有効であり、参照されていない関数や変数は、たとえ同一モジュール内の他の関数へ変数が参照されていたとしても、削除されてしまうからです。
 *	  その結果、".CRT$XIU"に配置した変数が削除されてしまい、初期化関数が実行されません。
 *	  この問題については、下記のスレッドでも議論されており、結論としては、VS2012,VS2013,VS2015ではそういう仕様のようです。(下記のスレッド内で解決方法は提示されていません。)
 *	  │Visual Studio and .NET Framework ホーム - Whole program optimization removes (otherwise unreferenced) functions registered with #pragma section(".CRT$XCU")」(https://connect.microsoft.com/VisualStudio/Feedback/Details/1587892)
 *	  尚、VC++6.0では問題が起きなかった理由は、VC++6.0にも上記のオプションは有るのですが、Releaseビルドのデフォルトで無効であるからです。(手元で試した限りではグレーアウトしていて有効にする事も出来ないようでした。)
 *	- VS2015のReleaseビルドでも上記の問題を解決するための対策は、".CRT$XIU"に配置した変数全部について、アプリケーションからインクルードされるヘッダファイル内で、「#pragma comment(linker, "/INCLUDE:～")」を指定する事です。
 *	  何だか本末転倒なのですが、現在の所、これ以外に方法が判りませんでした。(又は、ソース個別に「/Gy」を無効化する等の方法も有るかも知れませんが、管理上好ましく無いと思うので…)
 *	  この対策はVS2015のReleaseビルドに対してのみ必要なのですが、確実のために全てのビルド(VC++6.0 Debug,VC++6.0 Release,VS2015 Debug,VS2015 Release)に対して有効にしておく事にしました。
 *	* Thu Sep 29 21:37:05 JST 2016 Naoyuki Sawa
 *	- Win32用のtruncate()関数,及び,ftruncate()関数を追加しました。
 *	  これまではWin32版は仮にclipstdw.h内でマクロ定義していましたが、今後はWin32版もcliplibc.c内で関数として実装する事にしました。
 *	  尚、P/ECE用のtruncate()関数,及び,ftruncate()関数の実装は、clipio.cに有ります。
 *	  clipio.cはP/ECE専用のモジュールであり、Win32プロジェクトには含めないので、Win32版のtruncate()とftruncate()は、cliplibc.c内で実装する事にしました。
 *	- log2()をアセンブラ化しました。
 *	- VS2015では当モジュール内でlog2を実装せず、ランタイムライブラリ内の実装を使用するようにしました。
 *	  これまでVC++6.0では問題無かったのですが、本日、VS2015の64ビット環境でもcliplibc.cを含めるように変更した所、VS2015の64ビット環境では以下の警告が出たからです。
 *	  │warning C4273: 'log2': dll リンクが一貫していません。
 *	  │c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\math.h(506): note: 'log2' の以前の定義を確認してください
 *	  もしかするとVS2015や64ビット環境の問題ではなく、VC++6.0のプロジェクトは/MT,VS2015のプロジェクトは/MDで作成しているのが原因かも知れないのですが、とりあえずVS2015ではこれらの定義を行わない事にしました。
 *	- VS2015では当モジュール内でcopysign,lround,lrint,round,nearbyint,rint,truncを実装せず、ランタイムライブラリ内の実装を使用するようにしました。
 *	  これまでVC++6.0では問題無かったのですが、本日、VS2015の64ビット環境でもcliplibc.cを含めるように変更した所、VS2015の64ビット環境では以下の警告が出たからです。
 *	  │warning C4273: 'copysign': dll リンクが一貫していません。
 *	  │c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\math.h(486): note: 'copysign' の以前の定義を確認してください
 *	  もしかするとVS2015や64ビット環境の問題ではなく、VC++6.0のプロジェクトは/MT,VS2015のプロジェクトは/MDで作成しているのが原因かも知れないのですが、とりあえずVS2015ではこれらの定義を行わない事にしました。
 *	- VS2015では当モジュール内でfdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。
 *	  これまでVC++6.0では問題無かったのですが、本日、VS2015の64ビット環境でもcliplibc.cを含めるように変更した所、VS2015の64ビット環境では以下の警告が出たからです。
 *	  │warning C4273: 'fdim': dll リンクが一貫していません。
 *	  │c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\math.h(492): note: 'fdim' の以前の定義を確認してください
 *	  もしかするとVS2015や64ビット環境の問題ではなく、VC++6.0のプロジェクトは/MT,VS2015のプロジェクトは/MDで作成しているのが原因かも知れないのですが、とりあえずVS2015ではこれらの定義を行わない事にしました。
 *	* Fri Sep 30 21:41:38 JST 2016 Naoyuki Sawa
 *	- memchr()のC言語版の実装で、参照されない変数'tmp'への代入が行われていた無駄を修正しました。
 *	  動作上はそのままでも問題ありませんし、P/ECE開発環境(GCC)やWindows(VC++6.0,VS2015)では警告も出ないのですが、Hewで「C5550 (W) Variable "tmp" was set but never used」という警告が出たからです。
 *	  確かに無駄だったので、修正する事にしました。
 *	  今回の修正による動作の変化は有りません。
 *	- bsearch_r()のC言語版の実装で、'goto L_START'が変数'mid'と'retVal'の宣言をまたいでいた問題を修正しました。
 *	  動作上はそのままでも問題ありませんし、P/ECE開発環境(GCC)やWindows(VC++6.0,VS2015)では警告も出ないのですが、Hewで「C5546 (W) Transfer of control bypasses initialization of:」という警告が出たからです。
 *	  今回の変更は、Hewで上記の警告を消すためだけの修正であり、今回の修正による動作の変化は有りません。
 *	  尚、今回の変更を行っても、Hewではなお「C5128 (I) Loop is not reachable from preceding code」という警告が出るのですが、C言語仕様的に問題無く、明らかに誤った警告なので、その警告は無視する事にしました。
 *	* Sat Oct 01 21:11:57 JST 2016 Naoyuki Sawa
 *	- pow()のコンパイル条件を変更しました。
 *	  pow()の再定義はP/ECE環境でのみ必要なので、'VC++以外'とするよりも'P/ECEのみ'とする方が自然だからです。
 *	- Hewでは当モジュール内でlog2,copysign,lround,lrint,round,nearbyint,rint,trunc,fdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。
 *	  Hewのランタイムライブラリには、C99で追加されたこれらの関数が、ビルトイン関数として含まれているからです。
 *	* Sat Nov 26 23:52:51 JST 2016 Naoyuki Sawa
 *	- htonl(),htons(),ntohl(),ntohs()を追加しました。(P/ECE専用です)
 *	  宣言はinclude/arpa/inet.h,及び,netinet/in.hに有ります。
 *	- Windowsの場合は、winsock.h,又は,winsock2.hで宣言され、wsock32.lib,又は,ws2_32.libで実装されています。
 *	  従って、Windowsの場合は、当ライブラリ内でhtonl(),htons(),ntohl(),ntohs()を実装する必要は有りません。
 *	* Thu Dec 29 07:57:44 JST 2016 Naoyuki Sawa
 *	- mbslen(),mbsnlen()を追加しました。
 *	  宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
 *	- P/ECEには_mbslen()も_mbsnlen()も無いので、当ライブラリのmbslen(),及び,mbsnlen()を使って下さい。
 *	- VC++6.0には組み込みの_mbslen()が有りますが、検証のために当ライブラリのmbslen()を使って下さい。
 *	  また、VC++6.0には組み込みの_mbsnlen()は無いので、検証も兼ねて当ライブラリのmbslen()を使って下さい。
 *	- VS2015には組み込みの_mbslen(),及び,_mbsnlen()が有りますが、検証のために当ライブラリのmbslen()を使って下さい。
 *	* Wed Mar 15 22:06:35 JST 2017 Naoyuki Sawa
 *	- strtol(),strtoul()を自前で実装しました。
 *	  宣言は、元々 include/stdlib.h に有ったので、追加していません。
 *	  strtol(),strtoul()を自前で実装した理由は、以下の通りです。
 *	- P/ECE開発環境のEPSONライブラリのstrtol(),strtoul()には、以下の問題点が有ります。
 *	  │①引数baseには(0,又は,2～36)だけを許容すべきだが、EPSONライブラリのstrtoul()は(base=1)も含めて許容しており正しくない。
 *	  │②文字列の先頭の空白文字を読み飛ばす際isspace()を使って判断すべきだが、EPSONライブラリのstrtoul()はisspace()を使わずに' 'だけを読み飛ばしており正しくない。
 *	  ①は、呼び出し側のアプリケーションが正しければ顕在化しないので、小さな問題です。
 *	  しかし、②は、本来正常に変換されるべき文字列を誤って変換出来ない場合が有るので、大きな問題です。
 *	  今回は主に②の問題を修正するために、strtol(),strtoul()を自前で実装しました。(ついでに①も修正しました)
 *	- 今回自前で実装したstrtol(),strtoul()は、オーバーフロー検査を省略しました。
 *	  本来のstrtol(),strtoul()は、オーバーフローが発生した場合、errnoを設定してULONG_MAX等を返す仕様です。
 *	  しかし実際には、strtol(),strtoul()がオーバーフロー検査を行う事に依存するようなアプケーションは作成しないし、アプリケーション側でerrnoを確認する事もしません。
 *	  つまりstrtol(),strtoul()のオーバーフロー検査はほとんど無駄なので、strtol(),strtoul()側でもオーバーフロー検査を省略しました。
 *	- オーバーフロー検査を省略した事により、strtol()とstrtoul()の動作は全く同じになりました。
 *	  そこで、strtol()とstrtoul()の実装をまとめて、コードサイズ削減しました。
 *	  今回の変更による、コードサイズ削減の効果は、以下の通りです。
 *	  □変更前(EPSONライブラリ)
 *	  │C:\usr\PIECE\lib\lib.lib:strtoul.o  300バイト ┬ 合計648バイト┐
 *	  │C:\usr\PIECE\lib\lib.lib:strtol.o   348バイト ┘              │
 *	  □変更後                                                        │460バイト削減
 *	  │strtoul()とstrtol()の両対応の関数   188バイト ←───────┘
 *	  尚、strtoul()とstrtol()をcliplibc.cに含めた事により、常にこれらの関数や呼び出しているctype.oがリンクされるようになりますが、
 *	  元々ライブラリの他の箇所からstrtoul()やctype.oの関数を呼び出していたので、元々それらは常にリンクされており、今回の変更による新たなオーバーヘッドは有りません。
 *	- 今回strtol(),strtoul()を自前で実装した理由は、上述の通り、P/ECE開発環境のEPSONライブラリの問題点回避,及び,コードサイズ削減のためであり、
 *	  Windows環境では自前実装に置き換える必要は無いのですが、今回作成したコードの検証のために、Windows環境でも自前実装を使用する事にしました。
 *	* Sun Aug 06 22:33:03 JST 2017 Naoyuki Sawa
 *	- div(),ldiv()を自前で実装しました。
 *	  宣言は、元々 include/stdlib.h に有ったので、追加していません。
 *	  div(),ldiv()を自前で実装した理由は、以下の通りです。
 *	- P/ECE開発環境のEPSONライブラリのdiv(),ldiv()には、以下の問題点が有ります。
 *	  │除算と剰余を個別に実行しており、__divdi3と__moddi3が呼び出されて、遅い。
 *	  ksvec200.sでの独自実装の__divsi3を使えば、一回の呼び出しで済みます。
 *	  今回、そのように自前で実装して、div(),及び,ldiv()を高速化しました。
 *	  EPSONライブラリのdiv(),ldiv()よりも、おおよそ二倍高速になりました。
 *	* Mon Aug 21 21:39:56 JST 2017 Naoyuki Sawa
 *	 - cacheflush()を追加しました。
 *	  現在の所、どのヘッダファイルでも、宣言していません。
 *	  当関数を追加した目的は、トランポリンコードを生成した時のリンクエラーを回避するためだけであり、アプリケーションが明示的に呼び出す事は想定していないからです。
 *	  トランポリンコードの詳細については、/clip/tool/dpcc33/app.c の同日のコメントを参照して下さい。
 *	* Sun Aug 27 21:28:26 JST 2017 Naoyuki Sawa
 *	- strerror()を追加しました。
 *	  P/ECE環境でのみ使用し、EPSONライブラリ版のstrerror()を置き換えます。
 *	  EPSONライブラリ版のstrerror()を置き換える事にした理由は、以下の通りです。
 *	- EPSONライブラリ版のstrerror()は、動作は問題無いのですが、コードサイズが大き過ぎる(400バイトも占めている)という問題が有ります。
 *	  「C:\usr\PIECE\docs\datasheet\EPSON\cc33\UTILITY\lib_src\ansilib33\string\src\strerror.c」を見ると、一見小さな関数なのですが、
 *	  微妙に飛び飛びのエラー番号に対してswitchで処理分けしており、密度が低く無駄の多いジャンプテーブルが生成されてしまっている事が、コードサイズが大きい理由です。
 *	  strerror()は、clipio.c内のperror()から呼び出されており、clipio.cは常にリンクされるので、strerror()も常にリンクされるので、コードサイズが大きい事は問題です。
 *	  そこで、動作は同じままで、コードサイズを節約した自前のstrerror()を実装して、EPSONライブラリ版のstrerror()を置き換える事にしました。
 *	  コードサイズを節約した方法については、下記の実装を参照して下さい。(データ構造を工夫しただけで、さほど複雑な事はしていません。)
 *	  コードサイズ節約の効果は、400バイト→188バイトになり、212バイトも節約出来ました。
 *	- 今回の対応は、P/ECE環境のみであり、P/ECE以外の環境(Windowsやその他一般)には適用しないように注意して下さい。	←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←【重要】
 *	  P/ECE環境のエラー番号は「(前述の)\strerror.c」及び「C:\usr\PIECE\include\errno.h」定義されている7種類だけですが、その他の環境ではもっと多いかも知れないからです。
 *	  万一、P/ECE以外の環境にも適用してしまうと、それらの環境で定義されているエラー番号に対するエラーメッセージが"unknown error"と表示されてしまう問題が生じます。
 *	* Wed Nov 15 23:26:53 JST 2017 Naoyuki Sawa
 *	- mbscmp(),mbsncmp()を追加しました。
 *	  宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
 *	- mbscasecmp(),mbsncasecmp()を追加しました。
 *	  宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
 *	- mbscasestr()を追加し、既存のmbsstr()も処理を共通化して再実装しました。
 *	  宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
 *	- mbscasecmp(),mbsncasecmp(),mbscasestr()は、Gnulib(http://www.gnu.org/software/gnulib/)から名前を取りました。(実装は独自です。)
 *	  mbscasecmp(),mbsncasecmp()は、Visual C++では_mbsicmp(),_mbsnicmp()相当なのですが、Gnulibの名前の方が判り易いと思ったからです。
 *	  尚、mbscasestr()相当の関数は、Visual C++には無いようです。もし有れば_stristr()や_mbsistr()なのでしょうけれど、無いようでした。
 *	* Sat Mar 31 23:59:59 JST 2018 Naoyuki Sawa
 *	- mbscmp(),mbsncmp()のアセンブラ版のバグを修正しました。
 *	  これまでは、同じ文字列を比較しても、違う文字列と判断してしまっていました。
 *	  P/ECE環境だけで発生していたバグであり、Windows環境ではこれまでも正しく動いていました。
 *	  本日の修正により、P/ECE環境でも正しく動作するようになりました。
 */
#include "clip.h"

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

#ifdef  PIECE
/* S1C33 Family Cコンパイラパッケージでは、errnoはアプリケーション側で定義する必要があります。
 * [S5U1C33000C_J.pdf p.140] */
int errno;
#endif//PIECE

// * Sat Oct 24 14:03:11 JST 2015 Naoyuki Sawa
// - program_invocation_name,program_invocation_short_nameの変数定義を追加しました。変数宣言はerrno.hに有ります。
//   詳細は、/clip/include/errno.h の同日のコメントを参照して下さい。
char* program_invocation_name       = "";
char* program_invocation_short_name = "";

#ifdef  _MSC_VER
// * Mon Nov 02 21:32:47 JST 2015 Naoyuki Sawa
// - Win32プロジェクトでは、main()が呼び出される前に、program_invocation_name,program_invocation_short_nameが初期化されるように変更しました。
//   これまでは、/clip/test.win/app.cのmain()の先頭で明示的に初期化していましたが、その処理は不要になったので削除しました。
// - main()が呼び出される前に変数を初期化する仕組みについては、下記の資料を参照して下さい。
//   │①「MSDN」の『CRT の初期化』⇒『グローバル オブジェクトの初期化』(http://msdn.microsoft.com/ja-jp/library/bb918180.aspx)
//   │②「猫科研究所(felid labo)」(http://up-cat.net/)さんの『mainとスレッドの前後処理(1)』(http://up-cat.net/main%25A4%25C8%25A5%25B9%25A5%25EC%25A5%25C3%25A5%25C9%25A4%25CE%25C1%25B0%25B8%25E5%25BD%25E8%25CD%25FD%25281%2529.html)
//   │③「OSDev.org」(http://wiki.osdev.org/)さんの『C++』⇒『Visual C++』(http://wiki.osdev.org/C%2B%2B)
//   │④/Program Files/Microsoft Visual Studio/VC98/CRT/SRC/CRTEXE.Cの__xi_a[],__xi_z[],__xc_a[],__xc_z[]の変数定義
//   │⑤/Program Files/Microsoft Visual Studio/VC98/CRT/SRC/CINITEXE.CのmainCRTStartup()の関数実装
// - ①②の記事はセクション名".CRT$XC?"について説明されていますが、厳密には".CRT$XC?"はC++用であり、C言語では".CRT$XI?",を使うのが適切です。
//   実際には、呼び出し順序だけの違いであり、C言語プログラムならば".CRT$XI?"と".CRT$XC?"のどちらを使っても同じ結果になります。
//   詳細は、③の記事,及び,⑤の実装を参照して下さい。
// - ①②の記事を読むと、セクション名".CRT$XI?"の'?'の部分は'A'～'Z'の範囲を取れるように読めますが、'B'～'W'の範囲にしておくのが安全だと思います。
//   'A'と'Z'は先頭と終端のマーカーであり、その間の名前のセクションに定義されたポインタの関数を呼び出すからです。
//   詳細は、④⑤の実装を参照して下さい。
// - ②の記事には、'#pragma data_seg(".CRT$XCU")'の代りに'#pragma init_seg(user)'でも良いと書かれていますが、それはC++用のプラグマでありC言語プログラムでは使用出来ません。
//   C言語プログラムで'#pragma init_seg(user)'を使用すると、「warning C4157: ﾌﾟﾗｸﾞﾏは無視されます。」になります。
// - '#pragma data_seg(".CRT$XI?")'や'#pragma data_seg(".CRT$XC?")'のセグメントに定義するポインタ変数は、定数にしてはいけません。
//   定数にすると、④の定義とは違う種類の変数の位置に配置されてしまうようで、初期化関数が呼び出されなくなります。
//   なお、変数をグローバルにする必要は無く、staticでも良いようです(実験より)。従って、下記の定義ではstaticとしました。
// - P/ECEには上記のような仕組みは無いので、引き続き、/clip/test.pce/app.cのmain()の先頭でprogram_invocation_name,program_invocation_short_nameを初期化しています。
//   P/ECEのスタートアップ(/clip/cstart.c)を変更して同様の仕組みを導入する事も検討しましたが、重要度が低い事,複雑になって良くないと思ったので、その変更は行わない事にしました。
// * Sun May 29 21:54:56 JST 2016 Naoyuki Sawa
// - Visual C++ .NETで、main()が呼び出される前にプロセスが終了してしまう問題を修正しました。
//   詳細は、以下の通りです。
// - これまでVC++6.0では問題無く動作していた上記の仕組みが、VC++2015でコンパイルすると、動作しなくなりました。
//   VC++2015で、main()にブレークポイントを設定してデバッグ実行すると、main()が呼び出されずにハングアップしてしまいました。
//   調査した所、VC++2015(厳密には、VC++6.0以降VC++2015までのVisual C++ .NETのどれかのバージョンから)では、main()が呼び出される前に実行する関数が、voidではなく0を返さなければいけない事が判りました。
//   VC++6.0では、_initterm()(C:\Program Files (x86)\Microsoft Visual Studio\VC98\CRT\SRC\CRT0DAT.C)は、main()が呼び出される前に実行する関数の戻り値を検査せず、それらが失敗する事は想定していません。
//   それに対して、VC++2015では、_initterm_e()(C:\Program Files (x86)\Windows Kits\10\Source\10.0.10240.0\ucrt\startup\initterm.cpp)は、関数が0以外の値を返すと処理を中断し、
//   _initterm_e()の呼び出し元である__scrt_common_main_seh()(C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\exe_common.inl)は、main()を呼び出さずに終了してしまいます。
//   VC++2015のデバッグ実行がなぜ終了しないのかが不思議なのですが、おそらくデバッガがプロセスにアタッチする前に終了してしまい、VC++2015がデバッグを終了するトリガが無いためではないかと思います。
// - 修正方法は、VC++.NETならば、main()が呼び出される前に実行する関数が、0を返すように修正するだけです。
//   尚、VC++6.0とVC++.NETを区別する必要は無く、VC++6.0でも、main()が呼び出される前に実行する関数が、0を返すように修正してしまっても構いません。(_initterm()は戻り値を無視するから)
//   しかし、VC++6.0では戻り値がvoidで構わない事を明示するために、あえて場合分けして、VC++6.0の場合はこれまで通り戻り値をvoidのままとする事にしました。
//{{2016/05/29変更:Visual C++ .NETで、main()が呼び出される前にプロセスが終了してしまう問題を修正しました。
//static void init_program_invocation_name() {	//main()が呼び出される前に、mainCRTStartup()⇒_cinit()⇒_initterm()から呼び出される。
//↓2016/05/29変更:Visual C++ .NETで、main()が呼び出される前にプロセスが終了してしまう問題を修正しました。
#if     (_MSC_VER > 1200)	//Visual C++ .NETならば…
static int  init_program_invocation_name() {	//main()が呼び出される前に、mainCRTStartup()⇒_cinit()⇒_initterm()から呼び出される。
#else //(_MSC_VER > 1200)	//Visual C++ 6.0 ならば…
static void init_program_invocation_name() {	//main()が呼び出される前に、mainCRTStartup()⇒_cinit()⇒_initterm()から呼び出される。
#endif//(_MSC_VER > 1200)
//}}2016/05/29変更:Visual C++ .NETで、main()が呼び出される前にプロセスが終了してしまう問題を修正しました。
	static char path[MAX_PATH];
	GetModuleFileName(NULL, path, sizeof path);
	program_invocation_short_name = basename((program_invocation_name = path));
//{{2016/05/29追加:Visual C++ .NETで、main()が呼び出される前にプロセスが終了してしまう問題を修正しました。
#if     (_MSC_VER > 1200)	//Visual C++ .NETならば…
	return 0;	//VC++2015では、main()が呼び出される前に実行する関数が0を返すと、main()を呼び出さずに終了する。VC++6.0では、main()が呼び出される前に実行する関数の戻り値は検査されない。
#endif//(_MSC_VER > 1200)
//}}2016/05/29追加:Visual C++ .NETで、main()が呼び出される前にプロセスが終了してしまう問題を修正しました。
}
//{{2016/09/07追加:64ビット版では、「#pragma section(".CRT$XIU", long, read)」を追加しました。
#ifdef  _WIN64
#pragma section(".CRT$XIU", long, read)	//x64ではこの指定を追加しないと初期化関数が呼び出されません。詳細は/clip/test.win/keep/開発メモ.txtの'Thu Aug 11 23:59:25 JST 2016'のコメントを参照して下さい。
#endif//_WIN64
//}}2016/09/07追加:64ビット版では、「#pragma section(".CRT$XIU", long, read)」を追加しました。
#pragma data_seg(".CRT$XIU")	//'#pragma data_seg(".CRT$XCU")'でも構いません。
//{{2016/09/08変更:init_program_invocation_name()が確実に実行されるように、「#pragma comment(linker, "/INCLUDE:_pinit_program_invocation_name")」及び「#pragma comment(linker, "/INCLUDE:pinit_program_invocation_name")」を追加しました。
//static void* /*!!const不可!!*/ pinit_program_invocation_name/*変数名は関係無いので何でも良い*/ = init_program_invocation_name;	//このポインタが".CRT$XI?"セグメント内に配置されて、mainCRTStartup()がポインタの指す関数を呼び出します。
//↓2016/09/08変更:init_program_invocation_name()が確実に実行されるように、「#pragma comment(linker, "/INCLUDE:_pinit_program_invocation_name")」及び「#pragma comment(linker, "/INCLUDE:pinit_program_invocation_name")」を追加しました。
/*static*/ void* /*!!const不可!!*/ pinit_program_invocation_name/*変数名は関係無いので何でも良い*/ = init_program_invocation_name;	//このポインタが".CRT$XI?"セグメント内に配置されて、mainCRTStartup()がポインタの指す関数を呼び出します。
//}}2016/09/08変更:init_program_invocation_name()が確実に実行されるように、「#pragma comment(linker, "/INCLUDE:_pinit_program_invocation_name")」及び「#pragma comment(linker, "/INCLUDE:pinit_program_invocation_name")」を追加しました。
#pragma data_seg()		//元のセグメントに戻す。忘れないで!!
#endif//_MSC_VER

/*****************************************************************************
 *	下位レベル関数/ダミー関数 : 標準Ｃライブラリ
 *****************************************************************************/

/* 下位レベル関数：	標準ライブラリ内部から呼ばれるもの。
 *			アプリケーションから直接使うことはありません。
 * ダミー関数：		標準ライブラリのヘッダで宣言されているが、
 *			実装されていない（ライブラリ内に存在しない）。
 * 従って、これらの関数を clippce.h で宣言する必要はありません。
 */

#ifdef  PIECE
/* exit(),abort()から呼ばれます。 */
void
_exit()
{
	pceAppReqExit(0); /* 終了要求を発行して、 */
	yield();          /* システムに制御を返す */
	/* もう、yield()は制御を返しません。      */
}
#endif//PIECE

//{{2015/03/18削除:ダミーのread()の実装を削除しました。ダミーのread()はEPSONライブラリのsscanf()のために定義していた物で、だいぶん前にsscanf()を独自実装した時点で不要になっていた事にこれまで気付かず無駄にコード容量を消費していました。ダミーのread()を削除した事により、コード容量が12バイト少なくなりました。
//#ifdef  PIECE
///* * EPSONライブラリの標準入出力用下位レベル関数です。
// *   EPSONライブラリのsscanf()を使うために定義しました。
// * - sscanf()はread()を使っていませんが、sscanf()が呼び出す_doscan()がscanf()と共用になっていて、
// *   _doscan()がscanf()から呼ばれた場合にread()を使っているため、read()のシンボル解決が要求されてしまいます。
// *   リンクエラーを回避するため、ダミーのread()関数を定義することにしました。
// * - sscanf()を使うだけならread()が呼ばれることはありません。
// */
//int
//read(int fd, char* buf, int nbytes)
//{
//	DIE();
//}
//#endif//PIECE
//}}2015/03/18削除:ダミーのread()の実装を削除しました。ダミーのread()はEPSONライブラリのsscanf()のために定義していた物で、だいぶん前にsscanf()を独自実装した時点で不要になっていた事にこれまで気付かず無駄にコード容量を消費していました。ダミーのread()を削除した事により、コード容量が12バイト少なくなりました。

/*****************************************************************************
 *	EPSONライブラリの置き換え
 *****************************************************************************/

#ifdef  PIECE
/* * Thu Jun 17 05:14:00 JST 2004 Naoyuki Sawa 追記
 * - メモリ管理を、EPSONライブラリmalloc()から、P/ECE API pceHeapAlloc()に変更しました。
 *   本モジュールにてmalloc()/calloc()/realloc()/free()を再定義し、EPSONライブラリ内の同名関数をオーバーライドします。
 */
void*
malloc(size_t size)
{
	return pceHeapAlloc(size);
}
void
free(void* ptr)
{
	/* pceHeapFree()はfree()と違ってNULLポインタを無視せず、開放しようとして失敗するので、まず明示的に検査します。
	 * メモリブロックの開放に失敗した場合、たぶんアプリケーションプログラムのバグなので、DIE()することにしました。
	 */
	int retval;

	if(ptr) {
		retval = pceHeapFree(ptr);
		if(retval != 0) DIE();
	}
}
void*
realloc(void* oldptr, size_t newsize)
{
	/* pceHeapRealloc()とrealloc()は、かなり仕様が異なります。
	 * pceHeapRealloc()はメモリブロックを移動させません。
	 * その場所のまま、サイズ拡張または縮小を行おうとします。(縮小は必ず成功するはずです)
	 * realloc()の場合、その場所でサイズ拡張に失敗した場合、別のメモリブロックを確保して元の内容をコピーしますが、
	 * pceHeapRealloc()はこの動作を行わず、ただちに失敗を返します。
	 * →* Thu Jun 17 07:27:00 JST 2004 Naoyuki Sawa 追記
	 * →  PieceSystem 1.20 現在のpceHeapRealloc()にはバグがあることがわかりました。
	 * →  詳しくは、後述のコメントを参照してください。
	 * →* Mon Mar 30 19:21:12 JST 2009 Naoyuki Sawa
	 * →- PieceSystem1.20現在のpceHeapRealloc()バグ修正パッチを適用しました。
	 * →  詳しくは、後述のコメントを参照してください。
	 */
	int retval;
	int oldsize;
	void* newptr;
	HEAPMEM* phm;

	/* * realloc(NULL, newsize)は、malloc(newsize)と等価です。(realloc仕様)
	 *   realloc(oldptr, 0)は、free(oldptr)と等価です。(realloc仕様)
	 * * realloc(NULL, 0)の動作は、標準Cライブラリ仕様では未定義のようです。(※要確認)
	 *   2004/06/17現在の実装は、{ free(NULL); return NULL; } の動作としてあります。
	 */
	if(!newsize) {
		free(oldptr);
		return NULL;
	}
	if(!oldptr) {
		return malloc(newsize);
	}

	///* まず、その場でサイズ変更してみます。
	// * 縮小の場合は、必ず成功するはずです。
	// */
	//retval = pceHeapRealloc(oldptr, newsize);
	//if(retval == 0) return oldptr;
	//→* Thu Jun 17 07:27:00 JST 2004 Naoyuki Sawa
	//→  PieceSystem1.20現在のpceHeapRealloc()にはバグがあります。
	//→  /usr/PIECE/system/pcekn/heapman.c 195行目の
	//→	oldsize = (unsigned long)phm->chain - (unsigned long)phm;
	//→  は間違いで、正しくは
	//→	oldsize = (unsigned long)phm->chain - ((unsigned long)phm + sizeof(HEAPMEM));
	//→  としなければいけないはずです。
	//→  pceHeapRealloc()を呼ぶと、ヒープが破壊されたり、アドレス不整例外が発生したりします。
	//→  仕方が無いので、常にメモリブロックの再割り当てを行うことにしました。
	//→  サイズ縮小時は旧いメモリブロックをそのまま使うという手段もありますが、
	//→  不要に大きなメモリブロックがいつまでも残ってしまうことを避けるために、
	//→  常に要求されたサイズのメモリブロックを再割り当てすることにしました。
	//----------------------------------------------------------------------------------------
	// * Mon Mar 30 19:21:12 JST 2009 Naoyuki Sawa
	// - PieceSystem1.20のpceHeapRealloc()バグに対する、上述の修正方法は誤りでした。
	//   上述の修正方法では、元々不正だったHeapを増やすバグが治る代わりに、元々正しかったHeapを減らす処理が不正になってしまいます。
	//   正しい修正方法は、/usr/PIECE/system/pcekn/heapman.cの195行目は変更せず、202行目と203行目の間に次の一行を追加することです。
	//     if ( phm2->owner ) return 2; // 失敗(後ろに空きがない)
	//     if(n < sizeof(HEAPMEM)) n = sizeof(HEAPMEM); // ←この一行を追加!!
	//     n2 = (unsigned long)phm2->chain - (unsigned long)phm2;
	// - PieceSystem1.20における、pceHeapRealloc()のバグを再現する方法は、以下のとおりです。
	//     p = pceHeapAlloc(4);
	//     pceHeapRealloc(p, 8); // ←ここでアドレス不正例外が発生します。
	//   バグの原因は、204行目のAllocHEAPMEM()呼び出しにおいて、第二引数nに後続の空きブロックから取り込むバイト数を指定しています。
	//   AllocHEAPMEM()の第二引数には、HEAPMEM構造体を含めた割り当てサイズを指定するのですが、上記のバグ再現コードのように、
	//   4バイトだけ拡張する場合、HEAPMEM構造体のサイズ(8バイト)未満の新規メモリブロックを作成しようとしてしまいます。
	//   AllocHEAPMEM()の中で、phm1の4バイト後にphm2を作成してしまい、phm1のchainフィールドを破壊してしまいます。
	//   この問題を修正するには、拡張するバイト数がHEAPMEM構造体のサイズ(8バイト)未満の場合は、8バイトとすることです。
	// - 以上の修正方法によって、pceHeapRealloc()が正しく動作するようになります。
	//   なお、Heapを減らす処理(208行目)は元々正しいので、前述の修正方法ならばHeapを減らす処理には影響を与えません。
	// - 以上を踏まえて、本ライブラリの初期化処理に、pceHeapRealloc()の修正パッチを組み込むことにしました。
	//   具体的には、pceAppInit()とpceAppExit()に追加した、bugfix_pceHeapRealloc()呼び出しを参照してください。
	//   pceHeapRealloc()修正パッチに伴い、上で"Thu Jun 17 07:27:00 JST 2004"にコメントアウトしていたコードを復元しました。
	//   これまでは、realloc()は常にメモリを移動していましたが、今後は、pceHeapRealloc()が成功すればメモリを移動しなくなりました。
	retval = pceHeapRealloc(oldptr, newsize);
	if(retval == 0) return oldptr;

	/* 新しいメモリブロックを確保します。
	 * 新しいメモリブロックが確保できなかった場合は、元のメモリブロックを開放せずに、NULLを返します。(relloc仕様)
	 */
	newptr = malloc(newsize);
	if(!newptr) return NULL;

	/* 元のメモリブロックの内容を、新しいメモリブロックにコピーします。
	 *
	 *	|              |
	 *	+--------------+ --- phm = oldptr - sizeof(HEAPMEM)
	 *	|    HEAPMEM   |
	 *	+--------------+ --- oldptr
	 *	|              |  |
	 *	| memory block |  |  oldsize = phm->chain - oldptr
	 *	|              |  |
	 *	+--------------+ --- phm->chain
	 *	| next HEAPMEM |
	 *	+--------------+
	 *	|              |
	 */
	phm = (HEAPMEM*)((unsigned char*)oldptr - sizeof(HEAPMEM));
	if(phm->mark != HEAPMEMMK) DIE();
	oldsize = (unsigned char*)phm->chain - (unsigned char*)oldptr;
	memcpy(newptr, oldptr, oldsize < newsize ? oldsize : newsize);

	/* 元のメモリブロックを開放します。 */
	free(oldptr);

	return newptr;
}
#endif//PIECE

#ifdef  PIECE
/* S1C33 Family Cコンパイラパッケージのcalloc()には問題があります。
 * calloc(～, 1)の形式で使ったときに、必要量の4倍ものメモリを確保しようとします。
 * おそらく、次のような実装になっているものと思われます。
 * 	void* calloc(size_t num, size_t size) {
 * 		int n;
 * 		void* p;
 * 		size = size + 3 & ~3;		// 要素サイズを4バイト単位に切り上げ
 * 		n = num * size;			// 割り当てバイト数を計算
 * 		p = malloc(n);			// 割り当てを行ってみる
 * 		if(p != NULL) memset(p, 0, n);	// 成功ならゼロクリア
 * 		return p;
 * 	}
 * これでは無駄が多すぎます。
 * 自前でcalloc()を用意し、ライブラリ内のcalloc()を置き換えることにします。
 * 参考文献：P.J.プラウガー著「標準Ｃライブラリ ANSI/ISO/JIS C規格」（トッパン）
 * 　　　　　初版 432ページ 図13.30 calloc.c
 *
 * * Thu Jun 17 05:14:00 JST 2004 Naoyuki Sawa 追記
 * - メモリ管理を、EPSONライブラリmalloc()から、P/ECE API pceHeapAlloc()に変更しました。
 *   EPSONライブラリ内のcalloc()をオーバーライドするために、引き続きここでcalloc()を定義しておきます。
 */
void*
calloc(size_t num, size_t size)
{
	size_t n = num * size;
	void* p = malloc(n);
	if(p) memset(p, 0, n);
	return p;
}
#endif//PIECE

//{{2015/11/23削除:strtok_r()を追加しました。この変更に伴いWin32プロジェクトでも自作のstrtok()を有効にしました。
//#ifdef  PIECE
//}}2015/11/23削除:strtok_r()を追加しました。この変更に伴いWin32プロジェクトでも自作のstrtok()を有効にしました。
/* S1C33 Family Cコンパイラパッケージのstrtok()は完全にバグってます。
 * s1に与えた文字列の終端の'\0'を突き抜けて走査してしまいます。
 * 例えば次のコードを実行すると：
 *	char* s1 = "a/b/c\0d/e/f";
 *	char* p;
 *	p = strtok(s1, "/");
 *	while(p != NULL) {
 *		pceFontPrintf("(%s)", p);
 *		p = strtok(NULL, "/");
 *	}
 * 「(a)(b)(c)」ではなく、「(a)(b)(c)(d)(e)(f)」と表示されてしまいます。
 * 文字列s1が「c」の後の「\0」で終わっているのを認識できていません。
 * この例では「f」で止まっていますが、たまたま「f」の後ろに「\0\0...」と
 * ヌル文字が連続していただけで、そうでなっていなければ永遠に終わりません。
 * （S1C33のstrtok()は、「\0」が二個以上連続していないと終わらないようです。）
 * これはさすがに論外です。
 * 自前でstrtok()を用意し、ライブラリ内のstrtok()を置き換えることにします。
 * 参考文献：P.J.プラウガー著「標準Ｃライブラリ ANSI/ISO/JIS C規格」（トッパン）
 * 　　　　　初版 467ページ 図14.20 strtok.c
 */
//{{2015/11/23変更:strtok_r()を追加しました。この変更に伴いWin32プロジェクトでも自作のstrtok()を有効にしました。
//char* strtok(char* s1, const char* s2) { /* find next token in s1[] delimited by s2[] */
//	static char* ssave = ""; /* for safety */
//	char *sbegin, *send;
//	sbegin = s1 ? s1 : ssave;
//	sbegin += strspn(sbegin, s2);
//	if(*sbegin == '\0') { /* end of scan */
//		ssave = ""; /* for safety */
//		return NULL;
//	}
//	send = sbegin + strcspn(sbegin, s2);
//	if(*send != '\0') *send++ = '\0';
//	ssave = send;
//	return sbegin;
//}
//↓2015/11/23変更:strtok_r()を追加しました。この変更に伴いWin32プロジェクトでも自作のstrtok()を有効にしました。
//{{2016/06/26変更:mbstok(),mbstok_r()を追加しました。既存のstrtok(),strtok_r()とも、処理を共通化しました。
//char* strtok(char* s1, const char* s2) { /* find next token in s1[] delimited by s2[] */
//	static char* ssave = ""; /* for safety */
//	return strtok_r(s1, s2, &ssave);
//}
//char* strtok_r(char* s1, const char* s2, char** saveptr) { /* find next token in s1[] delimited by s2[] */
//	char *sbegin, *send;
//	sbegin = s1 ? s1 : *saveptr;
//	sbegin += strspn(sbegin, s2);
//	if(*sbegin == '\0') { /* end of scan */
//		*saveptr = ""; /* for safety */
//		return NULL;
//	}
//	send = sbegin + strcspn(sbegin, s2);
//	if(*send != '\0') *send++ = '\0';
//	*saveptr = send;
//	return sbegin;
//}
//↓2016/06/26変更:mbstok(),mbstok_r()を追加しました。既存のstrtok(),strtok_r()とも、処理を共通化しました。
#ifndef PIECE
// * Sun Jun 26 15:24:18 JST 2016 Naoyuki Sawa
// - mbstok(),mbstok_r()を追加しました。
//   既存のstrtok(),strtok_r()とも、処理を共通化しました。
//   宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
static char* strtok_mbstok_r(char* s1, const char* s2, char** saveptr, size_t (*spn)(const char*, const char*), size_t (*cspn)(const char*, const char*)) {	//find next token in s1[] delimited by s2[]
	char *sbegin, *send;
	sbegin = s1 ? s1 : *saveptr;
	sbegin += (*spn)(sbegin, s2);
	if(*sbegin == '\0') {	//end of scan
		*saveptr = "";	//for safety
		return NULL;
	}
	send = sbegin + (*cspn)(sbegin, s2);
	if(*send != '\0') *send++ = '\0';
	*saveptr = send;
	return sbegin;
}
char* strtok(char* s1, const char* s2) {	//find next token in s1[] delimited by s2[]
	static char* ssave = "";	//for safety
	return strtok_r(s1, s2, &ssave);
}
char* mbstok(char* s1, const char* s2) {	//find next token in s1[] delimited by s2[]
	static char* ssave = "";	//for safety
	return mbstok_r(s1, s2, &ssave);
}
char* strtok_r(char* s1, const char* s2, char** saveptr) {	//find next token in s1[] delimited by s2[]
	return strtok_mbstok_r(s1, s2, saveptr, strspn, strcspn);
}
char* mbstok_r(char* s1, const char* s2, char** saveptr) {	//find next token in s1[] delimited by s2[]
	return strtok_mbstok_r(s1, s2, saveptr, mbsspn, mbscspn);
}
#else //PIECE
// * Sun Jun 26 15:24:18 JST 2016 Naoyuki Sawa
// - さらに、strtok(),strtok_r(),及び,mbstok(),mbstok_r()をアセンブラ化しました。
//   アセンブラ化した事によって、mbstok(),mbstok_r()を追加する前(strtok(),strtok_r()のみのC言語版)のコードサイズに比べて、12バイトしか増えていません。	←←←注目!!
// - アセンブラ版は、コードサイズ低減を優先したため、かなりトリッキーな実装になってしまいました。
//   コードサイズ低減のために、特に工夫した点は、以下の通りです。
//   1. インラインパラメータを使用して、アドレスを直接レジスタに代入するよりも、少ない命令数でアドレスを代入しました。
//      インラインパラメータを使用すると、常に命令数が少なくなる訳ではありませんが、当実装においてはレジスタの使い回しと合わせて、命令数を減らせました。
//   2. 空文字列定数を、下位8ビットが0である命令に重複させて、空文字列定数を削減しました。
//      下記の実装の、'call.d %r0'の部分を参照して下さい。
//   3. pushnとpopnの数をわざと非対称にして、途中でスタックに積んだ値を、popnで破壊しても良い%r4にダミーとして復帰させて、破棄しました。
// - 以上のように、かなりトリッキーで、読みづらいアセンブラプログラムになってしまいました。
//   ここまでコードサイズ低減する必要も無かったのですが、上記のテクニックの覚え書きも兼ねて、このように実装しておく事にしました。
//   読みづらくはありますが、全体のコード量は少ないので、レジスタとスタックの値を追っていけば、後で読み返した時にも理解出来ると思います。
char* strtok(char* s1, const char* s2);
char* mbstok(char* s1, const char* s2);
char* strtok_r(char* s1, const char* s2, char** saveptr);
char* mbstok_r(char* s1, const char* s2, char** saveptr);
asm("
		.code
		.align		2				;//'.align 1'ではなく'.align 2'。callとインライン引数の間に暗黙のnopが入らないようにするために必要!!
		.global		strtok
		.global		strtok_r
		.global		mbstok
		.global		mbstok_r
strtok:								;//
		ext		      strtok_ssave@h	;//0	;//
		ext		      strtok_ssave@m	;//1	;//
		ld.w		%r14, strtok_ssave@l	;//2	;//%r14 := saveptr = &strtok_ssave
strtok_r:							;//
		call		strtok_mbstok_r		;//3	;//┬この間に暗黙のnopが入らないように注意しろ。
		.word		strspn			;//4,5	;//┘
		.word		strcspn			;//6,7	;//
		;//---------------------------------------------;//
mbstok:								;//
		ext		      mbstok_ssave@h	;//0	;//
		ext		      mbstok_ssave@m	;//1	;//
		ld.w		%r14, mbstok_ssave@l	;//2	;//%r14 := saveptr = &mbstok_ssave
mbstok_r:							;//
		call		strtok_mbstok_r		;//3	;//┬この間に暗黙のnopが入らないように注意しろ。
		.word		mbsspn			;//4,5	;//┘
		.word		mbscspn			;//6,7	;//
		;//---------------------------------------------;//
strtok_mbstok_r:						;//
		pushn		%r3				;//
		;//%r12     := s1
		;//%r13     := s2
		;//%r14     := saveptr
		;//[%sp+0]  := %r0	┐								┐
		;//[%sp+4]  := %r1	│strtok_mbstok_rの最初のpushn %r3で積まれる。			│strtok_mbstok_rの最後のpopn %r4でポップされる。
		;//[%sp+8]  := %r2	│								│
		;//[%sp+12] := %r3	┘								│
		;//[%sp+16] := pfn	─strtok_r,又は,mbstok_rからstrtok_mbstok_rへのcallで積まれる。	┘					%r4にはpfnをダミーとしてpopする。
		;//[%sp+20] := retp	─アプリケーションからstrtok_r,又は,mbstok_rへのcallで積まれる。─strtok_mbstok_rの最後のretでポップされる。
		xld.w		%r1, [%sp+16]			;//%r1  :=                     pfn
		cmp		%r12, 0				;//%r12 :=                   sbegin = s1 ? s1 : *saveptr			*anti-interlock*
		ld.w		%r9, [%r1]+			;//%r9  :=              spn = *pfn++
		jrne.d		3				;//
		 ld.w		%r2, %r14			;//%r2  := saveptr								*delay*
		 ld.w		%r12, [%r2]			;//%r12 :=                   sbegin =           *saveptr
		ld.w		%r3, %r12			;//%r3  :=                   sbegin
		call.d		%r9				;//%r10 :=            (*spn)(sbegin, s2)
		ld.w		%r0, %r13			;//%r0  :=                           s2						*delay*
		add		%r3, %r10			;//%r3  :=  sbegin += (*spn)(sbegin, s2)
		ld.b		%r10, [%r3]			;//%r10 := *sbegin	
		cmp		%r10, 0				;//%psr := *sbegin <=> '￥０'	────────────────────┐	!INTERLOCK!
		call		strtok_mbstok_r_RET0		;//[%sp+0] := "" = strtok_mbstok_emptyString, goto RET0	────┐	│
strtok_mbstok_emptyString:					;//								│	│
		call.d		%r0				;//%r10 :=                  (*cspn)(sbegin, s2)	←───┐	│	│	'call.d %r0'の機械語は0x0700であり、下位バイトが""と等価である。
		ld.w		%r12, %r3			;//%r12 :=                          sbegin		│	│	│	*delay*
		add		%r10, %r3			;//%r10 :=  send = sbegin + (*cspn)(sbegin, s2)		│	│	│
		ld.b		%r9, [%r10]			;//%r9  := *send					│	│	│
		cmp		%r9, 0				;//if(     *send != '￥０') { *send++ = '￥０' }	│	│	│	!INTERLOCK!
		jreq		2				;//							│	│	│
		 ld.b		[%r10]+, %r8			;//							│	│	│
		ld.w		[%r2], %r10			;//*saveptr = send					│	│	│
		ld.w		%r10, %r3			;//%r10 := sbegin					│	│	│
strtok_mbstok_r_RET:						;//							│	│	│
		popn		%r4				;//							│	│	│	%r4にはpfnをダミーとしてpopする。
		ret						;//return  sbegin or NULL				│	│	│
		;//---------------------------------------------;//							│	│	│
strtok_mbstok_r_RET0:						;//							│	│	│
		ld.w		%r13, %r0			;//%r13 :=                                  s2	────┤	│	│
		popn		%r0				;//%r0  :=    "" = strtok_mbstok_emptyString	←───────┘	│
		ld.w		[%r2], %r0			;//*saveptr = ""					│		│	このすぐ下でRETへ分岐しない場合は、この行の'*saveptr = ""'はダミー処理となり、後の処理で'*saveptr = send'を行う時に上書きされる。
		jreq		strtok_mbstok_r_RET		;//if(*sbegin == '￥０') { return NULL }	←───────────┘
		ld.w		%r0, [%r1]			;//%r0  :=                    cspn = *pfn		│
		jp		strtok_mbstok_emptyString	;// ──────────────────────────┘
		;//---------------------------------------------;//
		.data
		.align		2
strtok_ssave:
		.word		strtok_mbstok_emptyString
mbstok_ssave:
		.word		strtok_mbstok_emptyString
		.code						;//必要(?) asmブロックの後でCコンパイラが自動的に.codeに戻してくれるとは限らない(?)みたいなので、明示的に.codeに戻しておく方が安全です。
");
#endif//PIECE
#if 0
//■テストプログラム
void test() {
	static const char src[]="表現_,_十回_,_予備";
	char dst[32], *s;
	if(strtok(NULL, ",\\")) { DIE(); }
	s = strtok(strcpy(dst, src), ",\\");
	while(s) {
		printf("'%s'\n", s);
		s = strtok(NULL, ",\\");
	}
	if(strtok(NULL, ",\\")) { DIE(); }
	//□結果
	//│'\x95'
	//│'現_'
	//│'_\x8F'
	//│'回_'
	//│'_\x97'
	//│'備'
	if(mbstok(NULL, ",\\")) { DIE(); }
	s = mbstok(strcpy(dst, src), ",\\");
	while(s) {
		printf("'%s'\n", s);
		s = mbstok(NULL, ",\\");
	}
	if(mbstok(NULL, ",\\")) { DIE(); }
	//□結果
	//│'表現_'
	//│'_十回_'
	//│'_予備'
}
#endif
//}}2016/06/26変更:mbstok(),mbstok_r()を追加しました。既存のstrtok(),strtok_r()とも、処理を共通化しました。
//}}2015/11/23変更:strtok_r()を追加しました。この変更に伴いWin32プロジェクトでも自作のstrtok()を有効にしました。
//{{2015/11/23削除:strtok_r()を追加しました。この変更に伴いWin32プロジェクトでも自作のstrtok()を有効にしました。
//#endif//PIECE
//}}2015/11/23削除:strtok_r()を追加しました。この変更に伴いWin32プロジェクトでも自作のstrtok()を有効にしました。

/* * Tue Jul 11 17:50:40 JST 2006 Naoyuki Sawa
 * - P/ECE開発環境付属のstrstr()には不具合があり、誤った結果を返します。
 *   不具合を再現するプログラム例を、以下に示します。
 *
 *	char dst1[] = { 'a', '\0', 'a', 'b', '\0' }; // 比較先文字列1
 *	char dst2[] = {       'a', 'a', 'b', '\0' }; // 比較先文字列2
 *	char src [] = {            'a', 'b', '\0' }; // 比較元文字列
 *	char* p1 = dstdst(dst1, "ab"); // NULLを返すべきだが、dst1[2]へのポインタを返してしまう
 *	char* p2 = dstdst(dst2, "ab"); // dst2[1]へのポインタを返すべきだが、NULLを返してしまう
 *
 *    誤りのパターンは二種類あります。以下、それぞれについて説明します。
 *
 *    1. マッチしてはいけないのにマッチしてしまう誤りの例:
 *
 *	dst1[]は、文字列として見ると、一文字目の"a"だけで終わっています。
 *	従って、
 *		strstr(dst1, src);
 *	は、
 *		strstr("a", "ab");
 *	と等価であり、結果はNULLになるはずです。
 *	しかし、P/ECE開発環境付属のstrstr()は誤って'\0'を突き抜け、
 *	三文字目～の"ab"にマッチして、三文字目の'a'へのポインタを返してしまいます。
 *
 *    2. マッチしなければいけないのにマッチしない誤りの例
 *
 *	dst2[]は、文字列として見ると、"aab"であり、二文字目～に"ab"を含んでいます。
 *	従って、
 *		strstr(dst2, src);
 *	は、二文字目の'a'へのポインタを返すはずです。
 *	しかし、P/ECE開発環境付属のstrstr()は誤ってNULLを返してしまいます。
 *	P/ECEのstrstr()は、まず、dst2[0～1]とsrc[0～1]の比較を行い、マッチしていないと判断した後、
 *	dst2[]側の走査位置をdst[1]に戻すのを忘れて、dst[2]から比較継続してしまっているみたいです。
 *
 * - P/ECE開発環境付属のstrstr()の不具合を回避するため、自前でstrstr()を用意しました。
 *   速度が要求される場面でstrstr()を使うことは無いと思うので、ごく単純な実装としました。
 *   ※要確認: strstr()の正式な仕様は、s1,s2が必ずヌル終端されていることを前提として良いのでしょうか?	┐
 *   もし、s1のどこかにs2が存在することが確実な場合に、s1がヌル終端されていないことを許す仕様ならば、	│2014/10/12追記
 *   以下の実装では正しく動作しません。(最初に行っているstrlen()が、どこまで行くか予測できないから。)	│strstr()がs1がヌル終端されていなくても動作するように修正しました。
 *   strstr()の正式な仕様を確認する必要があります。							┘
 */
//{{2014/10/12変更:strstr()がs1がヌル終端されていなくても動作するように修正しました。
//char*
//strstr(const char* s1, const char* s2)
//{
//	int n1 = strlen(s1);
//	int n2 = strlen(s2);
//
//	n1 -= n2;
//	while(n1 >= 0) {
//		if(!memcmp(s1, s2, n2)) {
//			return (char*)s1;
//		}
//		s1++;
//		n1--;
//	}
//	return NULL;
//}
//↓2014/10/12変更:strstr()がs1がヌル終端されていなくても動作するように修正しました。
/* * Sun Oct 12 18:49:43 JST 2014 Naoyuki Sawa
 * - strstr()がs1がヌル終端されていなくても動作するように修正しました。s2はヌル終端されていなければなりません。
 * - strcasestr()を追加しました。宣言はinclude/string.hに追加しました。strcasestr()はGNU拡張です。
 */
#ifndef PIECE
static char* strstr_sub(const char* s1, const char* s2, int (*fn)(const char*, const char*, size_t));
char* strstr(const char* s1, const char* s2) {
	return strstr_sub(s1, s2, strncmp);
}
char* strcasestr(const char* s1, const char* s2) {
	return strstr_sub(s1, s2, strncasecmp);
}
static char* strstr_sub(const char* s1, const char* s2, int (*fn)(const char*, const char*, size_t)) {
	/* 部分文字列の文字数を求める。(s2="")でも構わない。 */
	int n = strlen(s2);
	/* 文字列の先頭から検索を開始する。
	 * - (s1="")の場合も、一回目のループは実行する。
	 *   (s2="")の場合は、s1の内容にかかわらず((s1="")であってもなくても)常に一致するからです。 */
	do {
		/* 文字列の現在位置から、部分文字列の文字数分比較を行い、一致したら現在位置を返す。
		 * - (s2="")だった場合、s1の内容にかかわらず((s1="")であってもなくても)、一致する。
		 * - (s1="")だった場合、(s2="")でなければ、一致しない。 */
		if(!(*fn)(s1, s2, n)) { return (char*)s1; }
	/* 文字列の終端でなければ、位置を次へ進める。
	 * - (s1="")だった場合は、一回目で抜けることになる。 */
	} while(*s1++);
	/* 文字列の終端における比較でも一致しなければ、NULLを返す。 */
	return NULL;
}
#else /*PIECE*/
char* strstr(const char* s1, const char* s2);
char* strcasestr(const char* s1, const char* s2);
asm("
		.code
		.align		1
		.global		strstr
		.global		strcasestr
strstr:
		xld.w		%r14, strncmp			;//%r14 := fn = strncmp
		jp		strstr_sub
strcasestr:
		xld.w		%r14, strncasecmp		;//%r14 := fn = strncasecmp
strstr_sub:
		pushn		%r3
		ld.w		%r0, %r12			;//%r0  := s1
		ld.w		%r1, %r13			;//%r1  := s2
		ld.w		%r2, %r14			;//%r2  := fn
		xcall.d		strlen				;//%r10 := n = strlen(s2)
		ld.w		%r12, %r1			;//%r12 :=            s2					*delay*
		ld.w		%r3, %r10			;//%r3  := n
strstr_sub_L10:							;//do {
		ld.w		%r12, %r0			;//  %r12 :=                s1
		ld.w		%r13, %r1			;//  %r13 :=                    s2
		call.d		%r2				;//  %r10 := retval = (*fn)(s1, s2, n)
		ld.w		%r14, %r3			;//  %r14 :=                        n				*delay*
		cmp		%r10, 0				;//  if(!retval) { return s1 }			────┐
		jreq.d		strstr_sub_L20			;//  							│
		ld.w		%r10, %r0			;//  %r10 :=              s1				│	*delay*
		ld.b		%r10, [%r0]+			;//  %r10 := c = *s1++					│
		cmp		%r10, 0				;//} while(c)					────┤
		jrne		strstr_sub_L10			;//							│
strstr_sub_L20:							;//							│
		popn		%r3				;//						←───┘
		ret						;//return  s1 or NULL
");
#endif/*PIECE*/
//}}2014/10/12変更:strstr()がs1がヌル終端されていなくても動作するように修正しました。

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

// * Fri May 20 23:37:14 JST 2016 Naoyuki Sawa
// - mbschr(),mbsstr(),mbspbrk()を追加しました。
//   宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
//{{2017/11/15変更:mbscasestr()を追加し、既存のmbsstr()も処理を共通化して再実装しました。
//#ifndef PIECE
//char* mbsstr(const char* s1, const char* s2) {
//	//部分文字列の文字数を求める。(s2="")でも構わない。
//	int n = strlen(s2);
//	//      ~~~~~~下の処理で_mbsncmp()ではなくstrncmp()を使って比較するので、ここでもmbslen()ではなくstrlen()を使って文字数を求める必要が有る事に注意して下さい。
//	//文字列の先頭から検索を開始する。
//	// - (s1="")の場合も、一回目のループは実行する。
//	//   (s2="")の場合は、s1の内容にかかわらず((s1="")であってもなくても)常に一致するからです。
//	for(;;) {
//		//文字列の現在位置から、部分文字列の文字数分比較を行い、一致したら現在位置を返す。
//		// - (s2="")だった場合、s1の内容にかかわらず((s1="")であってもなくても)、一致する。
//		// - (s1="")だった場合、(s2="")でなければ、一致しない。
//		if(!strncmp(s1, s2, n)) { return (char*)s1; }
//		//  ~~~~~~~一致不一致だけの比較なので、_mbsncmp()を使わなくてもstrncmp()で大丈夫です。そのため、上の処理でmbslen()ではなくstrlen()でnを求めています。
//		//文字列の終端でなければ、位置を次へ進める。
//		// - (s1="")だった場合は、一回目で抜けることになる。
//		if(!mbsnextc(s1)) { return NULL; }
//		s1 = mbsinc(s1);
//	}
//}
//#else //PIECE
//char* mbsstr(const char* s1, const char* s2);
//asm("
//		.code
//		.align		1
//		.global		mbsstr
//mbsstr:
//		pushn		%r2
//		ld.w		%r0, %r12			;//%r0  := s1
//		ld.w		%r1, %r13			;//%r1  := s2
//		xcall.d		strlen				;//%r10 := n = strlen(s2)
//		ld.w		%r12, %r1			;//%r12 :=            s2				*delay*
//		ld.w		%r2, %r10			;//%r2  := n
//mbsstr_L10:							;//for(;;) {
//		ld.w		%r12, %r0			;//  %r12 :=                  s1
//		ld.w		%r13, %r1			;//  %r13 :=                      s2
//		xcall.d		strncmp				;//  %r10 := retval = strncmp(s1, s2, n)
//		ld.w		%r14, %r2			;//  %r14 :=                          n			*delay*
//		cmp		%r10, 0				;//  if(!retval) { return s1 }		────┐
//		jreq		mbsstr_L20			;//  						│
//		xcall.d		mbsnextc			;//  %r10 := c = mbsnextc(s1)			│
//		ld.w		%r12, %r0			;//  %r12 :=              s1			│	*delay*
//		cmp		%r10, 0				;//  if(    !c) { return NULL }		────┤
//		jreq		mbsstr_L30			;//  						│
//		xcall.d		mbsinc				;//  %r10 := s1 = mbsinc(s1)			│
//		ld.w		%r12, %r0			;//  %r12 :=             s1			│	*delay*
//		jp.d		mbsstr_L10			;//}						│
//		ld.w		%r0, %r10			;//  %r0  := s1					│	*delay*
//mbsstr_L20:							;//						│
//		ld.w		%r10, %r0			;//%r10 := s1					│
//mbsstr_L30:							;//						│
//		popn		%r2				;//					←───┘
//		ret						;//return  s1 or NULL
//");
//#endif//PIECE
//↓2017/11/15変更:mbscasestr()を追加し、既存のmbsstr()も処理を共通化して再実装しました。
// * Wed Nov 15 23:26:53 JST 2017 Naoyuki Sawa
// - mbscasestr()を追加し、既存のmbsstr()も処理を共通化して再実装しました。
//   宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
#ifndef PIECE
static char* mbsstr_sub(const char* s1, const char* s2, int (*fn)(const char*, const char*, size_t));
char* mbsstr(const char* s1, const char* s2) {
	return mbsstr_sub(s1, s2, mbsncmp);
}
char* mbscasestr(const char* s1, const char* s2) {
	return mbsstr_sub(s1, s2, mbsncasecmp);
}
static char* mbsstr_sub(const char* s1, const char* s2, int (*fn)(const char*, const char*, size_t)) {
	//部分文字列の文字数を求める。(s2="")でも構わない。
	int n = mbslen(s2);
	//文字列の先頭から検索を開始する。
	// - (s1="")の場合も、一回目のループは実行する。
	//   (s2="")の場合は、s1の内容にかかわらず((s1="")であってもなくても)常に一致するからです。
	for(;;) {
		//文字列の現在位置から、部分文字列の文字数分比較を行い、一致したら現在位置を返す。
		// - (s2="")だった場合、s1の内容にかかわらず((s1="")であってもなくても)、一致する。
		// - (s1="")だった場合、(s2="")でなければ、一致しない。
		if(!(*fn)(s1, s2, n)) { return (char*)s1; }
		//文字列の終端でなければ、位置を次へ進める。
		// - (s1="")だった場合は、一回目で抜けることになる。
		if(!mbsnextc(s1)) { return NULL; }
		s1 = mbsinc(s1);
	}
}
#else //PIECE
char* mbsstr(const char* s1, const char* s2);
char* mbscasestr(const char* s1, const char* s2);
asm("
		.code
		.align		1
		.global		mbsstr
		.global		mbscasestr
mbsstr:
		xld.w		%r14, mbsncmp			;//%r14 := fn = mbsncmp
		jp		mbsstr_sub
mbscasestr:
		xld.w		%r14, mbsncasecmp		;//%r14 := fn = mbsncasecmp
mbsstr_sub:
		pushn		%r3
		ld.w		%r0, %r12			;//%r0  := s1
		ld.w		%r1, %r13			;//%r1  := s2
		ld.w		%r2, %r14			;//%r2  := fn
		xcall.d		mbslen				;//%r10 := n = mbslen(s2)
		ld.w		%r12, %r1			;//%r12 :=            s2				*delay*
		ld.w		%r3, %r10			;//%r3  := n
mbsstr_sub_L10:							;//for(;;) {
		ld.w		%r12, %r0			;//  %r12 :=                s1
		ld.w		%r13, %r1			;//  %r13 :=                    s2
		call.d		%r2				;//  %r10 := retval = (*fn)(s1, s2, n)
		ld.w		%r14, %r3			;//  %r14 :=                        n			*delay*
		cmp		%r10, 0				;//  if(!retval) { return s1 }		────┐
		jreq		mbsstr_sub_L20			;//  						│
		xcall.d		mbsnextc			;//  %r10 := c = mbsnextc(s1)			│
		ld.w		%r12, %r0			;//  %r12 :=              s1			│	*delay*
		cmp		%r10, 0				;//  if(    !c) { return NULL }		────┤
		jreq		mbsstr_sub_L30			;//  						│
		xcall.d		mbsinc				;//  %r10 := s1 = mbsinc(s1)			│
		ld.w		%r12, %r0			;//  %r12 :=             s1			│	*delay*
		jp.d		mbsstr_sub_L10			;//}						│
		ld.w		%r0, %r10			;//  %r0  := s1					│	*delay*
mbsstr_sub_L20:							;//						│
		ld.w		%r10, %r0			;//%r10 := s1					│
mbsstr_sub_L30:							;//						│
		popn		%r3				;//					←───┘
		ret						;//return  s1 or NULL
");
#endif//PIECE
//}}2017/11/15変更:mbscasestr()を追加し、既存のmbsstr()も処理を共通化して再実装しました。

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

//{{2016/10/01変更:pow()のコンパイル条件を変更しました。pow()の再定義はP/ECE環境でのみ必要なので、'VC++以外'とするよりも'P/ECEのみ'とする方が自然だからです。
//#ifndef _MSC_VER	//Visual C++ 6.0ではリリースビルド時に「error C2169: 'pow' : 組み込み関数は定義できません。」になるため除外した。
//↓2016/10/01変更:pow()のコンパイル条件を変更しました。pow()の再定義はP/ECE環境でのみ必要なので、'VC++以外'とするよりも'P/ECEのみ'とする方が自然だからです。
#ifdef  PIECE
//}}2016/10/01変更:pow()のコンパイル条件を変更しました。pow()の再定義はP/ECE環境でのみ必要なので、'VC++以外'とするよりも'P/ECEのみ'とする方が自然だからです。
/* S1C33 Family C Compiler Package の pow() には不具合があり、間違った値を返すことがあります。
 * 例えば pow(1.25, 1.0) の結果が 0.0 になることがあります。まだ原因は究明できていません。
 * pow(1.0～1.5, 0.0以外) あたりが危ないようですが、微妙なコーディングの違いによって発症しないこともあります。
 * 現在、二種類の回避方法が発見されています。
 * ・pow()を呼ぶだけのラッピング関数pow_wrap()を用意し、常にpow_wrap()を経由してpow()を呼び出す。
 *   確かに回避できるのですが、なぜこれだけで回避できるのか全くわからないのが不安なところです。
 * ・pow()をexp()とlog()に展開し、pow()を使わないようにします。
 *   もしも不具合の原因がpow()単体の問題であったならば、この方法で確実に回避できるはずです。
 *   ただし、算術関数の呼び出しが増えるため、プログラムが遅くなってしまう欠点があります。
 * いずれにせよ、もともとの原因がわかっていないので、どちらの方法でも確実に回避できるとは言い切れません。
 */
//double
//pow(double x, double y)
//{
//	return exp(log(x) * y);
//}
//---------------------------------------------------------------------------
//↓* Sun Apr 22 21:28:18 JST 2007 Naoyuki Sawa
//↓- 上の実装では、xが負の場合に対応できていなかったので、以下のように修正しました。
//↓  以下の処理手順は、前述のように不具合のあるEPSONライブラリ版pow()とほぼ同じものですが、なぜか不具合を回避できるようです。
//↓  EPSONライブラリ版pow()の不具合は、処理手順そのものではなく、精度のために複雑な処理をしている部分に原因があるみたいです。
//↓- 不要なexp()やlog()が毎回リンクされてしまうのを避けるために、pow()を別モジュールに分離することも検討しましたが、
//↓  clipprnt.cがpow()を使っていて、結果的にほとんどのアプリケーションにリンク必要なので、分離する必要は無いと判断しました。
double
pow(double x, double y)
{
	double z;

	if(x > 0.0) {
		/* xが正ならば、そのまま計算します。
		 */
		z = exp(y * log(x));
	} else if(x < 0.0) {
		/* xが負ならば...
		 * 1. yが整数でなければ、エラーです。(負のn乗根を含むため)
		 * 2. xを正にして、計算します。
		 * 3. yが奇数ならば、結果を負にします。
		 */
		if((int)y != y) {
			DIE();
		}
		x = -x;
		z = exp(y * log(x));
		if((int)y & 1) {
			z = -z;
		}
	} else { /* (x == 0.0) */
		/* xが零ならば...
		 * 1. yが正ならば、結果は0.0です。
		 * 2. yが負ならば、エラーです。(ゼロ除算になるため)
		 * 3. yが零ならば、結果は1.0です。
		 */
		if(y > 0.0) {
			z = 0.0;
		} else if(y < 0.0) {
			DIE();
		} else { /* (y == 0.0) */
			z = 1.0;
		}
	}

	return z;
}
//{{2016/10/01変更:pow()のコンパイル条件を変更しました。pow()の再定義はP/ECE環境でのみ必要なので、'VC++以外'とするよりも'P/ECEのみ'とする方が自然だからです。
//#endif//_MSC_VER	//Visual C++ 6.0ではリリースビルド時に「error C2169: 'pow' : 組み込み関数は定義できません。」になるため除外した。
//↓2016/10/01変更:pow()のコンパイル条件を変更しました。pow()の再定義はP/ECE環境でのみ必要なので、'VC++以外'とするよりも'P/ECEのみ'とする方が自然だからです。
#endif//PIECE
//}}2016/10/01変更:pow()のコンパイル条件を変更しました。pow()の再定義はP/ECE環境でのみ必要なので、'VC++以外'とするよりも'P/ECEのみ'とする方が自然だからです。

/* * Thu Oct 25 14:45:01 JST 2007 Naoyuki Sawa
 * - Aquaplus版よりも高速な、memcpy()を作成しました。
 * - P/ECE開発環境のmemcpy()は、EPSON版よりも高速な、Aquaplus版を使っています。
 *   今回、Aquaplus版を基にして、もっと高速で省サイズな、CLiP版を作成しました。
 * - コピーのループを工夫して、遅延スロットを最大限に活用しました。
 *   高速化の効果は、以下のとおりです。(TURBO=-1で計測)
 *	コピー処理 (×100回)               | Aquaplus版 | CLiP版    | 高速化
 *	-----------------------------------+------------+-----------+-------
 *	memcpy(0xc00000,0xd00000,0x100000) | 21140[ms]  | 18915[ms] | 約10%
 *	memcpy(0xc00002,0xd00002,0x100000) | 33379[ms]  | 28929[ms] | 約13%
 *	memcpy(0xc00001,0xd00001,0x100000) | 66757[ms]  | 57856[ms] | 約13%
 * - 終了判断を、memcpy_BYTEの一箇所にまとめて、コードサイズを削減しました。
 *   コードサイズ削減の効果は、以下のとおりです。
 *	Aquaplus版=68[byte] → CLiP版=64[byte]
 * * Tue Jan 08 14:58:19 JST 2008 Naoyuki Sawa
 * - memcpy()を、ベクタ領域のFRAMに転送して実行するために、clipidiv.sへ移動しました。
 *   ベクタ領域に収まるように、並べ替えを行いましたが、コード内容は変更していません。
 */
//{{2008/01/08:削除(clipidiv.sへ移動)
//asm("
//	.code
//	.align	1
//	.global	memcpy
//memcpy:
//	;// %r12 := dst
//	;// %r13 := src
//	;// %r14 := cnt
//	cmp	%r14, 3		; if(cnt<=3) memcpy_BYTE
//	jrule.d	memcpy_BYTE
//	ld.w	%r10, %r12	; %r10 := dst (戻り値)			*delay*
//	ld.w	%r9, %r12	; %r9  := dst
//	or	%r9, %r13	; %r9  := dst|src
//	and	%r9, 3		; %r9  := (dst|src)&3
//	jreq	memcpy_WORD	; if(!((dst|src)&3)) memcpy_WORD
//	and	%r9, 1		; %r9  := (dst|src)&1
//	jreq	memcpy_HALF	; if(!((dst|src)&1)) memcpy_HALF
//	;-----------------------;
//memcpy_BYTE:
//	sub	%r14, 1		; %r14 := (cnt-1) ------+
//	jrult	memcpy_RET	; if((cnt-1)<0) return  |
//	 ld.b	%r9, [%r13]+	;   byte = *src++       |
//	 ld.b	[%r12]+, %r9	;   *dst++ = byte       |
//	jrugt.d	-2		; while((cnt-1)>0) <----+ フラグ影響
//	sub	%r14, 1		; %r14 := (cnt-1) -= 1 -+		*delay*
//memcpy_RET:
//	ret
//	;-----------------------;
//memcpy_HALF:
//	xsub	%r15, %r14, 3	; %r15 := (cnt-3) ------+
//	 ld.h	%r9, [%r13]+	;   half = *src++       |
//	 ld.h	[%r12]+, %r9	;   *dst++ = half       |
//	jrugt.d	-2		; while((cnt-3)>0) <----+ フラグ影響
//	sub	%r15, 2		; %r15 := (cnt-3) -= 2 -+		*delay*
//	jp.d	memcpy_BYTE
//	and	%r14, 1		; %r14 := cnt &= 1			*delay*
//	;-----------------------;
//memcpy_WORD:
//	xsub	%r15, %r14, 7	; %r15 := (cnt-7) ------+
//	 ld.w	%r9, [%r13]+	;   word = *src++       |
//	 ld.w	[%r12]+, %r9	;   *dst++ = word       |
//	jrugt.d	-2		; while((cnt-7)>0) <----+ フラグ影響
//	sub	%r15, 4		; %r15 := (cnt-7) -= 4 -+		*delay*
//	jp.d	memcpy_BYTE
//	and	%r14, 3		; %r14 := cnt &= 3			*delay*
//");
//}}2008/01/08:削除(clipidiv.sへ移動)

/* * Thu Oct 25 14:45:01 JST 2007 Naoyuki Sawa
 * - Aquaplus版よりも高速な、memset()を作成しました。
 * - P/ECE開発環境のmemset()は、EPSON版よりも高速な、Aquaplus版を使っています。
 *   今回、Aquaplus版を基にして、もっと高速で省サイズな、CLiP版を作成しました。
 * - フィルのループを工夫して、遅延スロットを最大限に活用しました。
 *   高速化の効果は、以下のとおりです。(TURBO=-1で計測)
 *	フィル処理 (×100回)        | Aquaplus版 | CLiP版    | 高速化
 *	----------------------------+------------+-----------+-------
 *	memset(0xc00000,0,0x100000) | 13352[ms]  | 11127[ms] | 約16%
 *	memset(0xc00002,0,0x100000) | 22252[ms]  | 17802[ms] | 約20%
 *	memset(0xc00001,0,0x100000) | 44505[ms]  | 35604[ms] | 約20%
 * - memset_WORDとmemset_HALFのための、フィル値拡張処理を一部共通化、および、
 *   終了判断を、memset_BYTEの一箇所にまとめて、コードサイズを削減しました。
 *   コードサイズ削減の効果は、以下のとおりです。
 *	Aquaplus版=80[byte] → CLiP版=68[byte]
 * * Tue Jan 08 14:58:19 JST 2008 Naoyuki Sawa
 * - memset()を、ベクタ領域のFRAMに転送して実行するために、clipidiv.sへ移動しました。
 *   ベクタ領域に収まるように、並べ替えを行いましたが、コード内容は変更していません。
 */
//{{2008/01/08:削除(clipidiv.sへ移動)
//asm("
//	.code
//	.align	1
//	.global	memset
//memset:
//	;// %r12 := dst
//	;// %r13 := v,?,?,?
//	;// %r14 := cnt
//	cmp	%r14, 3		; if(cnt<=3) memset_BYTE
//	jrule.d	memset_BYTE
//	ld.w	%r10, %r12	; %r10 := dst (戻り値)			*delay*
//	xand	%r9, %r12, 1	; %r9  := dst&1
//	jrne	memset_BYTE	; if(dst&1) memset_BYTE
//	ld.ub	%r15, %r13	; %r15 := v,0,0,0
//	sll	%r13, 8		; %r13 := 0,v,?,?
//;//	or	%r13, %r15	; %r13 := v,v,?,?-------+
//	xand	%r9, %r12, 2	; %r9  := dst&2         |
//	jrne.d	memset_HALF	; if(dst&2) memset_HALF |
//	or	%r13, %r15	; <---------------------+		*delay*
//	ld.uh	%r13, %r13	; %r13 := v,v,0,0
//	swap	%r15, %r13	; %r15 := 0,0,v,v
//	or	%r13, %r15	; %r13 := v,v,v,v
//	;-----------------------;
//memset_WORD:
//	xsub	%r15, %r14, 7	; %r15 := (cnt-7) ------+
//	 ld.w	[%r12]+, %r13	;   *dst++ = v,v,v,v    |
//	jrugt.d	-1		; while((cnt-7)>0) <----+ フラグ影響
//	sub	%r15, 4		; %r15 := (cnt-7) -= 4 -+		*delay*
//	jp.d	memset_BYTE
//	and	%r14, 3		; %r14 := cnt &= 3			*delay*
//	;-----------------------;
//memset_HALF:
//	xsub	%r15, %r14, 3	; %r15 := (cnt-3) ------+
//	 ld.h	[%r12]+, %r13	;   *dst++ = v,v        |
//	jrugt.d	-1		; while((cnt-3)>0) <----+ フラグ影響
//	sub	%r15, 2		; %r15 := (cnt-3) -= 2 -+		*delay*
//	and	%r14, 1		; %r14 := cnt &= 1
//	;-----------------------;
//memset_BYTE:
//	sub	%r14, 1		; %r14 := (cnt-1) ------+
//	jrult	memset_RET	; if((cnt-1)<0) return  |
//	 ld.b	[%r12]+, %r13	;   *dst++ = v          |
//	jrugt.d	-1		; while((cnt-1)>0) <----+ フラグ影響
//	sub	%r14, 1		; %r14 := (cnt-1) -= 1 -+		*delay*
//memset_RET:
//	ret
//");
//}}2008/01/08:削除(clipidiv.sへ移動)

#ifdef  PIECE
// * Sun Mar 11 22:22:46 JST 2012 Naoyuki Sawa
// - EPSONライブラリのmemmoveを置き換えました。
// - EPSONライブラリのmemmoveも正しく動作はしますが、以下の問題があります。
//   ・memcpyを利用できる場合は、memcpyを利用する方が効率が良い。
//	memcpyの方が、ワード単位コピーで最適化されており、高速だからである。
//	しかし、EPSONライブラリのmemmoveは、内部でバイト単位コピーを行ってしまい、低速である。
//   ・前方コピーの場合は、常にmemcpyを利用できる。
//	従って、memmove内部に、前方コピーの実装を含める必要は無い。
//	しかし、EPSONライブラリのmemmoveは、前方コピーを含んでしまっており、容量の無駄である。
//   ・EPSONライブラリのmemmoveは、後方コピーのループ終端判断を、ループの先頭で行っています。
//	ループ終端判断を、ループの末尾で行う方が、高速化できます。
// - 今回は、上記三点を改善した、memmoveを実装しました。
//
//-----------------------------------------------------------------------------------------------------------------------------
//
//	((dest <= src) || (dest >= src+count)) が成立する場合、memcpyを利用できる
//
//		○memcpyを利用できる
//
//			dest
//			├───────────┐
//			└───────────┘	┌───────────┐
//							├───────────┤
//							src			src+count
//
//		○memcpyを利用できる
//
//					dest
//					├───────────┐
//					└───────┬───┴───────┐
//							├───────────┤
//							src			src+count
//
//		○memcpyを利用できる
//
//							dest			
//							├───────────┐
//							├───────────┤
//							├───────────┤
//							src			src+count
//
//		×memcpyを利用できない
//
//									dest
//									├───────────┐
//							┌───────┴───┬───────┘
//							├───────────┤
//							src			src+count
//
//		○memcpyを利用できる
//
//										dest
//										├───────────┐
//							┌───────────┼───────────┘
//							├───────────┤
//							src			src+count
//
//		○memcpyを利用できる
//
//											dest
//											├───────────┐
//							┌───────────┐	└───────────┘
//							├───────────┤
//							src			src+count
//
//-----------------------------------------------------------------------------------------------------------------------------
//
//	(count == 0) が成立する場合も、memcpyを利用できる。その場合、
//		((dest <= src) || (dest >= src+count))　⇒　((dest <= src) || (dest >= src))　⇒　常に成立
//	となるので、明示的に判断する必要は無い。
//
//		○memcpyを利用できる
//
//			dest
//			│
//			│				
//							│
//							│
//							src(=src+count)
//
//		○memcpyを利用できる
//
//							dest
//							│
//							│
//							│
//							│
//							src(=src+count)
//
//		○memcpyを利用できる
//
//											dest
//											│
//											│
//							│
//							│
//							src(=src+count)
//
//-----------------------------------------------------------------------------------------------------------------------------
//
//	((dest <= src) || (dest >= src+count)) を効率良く判断する方法は、両辺からsrcを引いて、
//		((dest-src <= src-src) || (dest-src >= src-src+count))　⇒　((dest-src <= 0) || (dest-src >= count))
//	(dest-src) がマイナス値になる場合は、符号無し比較を用いれば (マイナス値 > count) が成立するから、
//	(dest-src >= count) に集約でき、(dest-src) が0になる場合だけを判断すれば良い。従って、
//		((dest-src == 0) || (dest-src >= count))
//	で判断できる。
//
//-----------------------------------------------------------------------------------------------------------------------------
void* memmove(void* dest, const void* src, size_t count);
// * Mon Jun 06 21:45:00 JST 2016 Naoyuki Sawa
// - アセンブラmemmove()の高速バージョンを作成しました。
//   これまでは、memcpy()が使用出来ない時は、単純に1バイトづつコピーしていました。
//   高速バージョンでは、ksvec200.sのmemcpyと同様に、コピー元アドレスとコピー先アドレスのメモリアライメント,及び,残り転送バイト数によって、ワード単位,及び,ハーフワード単位でもコピーするようにしました。
//   memcpy()が使用出来ない時の速度が、最大3.2倍程度早くなりました。
// - ただし、この変更によって、コードサイズが64バイト大きくなりました。
//   これまで敢えて高速化していなかった理由は、memmove()は使用頻度が低いので全体の性能に影響するケースは少なく、高速化よりもコードサイズ低減を優先していたからです。
//   しかし、本日clipmemc.cにMemCpt_Resize()を追加し、MemCpt_Resize()はmemmove()を頻繁に使用するので、memmove()の速度が大きな影響を持つケースが生じました。
//   今後、clipmemc.cモジュールの使用頻度が高くなるかどうかがまだ判らないのですが、コードサイズ64バイト程度の増加ならば許容範囲だと思って、この機会にmemmove()を高速化しておく事にしました。
// - シンボルUSE_SPEED_OPTIMIZED_MEMMOVEを定義するかしないかによって、これまでのコードサイズが小さいバージョンと、本日作成した高速バージョンを選択出来ます。
//   今後は、本日作成した高速バージョンを使って行く予定ですが、もしコードサイズを減らす必要が生じたり、高速バージョンの不具合が生じた場合は、USE_SPEED_OPTIMIZED_MEMMOVEをコメントアウトして古いバージョンに戻してみてください。
#define USE_SPEED_OPTIMIZED_MEMMOVE	//このシンボルを定義するかしないかによって、memmoveのバージョンを選択出来ます。定義無し=省メモリバージョン,定義有り=高速バージョン
#ifndef USE_SPEED_OPTIMIZED_MEMMOVE
//□コードサイズは小さいが、memcpy()が使用出来なかった時は低速なバージョン。2012/03～2016/06まで長期間安定して動作していた実績が有ります。
asm("
		.code
		.align		1
		.global		memmove
memmove:
		sub		%r12, %r13		;// %r12 := dest-src
		jreq		2			;// if((dest-src == 0) ||	//jreq条件が成立しているので、xjrugeで必ず分岐する
		cmp		%r12, %r14		;//    (dest-src >= count))
		xjruge.d	memcpy			;//       return memcpy(dest, src, count)
		add		%r12, %r13		;// %r12 := dest			*delay*
		;//
		add		%r12, %r14		;// %r12 := dest += count
		add		%r13, %r14		;// %r13 := src  += count
		;//
		sub		%r13, 1			;// <---------------------------+
memmove_LOOP:						;// do {			|
;//		sub		%r13, 1			;//   %r13 := src--	--------+
		ld.b		%r9, [%r13]		;//   %r9  := c = *src		|
		sub		%r12, 1			;//   %r12 := dest--		|	*anti-interlock*
		ld.b		[%r12], %r9		;//   *dest = c			|
		sub		%r14, 1			;//   %r14 := count--		|
		jrne.d		memmove_LOOP		;// } while(count != 0)		|
		sub		%r13, 1			;// <---------------------------+	*delay*
		ret.d					;// return  dest
		ld.w		%r10, %r12		;// %r10 := dest			*delay*
");
#else //USE_SPEED_OPTIMIZED_MEMMOVE
//□コードサイズは大きいが、memcpy()が使用出来ない時も(メモリアライメントの条件が合えば)高速に動作するバージョン。2016/06～使用開始します。
asm("
		.code
		.align		1
		.global		memmove
memmove:
		sub		%r12, %r13		;// %r12 := dest-src
		jreq		2			;// if((dest-src == 0) ||	//jreq条件が成立しているので、xjrugeで必ず分岐する	ここで%r12は変更されているのでmemmove_RETへ分岐してはいけません。	{{2016/06/16コメント追記:(dest==src)だった場合の最適化処理をmemmove自身で行う必要は有りません。本日ksvec200.sのmemcpyを(dest==src)ならば何もせずに帰るように最適化したので、memmove(foo,foo,count)の場合はこれまで通りmemcpyへ分岐しておけば、countがどんなに大きくても一瞬で終わります。まとめますと、本日memmoveは変更していませんが、memcpyの最適化の恩恵によって、memmove(foo,foo,count)の場合も最適化されるようになりました。memmoveの呼び出し側でも、(dest==src)かどうかを判断してmemmove呼び出しをやめるような最適化は不要であり、常にmemmoveを呼び出して構いません。}}
		cmp		%r12, %r14		;//    (dest-src >= count))
		xjruge.d	memcpy			;//       return memcpy(dest, src, count)
		add		%r12, %r13		;// %r12 := dest			*delay*
		;//
		add		%r12, %r14		;// %r12 := dest += count
		add		%r13, %r14		;// %r13 := src  += count
		;//
		cmp		%r14, 3			;// ～～～～～ここから下は、ksvec200.sのmemcpyを元に改造して作りました。～～～～～
		jrule		memmove_BYTE		;//
		ld.w		%r9, %r12		;//
		or		%r9, %r13		;//
		and		%r9, 3			;//
		jreq		memmove_WORD		;//
		and		%r9, 1			;//
		jreq		memmove_HALF		;//
memmove_BYTE:						;//
		sub		%r14, 1			;//	既に(%r14=0)の場合は以下の処理を行わないために判定する。
		jrult		memmove_RET		;//
		sub		%r13, 1			;//	────┐
		ld.b		%r9, [%r13]		;//	←───┤
		 sub		%r12, 1			;//		│
		 ld.b		[%r12], %r9		;//		│
		 sub		%r14, 1			;//		│
		jruge.d		-4			;//		│			最初に1引いているので、継続条件は'>'ではなく'>='です。
		sub		%r13, 1			;//	────┘			*delay*
memmove_RET:						;//	もう%r13は参照しないので最後の遅延スロットで引き過ぎたsub %r13,1を相殺する必要は無い。
		ret.d					;//
		ld.w		%r10, %r12		;//					*delay*
		;//-------------------------------------;//
memmove_HALF:						;//
		sub		%r14, 2			;//	((%r14)&1)の成分はループ回数に影響しないようにするため。
		sub		%r13, 2			;//	────┐		│
		ld.h		%r9, [%r13]		;//	←───┤		│
		 sub		%r12, 2			;//		│		│
		 ld.h		[%r12], %r9		;//		│		│
		 sub		%r14, 2			;//		│		│
		jruge.d		-4			;//		│		│	最初に2引いているので、継続条件は'>'ではなく'>='です。
		sub		%r13, 2			;//	────┘───┐	│	*delay*
		add		%r13, 2			;//	相殺←─────┘	│
		jp.d		memmove_BYTE		;//				│
		add		%r14, 2			;//	相殺←─────────┘	*delay*
		;//-------------------------------------;//
memmove_WORD:						;//
		sub		%r14, 4			;//	((%r14)&3)の成分はループ回数に影響しないようにするため。
		sub		%r13, 4			;//	────┐		│
		ld.w		%r9, [%r13]		;//	←───┤		│
		 sub		%r12, 4			;//		│		│
		 ld.w		[%r12], %r9		;//		│		│
		 sub		%r14, 4			;//		│		│
		jruge.d		-4			;//		│		│	最初に4引いているので、継続条件は'>'ではなく'>='です。
		sub		%r13, 4			;//	────┘───┐	│	*delay*
		add		%r13, 4			;//	相殺←─────┘	│
		jp.d		memmove_BYTE		;//				│
		add		%r14, 4			;//	相殺←─────────┘	*delay*
");
#endif//USE_SPEED_OPTIMIZED_MEMMOVE
#endif//PIECE
/*---------------------------------------------------------------------------*/
//{{2016/06/16追加:「memcpy,memmove,memsetのテストプログラム」を追記しました。
// * Thu Jun 16 20:40:56 JST 2016 Naoyuki Sawa
// - cliplibc.cのアセンブラ版memmoveの実装の下に、「memcpy,memmove,memsetのテストプログラム」を追記しました。
//   本日(2016/06/16)時点で、VC++6.0とP/ECEで結果が一致する事を確認しました。
//   もし今後memcpy,memmove,memsetを変更する場合は、このテストプログラムを使って動作確認を行って下さい。
#if 0
//□memcpy,memmove,memsetのテストプログラム。	2016/06/16時点で、VC++6.0とP/ECEで結果が一致する事を確認しました。
char buf[131072];
int app_main(int argc, char* argv[]) {
	int i;
	seed = 0;
	for(i = 0; i < sizeof buf; i++) { buf[i] = RND8(seed); }
	for(i = 0; i < 10000; i++) {
		int dest  = RND16_RANGE(seed, 64, sizeof buf - 64);
		int src   = RND16_RANGE(seed, 64, sizeof buf - 64);
		int count = RND16_RANGE(seed,  0,            4096);
		switch(RND16_RANGE(seed, 0, 3)) {
		case 0:
			if(!RND16_RANGE(seed, 0, 100)) { dest = src; }
			if(((int)(dest + count) > (int)(sizeof buf - 64)) ||
			   ((int)(src  + count) > (int)(sizeof buf - 64))) { count = 0; }
			if((src < dest) && (dest < src + count)) {	//VC++6.0のmemcpyは常にmemmoveの動作をしているようなので、memmoveが必要なケースではVC++6.0もP/ECEもmemmoveに統一して比較する事にした。
				if(memmove(&buf[dest], &buf[src], count) != &buf[dest]) { DIE(); }
			} else {
				if(memcpy(&buf[dest], &buf[src], count) != &buf[dest]) { DIE(); }
			}
			break;
		case 1:
			if(!RND16_RANGE(seed, 0, 100)) { dest = src; }
			if(((int)(dest + count) > (int)(sizeof buf - 64)) ||
			   ((int)(src  + count) > (int)(sizeof buf - 64))) { count = 0; }
			if(memmove(&buf[dest], &buf[src], count) != &buf[dest]) { DIE(); }
			break;
		case 2:
			if((int)(dest + count) > (int)(sizeof buf - 64)) { count = 0; }
			if(memset(&buf[dest], src, count) != &buf[dest]) { DIE(); }
			break;
		}
	}
	die("正常終了\nCRC = %08X", crc32_ieee_r(buf, sizeof buf));	//'正常終了 CRC = 9BF1BF0E'と表示されたら正常です。値が違う場合はバグの可能性が有ります。
	return 0;
}
#endif
//}}2016/06/16追加:「memcpy,memmove,memsetのテストプログラム」を追記しました。
/*****************************************************************************
 *	標準Ｃライブラリの補助
 *****************************************************************************/

#ifndef PIECE
#ifndef _MSC_VER	//Visual C++ 6.0ではリリースビルド時に「error C2169: 'strlen' : 組み込み関数は定義できません。」になるため除外した。
size_t strlen(const char* string) {
	return strnlen(string, SIZE_MAX);
}
#endif//_MSC_VER	//Visual C++ 6.0ではリリースビルド時に「error C2169: 'strlen' : 組み込み関数は定義できません。」になるため除外した。
/* strnlen()関数は、stringが指す文字列の長さを、バイト数で返す。
 * 長さには、終端の'\0'は含まない。
 * 長さは最大で、nまでである。
 * strnlen()関数は、stringの最初のnバイトのみを検査し、string+nより先を検査することはない。
 */
size_t strnlen(const char* string, size_t n) {
	size_t len = 0;
	while((n--) && (*string++)) {
		len++;
	}
	return len;
}
#else /*PIECE*/
size_t strlen(const char* string);
size_t strnlen(const char* string, size_t n);
asm("
		.code
		.align		1
		.global		strlen
		.global		strnlen
strlen:
		ld.w		%r13, -1		;// %r13 := n = SIZE_MAX
strnlen:
		ld.w		%r10, -1		;// %r10 := len = -1	最後のループで余分に+1するので、予め-1しておく
		sub		%r13, 1			;// %r13 := n--				<---------------+
		jrult		4			;// if(n == -1) { return len + 1 }	--------+	|
		ld.b		%r9, [%r12]+		;// %r9  := c = *string++			|	|
		cmp		%r9, 0			;// if(c ==  0) { return len + 1 }		|	|
		jrne.d		-4			;//					--------|-------+
		add		%r10, 1			;// %r10 := len++			<-------+		*delay*
		ret
");
#endif/*PIECE*/

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

// * Thu Dec 29 07:57:44 JST 2016 Naoyuki Sawa
// - mbslen(),mbsnlen()を追加しました。
//   宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
// - P/ECEには_mbslen()も_mbsnlen()も無いので、当ライブラリのmbslen(),及び,mbsnlen()を使って下さい。
// - VC++6.0には組み込みの_mbslen()が有りますが、検証のために当ライブラリのmbslen()を使って下さい。
//   また、VC++6.0には組み込みの_mbsnlen()は無いので、検証も兼ねて当ライブラリのmbslen()を使って下さい。
// - VS2015には組み込みの_mbslen(),及び,_mbsnlen()が有りますが、検証のために当ライブラリのmbslen()を使って下さい。
#ifndef PIECE
size_t mbslen(const char* string) {
	return mbsnlen(string, SIZE_MAX);
}
size_t mbsnlen(const char* string, size_t n) {
	size_t len = 0;
	while(n-- && mbsnextc(string)) {
		string = mbsinc(string);
		len++;
	}
	return len;
}
#else //PIECE
size_t mbslen(const char* string);
size_t mbsnlen(const char* string, size_t n);
asm("
		.code
		.align		1
		.global		mbslen
		.global		mbsnlen
mbslen:
		ld.w		%r13, -1		;//%r13 := n = SIZE_MAX
mbsnlen:
		pushn		%r2			;//
		ld.w		%r0, %r12		;//%r0  := string
		ld.w		%r1, %r13		;//%r1  := n
		ld.w		%r2, 0			;//%r2  := len = 0
mbsnlen_LOOP:						;//for(;;) {
		sub		%r1, 1			;//  %r1  := n--
		jrult		mbsnlen_RET		;//  if(n == -1) { return len }
		xcall.d		mbsnextc		;//  %r10 := c      = mbsnextc(string)
		ld.w		%r12, %r0		;//  %r12 :=                   string		*delay*
		cmp		%r10, 0			;//  if(!c) {      return len }
		jreq		mbsnlen_RET		;//  
		xcall.d		mbsinc			;//  %r10 := string = mbsinc(  string)
		ld.w		%r12, %r0		;//  %r12 :=                   string		*delay*
		ld.w		%r0, %r10		;//  %r0  := string
		jp.d		mbsnlen_LOOP		;//}
		add		%r2, 1			;//  %r2  := len++				*delay*
mbsnlen_RET:						;//
		ld.w		%r10, %r2		;//%r10 := len
		popn		%r2			;//
		ret					;//return  len
");
#endif//PIECE

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

#ifndef PIECE
#ifndef GC_LEAK_DETECTOR_H	//Boehm GCのleak_detector.hをインクルードしている場合は、定義が重複するのでここでは定義しない。
char* strdup(const char* string) {
	return strndup(string, SIZE_MAX);
}
/* strndup()関数は、strdup()と同様であるが、最大でnバイトだけを複製する。
 * stringがnよりも長い場合は、nバイトだけを複製し、終端の'\0'を追加する。
 */
char* strndup(const char* string, size_t n) {
	size_t len = strnlen(string, n);
	char* p = malloc(len + 1/*終端の'\0'の分、忘れずに!*/);
	if(p != NULL) {
		p[len] = '\0';
		memcpy(p, string, len);
	}
	return p;
}
#endif//GC_LEAK_DETECTOR_H	//Boehm GCのleak_detector.hをインクルードしている場合は、定義が重複するのでここでは定義しない。
#else /*PIECE*/
char* strdup(const char* string);
char* strndup(const char* string, size_t n);
asm("
		.code
		.align		1
		.global		strdup
		.global		strndup
strdup:
		ld.w		%r13, -1		;// %r13 := n = SIZE_MAX
strndup:
		pushn		%r1
		;// %r12 := string
		;// %r13 := n
		xcall.d		strnlen			;// %r10 := len = strnlen(string, n)
		ld.w		%r0, %r12		;// %r0  :=               string	*delay*
		ld.w		%r1, %r10		;// %r1  := len
		;// %r0  := string
		;// %r1  := len
		ld.w		%r12, %r1		;// %r12 :=            len
		xcall.d		malloc			;// %r10 := p = malloc(len + 1)
		add		%r12, 1			;// %r12 :=            len + 1		*delay*
		cmp		%r10, 0			;// if(p != NULL) {
		jreq		strndup_RET
		;// %r0  := string
		;// %r1  := len
		;// %r10 := p
		ld.w		%r12, %r10		;//   %r12 :=            p
		add		%r10, %r1		;//   %r10 := p + len
		ld.b		[%r10], %r8		;//         *(p + len) = '￥0'		■注1■
		ld.w		%r13, %r0		;//   %r13 :=               string
		xcall.d		memcpy			;//   %r10 := p = memcpy(p, string, len)
		ld.w		%r14, %r1		;//   %r14 :=                       len	*delay*
strndup_RET:	;// %r10 := p				;// }
		popn		%r1
		ret					;// return p
");
//■注1■
// - asmブロック内のコメントに、半角「\」を使用しないよう、注意してください。
// - たとえば、上記コメント内の「*(p+len)='￥0'」を「*(p+len)='\0'」とすると、そこでasmブロックが終了してしまいます。
//   なぜなら、コンパイラはasmブロックを文字列リテラルとして扱い、'\0' をエスケープシーケンスとして展開するからです。
// - 上記コードの場合は、たまたま、strndup_RET の未定義エラーとなり、リンク時に発見できます。
//   しかし、常にビルド時のエラーとして発見できるとは限らず、見つけ辛いバグの原因となります。
#endif/*PIECE*/

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

#ifndef PIECE
int strcasecmp(const char* s1, const char* s2) {
	return strncasecmp(s1, s2, SIZE_MAX);
}
int strncasecmp(const char* s1, const char* s2, size_t n) {
	int c1, c2;
	do {
		if((int)--n == -1) { return 0; }
		c1 = tolower((unsigned char)*s1++);
		c2 = tolower((unsigned char)*s2++);
	} while(c1 && (c1 == c2));
	return c1 - c2;
}
#else /*PIECE*/
/* * Fri Apr 20 11:09:47 JST 2012 Naoyuki Sawa
 * - strcasecmp()とstrncasecmp()をアセンブラ化しました。
 */
int strcasecmp(const char* s1, const char* s2);
int strncasecmp(const char* s1, const char* s2, size_t n);
asm("
		.code
		.align		1
		.global		strcasecmp
		.global		strncasecmp
strcasecmp:
		ld.w		%r14, -1		;//%r14 := n = UINT_MAX
strncasecmp:
		pushn		%r3
		ld.w		%r1, %r12		;//%r1  := s1
		ld.w		%r2, %r13		;//%r2  := s2
		ld.w		%r3, %r14		;//%r3  := n
		sub		%r3, 1			;//	←───────────────────┐
strncasecmp_LOOP:					;//for(;;) {					│
;//		sub		%r3, 1			;//  %r3  := n--	────────────┤
		jrult		strncasecmp_RET0	;//  if(n == -1) { return 0 }			│
		ld.ub		%r12, [%r2]+		;//  %r12 := c2 = (unsigned char)*s2++		│
		xcall		tolower			;//  %r10 := c2 = tolower(c2)			│
;//		ld.w		%r0, %r10		;//  %r0  := c2		────────┐	│
		ld.ub		%r12, [%r1]+		;//  %r12 := c1 = (unsigned char)*s1++	│	│
		xcall.d		tolower			;//  %r10 := c1 = tolower(c1)		│	│
		ld.w		%r0, %r10		;//  	←───────────────┘	│	*delay*
		cmp		%r10, 0			;//  if(c1 ==  0) { return c1 - c2 }		│
		jreq.d		strncasecmp_EXIT	;//  						│
		sub		%r10, %r0		;//  %r10 :=               c1 - c2		│	*delay*
		jreq.d		strncasecmp_LOOP	;//  if(c1 != c2) { return c1 - c2 }		│
		sub		%r3, 1			;//  	←───────────────────┘	*delay*
strncasecmp_EXIT:					;//}
		popn		%r3
		ret
strncasecmp_RET0:
		jp.d		strncasecmp_EXIT
		ld.w		%r10, 0			;//%r10 := 0						*delay*
");
#endif /*PIECE*/

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

// * Wed Nov 15 23:26:53 JST 2017 Naoyuki Sawa
// - mbscasecmp(),mbsncasecmp()を追加しました。
//   宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
#ifndef PIECE
int mbscasecmp(const char* s1, const char* s2) {
	return mbsncasecmp(s1, s2, SIZE_MAX);
}
int mbsncasecmp(const char* s1, const char* s2, size_t n) {
	int c1, c2;
	for(;;) {
		if((int)--n == -1) { return 0; }
		c1 = tolower(mbsnextc(s1));
		c2 = tolower(mbsnextc(s2));
		if(!c1 || (c1 != c2)) { break; }
		s1 = mbsinc(s1);
		s2 = mbsinc(s2);
	}
	return c1 - c2;
}
#else //PIECE
int mbscasecmp(const char* s1, const char* s2);
int mbsncasecmp(const char* s1, const char* s2, size_t n);
asm("
		.code
		.align		1
		.global		mbscasecmp
		.global		mbsncasecmp
mbscasecmp:
		ld.w		%r14, -1		;//%r14 := n = UINT_MAX
mbsncasecmp:
		pushn		%r3
		ld.w		%r1, %r12		;//%r1  := s1
		ld.w		%r2, %r13		;//%r2  := s2
		ld.w		%r3, %r14		;//%r3  := n
mbsncasecmp_LOOP:					;//for(;;) {
		sub		%r3, 1			;//  %r3  := n--
		jrult		mbsncasecmp_RET0	;//  if(n == -1) { return 0 }
		xcall.d		mbsnextc		;//  %r10 := c2 = mbsnextc(s2)
		ld.w		%r12, %r2		;//  %r12 :=               s2		*delay*
		xcall.d		tolower			;//  %r10 := c2 = tolower(c2)
		ld.w		%r12, %r10		;//  %r12 :=              c2		*delay*
		ld.w		%r0, %r10		;//  %r0  := c2
		xcall.d		mbsnextc		;//  %r10 := c1 = mbsnextc(s1)
		ld.w		%r12, %r1		;//  %r12 :=               s1		*delay*
		xcall.d		tolower			;//  %r10 := c1 = tolower(c1)
		ld.w		%r12, %r10		;//  %r12 :=              c1		*delay*
		cmp		%r10, 0			;//  if(c1 ==  0) { return c1 - c2 }
		jreq.d		mbsncasecmp_EXIT	;//  
		sub		%r10, %r0		;//  %r10 :=               c1 - c2	*delay*
		jrne		mbsncasecmp_EXIT	;//  if(c1 != c2) { return c1 - c2 }
		xcall.d		mbsinc			;//  %r10 := s1 = mbsinc(s1)
		ld.w		%r12, %r1		;//  %r12 :=             s1		*delay*
		ld.w		%r1, %r10		;//  %r1  := s1
		xcall.d		mbsinc			;//  %r10 := s2 = mbsinc(s2)
		ld.w		%r12, %r2		;//  %r12 :=             s2		*delay*
		jp.d		mbsncasecmp_LOOP	;//}
		ld.w		%r2, %r10		;//  %r2  := s2				*delay*
mbsncasecmp_RET0:
		ld.w		%r10, 0			;//%r10 := 0
mbsncasecmp_EXIT:
		popn		%r3
		ret
");
#endif//PIECE

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

/* * Sat Dec 15 14:14:55 JST 2012 Naoyuki Sawa
 * - strcmp()とstrncmp()は、P/ECE開発環境のEPSONライブラリにも含まれていますが、自分で再実装しました。
 *   EPSON版は、strcmp()とstrncmp()が個別に実装されていますが、まとめて実装し、少し省メモリになりました。
 * - 実際には、本モジュールに含めると、必要の無い場合も必ずリンクされてしまい、却ってメモリ消費が増えます。
 *   アプリケーションが、strcmp()とstrncmp()を両方使用している場合のみ、省メモリになったと考えてください。
 */
#ifndef PIECE
#ifndef _MSC_VER	//Visual C++ 6.0ではリリースビルド時に「error C2169: 'strcmp' : 組み込み関数は定義できません。」になるため除外した。
int strcmp(const char* s1, const char* s2) {
	return strncmp(s1, s2, SIZE_MAX);
}
#endif//_MSC_VER	//Visual C++ 6.0ではリリースビルド時に「error C2169: 'strcmp' : 組み込み関数は定義できません。」になるため除外した。
int strncmp(const char* s1, const char* s2, size_t n) {
	int t = 0; /* 引数nに0が指定された場合に戻り値を0とするため、(t=0)に初期化しておく。 */
	for(;;) {
		int c1, c2;
		if((int)--n == -1) {
			break; /* この位置では必ず(t==0)である。従って"return 0"に相当する。 */
		}
		c1 = *(unsigned char*)s1++;
		c2 = *(unsigned char*)s2++;
		if((t = c1 - c2) != 0) {
			break;
		}
		if(c2 == 0) {
			break; /* この位置では必ず(t==0)である。従って"return 0"に相当する。 */
		}
	}
	return t;
}
#else /*PIECE*/
int strcmp(const char* s1, const char* s2);
int strncmp(const char* s1, const char* s2, size_t n);
asm("
		.code
		.align		1
		.global		strcmp
		.global		strncmp
strcmp:
		ld.w		%r14, -1		;//%r14 := n = SIZE_MAX
strncmp:
		ld.w		%r10, 0			;//%r10 := t = 0
strncmp_LOOP:						;//for(;;) {
		sub		%r14, 1			;//  if((int)--n == -1) { break }
		jrult		strncmp_RET		;//  
		ld.ub		%r10, [%r12]+		;//  %r10 := c1 = *(unsigned char*)s1++
		ld.ub		%r11, [%r13]+		;//  %r11 := c2 = *(unsigned char*)s2++
		sub		%r10, %r11		;//  %r10 := t  = c1 - c2
		jrne		strncmp_RET		;//  if(t  != 0) break
		cmp		%r11, 0			;//  if(c2 == 0) { break }
		jrne		strncmp_LOOP		;//  
strncmp_RET:						;//}
		ret
");
#endif /*PIECE*/

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

// * Wed Nov 15 23:26:53 JST 2017 Naoyuki Sawa
// - mbscmp(),mbsncmp()を追加しました。
//   宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
// * Sat Mar 31 23:59:59 JST 2018 Naoyuki Sawa
// - mbscmp(),mbsncmp()のアセンブラ版のバグを修正しました。
//   これまでは、同じ文字列を比較しても、違う文字列と判断してしまっていました。
//   P/ECE環境だけで発生していたバグであり、Windows環境ではこれまでも正しく動いていました。
//   本日の修正により、P/ECE環境でも正しく動作するようになりました。
#ifndef PIECE
int mbscmp(const char* s1, const char* s2) {
	return mbsncmp(s1, s2, SIZE_MAX);
}
int mbsncmp(const char* s1, const char* s2, size_t n) {
	int t = 0; //引数nに0が指定された場合に戻り値を0とするため、(t=0)に初期化しておく。
	for(;;) {
		int c1, c2;
		if((int)--n == -1) {
			break; //この位置では必ず(t==0)である。従って"return 0"に相当する。
		}
		c1 = mbsnextc(s1);
		c2 = mbsnextc(s2);
		if((t = c1 - c2) != 0) {
			break;
		}
		if(c2 == 0) {
			break; //この位置では必ず(t==0)である。従って"return 0"に相当する。
		}
		s1 = mbsinc(s1);
		s2 = mbsinc(s2);
	}
	return t;
}
#else //PIECE
int mbscmp(const char* s1, const char* s2);
int mbsncmp(const char* s1, const char* s2, size_t n);
asm("
		.code
		.align		1
		.global		mbscmp
		.global		mbsncmp
mbscmp:
		ld.w		%r14, -1		;//%r14 := n = SIZE_MAX
mbsncmp:
		pushn		%r3
		ld.w		%r0, 0			;//%r0  := t = 0
		ld.w		%r1, %r12		;//%r1  := s1
		ld.w		%r2, %r13		;//%r2  := s2
		ld.w		%r3, %r14		;//%r3  := n
mbsncmp_LOOP:						;//for(;;) {
		sub		%r3, 1			;//  if((int)--n == -1) { break }
		jrult		mbsncmp_RET		;//  
		xcall.d		mbsnextc		;//  %r10 := c1 = mbsnextc(s1)
		ld.w		%r12, %r1		;//  %r12 :=               s1		*delay*
		ld.w		%r0, %r10		;//  %r0  := c1
		xcall.d		mbsnextc		;//  %r10 := c2 = mbsnextc(s2)
		ld.w		%r12, %r2		;//  %r12 :=               s2		*delay*
		sub		%r0, %r10		;//  %r0  := t  = c1 - c2
		jrne		mbsncmp_RET		;//  if(t  != 0) { break }
		cmp		%r10, 0			;//  if(c2 == 0) { break }
		jreq		mbsncmp_RET		;//  
		xcall.d		mbsinc			;//  %r10 := s1 = mbsinc(s1)
		ld.w		%r12, %r1		;//  %r12 :=             s1		*delay*
		ld.w		%r1, %r10		;//  %r1  := s1
		xcall.d		mbsinc			;//  %r10 := s2 = mbsinc(s2)
		ld.w		%r12, %r2		;//  %r12 :=             s2		*delay*
		jp.d		mbsncmp_LOOP		;//}
		ld.w		%r2, %r10		;//  %r2  := s2				*delay*
mbsncmp_RET:						;//
		ld.w		%r10, %r0		;//%r10 := t
		popn		%r3
		ret
");
#endif//PIECE

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

/* * Sat Dec 15 14:14:55 JST 2012 Naoyuki Sawa
 * - strchr()とstrrchr()は、P/ECE開発環境のEPSONライブラリにも含まれていますが、自分で再実装しました。
 *   EPSON版のstrchr()とstrrchr()で処理に無駄がある点を改善し、今回の実装の方が少し省メモリになりました。
 * - 実際には、本モジュールに含めると、必要の無い場合も必ずリンクされてしまい、却ってメモリ消費が増えます。
 *   アプリケーションが、strchr()とstrrchr()を両方使用している場合のみ、省メモリになったと考えてください。
 * * Sat Nov 21 21:27:06 JST 2015 Naoyuki Sawa
 * - strchrnul()を追加しました。宣言はinclude/string.hに追加しました。
 *   □「Linux Programmer's Manual - STRCHR」(http://linuxjm.osdn.jp/html/LDP_man-pages/man3/strchr.3.html)より引用:
 *   │strchrnul()関数はstrchr()と同様だが、cがs中に見つからなかった場合に、返り値としてNULLでなく、sの末尾のヌルバイトへのポインターを返す点が異なる。
 *   │strchrnul()関数は一致した文字へのポインターを返す。文字が見つからない場合は、sの末尾のヌルバイトへのポインター(つまりs+strlen(s))を返す。
 *   │strchrnul() は GNU 拡張である。
 */
#ifndef PIECE
char* strchr(const char* s, int c) {
	int tmp;
	do {
		if((tmp = *(unsigned char*)s) == c) {
			return (char*)s; /* 文字列s中に最初に文字cが現れた位置へのポインタを返す。 */
		}
		s++;
	} while(tmp != 0);
	return NULL; /* 文字列s中に文字cが見つからない場合はNULLを返す。 */
}
char* strrchr(const char* s, int c) {
	char* result = NULL; /* 文字列s中に最後に文字cが現れた位置へのポインタ。文字列s中に文字cが見つからない場合はNULLを返す。 */
	int tmp;
	do {
		if((tmp = *(unsigned char*)s) == c) {
			result = (char*)s; /* 文字列s中に最後に文字cが現れた位置へのポインタを更新する。 */
		}
		s++;
	} while(tmp != 0);
	return result; /* 文字列s中に最後に文字cが現れた位置へのポインタを返す。文字列s中に文字cが見つからない場合はNULLを返す。 */
}
char* strchrnul(const char* s, int c) {
	for(;;) {
		int tmp = *(unsigned char*)s++;
		if((tmp == c) || !tmp) { break; }
	}
	return (char*)(s - 1); //strchrnul()関数は一致した文字へのポインターを返す。文字が見つからない場合は、sの末尾のヌルバイトへのポインター(つまりs+strlen(s))を返す。
}
#else /*PIECE*/
char*  strchr(const char* s, int c);
char* strrchr(const char* s, int c);
char* strchrnul(const char* s, int c);
asm("
		.code
		.align		1
		.global		strchr
		.global		strrchr
		.global		strchrnul
;//---------------------------------------------;//
;// strchr()関数は、文字列s中に最初に文字cが現れた位置へのポインタを返す。
;// 文字列s中に文字cが見つからない場合はNULLを返す。
;// 終端のnulは文字列の一部と見なされ、cにnulが指定された場合は終端のnulへのポインタを返す。←←←要注意!!
;//---------------------------------------------;//
strchr:						;// do {
		ld.ub		%r10, [%r12]	;//   %r10 := tmp = *(unsigned char*)s
		cmp		%r10, %r13	;//   if(tmp == c)			!INTERLOCK!
		jreq		strchr_RET	;//     goto strchr_RET
		cmp		%r10, 0		;//   
		jrne.d		strchr		;// } while(tmp != 0)
		add		%r12, 1		;//   %r12 := s++			*delay*
		ret				;// return  0
strchr_RET:					;//
		ret.d				;// return  s
		ld.w		%r10, %r12	;// %r10 := s				*delay*
;//---------------------------------------------;//
;// strrchr()関数は、文字列s中に最後に文字cが現れた位置へのポインタを返す。
;// 文字列s中に文字cが見つからない場合はNULLを返す。
;// 終端のnulは文字列の一部と見なされ、cにnulが指定された場合は終端のnulへのポインタを返す。←←←要注意!!
;//---------------------------------------------;//
strrchr:					;//
		ld.w		%r10, 0		;// %r10 := result = 0
strrchr_LOOP:					;// do {
		ld.ub		%r9, [%r12]	;//   %r9  := tmp = *(unsigned char*)s
		cmp		%r9, %r13	;//   					!INTERLOCK!
		jrne.d		3		;//   if(tmp == c)
		cmp		%r9, 0		;//   <-------------------------+	*delay*
		 ld.w		%r10, %r12	;//     %r10 := result = s	|
;//		cmp		%r9, 0		;//   --------------------------+
		jrne.d		strrchr_LOOP	;// } while(tmp != 0)
		add		%r12, 1		;//   %r12 := s++			*delay*
		ret				;// return result
;//---------------------------------------------;//
// □「Linux Programmer's Manual - STRCHR」(http://linuxjm.osdn.jp/html/LDP_man-pages/man3/strchr.3.html)より引用:
// │strchrnul()関数はstrchr()と同様だが、cがs中に見つからなかった場合に、返り値としてNULLでなく、sの末尾のヌルバイトへのポインターを返す点が異なる。
// │strchrnul()関数は一致した文字へのポインターを返す。文字が見つからない場合は、sの末尾のヌルバイトへのポインター(つまりs+strlen(s))を返す。
// │strchrnul() は GNU 拡張である。
;//---------------------------------------------;//
strchrnul:
		ld.w		%r10, %r12	;//%r10 := s
strchrnul_LOOP:					;//for(;;) {
		ld.ub		%r9, [%r10]+	;//   %r9  := tmp = *(unsigned char*)s++
		cmp		%r9, %r13	;//   if( tmp == c) { break }
		jreq		strchrnul_BREAK	;//   
		cmp		%r9, 0		;//   if(!tmp     ) { break }
		jrne		strchrnul_LOOP	;//}
strchrnul_BREAK:				;//
		ret.d				;//return  s - 1
		sub		%r10, 1		;//%r10 := s - 1			*delay*
;//---------------------------------------------;//
");
#endif /*PIECE*/

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

// * Fri May 20 23:37:14 JST 2016 Naoyuki Sawa
// - mbschr(),mbsstr(),mbspbrk()を追加しました。
//   宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
#ifndef PIECE
char* mbschr(const char* s, int c) {
	for(;;) {
		int tmp = mbsnextc(s);
		if(tmp == c) { return (char*)s; }	//(tmp=='\0')&&(c=='\0')の場合もマッチする。	┬この順序を逆にしないように!!
		if(!tmp) { return NULL; }		//						┘
		s = mbsinc(s);
	}
}
#else //PIECE
char* mbschr(const char* s, int c);
asm("
		.code
		.align		1
		.global		mbschr
mbschr:
		pushn		%r1			;//
		ld.w		%r0, %r12		;//%r0  := s
		ld.w		%r1, %r13		;//%r1  := c
mbschr_LOOP:						;//for(;;) {
		xcall.d		mbsnextc		;//  %r10 := tmp = mbsnextc(s)
		ld.w		%r12, %r0		;//  %r12 :=                s			*delay*
		cmp		%r10, %r1		;//  if(tmp == c) { return s }	────┐
		jreq		mbschr_RETs		;//  					│
		cmp		%r10, 0			;//  if(!tmp) { return NULL }	────┤
		jreq		mbschr_RET0		;//  					│
		xcall.d		mbsinc			;//  %r10 := s = mbsinc(s)		│
		ld.w		%r12, %r0		;//  %r12 :=            s		│	*delay*
		jp.d		mbschr_LOOP		;//}					│
		ld.w		%r0, %r10		;//  %r0  := s				│	*delay*
mbschr_RETs:						;//					│
		ld.w		%r10, %r0		;//%r10 := s				│
mbschr_RET0:						;//				←───┘
		popn		%r1			;//
		ret					;//return  s or NULL
");
#endif//PIECE

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

/* * Tue Dec 18 21:23:57 JST 2012 Naoyuki Sawa
 * - memchr()とmemcmp()は、P/ECE開発環境のEPSONライブラリにも含まれていますが、自分で再実装しました。
 *   EPSON版のmemchr()とmemcmp()で処理に無駄がある点を改善し、今回の実装の方が4バイト省メモリになりました。
 * - 本モジュールに含めると、必要の無い場合も必ずリンクされ、却ってメモリ消費が増える恐れがあります
 *   しかし、memchr()はdoprnt.cモジュールから,memcmp()はclippce.cから参照されているので、元々必ず必要です。
 *   従って、無駄は生じません。
 * * Sat Feb 13 21:29:21 JST 2016 Naoyuki Sawa
 * - rawmemchr()を追加しました。
 *   □「Linux Programmer's Manual - MEMCHR」(http://linuxjm.osdn.jp/html/LDP_man-pages/man3/memchr.3.html)より引用:
 *   │rawmemchr()関数はmemchr()と同様だが、cがsが指す場所から始まるメモリー領域のどこかにあることを前提にして(つまり、プログラマはそのことを知っている)、最適化した方法でcの検索を実行する(すなわち、検索範囲の上限を示す引き数も使用されない)。
 *   │rawmemchr()関数はマッチするバイトが見つかった場合はマッチしたバイトへのポインターを返す。マッチするバイトが見つからなかった場合、結果は不定である。
 *   │rawmemchr()関数はGNU拡張であり、glibc2.1から使用可能である。
 * - 上記の説明には'最適化した方法でcの検索を実行する'と書いてありますが、当実装では普通のmemchr()を呼び出すだけす。
 *   呼び出し側が、検索範囲の上限を示す引数を省略出来る、利便性のためだけに用意しました。
 * * Fri Sep 30 21:41:38 JST 2016 Naoyuki Sawa
 * - memchr()のC言語版の実装で、参照されない変数'tmp'への代入が行われていた無駄を修正しました。
 *   動作上はそのままでも問題ありませんし、P/ECE開発環境(GCC)やWindows(VC++6.0,VS2015)では警告も出ないのですが、Hewで「C5550 (W) Variable "tmp" was set but never used」という警告が出たからです。
 *   確かに無駄だったので、修正する事にしました。
 *   今回の修正による動作の変化は有りません。
 */
#ifndef PIECE
void* rawmemchr(const void* s, int c) {
	return memchr(s, c, -1);
}
void* memchr(const void* _s, int c, size_t n) {
	unsigned char* s      = (unsigned char*)_s;
	unsigned char* result = (unsigned char*) 1;
//{{2016/09/30削除:memchr()のC言語版の実装で、参照されない変数'tmp'への代入が行われていた無駄を修正しました。
//	int tmp;
//}}2016/09/30削除:memchr()のC言語版の実装で、参照されない変数'tmp'への代入が行われていた無駄を修正しました。
	for(;;) {
		if((int)--n == -1) {
			break; /* この位置では必ず(result==1)である。 */
		}
//{{2016/09/30変更:memchr()のC言語版の実装で、参照されない変数'tmp'への代入が行われていた無駄を修正しました。
//		if((tmp = *s++) == c) {
//↓2016/09/30変更:memchr()のC言語版の実装で、参照されない変数'tmp'への代入が行われていた無駄を修正しました。
		if(*s++ == c) {
//}}2016/09/30変更:memchr()のC言語版の実装で、参照されない変数'tmp'への代入が行われていた無駄を修正しました。
			result = s; /* ポインタsは文字cを見つけた位置よりも+1進んでいる。 */
			break;
		}
	}
	return result - 1; /* どちらのbreakで抜けた場合も(result-1)が正しい戻り値となる。 */
}
#ifndef _MSC_VER	//Visual C++ 6.0ではリリースビルド時に「error C2169: 'memcmp' : 組み込み関数は定義できません。」になるため除外した。
int memcmp(const void* _s1, const void* _s2, size_t n) {
	const unsigned char* s1 = _s1;
	const unsigned char* s2 = _s2;
	int c1, c2, t;
	for(;;) {
		if((int)--n == -1) {
			break;
		}
		c1 = *s1++;
		c2 = *s2++;
		if((t = c1 - c2) != 0) {
			return t;
		}
	}
	return 0;
}
#endif//_MSC_VER	//Visual C++ 6.0ではリリースビルド時に「error C2169: 'memcmp' : 組み込み関数は定義できません。」になるため除外した。
#else /*PIECE*/
void* rawmemchr(const void* s, int c);
void* memchr(const void* _s, int c, size_t n);
int memcmp(const void* _s1, const void* _s2, size_t n);
asm("
		.code
		.align		1
		.global		rawmemchr
		.global		memchr
		.global		memcmp
;//---------------------------------------------;//
rawmemchr:
		ld.w		%r14, -1	;// %r14 := n = -1
memchr:						;//
		ld.w		%r10, 1		;// %r10 := result = 1
memchr_LOOP:					;// for(;;) {
		sub		%r14, 1		;//   if((int)--n == -1)
		jrult		memchr_RET	;//     break
		ld.ub		%r9, [%r12]+	;//   %r9  := tmp = *s++
		cmp		%r9, %r13	;//   if(tmp == c) {
		jrne		memchr_LOOP	;//     %r10 := result = s
		ld.w		%r10, %r12	;//     break }
memchr_RET:					;// }
		ret.d				;// return  result
		sub		%r10, 1		;// %r10 := result--	*delay*
;//---------------------------------------------;//
memcmp:						;// for(;;) {
		sub		%r14, 1		;//   if((int)--n == -1)
		jrult		memcmp_RET0	;//     break
		ld.ub		%r10, [%r12]+	;//   %r10 := c1 = *(unsigned char*)s1++
		ld.ub		%r11, [%r13]+	;//   %r11 := c2 = *(unsigned char*)s2++
		sub		%r10, %r11	;//   %r10 := t  = c1 - c2
		jreq		memcmp		;//   if(t != 0)
		ret				;//     return t
memcmp_RET0:					;// }
		ret.d				;// return  0
		ld.w		%r10, 0		;// %r10 := 0		*delay*
;//---------------------------------------------;//
");
#endif /*PIECE*/

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

/* * Fri Dec 21 00:22:01 JST 2012 Naoyuki Sawa
 * - strspn()とstrcspn()は、P/ECE開発環境のEPSONライブラリにも含まれていますが、自分で再実装しました。
 *   EPSON版のmemchr()とmemcmp()で処理に無駄がある点を改善し、今回の実装の方が4命令分省メモリになりました。
 * - 本モジュールに含めると、必要の無い場合も必ずリンクされ、却ってメモリ消費が増える恐れがあります
 *   しかし、strspn()とstrcspn()は、clippce.c内で再実装したstrtok()から参照しているので、元々必ず必要です。
 *   従って、無駄は生じません。
 */
#ifndef PIECE
size_t  strspn(const char* s1, const char* s2) {
	int result = -1;
	int c1, c2, c;
	const char* p;
	c2 = 0;					//異なるのはこの二行だけ
	for(;;) {
		c1 = *(unsigned char*)s1++;	//異なるのはこの二行だけ
		//{{--- strspn_strcspn ---
		result++;
		if(c1 == c2) { return result; }
		p = s2;
		for(;;) {
			c = *(unsigned char*)p++;
			if(c == c1) { break; }
			if(c == c2) { return result; }
		}
		//}}--- strspn_strcspn ---
	}
}
size_t strcspn(const char* s1, const char* s2) {
	int result = -1;
	int c1, c2, c;
	const char* p;
	c1 = 0;					//異なるのはこの二行だけ
	for(;;) {
		c2 = *(unsigned char*)s1++;	//異なるのはこの二行だけ
		//{{--- strspn_strcspn ---
		result++;
		if(c1 == c2) { return result; }
		p = s2;
		for(;;) {
			c = *(unsigned char*)p++;
			if(c == c1) { break; }
			if(c == c2) { return result; }
		}
		//}}--- strspn_strcspn ---
	}
}
#else /*PIECE*/
size_t  strspn(const char* s1, const char* s2);
size_t strcspn(const char* s1, const char* s2);
asm("
		.code
		.align		1
		.global		strspn
		.global		strcspn
strspn:
		ld.w		%r10, -1		;// %r10 := result = -1
		ld.w		%r5, 0			;// %r5  := c2 = 0
		;//					;// for(;;) {
		ld.ub		%r4, [%r12]+		;//   %r4  := c1 = *(unsigned char*)s1++
		call		strspn_strcspn		;//   %r10 := result++
		jp		-2			;// }
		;//-------------------------------------;//
strcspn:
		ld.w		%r10, -1		;// %r10 := result = -1
		ld.w		%r4, 0			;// %r4  := c1 = 0
		;//					;// for(;;) {
		ld.ub		%r5, [%r12]+		;//   %r5  := c2 = *(unsigned char*)s1++
		call		strspn_strcspn		;//   %r10 := result++
		jp		-2			;// }
		;//-------------------------------------;//
strspn_strcspn:
		cmp		%r4, %r5		;// if(c1 == c2)
		jreq.d		8			;//   return result	--------------------------------+	strspn又はstrcspnへ戻らず、その呼び出し元へ戻る
		add		%r10, 1			;// %r10 := result++	*delay*				|
		ld.w		%r6, %r13		;// %r6  := p = s2					|
		;//					;// for(;;) {						|
		ld.ub		%r7, [%r6]+		;//   %r7  := c = *(unsigned char*)p++	<-------+	|
		cmp		%r7, %r4		;//   if(c == c1)				|	|
		jreq		4			;//     return result	----------------+	|	|	strspn又はstrcspnへ戻る
		cmp		%r7, %r5		;//   if(c != c2)			|	|	|
		jrne		-4			;//     continue	----------------|-------+	|
		add		%sp, 1			;//   else		<---------------|---------------+
		ret					;//     return result	<---------------+			strspn又はstrcspnへ戻らず、その呼び出し元へ戻る
							;// }
");
#endif /*PIECE*/

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

// * Sun May 01 23:11:51 JST 2016 Naoyuki Sawa
// - mbsspn(),mbscspn()を追加しました。
//   宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
// - mbsspn(),mbscspn()を追加した理由は、clipyaml.cにて以下のような問題が生じ、strcspn()ではなくmbscspn()を使う必要が生じたからです。
//   │【誤】strcspn("キー[12]", "[,") ⇒ 3 … 誤り。'ー'(\x81\x5B)の2バイト目が誤って'['(\x5B)に一致してしまう。
//   │【正】mbscspn("キー[12]", "[,") ⇒ 4 … 正しい。
// - 今回はたまたまmbscspn()で問題が顕在化しましたが、同様の問題はstrchr()でも起こり得ます。
//   近い内に、mbschr()も追加しようと思います。	⇒{{2016/05/20コメント追記:mbschr(),mbsstr(),mbspbrk()を追加しました。}}
// * Mon May 02 00:16:14 JST 2016 Naoyuki Sawa
// - mbsspn(),mbscspn()をアセンブラ化しました。
//   コードサイズが120バイトも小さくなりました。
#ifndef PIECE
size_t  mbsspn(const char* s1, const char* s2) {
	int result = -1;
	int c1, c2, c;
	const char* p;
	c1 = c2 = 0;
	for(;;) {
//{{2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
//		do { result++; } while((c1 >>= 8));				//┐
//		c1 = *(unsigned char*)s1++;					//│
//		if(ismbblead(c1) && ismbbtrail(*(unsigned char*)s1)) {		//│異なるのはこの範囲だけ
//			c1 = (c1 << 8) | *(unsigned char*)s1++;			//│
//		}								//┘
//↓2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
		do { result++; } while((c1 >>= 8));				//┐
		c1 = mbsnextc(s1);						//│異なるのはこの範囲だけ
		s1 = mbsinc(s1);						//┘
//}}2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
		//{{--- mbsspn_mbscspn ---
		if(c1 == c2) { return result; }
		p = s2;
		for(;;) {
//{{2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
//			c = *(unsigned char*)p++;
//			if(ismbblead(c) && ismbbtrail(*(unsigned char*)p)) {
//				c = (c << 8) | *(unsigned char*)p++;
//			}
//↓2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
			c = mbsnextc(p);
			p = mbsinc(p);
//}}2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
			if(c == c1) { break; }
			if(c == c2) { return result; }
		}
		//}}--- mbsspn_mbscspn ---
	}
}
size_t mbscspn(const char* s1, const char* s2) {
	int result = -1;
	int c1, c2, c;
	const char* p;
	c1 = c2 = 0;
	for(;;) {
//{{2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
//		do { result++; } while((c2 >>= 8));				//┐
//		c2 = *(unsigned char*)s1++;					//│
//		if(ismbblead(c2) && ismbbtrail(*(unsigned char*)s1)) {		//│異なるのはこの範囲だけ
//			c2 = (c2 << 8) | *(unsigned char*)s1++;			//│
//		}								//┘
//↓2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
		do { result++; } while((c2 >>= 8));				//┐
		c2 = mbsnextc(s1);						//│異なるのはこの範囲だけ
		s1 = mbsinc(s1);						//┘
//}}2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
		//{{--- mbsspn_mbscspn ---
		if(c1 == c2) { return result; }
		p = s2;
		for(;;) {
//{{2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
//			c = *(unsigned char*)p++;
//			if(ismbblead(c) && ismbbtrail(*(unsigned char*)p)) {
//				c = (c << 8) | *(unsigned char*)p++;
//			}
//↓2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
			c = mbsnextc(p);
			p = mbsinc(p);
//}}2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
			if(c == c1) { break; }
			if(c == c2) { return result; }
		}
		//}}--- mbsspn_mbscspn ---
	}
}
#else //PIECE
size_t  mbsspn(const char* s1, const char* s2);
size_t mbscspn(const char* s1, const char* s2);
asm("
		.code
		.align		1
		.global		mbsspn
		.global		mbscspn
mbsspn:
		call		mbsspn_mbscspn_init
		;//%r0      := c1
		;//%r1      := c2
		;//[%sp+ 0] := result
		;//[%sp+ 4] := s1
		;//[%sp+ 8] := s2
		;//[%sp+12] := %r0
		;//[%sp+16] := %r1
		;//[%sp+20] := %r2
		;//[%sp+24] := %r3
		;//[%sp+28] := retp
mbsspn_LOOP:							;//for(;;) {
		call.d		mbsspn_mbscspn_result		;//  %r3     := c = mbsspn_mbscspn_result(c)
		ld.w		%r3, %r0			;//  %r3     :=                           c = c1	*delay*
		call.d		mbsspn_mbscspn			;//  {{--- mbsspn_mbscspn ---}}
		ld.w		%r0, %r3			;//  %r0     := c1 = c					*delay*
		jp		mbsspn_LOOP			;//}
		;//---------------------------------------------;//
mbscspn:
		call		mbsspn_mbscspn_init
		;//%r0      := c1
		;//%r1      := c2
		;//[%sp+ 0] := result
		;//[%sp+ 4] := s1
		;//[%sp+ 8] := s2
		;//[%sp+12] := %r0
		;//[%sp+16] := %r1
		;//[%sp+20] := %r2
		;//[%sp+24] := %r3
		;//[%sp+28] := retp
mbscspn_LOOP:							;//for(;;) {
		call.d		mbsspn_mbscspn_result		;//  %r3     := c = mbsspn_mbscspn_result(c)
		ld.w		%r3, %r1			;//  %r3     :=                           c = c2	*delay*
		call.d		mbsspn_mbscspn			;//  {{--- mbsspn_mbscspn ---}}
		ld.w		%r1, %r3			;//  %r1     := c2 = c					*delay*
		jp		mbscspn_LOOP			;//}
		;//---------------------------------------------;//
mbsspn_mbscspn_init:
		;//%r12     := s1
		;//%r13     := s2
		;//[%sp+ 0] := retp to mbsspn/mbscspn
		;//[%sp+ 4] := retp
		xld.w		%r9, [%sp+0]			;//%r9     := retp to mbsspn/mbscspn
		xld.w		[%sp+0], %r3			;//
		pushn		%r2				;//
		xsub		%sp, %sp, 12			;//
		ld.w		%r0, -1				;//[%sp+0] := result = -1
		xld.w		[%sp+0], %r0			;//
		xld.w		[%sp+4], %r12			;//[%sp+4] := s1
		xld.w		[%sp+8], %r13			;//[%sp+8] := s2
		ld.w		%r0, 0				;//%r0     := c1 = 0
		ld.w		%r1, 0				;//%r1     := c2 = 0
		;//%r0      := c1
		;//%r1      := c2
		;//%r9      := retp to mbsspn/mbscspn
		;//[%sp+ 0] := result
		;//[%sp+ 4] := s1
		;//[%sp+ 8] := s2
		;//[%sp+12] := %r0
		;//[%sp+16] := %r1
		;//[%sp+20] := %r2
		;//[%sp+24] := %r3
		;//[%sp+28] := retp
		jp		%r9				;//goto        retp to mbsspn/mbscspn			(jp.d %rbは不可)
		;//---------------------------------------------;//
mbsspn_mbscspn_result:
		;//%r3      := c
		;//[%sp+ 0] := retp to mbsspn/mbscspn
		;//[%sp+ 4] := result
		;//[%sp+ 8] := s1
		;//[%sp+12] := s2
		;//[%sp+16] := %r0
		;//[%sp+20] := %r1
		;//[%sp+24] := %r2
		;//[%sp+28] := %r3
		;//[%sp+32] := retp
		xld.w		%r9, [%sp+4]			;//%r9     := result
		sra		%r3, 8				;//do { %r0 := (c >>= 8)
		jrne.d		-1				;//} while(    (c >>= 8))
		add		%r9, 1				;//%r9     := result++					*delay*
		xld.w		[%sp+4], %r9			;//[%sp+4] := result
		xld.w		%r2, [%sp+8]			;//%r2     := p = s1
		call		mbsspn_mbscspn_getc		;//%r3     := c = mbsspn_mbscspn_getc(p)
		xld.w		[%sp+8], %r2			;//[%sp+8] := s1 = p
		;//%r3      := c
		ret
		;//---------------------------------------------;//
;//{{2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
;//		;//%r2      := p
;//mbsspn_mbscspn_getc:
;//		ld.ub		%r3, [%r2]+			;//%r3  := c = *(unsigned char*)p++
;//		xcall.d		ismbblead			;//%r10 := ismbblead(c)
;//		ld.w		%r12, %r3			;//%r12 :=           c					*delay*
;//		cmp		%r10, 0				;//if(     ismbblead(c)) {
;//		jreq		mbsspn_mbscspn_getc_RET		;//  
;//		ld.ub		%r12, [%r2]			;//  %r12 :=            *(unsigned char*)p
;//		xcall		ismbbtrail			;//  %r10 := ismbbtrail(*(unsigned char*)p)
;//		cmp		%r10, 0				;//  if(     ismbbtrail(*(unsigned char*)p)
;//		jreq		mbsspn_mbscspn_getc_RET		;//    
;//		ld.ub		%r9, [%r2]+			;//    %r9  :=            *(unsigned char*)p++
;//		sla		%r3, 8				;//    %r3  := (c << 8)
;//		or		%r3, %r9			;//    %r3  := (c << 8) | *(unsigned char*)p++
;//mbsspn_mbscspn_getc_RET:					;//} }
;//		ret						;//return  c
;//		;//%r2      := p
;//		;//%r3      := c
;//↓2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
		;//%r2      := p
mbsspn_mbscspn_getc:
		xcall.d		mbsnextc			;//%r10 := c = mbsnextc(p)
		ld.w		%r12, %r2			;//%r12 :=              p				*delay*
		ld.w		%r3, %r10			;//%r3  := c
		xcall.d		mbsinc				;//%r10 := p = mbsinc(p)
		ld.w		%r12, %r2			;//%r12 :=            p					*delay*
		ret.d						;//return  c
		ld.w		%r2, %r10			;//%r2  := p						*delay*
		;//%r2      := p
		;//%r3      := c
;//}}2016/05/11変更:mbsspn(),mbscspn()内の、これまでismbblead(),ismbbtrail()を使っていた部分を、mbsnextc(),mbsinc()を使うように変更しました。結果は変わりません。
		;//---------------------------------------------;//
mbsspn_mbscspn:
		;//%r0      := c1
		;//%r1      := c2
		;//[%sp+ 0] := retp to mbsspn/mbscspn
		;//[%sp+ 4] := result
		;//[%sp+ 8] := s1
		;//[%sp+12] := s2
		;//[%sp+16] := %r0
		;//[%sp+20] := %r1
		;//[%sp+24] := %r2
		;//[%sp+28] := %r3
		;//[%sp+32] := retp
		cmp		%r0, %r1			;//if(c1 == c2) { return result }
		jreq		mbsspn_mbscspn_RET		;//
		xld.w		%r2, [%sp+12]			;//%r2  := p = s2
mbsspn_mbscspn_LOOP:						;//for(;;) {
		call		mbsspn_mbscspn_getc		;//  %r3  := c = mbsspn_mbscspn_getc(p)
		cmp		%r3, %r0			;//  if(c == c1) { break }
		jreq		mbsspn_mbscspn_BREAK		;//  
		cmp		%r3, %r1			;//  if(c == c2) { return result }
		jrne		mbsspn_mbscspn_LOOP		;//}
mbsspn_mbscspn_RET:						;//
		xld.w		%r10, [%sp+4]			;//%r10 := result
		xadd		%sp, %sp, 16			;//
		popn		%r3				;//return  result
mbsspn_mbscspn_BREAK:						;//
		ret						;//return to mbsspn/mbscspn
");
#endif//PIECE

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

// * Wed May 11 21:35:57 JST 2016 Naoyuki Sawa
// - mbsnextc(),mbsinc(),mbsninc()を追加しました。
//   宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
// - mbsnextc(),mbsinc()を追加した理由は、これまでは個別にismbblead(),ismbbtrail()を呼び出してシフトJIS文字列をパースしていた処理を、
//   mbsnextc(),mbsinc()を使う方が呼び出し側の条件分岐が減らせてメモリ節約になると思ったからです。
//   mbsninc()は、現在の所使う予定は有りませんが、mbsinc()と合わせて実装しておく事にしました。
// * Thu May 19 22:52:12 JST 2016 Naoyuki Sawa
// - mbsdec()を追加しました。
//   宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
// - 現在の所、mbsdec()はまだ使っていません。
//   VC++6.0のオリジナル版ライブラリでは_makepath()等で使われているようです。
//   当ライブラリのmakepath()の実装はまだマルチバイト対応していないのですが、もし今後マルチバイト対応する(?)時にmbsdec()を使う予定です。	⇒{{2016/05/20コメント追記:makepath()をマルチバイト対応しました。}}
#ifndef PIECE
//□「MSDNライブラリ VisualStudio 6.0 - _mbsnextc」より:
//│_mbsnextc関数はstring内の次のマルチバイト文字の整数値を返します。
//│文字列ポインタは進めません。
//│_mbsnextcは現在使用中のマルチバイトコードページに応じてマルチバイト文字の並びを認識します。
int mbsnextc(const char* _s) {
	const unsigned char* s = _s;	//mbsnextc()の戻り値を0～0xFFFFの範囲とするために必要です。mbsinc()内のコメントを参照して下さい。
	int c1, c2;
	c1 = *s++;
	c2 = ismbblead(c1);
	if(c2) {
		c2 = ismbbtrail(*s);	//オリジナル版の「C:\Program Files (x86)\Microsoft Visual Studio\VC98\CRT\SRC\MBSNEXTC.C」は1バイト目の条件が成立したら2バイト目を検査せずに2バイト文字と判断してしまっていますが、2バイト目も検査する方が良いと思ったので当実装では2バイト目も検査する事にしました。正しい文字列ならばどちらでも同じ結果になると思います。
		if(c2) {
			c2 = *s;
			c1 <<= 8;
		}
	}
	return c2 | c1;			//分岐した場合はc2=0でここへ来ます。
}
//□「MSDNライブラリ VisualStudio 6.0 - _mbsinc」より:
//│_mbsinc関数はcurrent直後のマルチバイト文字の第1バイトへのポインタを返します。
//│_mbsinccは現在使用中のマルチバイトコードページに応じてマルチバイト文字の並びを認識します。
char* mbsinc(const char* s) {
	int c = mbsnextc(s);		//mbsnextc()の戻り値は0～0xFFFFの範囲である事が保障されるのでintで受けてもunsignedで受けても'while(c>>=8)'には影響しません。万一mbsnextc()のバクで(c<0)が返されたら'while(c>>=8)'が終わらなくなるので注意して下さい。
	do {
		s++;			//cが'\0'だった場合もsは1バイト進む仕様です。オリジナル版の「C:\Program Files (x86)\Microsoft Visual Studio\VC98\CRT\SRC\MBSINC.C」の処理がそうなっていたのでそれを仕様と見なして当実装の動作もそうする事にしました。
	} while(c >>= 8);
	return (char*)s;
}
//□「MSDNライブラリ VisualStudio 6.0 - _mbsninc」より:
//│これらのルーチンは、stringをcount文字数インクリメントした後で、stringへのポインタを返します。
//│指定されたポインタがNULLの場合はNULLを返します。
//│countがstringの文字数以上であるときの結果は未定義です。	⇒MSDNでは左記のように'未定義'となっていますが、オリジナル版のソースコードのコメントには"文字列の終端の'\0'へのポインタを返す"旨が書いてあります(「C:\Program Files (x86)\Microsoft Visual Studio\VC98\CRT\SRC\MBSNINC.C」⇒"Returns pointer to end of string if string is not ccnt chars long.")。当実装も、オリジナル版の動作に倣う事にしました。
char* mbsninc(const char* s, size_t count) {
	if(s) {
		while(count--) {
			if(!mbsnextc(s)) { break; }
			s = mbsinc(s);
		}
	}
	return (char*)s;
}
//□「MSDNライブラリ VisualStudio 6.0 - _mbsdec」より:
//│_mbsdecは、currentの直前の文字を指すポインタを返します。_mbsdecはstartの値がcurrentの値以上であるとNULLを返します。
//│start   ソース文字列内のいずれかのマルチバイト文字の第1バイトへのポインタ。startはソース文字列内でcurrentより前になければならない。
//│current ソース文字列内のいずれかのマルチバイト文字の第1バイトへのポインタ。currentはソース文字列内でstartより後になければならない。
//[note]
// - VC++ 6.0のオリジナル版では、currentに(誤って)マルチバイト文字の第2バイトへのポインタが指定された場合に、余分にさらに一文字前へ送る処理が入っています。
//   安全対策のためだとは思いますが、当実装ではその処理は行わない事にしました。
//   currentにマルチバイト文字の第2バイトへのポインタが指定される事自体がアプリケーションのバグであり、上記の対策を行っても根本的な解決にならないからです。
//   そんな無駄な安全対策は入れないで、コードを簡潔に保つ方が良いと思いました。
char* mbsdec(const char* start, const char* current) {
	const char* last = NULL;	//最初から(start>=current)だった場合のために、結果の初期値をNULLとしておく。
	goto L_START;
	do {
		last = start;		//前回の文字の位置を、(暫定の)結果として記憶しておく。
		start = mbsinc(start);	//次の文字の位置を求める。
L_START:	;
	} while(start < current);	//(start>=current)になるまで繰り返す。
	return (char*)last;		//最後に(暫定の)結果として記憶した位置を、最終的な結果として返す。
}
#else //PIECE
int mbsnextc(const char* s);
char* mbsinc(const char* s);
char* mbsninc(const char* s, size_t count);
char* mbsdec(const char* start, const char* current);
asm("
		.code
		.align		1
		.global		mbsnextc
		.global		mbsinc
		.global		mbsninc
		.global		mbsdec
mbsnextc:
		pushn		%r1			;//
		ld.w		%r0, %r12		;//%r0  := s
		ld.ub		%r1, [%r0]+		;//%r1  := c1 = *s++
		xcall.d		ismbblead		;//%r10 := c2 = ismbblead(c1)
		ld.w		%r12, %r1		;//%r12 :=                c1				*delay*
		cmp		%r10, 0			;//if(c2) {			────────┐
		jreq		mbsnextc_RET		;//  						│
		ld.ub		%r12, [%r0]		;//  %r12 :=                 *s			│
		xcall		ismbbtrail		;//  %r10 := c2 = ismbbtrail(*s)		│
		cmp		%r10, 0			;//  if(c2) {			────────┤
		jreq		mbsnextc_RET		;//    						│
		ld.ub		%r10, [%r0]		;//    %r10 := c2 = *s				│
		sla		%r1, 8			;//    %r1  := c1 <<= 8				│	*anti-interlock*
mbsnextc_RET:						;//} }						↓
		or		%r10, %r1		;//%r10 := c2 | c1		分岐した場合は%r10:=c2=0でここへ来ます。
		popn		%r1			;//
		ret					;//
		;//-------------------------------------;//
mbsinc:
		pushn		%r0			;//
		call.d		mbsnextc		;//%r10 := c = mbsnextc(s)
		ld.w		%r0, %r12		;//%r0  := s						*delay*
		sra		%r10, 8			;//do { %r10 := c >>= 8
		jrne.d		-1			;//} while(c)
		add		%r0, 1			;//  %r0  := s++					*delay*
		ld.w		%r10, %r0		;//%r10 := s
		popn		%r0			;//
		ret					;//
		;//-------------------------------------;//
mbsninc:
		pushn		%r1			;//
		cmp		%r12, 0			;//if(s) {
		jreq.d		mbsninc_RET		;//  
		ld.w		%r0, %r12		;//  %r0  := s						*delay*
		ld.w		%r1, %r13		;//  %r1  := count
mbsninc_LOOP:						;//  for(;;) {
		sub		%r1, 1			;//    %r1  := count--
		jrult		mbsninc_RET		;//    if(count == -1) { break }
		call.d		mbsnextc		;//    %r10 := mbsnextc(s)
		ld.w		%r12, %r0		;//    %r12 :=          s				*delay*
		cmp		%r10, 0			;//    if(!mbsnextc(s)) { break }
		jreq		mbsninc_RET		;//    
		call.d		mbsinc			;//    %r10 := s = mbsinc(s)
		ld.w		%r12, %r0		;//    %r12 :=            s				*delay*
		jp.d		mbsninc_LOOP		;//  }
		ld.w		%r0, %r10		;//    %r0  := s					*delay*
mbsninc_RET:						;//}
		ld.w		%r10, %r0		;//%r10 := s
		popn		%r1			;//
		ret					;//
		;//-------------------------------------;//
mbsdec:
		pushn		%r1			;//
		ld.w		%r0, 0			;//%r0  := last = NULL			────────────────────┐
		ld.w		%r1, %r13		;//%r1  := current								│
		jp.d		mbsdec_START		;//goto L_START									│
		ld.w		%r10, %r12		;//%r10 := start			────────────┐		│	*delay*
mbsdec_LOOP:						;//do {								│		│
		call.d		mbsinc			;//  %r10 := start = mbsinc(start)	←───┐	────┤		│
		ld.w		%r0, %r12		;//  %r0  := last  =        start	←───┤		│	────┤	*delay*
mbsdec_START:						;//L_START:					│		│		│
		cmp		%r10, %r1		;//} while(start < current)			│	←───┤		│
		jrult.d		mbsdec_LOOP		;//						│		│		│
		ld.w		%r12, %r10		;//  %r12 :=                start	────┘	←───┘		│	*delay*		遅延スロットを全て活用するために、各遅延スロットに入れる命令を直観とは逆に配置した。ループしなかった場合はこの行の'ld.w %r12,%r10'は無駄処理となるが害は無い。尚、この遅延スロットに'ld.w %r0,%r12'を入れると結果が正しくなくなるので注意せよ。
		ld.w		%r10, %r0		;//%r10 := last				←───────────────────┘
		popn		%r1			;//
		ret					;//return  last
");
#endif//PIECE

// * Tue Jun 14 20:57:03 JST 2016 Naoyuki Sawa
// - strnextc(),strinc(),strninc(),strdec()を追加しました。
//   宣言はinclude/string.hに追加しました。(VC++環境ではtchar.hですがP/ECE環境ではstring.hが適切だと思います。)
// - これらはVC++特有の関数です。
//   mbsnextc(),mbsinc(),mbsninc(),mbsdec()と違って、strnextc(),strinc(),strninc(),strdec()が必要な事はほとんど無いと思いますが、mbsnextc(),mbsinc(),mbsninc(),mbsdec()との対称性のために実装しておく事にしました。
//   使用例は、cliplibc.cモジュール内の、ローカル関数strupr_strlwr_mbsupr_mbslwr()を参照して下さい。
#ifndef PIECE
int strnextc(const char* s) {
	return *(unsigned char*)s;
	//      ~~~~~~~~~~~~~~~~このポインタ型キャストは必須です。'\x80'～'\xFF'の範囲の文字に対しては、符号拡張せずに0x00～0xFFの値を返す必要が有ります。もし符号拡張してしまうと、strupr_strlwr_mbsupr_mbslwr()が文字のバイト数を正しく判断出来ず、間違った動作になるからです。
}
char* strinc(const char* s) {
	return (char*)(s + 1);
}
char* strninc(const char* s, size_t count) {
	count = strnlen(s, count);	//MSDNには「countがstringの文字数以上であるときの結果は未定義です。」と書いてあります。実際にVC++6.0の実装も、countがstringの文字数以上であるときは文字列の終端を超えたポインタを返します。(C:\Program Files (x86)\Microsoft Visual Studio\VC98\Include\TCHAR.H参照)。しかしmbsninc()の動作との対称性のために、当実装ではstrninc()も文字列終端の'\0'へのポインタを返す事にしました。
	return (char*)(s + count);
}
char* strdec(const char* start, const char* current) {
	return (char*)((current > start) ? (current - 1) : NULL);
}
#else //PIECE
int strnextc(const char* s);
char* strinc(const char* s);
char* strninc(const char* s, size_t count);
char* strdec(const char* start, const char* current);
asm("
		.code
		.align		1
		.global		strnextc
		.global		strinc
		.global		strninc
		.global		strdec
strnextc:
		ld.ub		%r10, [%r12]		;//%r10 := c = *(unsigned char*)s
		ret					;//return  c    ~~~~~~~~~~~~~~~~このポインタ型キャストは必須です。'\x80'～'\xFF'の範囲の文字に対しては、符号拡張せずに0x00～0xFFの値を返す必要が有ります。もし符号拡張してしまうと、strupr_strlwr_mbsupr_mbslwr()が文字のバイト数を正しく判断出来ず、間違った動作になるからです。
strinc:
		add		%r12, 1			;//%r12 := s + 1
		ret.d					;//return  s + 1
		ld.w		%r10, %r12		;//%r10 := s + 1			*delay*
strninc:
		pushn		%r0			;//
		xcall.d		strnlen			;//%r10 := count = strnlen(s, count)
		ld.w		%r0, %r12		;//%r0  := s				*delay*
		add		%r10, %r0		;//%r10 := s + count
		popn		%r0			;//
		ret					;//return  s + count
strdec:
		cmp		%r13, %r12		;//%psr := current <=> start
		jrugt.d		3			;//if(     current <=  start) { %r13 :=        NULL }
		 sub		%r13, 1			;//%r13 :=                      current - 1		*delay*
		 ld.w		%r13, 0			;//  %r13 :=                                   NULL
		ret.d					;//return  (current > start) ? (current - 1) : NULL
		ld.w		%r10, %r13		;//%r10 := (current > start) ? (current - 1) : NULL	*delay*
");
#endif//PIECE

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

/* * Wed Mar 27 20:21:54 JST 2013 Naoyuki Sawa
 * - strsep()を追加しました。宣言は、include/string.hに追加しました。
 *   strsep()は、EPSONライブラリには含まれておらず、また、C言語標準関数ではありませんが、UNIX環境では一般的な関数です。
 *   strtok()と較べて、空のフィールドを扱えること，リエントラントであることの利点があり、便利なのでP/ECEにも追加することにしました。
 * - strpbrk()は、P/ECE開発環境のEPSONライブラリにも含まれていますが、自分で再実装しました。
 *   strpbrk()は、EPSONライブラリの実装のままでも、充分にコンパクトで高速です。
 *   しかし、strsep()と一緒に実装することで、一部のレジスタの退避を省略してよりコンパクトで高速にできるので、strpbrk()も実装することにしました。
 *   実際には、strpbrk()を使用しないアプリケーションにもリンクされてしまう欠点を勘案すると、必ずしも効率的とは言えないのですけれど、
 *   EPSONライブラリ版よりも良いアセンブラ化ができた(と思う)ので、せっかくなので含めることにしました(^^;
 */
#ifndef PIECE
/* strsep()関数は、*stringpにより参照される文字列の中で、文字列delim中のいずれかの文字(または終端の'\0')のうちで最初に出現する文字の位置を探し、その箇所を'\0'で置き換えます。
 * 区切り文字の次の文字の位置(または文字列の末尾に達した場合はNULL)が、*stringpに格納されます。
 * *stringpの元の値が、戻り値となります。
 * 「空」のフィールド、すなわち、2つの隣接する区切り文字によって生じたフィールドは、*stringpの中に返されたポインタが参照する位置を'\0'と比較することで検出可能です。
 * *stringpが最初からNULLである場合は、strsep()はNULLを返します。
 */
char* strsep(char** stringp, const char* delim) {
	char* begin = *stringp;
	if(begin) {
		char* end = strpbrk(begin, delim);
		if(end) { *end++ = '\0'; }
		*stringp = end;
	}
	return begin;
}
char* strpbrk(const char* s, const char* accept) {
	const char* p;
	for(;;) {
		int c = *s++;
		if(!c) { return NULL; }
		p = accept;
		for(;;) {
			int t = *p++;
			if(!t) { break; }
			if(t == c) { return (char*)(s - 1); }
		}
	}
}
#else /*PIECE*/
char* strsep(char** stringp, const char* delim);
char* strpbrk(const char* s, const char* accept);
asm("
		.code
		.align		1
		;//=====================================;//
		.global		strsep
strsep:
		ld.w		%r14, [%r12]		;// %r14 := begin = *stringp
		cmp		%r14, 0			;// if(begin) {
		jreq		strsep_RET
		ld.w		%r15, %r12		;//   %r15 := stringp
		call.d		strpbrk			;//   %r10 := end = strpbrk(begin, delim)
		ld.w		%r12, %r14		;//   %r12 :=               begin		*delay*
		cmp		%r10, 0			;//   if(end) {
		jreq		2			;//     *end++ = '￥0'		--------+
		ld.b		[%r10]+, %r8		;//   }					|
		ld.w		[%r15], %r10		;//   *stringp = end		<-------+
strsep_RET:	;//					;// }
		ret.d
		ld.w		%r10, %r14		;// %r10 := begin				*delay*
		;//=====================================;//
		.global		strpbrk
strpbrk:						;// for(;;) {
		ld.b		%r10, [%r12]+		;//   %r10 := c = *s++		<-------+
		cmp		%r10, 0			;//   if(!c)				|
		jreq		strpbrk_RET		;//     return NULL			|
		ld.w		%r11, %r13		;//   %r11 := p = accept		|
		;//					;//   for(;;) {				|
		ld.b		%r9, [%r11]+		;//     %r9  := t = *p++	<-------|-------+
		cmp		%r9, 0			;//     if(!t)				|	|
		jreq		strpbrk			;//       break			--------+	|
		cmp		%r9, %r10		;//     if(t == c) return s - 1			|
		jrne		-4			;//   }				----------------+
		sub		%r12, 1			;// %r12 := s - 1
		ld.w		%r10, %r12		;// %r10 := s - 1
strpbrk_RET:	;//					;// }
		ret
");
#endif/*PIECE*/

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

// * Fri May 20 23:37:14 JST 2016 Naoyuki Sawa
// - mbschr(),mbsstr(),mbspbrk()を追加しました。
//   宣言はinclude/string.hに追加しました。(VC++環境ではmbstring.hですがP/ECE環境ではstring.hが適切だと思います。)
#ifndef PIECE
char* mbspbrk(const char* s, const char* accept) {
	s += mbscspn(s, accept);
	return *s ? (char*)s : NULL;
}
#else //PIECE
char* mbspbrk(const char* s, const char* accept);
asm("
		.code
		.align		1
		.global		mbspbrk
mbspbrk:
		pushn		%r0			;//
		xcall.d		mbscspn			;//%r10 :=       mbscspn(s, accept)
		ld.w		%r0, %r12		;//%r0  :=  s				*delay*
		add		%r10, %r0		;//%r10 :=  s += mbscspn(s, accept)
		ld.b		%r9, [%r10]		;//%r9  := *s
		popn		%r0			;//					*anti-interlock*
		cmp		%r9, 0			;//if(!*s) {
		jrne		2			;//  %r10 := s = NULL
		 ld.w		%r10, 0			;//}
		ret					;//return s or NULL
");
#endif//PIECE

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

/* * Thu Oct 09 22:33:48 JST 2014 Naoyuki Sawa
 * - strcat()とstrncat()は、P/ECE開発環境のEPSONライブラリにも含まれていますが、自分で再実装しました。
 *   EPSON版は、strcat()とstrncat()が個別に実装されていますが、まとめて実装し、少し省メモリになりました。
 * - 実際には、本モジュールに含めると、必要の無い場合も必ずリンクされてしまい、却ってメモリ消費が増えます。
 *   アプリケーションが、strcat()とstrncat()を両方使用している場合のみ、省メモリになったと考えてください。
 * - 省メモリの効果は、以下の通りです。
 *   □EPSON版
 *   │strcat  10命令┐
 *   │strncat 16命令┴26命令
 *   □自作版            ↓…12命令節約
 *   │strcat＋strncat 14命令
 */
#ifndef PIECE
/* □「Linux Programmer's Manual - STRCAT」(http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/strcat.3.html)より引用:
 * │strcat()関数は、dest文字列の後にsrc文字列を付け加える。
 * │その際に、destの最後にある終端のヌルバイト('\0')は上書きされ、新たに生成された文字列の末尾に終端のヌルバイトが付与される。
 * │二つの文字列srcとdestは重なってはならない。
 * │また、文字列destは、連結後の結果を格納するのに十分な大きさでなければならない。
 * │destが十分な大きさでない場合、プログラムがどのような動作をするか分からない。
 * │バッファオーバーランはセキュアなプログラムを攻撃する際に好んで使われる方法である。
 * │strncat()も同様だが、以下の点が異なる。
 * │- srcのうち最大nバイトが使用される。
 * │- srcがnバイト以上の場合、srcはヌル終端されている必要はない。
 * │strcat()と同じく、destに格納される結果の文字列は常にヌル終端される。
 * │srcがnバイト以上の場合、strncat()はdestにn+1バイトを書き込む(srcからのnバイトと終端のヌルバイトである)。
 * │したがって、destの大きさは最低でもstrlen(dest)+n+1でなければならない。 */
#ifndef _MSC_VER	//{{2015/05/31:Visual C++ 6.0でReleaseビルドすると「error C2169: 'strcat' : 組み込み関数は定義できません。」になるので除外しました。
char* strcat(char* dest, const char* src) {
	return strncat(dest, src, SIZE_MAX);
}
#endif//_MSC_VER	//}}2015/05/31:Visual C++ 6.0でReleaseビルドすると「error C2169: 'strcat' : 組み込み関数は定義できません。」になるので除外しました。
char* strncat(char* dest, const char* src, size_t n) {
	char* p = dest;
	while(*p++) { /** no job **/ }
	p--;
	while(n--) { if(!(*p++ = *src++)) { return (char*)dest; } }
	*p++ = '\0';
	return (char*)dest;
}
#else /*PIECE*/
char* strcat(char* dest, const char* src);
char* strncat(char* dest, const char* src, size_t n);
//素直なバージョン(15命令)と、1命令節約バージョン(14命令)を作成しました。サイズを優先して後者を使用することにしました。
//strcat()やstrncat()を使用するような処理は、速度があまり重要でないことが多く、少しでも小さい方が良いと思ったからです。
#if 0
//ほぼC言語どおり素直に実装したバージョンです。
asm("
		.code
		.align		1
		.global		strcat
		.global		strncat
strcat:
		ld.w		%r14, -1		;//%r14 := n = SIZE_MAX
strncat:
		ld.w		%r10, %r12		;//%r12 := p = dest
		ld.b		%r9, [%r12]+		;//while(*p++) { /** no job **/ }
		 cmp		%r9, 0
		jrne		-2
		sub		%r12, 1			;//%r12 := p--
strncat_LOOP:
		sub		%r14, 1			;//while(n--) {
		jrult		strncat_TERM
		ld.b		%r9, [%r13]+
		ld.b		[%r12]+, %r9		;//  if(!(*p++ = *src++)) { return dest }
		cmp		%r9, 0
		jrne		strncat_LOOP
		ret					;//}
strncat_TERM:
		ld.b		[%r12], %r8		;//*p = '￥0'
		ret					;//return dest
");
#else
//1命令節約したバージョン。ただし少し低速です。
asm("
		.code
		.align		1
		.global		strcat
		.global		strncat
strcat:
		ld.w		%r14, -1		;//%r14 := n = SIZE_MAX
strncat:
		ld.w		%r10, %r12		;//%r12 := p = dest
		ld.b		%r9, [%r12]		;//while(*p) { p++ }
		 cmp		%r8, %r9		;//					%psr(C) := %r9 ? 1 : 0
		jrne.d		-2
strncat_LOOP:						;//for(;;) {
		adc		%r12, %r8		;//  if(!初回) { p++ }			上のループ中は+1。上のループ抜け時のみ+0。下のループ中は+1。	*delay*
		sub		%r14, 1			;//  if(!n--) { break }
		jrult		strncat_TERM
		ld.b		%r9, [%r13]+		;//  if(!(*p = *src++)) { break }
		ld.b		[%r12], %r9
		cmp		%r8, %r9		;//					%psr(C) := %r9 ? 1 : 0
		jrne		strncat_LOOP		;//}
strncat_TERM:
		ld.b		[%r12], %r8		;//*p = '￥0'				下のループから降りて来た場合は、2回nulを格納することになるが問題無い。
		ret					;//return dest
");
#endif
#endif/*PIECE*/

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

/* * Fri Oct 31 00:45:36 JST 2014 Naoyuki Sawa
 * - strlcpy()を追加しました。宣言は、include/string.hに追加しました。
 * □「Linux Programmer's Manual - STRCPY」(http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/strcpy.3.html)より引用:
 * │いくつかのシステム(BSD、Solarisなど)では以下の関数が提供されている。
 * │    size_t strlcpy(char *dest, const char *src, size_t size); 
 * │この関数はstrncpy()と同様だが、最大でもsize-1バイトしかdestにコピーをせず、末尾への終端のヌルバイトの追加が必ず行われ、宛先バッファ(の未使用部分)へのヌルバイトの書き込みが行われない。
 * │この関数ではstrcpy()やstrncpy()の持つ問題のいくつかが修正されているが、sizeが小さすぎた場合にはデータが失われる問題には、依然として呼び出し側で対処する必要がある。
 * │この関数の返り値はsrcの長さである。これにより、末尾の切り詰めが行われたかを簡単に検出することができる。返り値がsize以上の場合には、末尾の切り詰めが発生している。
 * │データロスが問題となる場合は、呼び出し側で、呼び出し前に引き数をチェックするか、この関数の返り値を検査するかのいずれかをしなければならない。
 * │strlcpy()はglibcには存在せず、POSIXによる標準化もされていないが、Linuxではlibbsdライブラリ経由で利用できる。 */
#ifndef PIECE
size_t strlcpy(char* dst, const char* src, size_t size) {
	size_t len = strlen(src);			//転送元の文字列の長さを求める。
	if(size--) {					//バッファサイズを1減らす。1減らす前のバッファサイズが0でなければ…
		if(size > len) { size = len; }		//転送する文字列の長さを求める。
		memcpy(dst, src, size);			//文字列を転送する。
		dst[size] = '\0';			//文字列をヌル終端する。
	}
	return len;					//転送元の文字列の長さを返す。
}
#else /*PIECE*/
size_t strlcpy(char* dst, const char* src, size_t size);
asm("
		.code
		.align		1
		.global		strlcpy
strlcpy:
		pushn		%r2
		ld.w		%r0, %r12		;//%r0  := dst
		ld.w		%r1, %r13		;//%r1  := src
		ld.w		%r2, %r14		;//%r2  := size
		xcall.d		strlen			;//%r10 := len = strlen(src)
		ld.w		%r12, %r1		;//%r12 :=              src			*delay*
		sub		%r2, 1			;//%r2  := size--
		jrult		strlcpy_RET		;//if(size >= 0) {
		ld.w		%r12, %r0		;//  %r12 :=              dst
		ld.w		%r0, %r10		;//  %r0  := len
		cmp		%r2, %r0		;//  if(size > len) {
		jrule.d		3			;//
		 ld.w		%r13, %r1		;//  %r13 :=                   src		*delay*
		 ld.w		%r2, %r0		;//     size = len  }
		xcall.d		memcpy			;//  %r10 := dst = memcpy(dst, src, size)
		ld.w		%r14, %r2		;//  %r14 :=                        size	*delay*
		add		%r10, %r2		;//  %r10 := &dst[size]
		ld.b		[%r10], %r8		;//           dst[size] = '￥０'
		ld.w		%r10, %r0		;//  %r10 := len
strlcpy_RET:						;//}
		popn		%r2
		ret
");
#endif/*PIECE*/

/* * Fri Oct 31 00:45:36 JST 2014 Naoyuki Sawa
 * - strlcat()を追加しました。宣言は、include/string.hに追加しました。
 * □「Linux Programmer's Manual - STRCAT」(http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/strcat.3.html)より引用:
 * │いくつかのシステム(BSD、Solarisなど)では以下の関数が提供されている。
 * │    size_t strlcat(char *dest, const char *src, size_t size); 
 * │この関数は、ヌル終端された文字列srcを文字列destに追加する。
 * │具体例には、sizeがstrlen(dest)より大きい場合には最大でsize-strlen(dest)-1バイトをsrcからコピーし、結果の末尾に終端のヌルバイトを追加する。
 * │この関数ではstrcat()のバッファオーバーランが発生するという問題が修正されているが、sizeが小さすぎた場合にはデータが失われる問題には、依然として呼び出し側で対処する必要がある。
 * │この関数はstrlcat()が作成しようとした文字列の長さを返す。返り値がsize以上の場合、データロスが発生している。
 * │データロスが問題となる場合は、呼び出し側で、呼び出し前に引き数をチェックするか、この関数の返り値を検査するかのいずれかをしなければならない。
 * │strlcat()はglibcには存在せず、POSIXによる標準化もされていないが、Linuxではlibbsdライブラリ経由で利用できる。 */
#ifndef PIECE
size_t strlcat(char* dst, const char* src, size_t size) {
	size_t dlen = strlen(dst);			//転送先の文字列の長さを求める。
	size_t slen = strlen(src);			//転送元の文字列の長さを求める。
	size_t tlen = dlen + slen;			//文字列の長さの合計を求める。
	if(size > dlen) {				//転送先の文字列がバッファ内でヌル終端されていたら…
		size -= dlen;				//(バッファ内に追加可能な文字数+1)を求める。
		if(slen >= size) { slen = size - 1; }	//追加する文字列の長さを求める。
		dst += dlen;				//文字列を追加する位置を求める。
		memcpy(dst, src, slen);			//文字列を追加する。
		dst[slen] = '\0';			//文字列をヌル終端する。
	}
	return tlen;					//文字列の長さの合計を返す。
}
#else /*PIECE*/
size_t strlcat(char* dst, const char* src, size_t size);
asm("
		.code
		.align		1
		.global		strlcat
strlcat:
		pushn		%r3
		ld.w		%r0, %r12		;//%r0  := dst
		ld.w		%r1, %r13		;//%r1  := src
		xcall.d		strlen			;//%r10 := dlen = strlen(dst)
		ld.w		%r2, %r14		;//%r2  := size					*delay*
		add		%r0, %r10		;//%r0  := dst += dlen
		ld.w		%r3, %r10		;//%r3  := dlen
		xcall.d		strlen			;//%r10 := slen = strlen(src)
		ld.w		%r12, %r1		;//%r12 :=               src			*delay*
		sub		%r2, %r3		;//%r2  := size -= dlen
		jrule.d		strlcat_RET		;//if(size > 0) {
		add		%r3, %r10		;//%r3  := tlen = dlen + slen			*delay*
		cmp		%r10, %r2		;//  if(slen >= size) {
		jrult.d		4			;//
		 ld.w		%r12, %r0		;//  %r12 :=        dst				*delay*
		 ld.w		%r10, %r2		;//    %r10 := slen = size - 1
		 sub		%r10, 1			;//  }
		ld.w		%r13, %r1		;//  %r13 :=             src
		ld.w		%r14, %r10		;//  %r14 :=                  slen
		xcall.d		memcpy			;//  %r10 := memcpy(dst, src, slen)
		add		%r0, %r10		;//  %r0  := &dst[slen]				*delay*
		ld.b		[%r0], %r8		;//           dst[slen] = '￥０'
strlcat_RET:						;//}
		ld.w		%r10, %r3		;//%r10 := tlen
		popn		%r3
		ret
");
#endif/*PIECE*/

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

//{{2016/05/03削除:iscsym(),iscsymf(),isblank(),ismbblead(),ismbbtrail()をcliplibc.c⇒ctype.cへ移動しました。
//int
//ismbblead(int c)
//{
//	c &= 0xff; /* bit31-8は無視（VC++の_ismbblead()もそうなってます） */
//	return (0x81 <= c && c <= 0x9f) ||
//	       (0xe0 <= c && c <= 0xfc);
//}
//
//int
//ismbbtrail(int c)
//{
//	c &= 0xff; /* bit31-8は無視（VC++の_ismbbtrail()もそうなってます） */
//	return (0x40 <= c && c <= 0x7e) ||
//	       (0x80 <= c && c <= /*0xec*/0xfc);
//	/* 2002/12/12: VC++6.0付属のMSDNの、_ismbbtrail()の説明はうそです。 */
//	/* 後半0x80～0xecでは、「ん」(0x82f1)の二バイト目が判定できません。 */
//}
//}}2016/05/03削除:iscsym(),iscsymf(),isblank(),ismbblead(),ismbbtrail()をcliplibc.c⇒ctype.cへ移動しました。

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

//{{2016/05/03削除:iscsym(),iscsymf(),isblank(),ismbblead(),ismbbtrail()をcliplibc.c⇒ctype.cへ移動しました。
//#ifndef PIECE
//#ifndef _MSC_VER	//Visual C++ 6.0のCTYPE.Hは、iscsym,iscsymfをマクロ定義しており、定義が重複するのでここでは定義しない。
//int iscsym(int c) {
//	return isalnum(c) || (c == '_');
//}
//int iscsymf(int c) {
//	return isalpha(c) || (c == '_');
//}
//#endif//_MSC_VER	//Visual C++ 6.0のCTYPE.Hは、iscsym,iscsymfをマクロ定義しており、定義が重複するのでここでは定義しない。
//#else /*PIECE*/
///* * Fri Apr 20 11:09:47 JST 2012 Naoyuki Sawa
// * - iscsym()とiscsymf()をアセンブラ化しました。
// */
//asm("
//		.code
//		.align		1
//		.global		iscsym
//		.global		iscsymf
//iscsym:
//		xcmp		%r12, 95	;// if(c != '_')
//		xjrne		isalnum		;//   return isalnum(c)
//		ret.d				;// else
//		ld.w		%r10, 1		;//   return 1	*delay*
//iscsymf:
//		xcmp		%r12, 95	;// if(c != '_')
//		xjrne		isalpha		;//   return isalpha(c)
//		ret.d				;// else
//		ld.w		%r10, 1		;//   return 1	*delay*
//");
//#endif /*PIECE*/
//}}2016/05/03削除:iscsym(),iscsymf(),isblank(),ismbblead(),ismbbtrail()をcliplibc.c⇒ctype.cへ移動しました。

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

//{{2016/05/03削除:iscsym(),iscsymf(),isblank(),ismbblead(),ismbbtrail()をcliplibc.c⇒ctype.cへ移動しました。
//#ifndef PIECE
//int isblank(int c) {
//	return (c == ' ') || (c == '\t');
//}
//#else /*PIECE*/
//asm("
//		.code
//		.align		1
//		.global		isblank
//isblank:
//		;// xcmpで比較すると、' 'の比較時に、ext拡張が必要になります。
//		;// subとaddを使うと、ext拡張が不要で、1命令分高速化できます。
//		sub		%r12, 32	;// if((c -=  ' '        ) == 0) { %psr[Z] = 1 } else
//		jreq		2
//		 add		%r12, 23	;// if((c += (' ' - '\t')) == 0) { %psr[Z] = 1 } else { %psr[Z] = 0 }
//		ld.w		%r10, %psr	;// %r10[1] := %psr[Z]
//		ret.d
//		and		%r10, 2		;// return     %r10[1] … 0 or 2	*delay*		戻り値が(0|1)でなく(0|2)となることに注意。isblank()の仕様には反しない。
//");
//#endif /*PIECE*/
//}}2016/05/03削除:iscsym(),iscsymf(),isblank(),ismbblead(),ismbbtrail()をcliplibc.c⇒ctype.cへ移動しました。

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

//{{2014/01/13削除:lfind()とlsearch()の実装を、clippce.c⇒clipsrch.cへ分離しました。
///* * Sat Jul 04 21:25:58 JST 2009 Naoyuki Sawa
// * - POSIX標準のリニアサーチ関数です。
// *   やや新しい標準のようで、P/ECE開発環境のEPSONライブラリには含まれていなかったので、ここで実装しました。
// *   単純な処理ですが、未ソートの配列をリニアサーチしたい(=bsearch()は使えない)場合は結構有るので、便利だと思います。
// * - 関数仕様は、Web上の資料や、MSDNなどを参照してください。(Visual C++ 6.0のMSDNにも載っています。)
// *   lfind()の引数で、numがポインタ渡しなのが無駄に見えますが、おそらく、lsearch()と引数型を一致させたためだと思います。
// *   また、base引数が、lfind()はconst、lsearch()は非constである違いに注意してください。
// *   lfind()は配列を変更せず、lsearch()は見つからなかったときに配列に要素を追加することを、明示するためだと思います。
// *   (その割には、lfind()の戻り値の型は非constになっている点が、一貫性がありませんが…)
// * - lsearch()を使って、簡単に、重複の無いセットを作成することができます。
// *   以下に、使用例を示します。
// *	<例>
// *		static int compare(const void* elem1, const void* elem2) {
// *			char* str1 = *(char**)elem1;
// *			char* str2 = *(char**)elem2;
// *			return strcmp(str1, str2);
// *		}
// *		void sample() {
// *			char* set[10];
// *			int num = 0;
// *			char* key;
// *			int i;
// *			if(num < 10) key = "One";   lsearch(&key, set, &num, sizeof set[0], compare);
// *			if(num < 10) key = "Two";   lsearch(&key, set, &num, sizeof set[0], compare);
// *			if(num < 10) key = "One";   lsearch(&key, set, &num, sizeof set[0], compare);
// *			if(num < 10) key = "Three"; lsearch(&key, set, &num, sizeof set[0], compare);
// *			if(num < 10) key = "Two";   lsearch(&key, set, &num, sizeof set[0], compare);
// *			if(num < 10) key = "Four";  lsearch(&key, set, &num, sizeof set[0], compare);
// *			for(i = 0; i < num; i++) { printf("%d: %s\n", i, set[i]); }
// *		}
// *	<結果>
// *		0: One
// *		1: Two
// *		2: Three
// *		3: Four
// */
//void*
//lfind(const void* key, const void* base, size_t* num, size_t width, int (*compare)(const void* elem1, const void* elem2))
//{
//	int i = *num;
//	while(i) {
//		if(!(*compare)(key, base)) {
//			return (void*)base;
//		}
//		base = (char*)base + width;
//		i--;
//	}
//	return NULL;
//}
//void*
//lsearch(const void* key, void* base, size_t* num, size_t width, int (*compare)(const void* elem1, const void* elem2))
//{
//	void* ptr = lfind(key, base, num, width, compare);
//	if(!ptr) {
//		ptr = memcpy((char*)base + ((*num)++ * width), key, width);
//	}
//	return ptr;
//}
//}}2014/01/13削除:lfind()とlsearch()の実装を、clippce.c⇒clipsrch.cへ分離しました。

/* * Sat Jul 04 21:25:58 JST 2009 Naoyuki Sawa
 * - 勉強のため自分で実装してみましたが、bsearch()はP/ECE開発環境のEPSONライブラリにも含まれており、以下のコードと全く同じ実装です。
 *   以下のコードをclippce.cに含めておくと、アプリケーションに常にリンクされてコードサイズが無駄になるので、コメントアウトしました。
 *   なお、Visual C++ 6.0にもbsearch()のソースが含まれており、参考にできますが、EPSONライブラリ(=以下のコードと同じ)の方が簡潔です。
 */
//	void*
//	bsearch(const void* key, const void* base, size_t num, size_t width, int (*compare)(const void* elem1, const void* elem2))
//	{
//		while(num) {
//			char* mid = (char*)base + ((num >> 1) * width);
//			int retval = (*compare)(key, mid);
//			if(retval >= 0) {
//				if(!retval) {
//					return mid;
//				}
//				base = mid + width;
//				num--;
//			}
//			num >>= 1;
//		}
//		return NULL;
//	}

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

//{{2016/06/14変更:既存のstrupr(),strlwr()を、新しいstrupr(),strlwr()とmbsupr(),mbslwr()に分離しました。
///* * Mon Jul 06 23:33:17 JST 2009 Naoyuki Sawa
// * - strupr()とstrlwr()は、標準関数かどうかよくわからず、VC++特有なのかも知れません。
// *   Web上の資料から推測すると、Unix環境のライブラリには含まれていない(?)ようです。
// *   P/ECE開発環境のEPSONライブラリにも、strupr()とstrlwr()は、含まれていません。
// *   便利なので、定義しておくことにしました。
// * - strupr()とstrlwr()のコードは小さいので、無駄なコードサイズ増の影響は僅かです。
// *   tolower()を呼び出していますが、tolower()は既に本モジュールで使用しているので、新たなコード増は生じません。
// *   toupper()を呼び出していますが、toupper()は必須のclipprnt.cで使用しているので、新たなコード増は生じません。
// */
////#ifndef PIECE
////char* strupr(char* string) {
////	int c;
////	char* p = string;
////	while((c = *p)) {
////		*p++ = toupper(c);
////	}
////	return string;
////}
////char* strlwr(char* string) {
////	int c;
////	char* p = string;
////	while((c = *p)) {
////		*p++ = tolower(c);
////	}
////	return string;
////}
////#else /*PIECE*/
////char* strupr(char* string);
////char* strlwr(char* string);
////asm("
////		.code
////		.align		1
////		.global		strupr
////		.global		strlwr
////strupr:
////		xld.w		%r13, toupper		;// %r13 := fn = toupper
////		jp		strupr_strlwr
////strlwr:
////		xld.w		%r13, tolower		;// %r13 := fn = tolower
////strupr_strlwr:
////		pushn		%r2
////		ld.w		%r0, %r12		;// %r0  := string
////		ld.w		%r1, %r12		;// %r1  := p
////		ld.w		%r2, %r13		;// %r2  := fn
////strupr_strlwr_LOOP:					;// for(;;) {
////		ld.b		%r12, [%r1]		;//   %r12 := c = *p
////		cmp		%r12, 0			;//   if(!c)
////		jreq		strupr_strlwr_RET	;//     break
////		call		%r2			;//   %r10 := c = fn(c)
////		ld.b		[%r1]+, %r10		;//   *p++ := c
////		jp		strupr_strlwr_LOOP	;// }
////strupr_strlwr_RET:
////		ld.w		%r10, %r0		;// %r10 := string
////		popn		%r2
////		ret
////");
////#endif /*PIECE*/
////↓---------------------------------------------------------------------------------------------------------------------
////↓ * Sat Dec 25 12:48:38 JST 2010 Naoyuki Sawa
////↓ - strupr()やstrlwr()で、全角文字を含む文字列を変換すると、文字化けすることがある問題を修正しました。
////↓ - これまでの版では、たとえば「テキスト」という文字列を、toupper()で変換すると「ウキスエ」になってしまっていました。
////↓   「テ」の2バイト目を「e」と見なして「E」に、「ト」の2バイト目を「g」と見なして「E」に変換してしまっていたためです。
////↓ - 今回の修正によって、全角文字を含む文字列も、strupr()やstrlwr()で正しく変換できるようになりました。
////↓ - なお、strupr()やstrlwr()で全角文字が化ける問題は、Visual C++ 6.0や、Borland C++ 5.5.1のライブラリでも発生します。
////↓   Windowsプログラミングの際には、気をつけてください。
////↓   回避策の一つは、MFCのCString::MakeUpper()を使うことです。Visual C++ 6.0のMFCで、文字化けしないことを確認しました。
////↓---------------------------------------------------------------------------------------------------------------------
//#ifndef PIECE
//char* strupr(char* string) {
//	char* p = string;
//	for(;;) {
//		/* 全角文字ならば、変換せずに飛ばす。
//		 * - 1バイト目がismbblead()でtrueならば、2バイト目を調べなくても全角文字と判断できるのですが、
//		 *   壊れた文字列で2バイト目がnulだった場合に、バッファオーバーランするおそれがあるため、
//		 *   安全を期して、2バイト目がismbbtrail()でtrueであることも確認することにしました。
//		 */
//		if(ismbblead(p[0]) && ismbbtrail(p[1])) {
//			p += 2;
//		/* 半角文字ならば、変換する。終端だったら抜ける。
//		 * - toupper()はnulを変換しないので、終端チェックはtoupper()より先に行っても後に行っても同じです。
//		 *   toupper()よりも後で行う方が、Asm化した時に条件ループを組み易いため、後で行うことにしました。
//		 */
//		} else {
//			if(!(*p = toupper(*p))) break;
//			p += 1;
//		}
//	}
//	return string;
//}
//char* strlwr(char* string) {
//	char* p = string;
//	for(;;) {
//		/* 全角文字ならば、変換せずに飛ばす。
//		 * - 1バイト目がismbblead()でtrueならば、2バイト目を調べなくても全角文字と判断できるのですが、
//		 *   壊れた文字列で2バイト目がnulだった場合に、バッファオーバーランするおそれがあるため、
//		 *   安全を期して、2バイト目がismbbtrail()でtrueであることも確認することにしました。
//		 */
//		if(ismbblead(p[0]) && ismbbtrail(p[1])) {
//			p += 2;
//		/* 半角文字ならば、変換する。終端だったら抜ける。 */
//		} else {
//			if(!(*p = tolower(*p))) break;
//			p += 1;
//		}
//	}
//	return string;
//}
//#else /*PIECE*/
//char* strupr(char* string);
//char* strlwr(char* string);
//asm("
//		.code
//		.align		1
//		.global		strupr
//		.global		strlwr
//strupr:
//		xld.w		%r13, toupper		;// %r13 := fn = toupper
//		jp		strupr_strlwr
//strlwr:
//		xld.w		%r13, tolower		;// %r13 := fn = tolower
//strupr_strlwr:
//		pushn		%r2
//		ld.w		%r0, %r12		;// %r0  := string
//		ld.w		%r1, %r12		;// %r1  := p
//		ld.w		%r2, %r13		;// %r2  := fn
//		;//-------------------------------------;//
//strupr_strlwr_LOOP:					;// for(;;) {
//		xld.b		%r12, [%r1+0]		;//   %r12 :=        p[0]
//		xcall		ismbblead		;//   if(!ismbblead (p[0])) goto SINGLE
//		cmp		%r10, 0
//		jreq		strupr_strlwr_SINGLE
//		;//
//		xld.b		%r12, [%r1+1]		;//   %r12 :=        p[1]
//		xcall		ismbbtrail		;//   if(!ismbbtrail(p[1])) goto SINGLE
//		cmp		%r10, 0
//		jreq		strupr_strlwr_SINGLE
//		;//
//		jp.d		strupr_strlwr_LOOP	;//   continue
//		add		%r1, 2			;//   p += 2	*delay*
//strupr_strlwr_SINGLE:
//		ld.b		%r12, [%r1]		;//   %r12 :=    *p
//		call		%r2			;//   %r10 := fn(*p)
//		ld.b		[%r1]+, %r10		;//   *p   := fn(*p)
//		;//
//		cmp		%r10, 0			;//   if(!*p) break
//		jrne		strupr_strlwr_LOOP
//		;//-------------------------------------;//
//strupr_strlwr_RET:					;// }
//		ld.w		%r10, %r0		;// %r10 := string
//		popn		%r2
//		ret
//");
//#endif /*PIECE*/
//↓2016/06/14変更:既存のstrupr(),strlwr()を、新しいstrupr(),strlwr()とmbsupr(),mbslwr()に分離しました。
// * Tue Jun 14 21:51:27 JST 2016 Naoyuki Sawa
// - 既存のstrupr(),strlwr()を、新しいstrupr(),strlwr()とmbsupr(),mbslwr()に分離しました。
//   詳細は、以下の通りです。
// - 既存のstrupr(),strlwr()は、下記のような経緯で、シフトJIS対応となっていました。
//   │① Mon Jul 06 23:33:17 JST 2009 : シングルバイト版のstrupr(),strlwr()を、作成しました。
//   │② Sat Dec 25 12:48:38 JST 2010 : strupr(),strlwr()を、シフトJIS対応に変更しました。
//   しかし、strupr(),strlwr()が常にシフトJIS対応として動作すると、(無いとは思いますが)UTF-8文字列に対してstrupr(),strlwr()を使う時に問題が生じます。
//   そこで、VC++のライブラリと同様に、シングルバイト版はstrupr(),strlwr()、シフトJIS対応は新規のmbsupr(),mbslwr()として、分けて実装する事にしました。
// - 分けて実装する方法として、上記の①と②のコードを単純にそのまま使う方法も有ったのですが、今回は、改めて再実装する事にしました。
//   そうした理由は、strupr(),strlwr(),mbsupr(),mbslwr()は共通処理が多く、まとめて実装する事で、コードサイズが節約出来るからです。
//   詳細は、strupr_strlwr_mbsupr_mbslwr()の実装を参照して下さい。
// - ■テストプログラム
//   │void test() {
//   │  const char* const tbl[]={
//   │    "TEXT123",
//   │    "text123",
//   │    "ﾃｷｽﾄ123",
//   │    "テキスト１２３",
//   │  };
//   │  char buf[99];
//   │  int i;
//   │  for(i = 0; i < ARRAY_SIZE(tbl); i++) {
//   │    printf("original = '%s'\n", tbl[i]);
//   │    printf("strupr() = '%s'\n", strupr(strcpy(buf, tbl[i])));
//   │    printf("mbsupr() = '%s'\n", mbsupr(strcpy(buf, tbl[i])));
//   │    printf("strlwr() = '%s'\n", strlwr(strcpy(buf, tbl[i])));
//   │    printf("mbslwr() = '%s'\n", mbslwr(strcpy(buf, tbl[i])));
//   │    putchar('\n');
//   │  }
//   │}
//   □結果 (2016/06/14、WindowsとP/ECEで正常に動作する事を確認しました。)
//   │original = 'TEXT123'
//   │strupr() = 'TEXT123'
//   │mbsupr() = 'TEXT123'
//   │strlwr() = 'text123'
//   │mbslwr() = 'text123'
//   │
//   │original = 'text123'
//   │strupr() = 'TEXT123'
//   │mbsupr() = 'TEXT123'
//   │strlwr() = 'text123'
//   │mbslwr() = 'text123'
//   │
//   │original = 'ﾃｷｽﾄ123'
//   │strupr() = 'ﾃｷｽﾄ123'
//   │mbsupr() = 'ﾃｷｽﾄ123'
//   │strlwr() = 'ﾃｷｽﾄ123'
//   │mbslwr() = 'ﾃｷｽﾄ123'
//   │
//   │original = 'テキスト１２３'
//   │strupr() = 'ウキスエ１２３'	←ここは文字化けして正しい。
//   │mbsupr() = 'テキスト１２３'
//   │strlwr() = 'テネベトＱＲＳ'	←ここは文字化けして正しい。
//   │mbslwr() = 'テキスト１２３'
#ifndef PIECE
static char* strupr_strlwr_mbsupr_mbslwr(char* string, int (*strnextc_mbsnextc)(const char* s), int (*toupper_tolower)(int c)) {
	char* s = string;
	int c, hi;
	do {
		c = (*strnextc_mbsnextc)(s);	//一文字取得する。mbsnextc()ならば0x00～0xFF,又は,0x0100～0xFFFFになる。strnextc()ならば0x00～0xFFになる(strnextc()の関数内のコメントを参照せよ)。
		c = (*toupper_tolower)(c);	//大文字化,又は,小文字化する。変換前と返還後で、0x00～0xFF,又は,0x0100～0xFFFFの区分は変わらないはずである。
		hi = c >> 8;			//文字の上位バイトを取得する。
		if(hi) { *s++ = hi; }		//文字の上位バイトが有れば(=文字が0x0100～0xFFFFだったならば)、上位バイトを格納する。
		*s++ = c;			//下位バイトを格納する。
	} while(c);				//いま格納した文字が終端文字だったならば抜ける。
	return string;				//引数stringをそのまま返す。
}
char* strupr(char* string) { return strupr_strlwr_mbsupr_mbslwr(string, strnextc, toupper); }
char* strlwr(char* string) { return strupr_strlwr_mbsupr_mbslwr(string, strnextc, tolower); }
char* mbsupr(char* string) { return strupr_strlwr_mbsupr_mbslwr(string, mbsnextc, toupper); }
char* mbslwr(char* string) { return strupr_strlwr_mbsupr_mbslwr(string, mbsnextc, tolower); }
#else //PIECE
char* strupr(char* string);
char* strlwr(char* string);
char* mbsupr(char* string);
char* mbslwr(char* string);
asm("
		.code
		.align		1
		.global		strupr
		.global		strlwr
		.global		mbsupr
		.global		mbslwr
strupr:
		xld.w		%r13, strnextc				;//%r13 := strnextc_mbsnextc = strnextc
		jp		strupr_mbsupr
		;//- - - - - - - - - - - - - - - - - - - - - - - - - - -;//
mbsupr:
		xld.w		%r13, mbsnextc				;//%r13 := strnextc_mbsnextc = mbsnextc
strupr_mbsupr:
		xld.w		%r14, toupper				;//%r14 := toupper_tolower   = toupper
		jp		strupr_strlwr_mbsupr_mbslwr
		;//- - - - - - - - - - - - - - - - - - - - - - - - - - -;//
strlwr:
		xld.w		%r13, strnextc				;//%r13 := strnextc_mbsnextc = strnextc
		jp		strlwr_mbslwr
		;//- - - - - - - - - - - - - - - - - - - - - - - - - - -;//
mbslwr:
		xld.w		%r13, mbsnextc				;//%r13 := strnextc_mbsnextc = mbsnextc
strlwr_mbslwr:
		xld.w		%r14, tolower				;//%r14 := toupper_tolower   = tolower
		;/* FALLTHRU */
		;//-----------------------------------------------------;//
strupr_strlwr_mbsupr_mbslwr:
		pushn		%r3					;//
		ld.w		%r0, %r12				;//%r0  := string
		ld.w		%r1, %r13				;//%r1  := strnextc_mbsnextc
		ld.w		%r2, %r14				;//%r2  := toupper_tolower
		ld.w		%r3, %r0				;//%r3  := s = string
strupr_strlwr_mbsupr_mbslwr_LOOP:					;//do {
		call.d		%r1					;//  %r10 := c = (*strnextc_mbsnextc)(s)
		ld.w		%r12, %r3				;//  %r12 :=                          s		*delay*
		call.d		%r2					;//  %r10 := c = (*toupper_tolower)(c)
		ld.w		%r12, %r10				;//  %r12 :=                        c		*delay*
		ld.w		%r9, %r10				;//  %r9  :=       c
		sra		%r9, 8					;//  %r9  :=  hi = c >> 8
		jreq.d		3					;//  if(      hi) {
		 cmp		%r10, 0					;//  %psr(Z) := !c	────┐		*delay*
		 ld.b		[%r3]+, %r9				;//    *s++ = hi  }		│
		ld.b		[%r3]+, %r10				;//  *s++ = c			│
		jrne		strupr_strlwr_mbsupr_mbslwr_LOOP	;//} while(c)	←───────┘
		ld.w		%r10, %r0				;//%r10 := string
		popn		%r3					;//
		ret							;//return  string
");
#endif//PIECE
//}}2016/06/14変更:既存のstrupr(),strlwr()を、新しいstrupr(),strlwr()とmbsupr(),mbslwr()に分離しました。

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

/* * Wed Apr 29 14:30:43 JST 2015 Naoyuki Sawa
 *  - memccpy(),mempcpy(),strncpy(),stpncpy(),strcpy(),stpcpy()を追加しました。
 *    宣言は、include/string.hに追加しました。
 *  - strncpy()とstrcpy()は、P/ECE開発環境のEPSONライブラリにも含まれていますが、自分で再実装しました。
 *    stpncpy()とstpcpy()と共通化できる部分が多く、まとめて実装する方がコードサイズ節約できるからです。
 */

//■「Linux Programmer's Manual (3) - MEMCCPY」(http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/memccpy.3.html)より引用:
//□名前
//│memccpy - メモリー領域をコピーする
//□説明
//│memccpy()は、メモリー領域srcからメモリー領域destに最大でnバイトのコピーを行う。
//│nバイトコピーする前に文字cが見つかると、そこでコピーを中止する。
//│コピー元とコピー先のメモリー領域が重なっている場合、結果は不定である。
//□返り値
//│memccpy()は、dest中に見つかったcの次にあるキャラクター型の変数を指すポインターを返す。
//│見つからなかった場合、NULLを返す。
//□準拠
//│SVr4,4.3BSD,POSIX.1-2001.
#ifndef PIECE
void* memccpy(void* dest, const void* src, int c, size_t n) {
	      unsigned char* d = dest;
	const unsigned char* s = src;
	do {
		if(!n--) { return NULL; }
	} while((*d++ = *s++) != c);
	return d;
}
#else //PIECE
void* memccpy(void* dest, const void* src, int c, size_t n);
asm("
		.code
		.align		1
		.global		memccpy
memccpy:
		ld.w		%r10, 0			;//%r10 := NULL
memccpy_LOOP:						;//do {
		sub		%r15, 1			;//  %r15 := n--
		jrult		memccpy_RET		;//  if(n < 0) { return NULL }
		ld.ub		%r9, [%r13]+		;//  %r9  := t = *src++
		ld.b		[%r12]+, %r9		;//  *dest++ = t
		cmp		%r9, %r14		;//} while(t != c)
		jrne		memccpy_LOOP		;//
		ld.w		%r10, %r12		;//%r10 := dest
memccpy_RET:						;//
		ret					;//return  dest
");
#endif//PIECE

//■「Linux Programmer's Manual (3) - MEMPCPY」(http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/mempcpy.3.html)より引用:
//□名前
//│mempcpy - メモリー領域をコピーする
//□説明
//│mempcpy()関数はmemcpy()関数とほぼ同一である。
//│この関数は、srcで始まるオブジェクトからnバイトを、destで指されるオブジェクトにコピーする。
//│ただしdestの値を返すのではなく、最後に書き込まれたバイトの後に続くバイトへのポインターを返す。
//│この関数はいくつかのオブジェクトが連続的なメモリーの場所にコピーされるような状況で役に立つ。
//□返り値
//│dest+n.
//□準拠
//│この関数はGNUによる拡張である。
#ifndef PIECE
void* mempcpy(void* dest, const void* src, size_t n) {
	return (char*)memcpy(dest, src, n) + n;
}
#else //PIECE
void* mempcpy(void* dest, const void* src, size_t n);
asm("
		.code
		.align		1
		.global		mempcpy
mempcpy:
		pushn		%r0			;//
		xcall.d		memcpy			;//%r10 := dest = memcpy(dest, src, n)
		ld.w		%r0, %r14		;//%r0  := n				*delay*
		add		%r10, %r0		;//%r10 := dest + n
		popn		%r0			;//
		ret					;//return  dest + n
");
#endif//PIECE

//■「Linux Programmer's Manual (3) - STPNCPY」(http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/stpncpy.3.html)より引用:
//□名前
//│stpncpy - 固定長の文字列をコピーして、その最後へのポインターを返す
//□説明
//│stpncpy()関数はsrcが指している文字列から終端のヌルバイト('\0')を含めて最大nバイトをdestにコピーする。
//│長さstrlen(src)がnより小さい場合にはdestの残りの部分にはヌルバイト('\0')文字が埋められる。
//│長さstrlen(src)がn以上ならば、destが指す文字列はヌルで終端されていない。
//│二つの文字列は重なってはならない。
//│プログラマーはdestに少なくともnバイトの空きがあることを保証しなければならない。
//□返り値
//│stpncpy()はdestの終端のヌルバイトを指すポインターを返すか、destがヌルバイトで終端されていない場合にはdest+nを返す。
//□準拠
//│この関数はPOSIX.1-2008に追加された。
//│それ以前はGNUによる拡張であった。
//│この関数は1993年にGNU Cライブラリのバージョン1.07で初めて登場した。
#ifndef PIECE
char* strncpy(char* dest, const char* src, size_t n) {
	char*  save_dest = dest;
	stpncpy(dest, src, n);
	return save_dest;
}
char* stpncpy(char* dest, const char* src, size_t n) {
	char* save_dest;
	do {
		if(!n--) { return dest; }
	} while((*dest++ = *src++));
	save_dest = dest - 1;
	goto L_START;
	do {
		*dest++ = '\0';
L_START:	;
	} while(n--);
	return save_dest;
}
#else //PIECE
char* strncpy(char* dest, const char* src, size_t n);
char* stpncpy(char* dest, const char* src, size_t n);
asm("
		.code
		.align		1
		.global		strncpy
		.global		stpncpy
strncpy:
		call		stpncpy			;//%r12 := save_dest = dest
		ret.d					;//return  save_dest		┐
stpncpy:						;//				│
		ld.w		%r10, %r12		;//%r10 := dest			┘	*delay*
stpncpy_LOOP:						;//do {
		sub		%r14, 1			;//  %r14 := n--
		jrult		stpncpy_RET		;//  if(n < 0) { return dest }
		ld.ub		%r9, [%r13]+		;//  %r9  := c = *src++
		ld.b		[%r10]+, %r9		;//  *dest++ = c
		cmp		%r9, 0			;//  
		jrne		stpncpy_LOOP		;//} while(c)
		ld.w		%r9, %r10		;//%r9  := dest
		jp.d		stpncpy_START		;//goto L_START
		sub		%r10, 1			;//%r10 := save_dest = dest - 1		*delay*
stpncpy_FILL:						;//do {
		ld.b		[%r9]+, %r8		;//  *dest++ = '￥0'
stpncpy_START:						;//L_START:
		sub		%r14, 1			;//  %r14 := n--
		jruge		stpncpy_FILL		;//} while(  n)
stpncpy_RET:						;//
		ret					;//return  save_dest
");
#endif//PIECE

//■「Linux Programmer's Manual (3) - STPCPY」(http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/stpcpy.3.html)より引用:
//□名前
//│stpcpy - 文字列をコピーし、コピーした文字列の終りへのポインターを返す
//□説明
//│stpcpy()関数は、srcで指された文字列を(文字列を終端するヌルバイト('\0')を含めて)destで指された配列にコピーする。
//│文字列は重複してはならず、コピー先の文字列destはコピーを受け取るのに十分大きくなくてはならない。
//□返り値
//│stpcpy()は、文字列destの始まりではなく終りを指すポインター(すなわち、文字列を終端するヌルバイト)を返す。
//□準拠
//│この関数はPOSIX.1-2008に追加された。
//│それ以前は、この関数はCやPOSIX.1標準の一部でもUNIXシステムの慣習的なものでもなかった。
//│この関数はLatticeCAmigaDOSコンパイラで初めて登場し、それは少なくとも1986年時点ではそうであった。
//│その後、1989年にGNUfileutilsとGNU textutilsに登場し、1992年までにはGNU Cライブラリにも存在していた。
//│この関数はBSD系にも存在する。
#ifndef PIECE
#ifndef _MSC_VER	//{{2015/05/31:Visual C++ 6.0でReleaseビルドすると「error C2169: 'strcpy' : 組み込み関数は定義できません。」になるので除外しました。
char* strcpy(char* dest, const char* src) {
	char*  save_dest = dest;
	stpcpy(dest, src);
	return save_dest;
}
#endif//_MSC_VER	//}}2015/05/31:Visual C++ 6.0でReleaseビルドすると「error C2169: 'strcpy' : 組み込み関数は定義できません。」になるので除外しました。
char* stpcpy(char* dest, const char* src) {
	do { /** no job **/ } while((*dest++ = *src++));
	return dest - 1;
}
#else //PIECE
char* strcpy(char* dest, const char* src);
char* stpcpy(char* dest, const char* src);
asm("
		.code
		.align		1
		.global		strcpy
		.global		stpcpy
strcpy:
		call		stpcpy			;//%r12 := save_dest = dest
		ret.d					;//return  save_dest		┐
stpcpy:							;//				│
		ld.w		%r10, %r12		;//%r10 := dest			┘	*delay*
stpcpy_LOOP:						;//do {
		ld.ub		%r9, [%r13]+		;//  %r9  := c = *src++
		ld.b		[%r10]+, %r9		;//  *dest++ = c
		cmp		%r9, 0			;//} while(c)
		jrne		stpcpy_LOOP		;//
		ret.d					;//return  dest - 1
		sub		%r10, 1			;//%r12 := dest - 1			*delay*
");
#endif//PIECE

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

// * Wed Mar 15 22:06:35 JST 2017 Naoyuki Sawa
// - strtol(),strtoul()を自前で実装しました。
//   宣言は、元々 include/stdlib.h に有ったので、追加していません。
//   strtol(),strtoul()を自前で実装した理由は、以下の通りです。
// - P/ECE開発環境のEPSONライブラリのstrtol(),strtoul()には、以下の問題点が有ります。
//   │①引数baseには(0,又は,2～36)だけを許容すべきだが、EPSONライブラリのstrtoul()は(base=1)も含めて許容しており正しくない。
//   │②文字列の先頭の空白文字を読み飛ばす際isspace()を使って判断すべきだが、EPSONライブラリのstrtoul()はisspace()を使わずに' 'だけを読み飛ばしており正しくない。
//   ①は、呼び出し側のアプリケーションが正しければ顕在化しないので、小さな問題です。
//   しかし、②は、本来正常に変換されるべき文字列を誤って変換出来ない場合が有るので、大きな問題です。
//   今回は主に②の問題を修正するために、strtol(),strtoul()を自前で実装しました。(ついでに①も修正しました)
// - 今回自前で実装したstrtol(),strtoul()は、オーバーフロー検査を省略しました。
//   本来のstrtol(),strtoul()は、オーバーフローが発生した場合、errnoを設定してULONG_MAX等を返す仕様です。
//   しかし実際には、strtol(),strtoul()がオーバーフロー検査を行う事に依存するようなアプケーションは作成しないし、アプリケーション側でerrnoを確認する事もしません。
//   つまりstrtol(),strtoul()のオーバーフロー検査はほとんど無駄なので、strtol(),strtoul()側でもオーバーフロー検査を省略しました。
// - オーバーフロー検査を省略した事により、strtol()とstrtoul()の動作は全く同じになりました。
//   そこで、strtol()とstrtoul()の実装をまとめて、コードサイズ削減しました。
//   今回の変更による、コードサイズ削減の効果は、以下の通りです。
//   □変更前(EPSONライブラリ)
//   │C:\usr\PIECE\lib\lib.lib:strtoul.o  300バイト ┬ 合計648バイト┐
//   │C:\usr\PIECE\lib\lib.lib:strtol.o   348バイト ┘              │
//   □変更後                                                        │460バイト削減
//   │strtoul()とstrtol()の両対応の関数   188バイト ←───────┘
//   尚、strtoul()とstrtol()をcliplibc.cに含めた事により、常にこれらの関数や呼び出しているctype.oがリンクされるようになりますが、
//   元々ライブラリの他の箇所からstrtoul()やctype.oの関数を呼び出していたので、元々それらは常にリンクされており、今回の変更による新たなオーバーヘッドは有りません。
// - 今回strtol(),strtoul()を自前で実装した理由は、上述の通り、P/ECE開発環境のEPSONライブラリの問題点回避,及び,コードサイズ削減のためであり、
//   Windows環境では自前実装に置き換える必要は無いのですが、今回作成したコードの検証のために、Windows環境でも自前実装を使用する事にしました。

#ifndef PIECE
//□「Linux Programmer's Manual - STRTOL」(https://linuxjm.osdn.jp/html/LDP_man-pages/man3/strtol.3.html)
//│strtol - 文字列をlong intに変換する
//│説明
//│strtol()関数は、nptrの文字列の最初の部分を、baseを基数としてlong intに変換する。
//│このbaseは2から36までの値あるいは特別な意味を持つ値0でなければならない。
//│文字列の先頭には、任意の数の空白があってもよく(空白はisspace()で判定される)、また数字の直前には'+'か'-'の符号があってもよい。
//│baseが0または16の場合には、文字列の先頭に"0x"を置くことができ、その場合には文字列は16進数として扱われる。
//│これ以外の文字列でbaseが0の場合は、文字列が'0'で始まるときは8進数として、それ以外のときは10進数として扱われる。
//│数字を表す文字列はlong intに変換されるが、基数に対して有効でない数字が現れた時点で変換は終了する。
//│(11進数以上では'A'は大文字・小文字に関わらず10を表し、'B'は11を表現し、以下同様に、'Z'は35を表す。)
//│endptrがヌル値(NULL)でない場合は、最初に現れた不正な文字がstrtol()によって*endptrに保存されている。
//│文字列に有効な数字がひとつもなければ、strtol()はnptrの元の値を*endptrに代入する(そして0を返す)。
//│特に、*nptrが'\0'以外で、返された**endptrが'\0'ならば、文字列全体が有効だったことになる。
//│返り値
//│アンダーフローもオーバーフローも起きなかった場合、strtol()関数は変換された値を返す。
//│オーバーフローした場合にはLONG_MAXが返り、アンダーフローした場合にはLONG_MINが返る。							←←←←←←←←←┬当実装では、オーバーフロー,及び,アンダーフローの検査を省略しました。
//│オーバーフロー、アンダーフローのいずれの場合にも大域変数errnoにはERANGEが設定される。							←←←←←←←←←┘
long strtol(const char* nptr, char** endptr, int base) {
	//strtol()とstrtoul()の仕様上の違いはオーバーフロー検査の範囲だけであり、当実装ではオーバーフローを無視するので、結果的にstrtol()とstrtoul()は同じ処理となります。
	return strtoul(nptr, endptr, base);
}
//□「Linux Programmer's Manual - STRTOUL」(https://linuxjm.osdn.jp/html/LDP_man-pages/man3/strtoul.3.html)
//│strtoul - 文字列をunsigned long intに変換する
//│説明
//│strtoul()関数は、nptrの文字列の最初の部分を、baseを基数としてunsigned long intに変換する。
//│このbaseは2から36までの値あるいは特別な意味を持つ値0でなければならない。
//│文字列の先頭には、任意の数の空白があってもよく(空白はisspace()で判定される)、また数字の直前には'+'か'-'の符号があってもよい。
//│baseが0または16の場合には、文字列の先頭に"0x"を置くことができ、その場合には文字列は16進数として扱われる。
//│これ以外の文字列でbaseが0の場合は、文字列が'0'で始まるときは8進数として、それ以外のときは10進数として扱われる。
//│文字列の残りの部分はunsigned long intに変換されるが、基数に対して有効でない数字が現れた時点で変換は終了する。
//│(11進数以上では'A'は大文字・小文字に関わらず10を表し、'B'は11を表現し,以下同様に、'Z'は35を表す。)
//│endptrがヌル値(NULL)でない場合、strtoul()は最初に不正な文字が現れたアドレスを*endptrに格納している。
//│文字列に有効な数字がひとつもなければ、strtoul()はnptrの元の値を*endptrに代入する(そして0を返す)。
//│特に、*nptrが'\0'以外で、返された**endptrが'\0'ならば、文字列全体が有効だったことになる。
//│返り値
//│strtoul()関数は変換結果を返す。
//│文字列がマイナス符号から始まっていた場合は、(符号反転前の値がオーバーフローしていなければ)変換結果を符号反転した値をunsigned型で返す。	←←←←←←←←←←【注目】strtoul()は(名前から受ける印象とは違い)、'-'を不正な文字と見なす*のではない*事に注意して下さい。(そういう仕様です。)
//│変換でオーバーフローが発生した場合はULONG_MAXを返し、errnoにERANGEを設定する。								←←←←←←←←←←当実装では、オーバーフローの検査を省略しました。
unsigned long strtoul(const char* nptr, char** endptr, int base) {
	const char* eptr = nptr;				//文字列に有効な数字がひとつ現れるまで、eptrは文字列の先頭位置を維持する。
	int c, sign, sum;
	//基数が0,又は,2～36である事を確認する。
	if((base == 1) || ((unsigned)base > 36)) { DIE(); }	//【P/ECE開発環境のバグ情報】EPSONライブラリのstrtoul()は(base=1)も含めて許容しており正しくない。
	//空白を読み飛ばす。
	do { c = *nptr++; } while(isspace(c));			//【P/ECE開発環境のバグ情報】EPSONライブラリのstrtoul()はisspace()を使わずに' 'だけを読み飛ばしており正しくない。
//不要	nptr--;							//空白ではなかった文字の位置へ戻す。			←┐
	//符号を判定する。												　├この二つの処理は相殺出来るので不要です。
//不要	c = *nptr++;						//符号(かも知れない)文字を読み出して、位置を進める。	←┘
	sign = -1;						//仮に、符号を負としておく。
	if(c != '-') {						//符号(かも知れない)文字が、'-'でなければ…
		sign = 1;					//符号を正とする。
		if(c != '+') {					//符号(かも知れない)文字が、'+'でなければ…
			nptr--;					//符号ではなかった文字の位置へ戻す。
		}
	}
	//基数が0,又は,16ならば…
	if(!(base & ~16)) {					//「if(!base || (base == 16)) {」と等価です。
		c = *nptr;					//'0'(かも知れない)文字を読み出す。位置は進めない。
		//"0x",又は,"0X"が有れば…
		if((c == '0') && (toupper(nptr[1]) == 'X')) {
			base = 16;				//基数を16とする。元から(base=16)だった場合はダミー処理です。
			nptr += 2;				//"0x"を読み飛ばす。
		//"0x",又は,"0X"が無く、基数が0ならば…
		} else if(!base) {
			base = 10;				//仮に、基数を10としておく。
			//"0"が有れば…
			if(c == '0') {
				base = 8;			//基数を8とする。
			//不可	nptr++;				//【注意】16進数の"0x"は読み飛ばす必要が有りますが、8進数の"0"は読み飛ばしてはいけません。16進数の"0x"の直後に不正な文字が現れた場合は文字列に有効な数字が一つも無いと見なすべきですが、8進数の"0"の直後に不正な文字が現れた場合は"0"単独で有効な数字だと見なすべきだからです。もしここで"0"を読み飛ばしてしまうと、例えば「strtoul("0",&p,0)」の結果、正しくはpが文字列の終端のnulを指すべきなのに、(間違って)文字列に有効な数字がひとつもないと判断してしまい、pが文字列の先頭を指してしまうというバグが生じます。
			}
		}
	}
	//不正な文字,又は,終端のnulが現れるまで…
	sum = 0;
	for(;;) {
		c = *nptr++;					//次の文字を読み出して、位置を進める。
		//'0'～'9'ならば…
		if(isdigit(c)) {
			c -= '0';				//0～9に変換する。
		//'A'～'Z',又は,'a'～'z'ならば…
		} else if(isalpha(c)) {
			c = toupper(c) - ('A' - 10);		//10～35に変換する。
		//不正な文字,又は,終端のnulならば…
		} else {
			break;					//変換を終了する。
		}
		//基数の範囲外ならば、変換を終了する。
		if(c >= base) { break; }
		//変換した値を、結果の値に含める。
		// - 正しくはここでオーバーフロー検査を行うべきですが、当実装ではオーバーフローを無視して強行します。
		//   尚、strtol()とstrtoul()の仕様上の違いはオーバーフロー検査の範囲だけであり、当実装ではオーバーフローを無視するので、結果的にstrtol()とstrtoul()は同じ処理となります。
		//   strtoul()は(名前から受ける印象とは違い)、'-'を不正な文字と見なす*のではない*事に注意して下さい。(そういう仕様です。)
		sum = (sum * base) + c;
		//今変換した文字の次の位置を、eptrに記憶する。
		// - もし次の位置で変換出来なくなった場合は、endptrに次の位置を返すためです。
		//   尚、初回にここへ到達するまでは、eptrは文字列の先頭位置を維持しています。
		//   文字列に有効な数字がひとつもなければ、空白や符号や"0x"を読み飛ばす前の、文字列の先頭位置をendptrに返します。
		eptr = nptr;
	}
	//最初に不正な文字が現れたアドレス,又は,文字列に有効な数字がひとつもなければnptrの元の値を*endptrに代入する。
	if(endptr) { *endptr = (char*)eptr; }
	//結果の値に符号を掛け合わせて返す。
	return sum * sign;
}
#else //PIECE
long strtol(const char* nptr, char** endptr, int base);
unsigned long strtoul(const char* nptr, char** endptr, int base);
asm("
		.code
		.align		1
		.global		strtol
		.global		strtoul
strtol:
strtoul:
		pushn		%r3
		xsub		%sp, %sp, 12
		;//---------------------------------------------;//
		xld.w		[%sp+0], %r12			;//[%sp+0].w := eptr = nptr
		ld.w		%r0, %r12			;//%r0       := nptr
		xld.w		[%sp+4], %r13			;//[%sp+4].w := endptr
		ld.w		%r1, %r14			;//%r1       := base
		;//%r0       := nptr
		;//%r1       := base
		;//[%sp+0].w := eptr
		;//[%sp+4].w := endptr
		;//---------------------------------------------;//
		cmp		%r14, 1				;//if(base == 1) { DIE() }
		jreq		strtoul_DIE			;//
		sub		%r14, 36			;//if((unsigned)base > 36) { DIE(); }					ここでは%r14を破壊しても構わないので、'xcmp %r14,36'(2命令)よりも'sub %r14,36'(1命令)の方が命令数が少なく済む。
		jrugt		strtoul_DIE			;//
		;//---------------------------------------------;//
strtoul_L10:							;//do {
		ld.b		%r2, [%r0]+			;//  %r2       := c = *nptr++
		xcall.d		isspace				;//  %r10      := isspace(c)
		ld.w		%r12, %r2			;//  %r12      :=         c				*delay*
		cmp		%r10, 0				;//  
		jrne		strtoul_L10			;//} while(       isspace(c))
		;//---------------------------------------------;//
		sub		%r2,  45			;//%r2       := c - '-'							ここでは%r2を破壊しても構わないので、'xcmp %r2,45'(2命令)よりも'sub %r2,45'(1命令)の方が命令数が少なく済む。
		jreq.d		strtoul_L20			;//if(        !(c - '-')) { goto L20 }
		ld.w		%r9, -1				;//%r9       := sign = -1				*delay*		文字が'-'だったら、(sign=-1)として、以下の処理を行わない。
		ld.w		%r9, 1				;//%r9       := sign = 1						文字が'-'でなければ、(sign=1)として、以下の処理を行う。
		add		%r2, (45-43)			;//%r2       :=           c - '+'					文字が'-'でも'+'でも無かった場合のみ、ここで(c!=0)になる。
		cmp		%r8, %r2			;//%psr(C)   :=          (c - '+') ? 1 : 0				文字が'-'でも'+'でも無かった場合のみ、ここで(%psr(C)=1)になる。
		sbc		%r0, %r8			;//%r0       := nptr -= ((c - '+') ? 1 : 0)				文字が'-'でも'+'でも無かった場合のみ、ここでnptrを一文字戻す。
strtoul_L20:							;//
		xld.b		[%sp+8], %r9			;//[%sp+8].b := sign
		;//%r0       := nptr
		;//%r1       := base
		;//[%sp+0].w := eptr
		;//[%sp+4].w := endptr
		;//[%sp+8].b := sign
		;//---------------------------------------------;//
		xand		%r9, %r1, ~16			;//if(base & ~16) { goto L40 }
		jrne.d		strtoul_L40			;//
		ld.w		%r2, 0				;//%r2       := sum = 0					*delay*
		ld.b		%r3, [%r0]			;//%r3       := c = *nptr
		sub		%r3, 48				;//%r3       := c - '0'					!INTERLOCK!	ここでは%r3を破壊しても構わないので、'xcmp %r3,48'(2命令)よりも'sub %r3,48'(1命令)の方が命令数が少なく済む。
		jrne		strtoul_L30			;//if(c - '0') { goto L30 }
		xld.b		%r12, [%r0+1]			;//%r12      :=         nptr[1]
		xcall		toupper				;//%r10      := toupper(nptr[1])
		xcmp		%r10, 88			;//if(          toupper(nptr[1]) != 'X') { goto L30 }
		jrne		strtoul_L30			;//
		ld.w		%r1, 16				;//%r1       := base = 16
		jp.d		strtoul_L40			;//goto L40
		add		%r0, 2				;//%r0       := nptr += 2				*delay*
strtoul_L30:							;//
		cmp		%r1, 0				;//if(base) { goto L40 }
		jrne		strtoul_L40			;//
		cmp		%r3, 0				;//if(c - '0') { goto L40 }
		jrne.d		strtoul_L40			;//
		ld.w		%r1, 10				;//%r1       := base = 10				*delay*
		ld.w		%r1, 8				;//%r1       := base = 8
		;//%r0       := nptr
		;//%r1       := base
		;//%r2       := sum
		;//[%sp+4].w := endptr
		;//[%sp+8].b := sign
		;//---------------------------------------------;//
strtoul_L40:							;//for(;;) {
		ld.b		%r3, [%r0]+			;//  %r3       := c = *nptr++
		xcall.d		isdigit				;//  %r10      :=  isdigit(c)
		ld.w		%r12, %r3			;//  %r12      :=          c				*delay*
		cmp		%r10, 0				;//  %psr(Z)   := !isdigit(c)
		jreq		strtoul_L50			;//  if(          !isdigit(c)) { goto L50 }
		jp.d		strtoul_L60			;//  goto L60
		sub		%r3, 48				;//  %r3       := c -= '0'				*delay*
strtoul_L50:							;//  
		xcall.d		isalpha				;//  %r10      :=  isalpha(c)
		ld.w		%r12, %r3			;//  %r12      :=          c				*delay*
		cmp		%r10, 0				;//  %psr(Z)   := !isalpha(c)
		jreq		strtoul_L70			;//  if(          !isalpha(c)) { goto L70 }
		xcall.d		toupper				;//  %r10      := c = toupper(c)
		ld.w		%r12, %r3			;//  %r12      :=             c				*delay*
		ld.w		%r3, %r10			;//  %r3       := c
		sub		%r3, (65-10)			;//  %r3       := c -= ('A' - 10)
strtoul_L60:							;//  
		cmp		%r3, %r1			;//  if(c >= base) { goto L70 }
		jrge		strtoul_L70			;//  
		mlt.w		%r2, %r1			;//  %alr      :=        sum * base
		ld.w		%r2, %alr			;//  %r2       :=        sum * base
		xld.w		[%sp+0], %r0			;//  [%sp+0].w := eptr = nptr
		jp.d		strtoul_L40			;//}
		add		%r2, %r3			;//  %r2       := sum = (sum * base) + val		*delay*
strtoul_L70:							;//
		;//---------------------------------------------;//
		xld.w		%r4, [%sp+4]			;//%r4       := endptr
		xld.b		%r5, [%sp+8]			;//%r5       := sign					*anti-interlock*
		cmp		%r4, 0				;//if(endptr) {
		jreq		strtoul_L80			;//  
		xld.w		%r9, [%sp+0]			;//  %r9     := eptr
		ld.w		[%r4], %r9			;//  *endptr := eptr					!INTERLOCK!
strtoul_L80:							;//}
		;//---------------------------------------------;//
		mlt.w		%r2, %r5			;//%alr      := sum * sign
		xadd		%sp, %sp, 12
		popn		%r3
		ret.d
		ld.w		%r10, %alr			;//%r10      := sum * sign				*delay*
");
static void __attribute__((noreturn,unused))/*asmブロックから参照*/ strtoul_DIE() { DIE(); }
#endif//PIECE

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

// * Sun Aug 06 22:33:03 JST 2017 Naoyuki Sawa
// - div(),ldiv()を自前で実装しました。
//   宣言は、元々 include/stdlib.h に有ったので、追加していません。
//   div(),ldiv()を自前で実装した理由は、以下の通りです。
// - P/ECE開発環境のEPSONライブラリのdiv(),ldiv()には、以下の問題点が有ります。
//   │除算と剰余を個別に実行しており、__divdi3と__moddi3が呼び出されて、遅い。
//   ksvec200.sでの独自実装の__divsi3を使えば、一回の呼び出しで済みます。
//   今回、そのように自前で実装して、div(),及び,ldiv()を高速化しました。
//   EPSONライブラリのdiv(),ldiv()よりも、おおよそ二倍高速になりました。
#ifndef PIECE
div_t div(int n, int d) {
	div_t t;
	t.quot = n / d;
	t.rem  = n % d;
	//VC++やglibcやEPSONライブラリのdiv()の実装の場合は、ここで被除数と剰余の符号が異なる場合の補正が行われている。
	//古いC言語の仕様では、除算や剰余の結果の符号が処理系依存だったので、汎用性の有る実装になっているからだと思う。
	//厳密には、C89までは処理系依存で、C99で定義されたらしい。⇒https://stackoverflow.com/questions/13208287/remainder-operator-in-c89-and-c99
	//実際の所、x86やC33の除算の挙動は昔からC99と同じで、補正は不要だと思うので省略した。
	return t;
}
ldiv_t ldiv(long n, long d) {
	ldiv_t t;
	t.quot = n / d;
	t.rem  = n % d;
	//VC++やglibcやEPSONライブラリのdiv()の実装の場合は、ここで被除数と剰余の符号が異なる場合の補正が行われている。
	//古いC言語の仕様では、除算や剰余の結果の符号が処理系依存だったので、汎用性の有る実装になっているからだと思う。
	//厳密には、C89までは処理系依存で、C99で定義されたらしい。⇒https://stackoverflow.com/questions/13208287/remainder-operator-in-c89-and-c99
	//実際の所、x86やC33の除算の挙動は昔からC99と同じで、補正は不要だと思うので省略した。
	return t;
}
#else //PIECE
//(l)div_t構造体のquot,remフィールドの並び順は、C言語の仕様では規定されておらず、quot,remの順もrem,quotの順も有り得る。
//一般的にはquot,remの順が多いようだが、EPSONライブラリではrem,quotの順となっており、当実装もrem,quotの順を前提とした。
//念のため、rem,quotの順である事を、この下でstatic_assert()にて確認しておく事にした。
static_assert((offsetof( div_t, rem) == 0) && (offsetof( div_t, quot) == 4),  "div_t構造体の並びが想定と異なります。");
static_assert((offsetof(ldiv_t, rem) == 0) && (offsetof(ldiv_t, quot) == 4), "ldiv_t構造体の並びが想定と異なります。");
 div_t  div(int  n, int  d);
ldiv_t ldiv(long n, long d);
asm("
		.code
		.align		1
		.global		div
		.global		ldiv
div:
ldiv:
		ld.w		%r15, %r12		;//%r15 := pRetVal
		ld.w		%r12, %r13		;//%r12 := n
		xcall.d		__divsi3		;//%r10 := n / d,  %ahr := n % d			独自実装の__divsi3の動作に依存している。(ksvec200.s参照)
		ld.w		%r13, %r14		;//%r13 :=     d			*delay*
		xld.w		[%r15+4], %r10		;//%r15 := pRetVal->quot = n / d
		ld.w		%r10, %ahr		;//%r10 :=                 n % d
		ld.w		[%r15], %r10		;//%r15 := pRetVal->rem  = n % d
		ret.d					;//return  pRetVal
		ld.w		%r10, %r15		;//%r10 := pRetVal			*delay*
");
#endif//PIECE

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

/* * Thu Jul 09 00:22:54 JST 2009 Naoyuki Sawa
 * - VC++6.0の_splitpath()を、ほぼそのまま移植しました。
 * * Fri May 20 21:59:17 JST 2016 Naoyuki Sawa
 * - splitpath()をマルチバイト対応しました。
 *   これまでは、ファイル名や拡張子に二バイト目が'\'の文字が含まれていた時に、誤ってその位置までディレクトリと見なしてしまうバグが有りました。
 *   修正後は、上記の問題は無くなりました。
 *   │//■検証プログラム
 *   │void test() {
 *   │  const char path[] = "C:\\フォルダ\\表現.txt";
 *   │  //                                  ~「表」="\x95\x5C"
 *   │  char drive[MAX_DRIVE], dir[MAX_DIR], fname[MAX_FNAME], ext[MAX_EXT];
 *   │  splitpath(path, drive, dir, fname, ext);
 *   │  printf("path:  '%s'\n", path);
 *   │  printf("drive: '%s'\n", drive);
 *   │  printf("dir:   '%s'\n", dir);
 *   │  printf("fname: '%s'\n", fname);
 *   │  printf("ext:   '%s'\n", ext);
 *   │}
 *   │//□結果【splitpath()修正前】
 *   │//│path:  'C:\フォルダ\表現.txt'
 *   │//│drive: 'C:'
 *   │//│dir:   '\フォルダ\表'	←┬間違い。「表」の2バイト目の'\'を最後のパスセパレータと見なしてしまっている。
 *   │//│fname: '現'        ~		←┘
 *   │//│ext:   '.txt'
 *   │//□結果【splitpath()修正後】
 *   │//│path:  'C:\フォルダ\表現.txt'
 *   │//│drive: 'C:'
 *   │//│dir:   '\フォルダ\'		←┬正しくなった。
 *   │//│fname: '表現'		←┘
 *   │//│ext:   '.txt'
 */
void
splitpath(const char* path, char drive[/*MAX_DRIVE*/], char dir[/*MAX_DIR*/], char fname[/*MAX_FNAME*/], char ext[/*MAX_EXT*/])
{
	const char* sep;
	const char* dot;
	const char* p;
	int len;

	/* パス名が2文字以上で、2文字目が':'ならば、最初の2文字がドライブ名。 */
	if((path[0] != '\0') && (path[1] == ':')) {
		if(drive) {
			memcpy(drive, path, 2);
			drive[2] = '\0';
		}
		path += 2; /* pathをドライブ名の次へ進める */
	} else {
		if(drive) {
			drive[0] = '\0';
		}
	}

	/* 最後の区切り文字の次の位置と、最後のドット文字の位置を検索します。 */
	sep = dot = NULL;
//{{2016/05/20変更:splitpath()をマルチバイト対応しました。
//	for(p = path; *p; p++) {
//↓2016/05/20変更:splitpath()をマルチバイト対応しました。
	for(p = path; *p; p = mbsinc(p)) {
//}}2016/05/20変更:splitpath()をマルチバイト対応しました。
		if((*p == '/') || (*p == '\\')) {
			sep = p + 1; /* 区切り文字の次の位置 */
		} else if(*p == '.') {
			dot = p;     /* ドット文字の位置 */
		}
	}

	/* 区切り文字が有れば、最後の区切り文字も含めてディレクトリ名を格納。 */
	if(sep) {
		if(dir) {
			len = sep - path;
			if(len > (MAX_DIR - 1)) {
				len = (MAX_DIR - 1);
			}
			memcpy(dir, path, len);
			dir[len] = '\0';
		}
		path = sep; /* pathをファイル名の先頭へ進める */
	} else {
		if(dir) {
			dir[0] = '\0';
		}
	}

	/* ファイル名の先頭よりも後ろに、ドット文字が有れば… */
	if(dot >= path) { /* この条件で !dot も除外できます。 */
		/* ドット文字の前まで、ファイル名として格納。 */
		if(fname) {
			len = dot - path;
			if(len > (MAX_FNAME - 1)) {
				len = (MAX_FNAME - 1);
			}
			memcpy(fname, path, len);
			fname[len] = '\0';
		}
		/* ドット文字から最後まで、拡張子として格納。 */
		if(ext) {
			len = p/*末端*/ - dot;
			if(len > (MAX_EXT - 1)) {
				len = (MAX_EXT - 1);
			}
			memcpy(ext, dot, len);
			ext[len] = '\0';
		}
	} else {
		/* 残りの文字をすべて、ファイル名として格納。 */
		if(fname) {
			len = p/*末端*/ - path;
			if(len > (MAX_FNAME - 1)) {
				len = (MAX_FNAME - 1);
			}
			memcpy(fname, path, len);
			fname[len] = '\0';
		}
		if(ext) {
			ext[0] = '\0';
		}
	}
}

/* * Thu Jul 09 00:22:54 JST 2009 Naoyuki Sawa
 * - VC++6.0の_makepath()を、少し変更して移植しました。
 *   VC++6.0の_makepath()は、path[MAX_PATH]への格納文字数をチェックしておらず、MAX_PATH文字を超えても格納してしまいます。
 *   以下のコードは、path[MAX_PATH]への格納文字数を、終端の'\0'を除いて(MAX_PATH-1)文字以下に切り詰めるよう変更しました。
 * * Fri May 20 21:59:17 JST 2016 Naoyuki Sawa
 * - makepath()をマルチバイト対応しました。
 *   これまでは、ディレクトリ名の最後の文字が二バイト目が'\'の文字だった時に、ディレクトリ名とファイル名の間に'\'を補われないバグが有りました。
 *   修正後は、上記の問題は無くなりました。
 *   │//■検証プログラム
 *   │void test() {
 *   │  const char drive[] = "C:";
 *   │  const char dir[]   = "\\フォルダ\\代表";
 *   │  const char fname[] = "ファイル";//   ~「表」="\x95\x5C"
 *   │  const char ext[]   = ".txt";
 *   │  char path[MAX_PATH];
 *   │  makepath(path, drive, dir, fname, ext);
 *   │  printf("drive: '%s'\n", drive);
 *   │  printf("dir:   '%s'\n", dir);
 *   │  printf("fname: '%s'\n", fname);
 *   │  printf("ext:   '%s'\n", ext);
 *   │  printf("path:  '%s'\n", path);
 *   │}
 *   │//□結果makepath()修正前】
 *   │//│drive: 'C:'
 *   │//│dir:   '\フォルダ\代表'
 *   │//│fname: 'ファイル'
 *   │//│ext:   '.txt'
 *   │//│path:  'C:\フォルダ\代表ファイル.txt'	←間違い。「表」の2バイト目の'\'を明示的なパスセパレータと見なしてしまい、パスセパレータの追加が不要と見なしてしまっている。
 *   │//                         ~
 *   │//□結果makepath()修正後】
 *   │//│drive: 'C:'
 *   │//│dir:   '\フォルダ\代表'
 *   │//│fname: 'ファイル'
 *   │//│ext:   '.txt'
 *   │//│path:  'C:\フォルダ\代表\ファイル.txt'	←正しくなった。
 */
void
makepath(char path[/*MAX_PATH*/], const char* drive, const char* dir, const char* fname, const char* ext)
{
	char* path_end = &path[MAX_PATH - 1];

	/* ドライブ名が1文字以上指定されていたら、ドライブ名を格納。 */
	if(drive && *drive) {
		*path++ = *drive;
		//if(path == path_end) goto L_EXIT; /* 有り得ない */
		*path++ = ':'; /* 2文字目は':'固定 */
		//if(path == path_end) goto L_EXIT; /* 有り得ない */
	}

	/* ディレクトリ名が1文字以上指定されていたら、ディレクトリ名を格納。 */
	if(dir && *dir) {
//{{2016/05/20追加:makepath()をマルチバイト対応しました。
		char *start = path, *last;
//}}2016/05/20追加:makepath()をマルチバイト対応しました。
		do {
			*path++ = *dir++;
			if(path == path_end) goto L_EXIT;
		} while(*dir);
		/* 末尾の区切り文字が省略されていたら、区切り文字を補う。
		 * - ディレクトリ名に空文字列が指定された場合(dir="")は、区切り文字を補わないことに注意。
		 *   もし空文字列(dir="")に対して区切り文字を補ってしまうと、以下のようなバグが発生する。
		 *     splitpath("xxx.yyy", drive, dir, fname, ext); -> drive = "", dir = "", fname = "xxx", ext = ".yyy"
		 *     makepath(path, drive, dir, fname, ext);       -> path = "\xxx.yyy" (元のpathに無い'\'が増えるバグ)
		 */
//{{2016/05/20変更:makepath()をマルチバイト対応しました。
//		if((*(path - 1) != '/') && (*(path - 1) != '\\')) {
//↓2016/05/20変更:makepath()をマルチバイト対応しました。
		last = mbsdec(start, path);
		if(last && (*last != '/') && (*last != '\\')) {
		// ~~~~このブロックはディレクトリ名が1文字以上有る場合のみ実行するブロックなので、ここで(last==NULL)になる事は無いはずであるが、形式上検査しておく事にした。検査しなくても結果は同じなので、省略しても構わない。
//}}2016/05/20変更:makepath()をマルチバイト対応しました。
			*path++ = '\\';
			if(path == path_end) goto L_EXIT;
		}
	}

	/* ファイル名を格納。 */
	if(fname) {
		while(*fname) {
			*path++ = *fname++;
			if(path == path_end) goto L_EXIT;
		}
	}

	/* 拡張子を格納。 */
//{{2016/05/18修正:makepath()のext引数に空文字を渡した時に'.'を補ってしまっていたバグを修正しました。makepath()が'.'を補うのはext引数が非NULL,かつ,空文字でない時のみとするのが正解でした。本物の_makepath()もそうなっています。
//	if(ext) {
//↓2016/05/18修正:makepath()のext引数に空文字を渡した時に'.'を補ってしまっていたバグを修正しました。makepath()が'.'を補うのはext引数が非NULL,かつ,空文字でない時のみとするのが正解でした。本物の_makepath()もそうなっています。
	if(ext && *ext) {
//}}2016/05/18修正:makepath()のext引数に空文字を渡した時に'.'を補ってしまっていたバグを修正しました。makepath()が'.'を補うのはext引数が非NULL,かつ,空文字でない時のみとするのが正解でした。本物の_makepath()もそうなっています。
		if(*ext != '.') {
			*path++ = '.'; /* 先頭のドット文字が省略されていたら、ドット文字を補う */
			if(path == path_end) goto L_EXIT;
		}
		while(*ext) {
			*path++ = *ext++;
			if(path == path_end) goto L_EXIT;
		}
	}
L_EXIT:
	*path = '\0'; /* 終端 */
}

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

/* ワードの中で最初にセットされている最下位ビットの位置を返します。(Find first set)
 * [in]
 *	i	ワード
 * [out]
 *	戻り値	(i = ???????? ???????? ???????? ???????1 の時) ⇒  1 … 最下位ビットは 1
 *		(i = ???????? ???????? ???????? ??????10 の時) ⇒  2
 *		(i = ???????? ???????? ???????? ?????100 の時) ⇒  3
 *		                  ( 中 略 )
 *		(i = ?1000000 00000000 00000000 00000000 の時) ⇒ 31
 *		(i = 10000000 00000000 00000000 00000000 の時) ⇒ 32 … 最上位ビットは32
 *		(i = 00000000 00000000 00000000 00000000 の時) ⇒  0 … ワードが0ならば0
 * [note]
 *	- 本関数は、ffs(): 4.3BSD, POSIX.1-2001. 準拠です。
 *	  参照: Linux Programmer's Manual (3) FFS
 *	        http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/ffs.3.html
 *	- P/ECEのコンパイラgcc33は、__builtin_ffsを認識しますが、インライン展開しません。
 *	  gcc33は、__builtin_ffsを見つけると、ffs関数を呼び出すコードを生成するようです。
 *	  つまり、プログラム中で__builtin_ffsを使うのとffsを使うのは、等価です。
 */
#ifndef PIECE
//----------------------------------------------------------------------------
//C言語版
//----------------------------------------------------------------------------
int ffs(int i) {
	int n;
	for(n = 1; n <= 32; n++) {
		if((i & 1) != 0) {
			return n;
		}
		i >>= 1;
	}
	return 0;
}
#else /*PIECE*/
int ffs(int i);
//----------------------------------------------------------------------------
//アセンブラ版①
//高速ですが、ややコードサイズが大きな実装です。
//----------------------------------------------------------------------------
//asm("
//		.code
//		.align		1
//		.global		ffs
//ffs:
//		swap		%r12, %r12	;//          +-- 反転後の値で､1が最上位ビットに有る時                                             反転後の値で､1が最下位ビットに有る時 --+   +-- 1が無い時
//		mirror		%r12, %r12	;//          |                                                                                                                           |   |
//		scan1		%r10, %r12	;// %r10 :=  0   1   2   3   4   5   6   7   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8
//		sll		%r12, %r10
//		scan1		%r11, %r12	;// %r11 :=  0   0   0   0   0   0   0   0   0   1   2   3   4   5   6   7   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8   8
//		sll		%r12, %r11
//		add		%r10, %r11	;// %r10 :=  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  16  16  16  16  16  16  16  16  16  16  16  16  16  16  16  16
//		scan1		%r11, %r12	;// %r11 :=  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   1   2   3   4   5   6   7   8   8   8   8   8   8   8   8   8
//		sll		%r12, %r11
//		add		%r10, %r11	;// %r10 :=  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  24  24  24  24  24  24  24  24
//		scan1		%r11, %r12	;// %r11 :=  0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   0   1   2   3   4   5   6   7   8
//		jruge.d		3
//		 add		%r10, %r11	;// %r10 :=  0   1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32	*delay*
//		 ld.w		%r10, -1
//		ret.d
//		add		%r10, 1		;// %r10 :=  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32   0
//");
//----------------------------------------------------------------------------
//アセンブラ版②
//低速で、省メモリな実装です。
//ffs()は使用頻度が低く、実行速度は重要でないので、こちらの方が良いです。
//----------------------------------------------------------------------------
//asm("
//		.code
//		.align		1
//		.global		ffs
//ffs:
//		cmp		%r12, 0		;// if(i != 0) {
//		jreq.d		7
//		 ld.w		%r10, 0		;// %r10 := n = 0						*delay*
//		 swap		%r12, %r12	;//   %r12 := i = 反転(i)
//		 mirror		%r12, %r12
//		  add		%r12, %r12	;//   do { %psr(C) := i[31], %r12 := i <<= 1, %r10 := n++
//		 jruge.d	-1		;//   } while(%psr(C) == 0)
//		 add		%r10, 1		;// }								*delay*
//		ret				;// return n
//");
//----------------------------------------------------------------------------
//アセンブラ版③
//さらに低速で、さらに省メモリな実装です。
//処理方法を工夫して、一命令節約しました。
//----------------------------------------------------------------------------
asm("
		.code
		.align		1
		.global		ffs
ffs:
		cmp		%r12, 0		;// if(i != 0) {
		jreq.d		6
		 ld.w		%r10, 0		;// %r10 := n = 0			*delay*
		  sll		%r12, 1		;//   do { %r12 := i <<= 1, %r10 := n++
		 jrne.d		-1		;//   } while(i != 0)
		 sub		%r10, 1		;//					*delay*
		 add		%r10, 33	;// %r10 := n += 33  (-1～-32)⇒(32～1)
		ret				;// return n
");
#endif/*PIECE*/

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

#ifdef  PIECE
//{{2014/09/29追加:malloc_usable_size()を追加しました。
size_t malloc_usable_size(void* ptr) {
	//                              |               |
	//  (((HEAPMEM*)ptr)-1)         +===============+
	//                              | mark  | owner |
	//                              +---------------|
	//                              |     chain     |
	//              ptr             +---------------+
	//               A              |///////////////|
	//               |              |///////////////|
	//               |size          |///////////////|
	//               |              |///////////////|
	//               V              |///////////////|
	// ((((HEAPMEM*)ptr)-1)->chain) +===============+
	//                              | mark  | owner |
	//                              +---------------|
	//                              |     chain     |
	//                              +---------------+
	//                              |               |
	return ptr ? ((int)((((HEAPMEM*)ptr)-1)->chain) - (int)ptr) : 0;
}
//}}2014/09/29追加:malloc_usable_size()を追加しました。
#endif//PIECE
#ifdef  WIN32
#ifdef  GC_H
size_t malloc_usable_size(void* ptr) {	//Visual C++ 6.0 with Boehm GC
	//Boehm GCを使用すると、malloc()はメモリブロックの途中(=管理情報の後)のポインタを返す。
	//GC_size()は、メモリブロックの途中が指定されても、メモリブロックの全体のサイズを返す。
	//malloc()が返したポインタ以降のサイズを得るには、以下のように計算しなくてはならない。(実験より推測)
//{{2016/09/03変更:malloc_usable_size()が、(int)でアドレス演算を行っていたのを、(char*)で行うように修正しました。
//	return ptr ? (GC_size(ptr) - ((int)ptr - (int)GC_base(ptr))) : 0;
//↓2016/09/03変更:malloc_usable_size()が、(int)でアドレス演算を行っていたのを、(char*)で行うように修正しました。
	return ptr ? (GC_size(ptr) - ((char*)ptr - (char*)GC_base(ptr))) : 0;
//}}2016/09/03変更:malloc_usable_size()が、(int)でアドレス演算を行っていたのを、(char*)で行うように修正しました。
}
#else //GC_H
size_t malloc_usable_size(void* ptr) {	//Visual C++ 6.0 without Boehm GC
	return ptr ? _msize(ptr) : 0;
}
#endif//GC_H
#endif//WIN32

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

// * Tue Mar 24 21:38:35 JST 2015 Naoyuki Sawa
// - EPSONライブラリのbsearch()を置き換えました。
//   EPSONライブラリのbsearch()のままでも問題無いのですが、C言語で実装されていて生成されるコードに少し無駄が有ります。
//   アセンブラ化する事によって、68バイト⇒60バイトになり、8バイトのサイズ節約になりました。
//   アセンブラ化による高速化の効果は、比較関数の処理時間を含めなければ、約15%の高速化です。
// * Wed May 04 21:28:21 JST 2016 Naoyuki Sawa
// - bsearch_r()を追加し、bsearch()はbsearch_r()への呼び出しに変更しました。宣言はinclude/stdlib.hに有ります。
//   標準Cライブラリではbsearch_r()は定義されていないのですが、必要な事が多いので追加する事にしました。
// - 標準Cライブラリでで、qsort()に対するqsort_r()は定義されているのに、bsearch()に対するbsearch_r()が定義されていないのが不思議です。
//   Webで検索すると同様の疑問を持つ人は少なくないようなのですが、なぜこうなっているのかの理由は見付かりませんでした。
// * Fri Sep 30 21:41:38 JST 2016 Naoyuki Sawa
// - bsearch_r()のC言語版の実装で、'goto L_START'が変数'mid'と'retVal'の宣言をまたいでいた問題を修正しました。
//   動作上はそのままでも問題ありませんし、P/ECE開発環境(GCC)やWindows(VC++6.0,VS2015)では警告も出ないのですが、Hewで「C5546 (W) Transfer of control bypasses initialization of:」という警告が出たからです。
//   今回の変更は、Hewで上記の警告を消すためだけの修正であり、今回の修正による動作の変化は有りません。
//   尚、今回の変更を行っても、Hewではなお「C5128 (I) Loop is not reachable from preceding code」という警告が出るのですが、C言語仕様的に問題無く、明らかに誤った警告なので、その警告は無視する事にしました。
#ifndef PIECE
void* bsearch(const void* key, const void* _base, size_t nmemb, size_t size, int (*compar)(const void*, const void*)) {
	return bsearch_r(key, _base, nmemb, size, (int (*)(const void*, const void*, void*))compar, NULL);	//bsearch()とbsearch_r()は、compar関数の形式が異なりますが、'cdecl呼出規約'ならば、bsearch()形式のcompar関数において、余分な引数が無視されるので問題ありません。
}
void* bsearch_r(const void* key, const void* _base, size_t nmemb, size_t size, int (*compar)(const void*, const void*, void*), void* arg) {	//独自拡張
//{{2016/09/30変更:bsearch_r()のC言語版の実装で、'goto L_START'が変数'mid'と'retVal'の宣言をまたいでいた問題を修正しました。
//	const unsigned char* base = _base;
//↓2016/09/30変更:bsearch_r()のC言語版の実装で、'goto L_START'が変数'mid'と'retVal'の宣言をまたいでいた問題を修正しました。
	const unsigned char *base = _base, *mid;
	int retVal;
//}}2016/09/30変更:bsearch_r()のC言語版の実装で、'goto L_START'が変数'mid'と'retVal'の宣言をまたいでいた問題を修正しました。
	goto L_START;
	do {
//{{2016/09/30変更:bsearch_r()のC言語版の実装で、'goto L_START'が変数'mid'と'retVal'の宣言をまたいでいた問題を修正しました。
//		const unsigned char* mid = base + ((nmemb >> 1) * size);
//		int retVal = (*compar)(key, mid, arg);
//↓2016/09/30変更:bsearch_r()のC言語版の実装で、'goto L_START'が変数'mid'と'retVal'の宣言をまたいでいた問題を修正しました。
		mid = base + ((nmemb >> 1) * size);
		retVal = (*compar)(key, mid, arg);
//}}2016/09/30変更:bsearch_r()のC言語版の実装で、'goto L_START'が変数'mid'と'retVal'の宣言をまたいでいた問題を修正しました。
		if(retVal == 0) { return (void*)mid; }
		if(retVal >  0) {
			base = mid + size;
			nmemb--;
		}
		nmemb >>= 1;
L_START:	;
	} while(nmemb);
	return NULL;
}
#else //PIECE
void* bsearch(const void* key, const void* _base, size_t nmemb, size_t size, int (*compar)(const void*, const void*));
void* bsearch_r(const void* key, const void* _base, size_t nmemb, size_t size, int (*compar)(const void*, const void*, void*), void* arg);	//独自拡張
asm("
		.code
		.align		1
		.global		bsearch
		.global		bsearch_r
bsearch:
bsearch_r:
		pushn		%r3
		xsub		%sp, %sp, 4
		xld.w		[%sp+0], %r12		;//[%sp+0] := key
		ld.w		%r0, %r13		;//%r0     := base
		ld.w		%r1, %r14		;//%r1     := nmemb
		ld.w		%r2, %r15		;//%r2     := size
		jp.d		bsearch_START		;//goto L_START
		cmp		%r1, 0			;//%psr(Z) := !nmemb			────┐	*delay*
bsearch_LOOP:						;//do {						│
		;//%r0      := base									│
		;//%r1      := nmemb									│
		;//%r2      := size									│
		;//%r10     := nmemb>>1									│
		;//[%sp+ 0] := key									│
		;//[%sp+ 4] := %r0									│
		;//[%sp+ 8] := %r1									│
		;//[%sp+12] := %r2									│
		;//[%sp+16] := %r3									│
		;//[%sp+20] := %pc									│
		;//[%sp+24] := compar									│
		;//[%sp+28] := arg									│
		mlt.w		%r10, %r2		;//  %alr :=             (nmemb>>1)*size	│
		ld.w		%r3, %alr		;//  %r3  :=             (nmemb>>1)*size	│
		add		%r3, %r0		;//  %r3  := mid = base+((nmemb>>1)*size)	│
		xld.w		%r9, [%sp+24]		;//  %r9  :=            compar			│
		xld.w		%r12, [%sp+0]		;//  %r12 :=                    key		│
		xld.w		%r14, [%sp+28]		;//  %r12 :=                              arg	│
		call.d		%r9			;//  %r10 := retVal = (*compar)(key, mid, arg)	│
		ld.w		%r13, %r3		;//  %r13 :=                         mid	│	*delay*
		cmp		%r10, 0			;//  						│
		jreq		bsearch_FOUND		;//  if(retVal == 0) { return mid }		│
		jrlt		4			;//  if(retVal >  0) {				│
		 ld.w		%r0, %r3		;//    %r0  :=        mid			│
		 add		%r0, %r2		;//    %r0  := base = mid + size		│
		 sub		%r1, 1			;//    %r1  := nmemb-- }			│
		srl		%r1, 1			;//  %r1  := nmemb >>= 1		────┤
bsearch_START:						;//  						│
		ld.w		%r10, %r1		;//  %r10 :=              nmemb			│
		jrne.d		bsearch_LOOP		;//} while(nmemb)			←───┘
		srl		%r10, 1			;//  %r10 :=              nmemb>>1			*delay*
bsearch_RET:						;//
		xadd		%sp, %sp, 4
		popn		%r3
		ret
bsearch_FOUND:
		jp.d		bsearch_RET		;//return  mid
		ld.w		%r10, %r3		;//%r10 := mid						*delay*
");
#endif//PIECE

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

// * Sat Jul 25 17:20:54 JST 2015 Naoyuki Sawa
// - log2()を追加しました。宣言は、include/math.h に有ります。log2()は、C99で追加された関数です。「Lua 5.3.1」が、使用しています。
//   EPSONライブラリにも、Visual C++ 6.0にも、log2()は含まれていません。
// * Thu Sep 29 21:37:05 JST 2016 Naoyuki Sawa
// - log2()をアセンブラ化しました。
// - VS2015では当モジュール内でlog2を実装せず、ランタイムライブラリ内の実装を使用するようにしました。
//   これまでVC++6.0では問題無かったのですが、本日、VS2015の64ビット環境でもcliplibc.cを含めるように変更した所、VS2015の64ビット環境では以下の警告が出たからです。
//   │warning C4273: 'log2': dll リンクが一貫していません。
//   │c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\math.h(506): note: 'log2' の以前の定義を確認してください
//   もしかするとVS2015や64ビット環境の問題ではなく、VC++6.0のプロジェクトは/MT,VS2015のプロジェクトは/MDで作成しているのが原因かも知れないのですが、とりあえずVS2015ではこれらの定義を行わない事にしました。
// * Sat Oct 01 21:11:57 JST 2016 Naoyuki Sawa
// - Hewでは当モジュール内でlog2,copysign,lround,lrint,round,nearbyint,rint,trunc,fdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。
//   Hewのランタイムライブラリには、C99で追加されたこれらの関数が、ビルトイン関数として含まれているからです。
#ifndef PIECE
#ifndef __RENESAS__	//{{2016/10/01追加:Hewでは当モジュール内でlog2,copysign,lround,lrint,round,nearbyint,rint,trunc,fdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。}}
#if     !(defined(_MSC_VER) && (_MSC_VER >= 1900))	//{{2016/09/29追加:VS2015では当モジュール内でlog2を実装せず、ランタイムライブラリ内の実装を使用するようにしました。}}
double log2(double x) {
	return log(x) / 0.69314718055994530941723212145818/*=log(2)*/;
}
#endif//!(defined(_MSC_VER) && (_MSC_VER >= 1900))	//{{2016/09/29追加:VS2015では当モジュール内でlog2を実装せず、ランタイムライブラリ内の実装を使用するようにしました。}}
#endif//__RENESAS__	//{{2016/10/01追加:Hewでは当モジュール内でlog2,copysign,lround,lrint,round,nearbyint,rint,trunc,fdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。}}
#else //PIECE
double log2(double x);
asm("
		.code
		.align		1
		.global		log2
log2:
		xcall		log			;//%r11:%r10 := log(x)
		xld.w		%r14, 0xFEFA39EF	;//%r15:%r14 :=          0.69314718055994530941723212145818/*=log(2)*/
		xld.w		%r15, 0x3FE62E42	;//
		ld.w		%r12, %r10		;//%r13:%r12 := log(x)
		xjp.d		__divdf3		;//return       log(x) / 0.69314718055994530941723212145818/*=log(2)*/
		ld.w		%r13, %r11		;//							*delay*
");
#endif//PIECE

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

// * Sat Oct 10 21:38:51 JST 2015 Naoyuki Sawa
// - /usr/PIECE/include/setjmp.hを$(CLIP)/include/setjmp.hにコピーして、jmp_buf[]の定義を修正しました。詳細は下記を参照して下さい。
//   /usr/PIECE/include/setjmp.hよりも$(CLIP)/include/setjmp.hが優先されるように、clip.mkの中でインクルードパスを設定してあります。
// - cliplibc.cの中でsetjmp(),longjmp()を実装して、EPSONライブラリの同名の関数を置き換えました。
//   EPSONライブラリ版の関数は、保存する必要の無いレジスタも保存していて、無駄が有ったからです。
// - EPSONライブラリ版の関数は、%alr,%ahr,%psrも保存してしますが、これらを保存する必要は有りません。
//   関数呼び出しの前後で、%alr,%ahr,%psrを保存する必要は無く、setjmp()とlongjmp()も同様だからです。
// - EPSONライブラリ版の関数が%psrを保存しているのは、割り込みレベルや割り込み許可フラグの復元を意図しているのかも知れませんが、
//   実際に割り込みサービスルーチンの中からlongjmp()で抜ける事はしないと思うので、不要だと思います。
//   仮にそういう意図だったとしても、割り込みサービスルーチンの中から抜ける時に%psr(I)や%psr(IL)を戻すだけでは不十分で、
//   割り込みコントローラ(ITC)の設定も必要で、CPUの割り込みレベルや割り込み許可フラグを復元するだけでは不十分です。
//   要するに、setjmp()とlongjmp()が%psrを保存しても無駄なので、%psrの保存は必要無いと判断しました。
// - EPSONライブラリ版のsetjmp.hで定義されているjmp_buf[]にも、無駄が有りました。
//   EPSONライブラリ版の関数は、9個の値を保存しているのでjmp_buf[9]で足りるのに、jmp_buf[16]となっていました。
//   自作の関数は、最低限の6個の値を保存するようにしたので、jmp_buf[6]としました。
// - 以上の変更によって、jmp_bufが64バイト⇒24バイトになり、40バイト分のメモリ節約になりました。
//   特に、jmp_bufをスタック上に確保する時に、スタックメモリを節約出来る効果が大きいと思います。
#ifdef  PIECE
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
asm("
		.code
		.align	1
		.global		setjmp
		.global		longjmp
setjmp:
		;//%r12 := env
		ld.w		%r4, %sp		;//%r4     := %sp
		ld.w		%r5, [%r4]+		;//%r5     := retp, %r4 := %sp+4
		ld.w		[%r12]+, %r0		;//env[0]  := %r0
		ld.w		[%r12]+, %r1		;//env[1]  := %r1
		ld.w		[%r12]+, %r2		;//env[2]  := %r2
		ld.w		[%r12]+, %r3		;//env[3]  := %r3
		ld.w		[%r12]+, %r4		;//env[4]  := %sp+4
		ld.w		[%r12]+, %r5		;//env[5]  := retp
		ret.d					;//return     0
		ld.w		%r10, 0			;//%r10    := 0				*delay*
longjmp:
		;//%r12 := env
		;//%r13 := val
		ld.w		%r0, [%r12]+		;//%r0     := env[0]
		ld.w		%r1, [%r12]+		;//%r1     := env[1]
		ld.w		%r2, [%r12]+		;//%r2     := env[2]
		ld.w		%r3, [%r12]+		;//%r3     := env[3]
		ld.w		%r4, [%r12]+		;//%r4     := env[4]
		ld.w		%r5, [%r12]+		;//%r5     := env[5]
		ld.w		%sp, %r4		;//%sp+4   := %r4
		ld.w		%r10, 0			;//%r10    := 0
		cmp		%r13, 1			;//%psr(C) :=           !val
		adc		%r10, %r13		;//%r10    := 0 + val + !val
		jp		%r5			;//return     0 + val + !val		'jp.d %rb'は使用不可 (CPU bug)
");
#endif//PIECE

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

// * Wed Oct 14 21:50:46 JST 2015 Naoyuki Sawa
// - □「Linux Programmer's Manual - BASENAME」(http://linuxjm.osdn.jp/html/LDP_man-pages/man3/basename.3.html)より引用:
//   │dirname()とbasename()は、ヌルで終端されたパス名の文字列を、ディレクトリ部分・ファイル名部分に分割する。
//   │dirname()は最後の'/'までの部分(最後の'/'は含まない)を返し、basename()は最後の'/'以降の部分を返す。
//   厳密には上記以外にも細かな仕様が有るのだが、実用上は上記だけで十分なので、当モジュールでは単純に実装する事にした。
// - basename()には二種類の異なるバージョンが有る。一つはPOSIXバージョンであり、もう一つはGNUバージョンである。
//   │GNUバージョンは引き数を変更することはなく、pathの末尾が'/'の場合は空の文字列を返す。
//   当モジュールのbasename()は、GNUバージョンに準拠する事にした。従って、basename()に定数文字列を指定する事が出来る。
// - 実際の所、dirname()を使う事はあまり無く、basename()を使う事の方が多いと思う。
//   なぜなら、複雑なパス解析を行う場合は、単純なdirname(),basename()ではなく、splitpath()を使うと思うからだ。
//   dirname(),basename()を使うのは、単純に済ませたいケースであり、具体的には、エラーメッセージの中にベース名だけを表示したいケース等である。
//   たとえば、以下のように使用する事が多いだろう。
//   │die("error at %s(%d)", basename(__FILE__), __LINE__);
//   この時、POSIXバージョンだとbasename()定数領域の文字列を書き換えようとしてしまい、不正な動作になる。
//   GNUバージョンならば、引数文字列を書き換えないので、上記のように使用する事が出来、都合が良い。
// - 尚、dirname()は引数文字列を書き換えるので、上記のような用途に使用する事は出来ない。
//   前述の通り、実際にdirname()を使う事はあまり無いだろうが、もし使用する場合は注意せよ。
char* dirname(char* path) {	//引数pathに定数文字列を指定する事は出来ません。
	char* p = strrchr(path, '/');			//┐
	if(!p) { p = strrchr(path, '\\'); }		//┴パスセパレータが'/'と'\'のどちらでも対応出来るようにした。
	return p ? (*p = '\0', path) : (char*)".";
}
char* basename(char* path) {	//当実装では引数pathに定数文字列を指定出来ます。
	char* p = strrchr(path, '/');			//┐
	if(!p) { p = strrchr(path, '\\'); }		//┴パスセパレータが'/'と'\'のどちらでも対応出来るようにした。
	return p ? (p + 1) : path;
}

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

// * Tue Oct 20 21:18:14 JST 2015 Naoyuki Sawa
// - a64l(),l64a(),l64a_r()を追加しました。宣言は、include/stdlib.h に有ります。
//   □「Linux Programmer's Manual - A64L」(http://linuxjm.osdn.jp/html/LDP_man-pages/man3/a64l.3.html)より引用:
//   │これらの関数は32ビットlong整数とリトルエンディアンbase-64ASCII文字列(長さ0～6)の間の変換を行う。
//   │a64l()の引き数の文字列が7文字以上の場合、最初の6バイトが使われる。
//   │l64a()で返される値は静的バッファーへのポインターかもしれないので、以降の呼び出しで上書きされる可能性がある。valueが0の場合は空文字列を返す。
//   │【準拠】POSIX.1-2001.
//   □「NetBSD 6.1.5 - man page for a64l」(http://www.unix.com/man-page/netbsd/3/a64l/)より引用:
//   │l64a_r() performs a conversion identical to that of l64a() and stores the resulting representation in the memory area pointed to by buffer,
//   │consuming at most buflen characters including the terminating NUL character.
//   │On successful completion, l64a_r() returns 0; if buffer is of insufficient length, -1 is returned.
// - !!注意!!
//   a64l(),l64a(),l64a_r()の数値⇔文字対応は、標準的なBase64エンコーディング(RFC3548/RFC4648)とは異なります。
//   l64a()でエンコードした文字列を、dbase64enc.exe等でデコードする事は出来ません。
//   dbase64enc.exe等でエンコードした文字列を、a64l()でデコードする事は出来ません。
//   a64l(),l64a(),l64a_r()はあくまでも、『1ワード単位の少ないデータを、バイナリセーフでない通信路上で、簡易的に通信する』場合だけに使用すべきです。
//   大きなデータを通信する場合は、clipcode.cのbase64_encode()/base64_decode()を使用して下さい。
//   また、標準的なBase64エンコーディングを用いた、他のアプリケーションと通信する場合も、a64l(),l64a(),l64a_r()ではなく、base64_encode()/base64_decode()を使用して下さい。
static const char TBL_l64a[]=
"."				//'.'は    0を表す。
"/"				//'/'は    1を表す。
"0123456789"			//0-9は 2-11を表す。
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"	//A-Zは12-37を表す。
"abcdefghijklmnopqrstuvwxyz";	//a-zは38-63を表す。
static char l64a_buffer[7] __attribute__((unused))/*asmブロックから参照*/;
#ifndef PIECE
char* l64a(long n) {
	if(l64a_r(n, l64a_buffer, sizeof l64a_buffer)) { DIE(); }	//ここで停止する事は起こり得ないはずです。もし停止したら当モジュールのバグです。
	return l64a_buffer;
}
int l64a_r(long n, char* buffer, int buflen) {
	for(;;) {
		if(--buflen < 0) { return -1; }	//If buffer is of insufficient length, -1 is returned.
		if(!n) { break; }
		*buffer++ = TBL_l64a[n & 63];
		n = (unsigned)n >> 6;
	}
	*buffer = '\0';
	return 0;	//On successful completion, l64a_r() returns 0.
}
long a64l(const char* s) {
	int n = 0, i = 0;
	for(;;) {
		int t = strchr(TBL_l64a, *s++) - TBL_l64a;
		if((unsigned)t > (unsigned)63) { break; }	//(*s=='\0')だった場合、(t=64)になりループを抜ける。
		n |= t << (i * 6);
		if(++i >= 6) { break; }		//a64l()の引き数の文字列が7文字以上の場合、最初の6バイトが使われる。
	}
	return n;
}
#else //PIECE
char* l64a(long n);
int l64a_r(long n, char* buffer, int buflen);
long a64l(const char* s);
asm("
		.code
		.align		1
		.global		l64a
		.global		l64a_r
		.global		a64l
l64a:
		;//%r12 := n
		xld.w		%r13, l64a_buffer		;//%r13 :=   l64a_buffer
		call.d		l64a_r				;//l64a_r(n, l64a_buffer, sizeof l64a_buffer)
		ld.w		%r14, 7				;//%r14 :=                sizeof l64a_buffer	*delay*
		xld.w		%r10, l64a_buffer		;//return l64a_buffer
		ret
		;//---------------------------------------------;//
l64a_r:
		;//%r12 := n
		;//%r13 := buffer
		;//%r14 := buflen
		ld.w		%r10, -1			;//%r10 := -1
l64a_r_LOOP:							;//for(;;) {
		sub		%r14, 1				;//  %r14 := buflen--
		jrlt		l64a_r_RET			;//  if(buflen < 0) { return -1 }
		cmp		%r12, 0				;//  if(!n) { break }
		jreq		l64a_r_BREAK			;//  
		xand		%r9, %r12, 63			;//  %r9  :=              n & 63
		ext		TBL_l64a@ah			;//  
		ext		TBL_l64a@al			;//  
		ld.ub		%r9, [%r9]			;//  %r9  :=     TBL_l64a[n & 63]
		ld.b		[%r13]+, %r9			;//  *buffer++ = TBL_l64a[n & 63]
		jp.d		l64a_r_LOOP			;//}
		srl		%r12, 6				;//  %r12 := n = (unsigned)n >> 6		*delay*
l64a_r_BREAK:
		ld.b		[%r13], %r8			;//*buffer = '￥0'
		ld.w		%r10, 0				;//return 0
l64a_r_RET:
		ret
		;//---------------------------------------------;//
a64l:
		;//%r12 := s
		pushn		%r3
		ld.w		%r0, %r12			;//%r0  := s
		ld.w		%r1, 0				;//%r1  := n = 0
		ld.w		%r2, 0				;//%r2  := i = 0
		xld.w		%r3, TBL_l64a			;//%r3  := TBL_l64a
a64l_LOOP:							;//for(;;) {
		ld.b		%r13, [%r0]+			;//  %r13 =                       c = *s++
		xcall.d		strchr				;//  %r10 :=     strchr(TBL_l64a, c)
		ld.w		%r12, %r3			;//  %r12 :=            TBL_l64a		*delay*
		sub		%r10, %r3			;//  %r10 := t = strchr(TBL_l64a, c) - TBL_l64a
		xcmp		%r10, 63			;//  if((unsigned)t > (unsigned)63) { break }
		jrugt		a64l_BREAK			;//  
		ld.w		%r9, 6				;//  %r10 :=      t <<= (i * 6)
		sla		%r10, %r2			;//  
		sub		%r9, 1				;//  
		jrne		-2				;//  
		add		%r2, 1				;//  %r2  := i++
		cmp		%r2, 6				;//  if(i >= 6) { break }
		jrlt.d		a64l_LOOP			;//}
		or		%r1, %r10			;//  %r1  := n |= t				*delay*
a64l_BREAK:							;//
		ld.w		%r10, %r1			;//%r10 := n
		popn		%r3
		ret
");
#endif//PIECE

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

/* * Tue Nov 24 21:41:25 JST 2015 Naoyuki Sawa
 * - sleep(),usleep()を追加しました。宣言はunistd.hに有ります。
 */
#ifdef  PIECE
unsigned sleep(unsigned seconds) {
	return usleep(seconds * 1000000);
}
int usleep(useconds_t usec) {
	unsigned st, ed;
	//注意!pceTimerGetPrecisionCount()の戻り値はリニアな値ではありません。
	//差分を求めるにはpceTimerAdjustPrecisionCount()を使う必要があります。
	st = pceTimerGetPrecisionCount();
	do {
		ed = pceTimerGetPrecisionCount();
	} while(pceTimerAdjustPrecisionCount(st, ed) < usec);
	return 0;
}
#endif//PIECE
#ifdef  WIN32
unsigned sleep(unsigned seconds) {
	Sleep(seconds * 1000);
	return 0;
}
int usleep(useconds_t usec) {
	Sleep((usec + 999) / 1000);
	return 0;
}
#endif//WIN32

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

// * Thu Mar 24 21:04:15 JST 2016 Naoyuki Sawa
// - signbit(),copysign(),copysignf()を追加しました。
//   これらはC99で追加された符号操作用のマクロ,及び,関数で、あまり使う事は無いと思いますが、覚えておくために実装しておく事にしました。
// - round(),roundf(),nearbyint(),nearbyintf(),rint(),rintf(),lround(),lroundf(),lrint(),lrintf()を追加しました。
//   これらはC99で追加された丸め関数で、特に、lround()が便利だと思います。
//   いままでは'(int)((x>=0.0)?(x+0.5):(x-0.5))'のように書いていた処理が、'lround(x)'だけで済むようになりました。
// - 宣言は全て、include/math.hに有ります。
//   倍精度バージョンの関数、copysign(),lround(),lrint(),round(),nearbyint(),rint(),trunc()の実装は、cliplibc.cに有ります。
//   単精度バージョンの関数、copysignf(),lroundf(),lrintf(),roundf(),nearbyintf(),rintf(),truncf()の実装は、framflt2.cに有ります。
//   実装を分けた理由は、単精度演算ライブラリが(アプリケーションが使用していないのに)無駄にリンクされて、サイズが増える事を防ぐためです。
// * Thu Sep 29 21:37:05 JST 2016 Naoyuki Sawa
// - VS2015では当モジュール内でcopysign,lround,lrint,round,nearbyint,rint,truncを実装せず、ランタイムライブラリ内の実装を使用するようにしました。
//   これまでVC++6.0では問題無かったのですが、本日、VS2015の64ビット環境でもcliplibc.cを含めるように変更した所、VS2015の64ビット環境では以下の警告が出たからです。
//   │warning C4273: 'copysign': dll リンクが一貫していません。
//   │c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\math.h(486): note: 'copysign' の以前の定義を確認してください
//   もしかするとVS2015や64ビット環境の問題ではなく、VC++6.0のプロジェクトは/MT,VS2015のプロジェクトは/MDで作成しているのが原因かも知れないのですが、とりあえずVS2015ではこれらの定義を行わない事にしました。
// * Sat Oct 01 21:11:57 JST 2016 Naoyuki Sawa
// - Hewでは当モジュール内でlog2,copysign,lround,lrint,round,nearbyint,rint,trunc,fdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。
//   Hewのランタイムライブラリには、C99で追加されたこれらの関数が、ビルトイン関数として含まれているからです。
#ifndef PIECE
#ifndef __RENESAS__	//{{2016/10/01追加:Hewでは当モジュール内でlog2,copysign,lround,lrint,round,nearbyint,rint,trunc,fdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。}}
#if     !(defined(_MSC_VER) && (_MSC_VER >= 1900))	//{{2016/09/29追加:VS2015では当モジュール内でcopysign,lround,lrint,round,nearbyint,rint,truncを実装せず、ランタイムライブラリ内の実装を使用するようにしました。}}
double  copysign(double x, double y) {
	int* px = (int*)&x + 1;					//*px := seeeeeee eeeemmmm mmmmmmmm mmmmmmmm
	int* py = (int*)&y + 1;					//*py := SEEEEEEE EEEEMMMM MMMMMMMM MMMMMMMM
	*px &= 0x7FFFFFFF;					//*px := 0eeeeeee eeeemmmm mmmmmmmm mmmmmmmm
	*py &= 0x80000000;					//*py := S0000000 00000000 00000000 00000000
	*px |= *py;						//*px := Seeeeeee eeeemmmm mmmmmmmm mmmmmmmm
	return x;						//return Seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm
}
long      lround(double x) { return (long)(x + ((x >= 0.0) ? 0.5 : -0.5)); }
long       lrint(double x) { return lround(x); }		//本当はfesetround()の設定に従うべきだが、簡単に実装するためにlround()と同じ動作にした。実用上はこれでも支障無いと思うし、実際の所当関数を使う事が無い気もする。
double     round(double x) { return lround(x); }
double nearbyint(double x) { return  round(x); }		//本当はfesetround()の設定に従うべきだが、簡単に実装するためにround()と同じ動作にした。実用上はこれでも支障無いと思うし、実際の所当関数を使う事が無い気もする。
double      rint(double x) { return  round(x); }		//本当はfesetround()の設定に従うべきだが、簡単に実装するためにround()と同じ動作にした。実用上はこれでも支障無いと思うし、実際の所当関数を使う事が無い気もする。
double     trunc(double x) { return (long)x; }
#endif//!(defined(_MSC_VER) && (_MSC_VER >= 1900))	//{{2016/09/29追加:VS2015では当モジュール内でcopysign,lround,lrint,round,nearbyint,rint,truncを実装せず、ランタイムライブラリ内の実装を使用するようにしました。}}
#endif//__RENESAS__	//{{2016/10/01追加:Hewでは当モジュール内でlog2,copysign,lround,lrint,round,nearbyint,rint,trunc,fdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。}}
#else //PIECE
double  copysign(double x, double y);
long      lround(double x);
long       lrint(double x);
double     round(double x);
double nearbyint(double x);
double      rint(double x);
double     trunc(double x);
asm("
		.code
		.align		1
		.global		copysign
		.global		lround
		.global		lrint
		.global		round
		.global		nearbyint
		.global		rint
		.global		trunc
copysign:
		add		%r15, %r15			;//%r15 := EEEEEEEE EEEMMMMM MMMMMMMM MMMMMMM0, %psr(C) := S
		adc		%r13, %r13			;//%r13 := eeeeeeee eeemmmmm mmmmmmmm mmmmmmmS
		rr		%r13, 1				;//%r13 := Seeeeeee eeeemmmm mmmmmmmm mmmmmmmm
		ld.w		%r11, %r13			;//
		ret.d						;//return  Seeeeeee eeeemmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm mmmmmmmm
		ld.w		%r10, %r12			;//											*delay*
		;//---------------------------------------------;//
lround:
lrint:
		xand		%r15, %r13, 0x80000000		;//%r15:%r14 :=      ((x >= 0.0) ? 0.5 : -0.5)
		xoor		%r15, %r15, 0x3FE00000		;//
		xcall.d		__adddf3			;//%r11:%r10 := x += ((x >= 0.0) ? 0.5 : -0.5)
		ld.w		%r14, 0				;//											*delay*
		ld.w		%r13, %r11			;//
		xjp.d		__fixdfsi			;//return (long)x
		ld.w		%r12, %r10			;//											*delay*
		;//---------------------------------------------;//
round:
nearbyint:
rint:
		call		lround				;//%r10 :=        lround(x)
		xjp.d		__floatsidf			;//return (double)lround(x)
		ld.w		%r12, %r10			;//											*delay*
		;//---------------------------------------------;//
trunc:
		xcall		__fixdfsi			;//%r10 :=        (int)x
		xjp.d		__floatsidf			;//return (double)(int)x
		ld.w		%r12, %r10			;//											*delay*
");
#endif//PIECE

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

// * Mon Mar 28 21:40:28 JST 2016 Naoyuki Sawa
// - fdim(),fdimf(),fmin(),fminf(),fmax(),fmaxf(),fma(),fmaf()を追加しました。
//   これらはC99で追加された関数です。
//   fdim()は、何に使うのか良く判らないのですが、覚えておくために実装しておく事にしました。
//   fmin(),fmax()は、min(),max()マクロよりも型を明示出来るし副作用が無いので、便利に使用出来ると思います。
//   fma()は、積和演算を短く・判り易く書けるので、(使う場面は少ないと思いますが)使い道は有ると思います。
// - 宣言は全て、include/math.hに有ります。
//   倍精度バージョンの関数、fdim(),fmin(),fmax(),fma()の実装は、cliplibc.cに有ります。
//   単精度バージョンの関数、fdimf(),fminf(),fmaxf(),fmaf()の実装は、framflt2.cに有ります。
//   実装を分けた理由は、単精度演算ライブラリが(アプリケーションが使用していないのに)無駄にリンクされて、サイズが増える事を防ぐためです。
// * Thu Sep 29 21:37:05 JST 2016 Naoyuki Sawa
// - VS2015では当モジュール内でfdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。
//   これまでVC++6.0では問題無かったのですが、本日、VS2015の64ビット環境でもcliplibc.cを含めるように変更した所、VS2015の64ビット環境では以下の警告が出たからです。
//   │warning C4273: 'fdim': dll リンクが一貫していません。
//   │c:\program files (x86)\windows kits\10\include\10.0.10240.0\ucrt\math.h(492): note: 'fdim' の以前の定義を確認してください
//   もしかするとVS2015や64ビット環境の問題ではなく、VC++6.0のプロジェクトは/MT,VS2015のプロジェクトは/MDで作成しているのが原因かも知れないのですが、とりあえずVS2015ではこれらの定義を行わない事にしました。
// * Sat Oct 01 21:11:57 JST 2016 Naoyuki Sawa
// - Hewでは当モジュール内でlog2,copysign,lround,lrint,round,nearbyint,rint,trunc,fdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。
//   Hewのランタイムライブラリには、C99で追加されたこれらの関数が、ビルトイン関数として含まれているからです。
#ifndef PIECE
#ifndef __RENESAS__	//{{2016/10/01追加:Hewでは当モジュール内でlog2,copysign,lround,lrint,round,nearbyint,rint,trunc,fdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。}}
#if     !(defined(_MSC_VER) && (_MSC_VER >= 1900))	//{{2016/09/29追加:VS2015では当モジュール内でfdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。}}
double fdim(double x, double y) {
	return fmax(x - y, 0.0);
}
double fmin(double x, double y) {
	return (x <= y) ? x : y;
}
double fmax(double x, double y) {
	return (x <= y) ? y : x;
}
double fma(double x, double y, double z) {
	return (x * y) + z;
}
#endif//!(defined(_MSC_VER) && (_MSC_VER >= 1900))	//{{2016/09/29追加:VS2015では当モジュール内でfdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。}}
#endif//__RENESAS__	//{{2016/10/01追加:Hewでは当モジュール内でlog2,copysign,lround,lrint,round,nearbyint,rint,trunc,fdim,fmin,fmax,fmaを実装せず、ランタイムライブラリ内の実装を使用するようにしました。}}
#else //PIECE
// * Mon Mar 28 22:52:58 JST 2016 Naoyuki Sawa
// - fdim(),fmin(),fmax(),fma()をアセンブラ化しました。
//   fdim(),fmin(),fmax()の共通処理を工夫して、上手くコードサイズを削減出来たと思います。
//   尚、__fcmpdが%r4～%r7を破壊しない事に依存しています。
//   もし今後、__fcmpdの実装を変更する場合は、注意して下さい。
double fdim(double x, double y);
double fmin(double x, double y);
double fmax(double x, double y);
double fma(double x, double y, double z);
asm("
		.code
		.align		1
		.global		fdim
		.global		fmin
		.global		fmax
		.global		fma
fdim:
		xcall		__subdf3		;//%r11:%r10 := x - y
		ld.w		%r12, %r10		;//%r13:%r12 := x - y
		ld.w		%r13, %r11		;//
		ld.w		%r14, 0			;//%r15:%r14 :=        0
		ld.w		%r15, 0
		;//* FALLTHRU *//			;//return  fmax(x - y, 0)
fmax:
		ld.w		%r4, %r14							;//%r5:%r4  :=       y
		ld.w		%r5, %r15							;//
		ld.w		%r6, %r12							;//%r7:%r6  := x
		jp.d		6								;//				─┐
		 ld.w		%r7, %r13							;//				　│	*delay*
		;//* FALLTHRU *//								;//				　│
fmin:												;//				　│
		 ld.w		%r4, %r12		;//%r5:%r4  :=  x			;//				　│			;//┐
		 ld.w		%r5, %r13		;//					;//				　│			;//│
		 ld.w		%r6, %r14		;//%r7:%r6  :=        y			;//				　│			;//│
		 ld.w		%r7, %r15		;//					;//				　│			;//│
		xcall		__fcmpd			;//%psr     := (x <=> y)		;//%psr     := (x <=> y)	←┘			;//│
		ld.w		%r10, %r4		;//					;//							;//├__fcmpdが%r4～%r7を破壊しない事に依存しています。
		jrle.d		4			;//          if(x <=  y) {		;//          if(x <=  y) {				;//│
		 ld.w		%r11, %r5		;//%r11:%r10 := x        } else {	;//%r11:%r10 :=       y  } else {	*delay*		;//│
		 ld.w		%r10, %r6		;//%r11:%r10 :=       y  }		;//%r11:%r10 := x        }				;//│
		 ld.w		%r11, %r7		;//					;//							;//│
		ret					;//					;//							;//┘
fma:
		;//%r13:%r12       := x
		;//%r15:%r14       := y
		;//[%sp+0]         := retp
		;//[%sp+8]:[%sp+4] := z
		xcall		__muldf3		;//%r11:%r10 := (x * y)
		xld.w		%r14, [%sp+4]		;//%r15:%r14 :=           z
		xld.w		%r15, [%sp+8]		;//
		ld.w		%r12, %r10		;//%r13:%r12 := (x * y)
		xjp.d		__adddf3		;//return       (x * y) + z
		ld.w		%r13, %r11		;//										*delay*
");
#endif//PIECE

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

// * Fri Apr 29 21:34:57 JST 2016 Naoyuki Sawa
// - strdupa(),strndupa()用のヘルパ関数__strndupa_helper()を追加しました。
//   strdupa(),strndupa()のマクロ定義は、include/string.hに有ります。
//   詳細は、include/string.h の同日のコメントを参照して下さい。
#ifndef PIECE
char* __strndupa_helper(const char* s, size_t n, void* p) {	//ヘルパ関数
	size_t len = strnlen(s, n);
	((char*)p)[len] = '\0';
	return memcpy(p, s, len);
}
#else //PIECE
char* __strndupa_helper(const char* s, size_t n, void* p);	//ヘルパ関数
asm("
		.code
		.align		1
		.global		__strndupa_helper
__strndupa_helper:
		;//%r12 := s
		;//%r13 := n
		;//%r14 := p
		pushn		%r1
		ld.w		%r0, %r12		;//%r0  :=          s
		xcall.d		strnlen			;//%r10 := len = strnlen(s, n)
		ld.w		%r1, %r14		;//%r1  :=       p			*delay*
		ld.w		%r12, %r1		;//%r12 :=       p
		ld.w		%r13, %r0		;//%r13 :=          s
		add		%r1, %r10		;//%r1  := &p[len]
		ld.b		[%r1], %r8		;//         p[len] = '￥０'
		popn		%r1
		xjp.d		memcpy			;//return memcpy(p, s, len)
		ld.w		%r14, %r10		;//%r14 :=             len		*delay*
");
#endif//PIECE

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

// * Thu Sep 29 21:37:05 JST 2016 Naoyuki Sawa
// - Win32用のtruncate()関数,及び,ftruncate()関数を追加しました。
//   これまではWin32版は仮にclipstdw.h内でマクロ定義していましたが、今後はWin32版もcliplibc.c内で関数として実装する事にしました。
//   尚、P/ECE用のtruncate()関数,及び,ftruncate()関数の実装は、clipio.cに有ります。
//   clipio.cはP/ECE専用のモジュールであり、Win32プロジェクトには含めないので、Win32版のtruncate()とftruncate()は、cliplibc.c内で実装する事にしました。
#ifdef  WIN32
int truncate(const char* path, /*off_t*/long length) {
	int result = -1;
	int fd = _open(path, _O_BINARY|_O_RDWR);
	if(fd != -1) {
		result  = ftruncate(fd, length);	//ftruncate()が成功ならばresult=0になる。ftruncate()が失敗ならばresult=-1になる。
		result |= _close(fd);			//ftruncate()の結果に関わらず_close()を行う。ftruncate()も_close()も成功した場合のみresult=0になる。どちらかが失敗したらresult=-1になる。
	}
	return result;
}
int ftruncate(int fd, /*off_t*/long length) {
	return _chsize(fd, length);
}
#endif//WIN32

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

// * Sat Nov 26 23:52:51 JST 2016 Naoyuki Sawa
// - htonl(),htons(),ntohl(),ntohs()を追加しました。(P/ECE専用です)
//   宣言はinclude/arpa/inet.h,及び,netinet/in.hに有ります。
// - Windowsの場合は、winsock.h,又は,winsock2.hで宣言され、wsock32.lib,又は,ws2_32.libで実装されています。
//   従って、Windowsの場合は、当ライブラリ内でhtonl(),htons(),ntohl(),ntohs()を実装する必要は有りません。
#ifdef  PIECE
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
asm("
		.code
		.align		1
		.global		htons
		.global		ntohs
		.global		htonl
		.global		ntohl
htons:																;//┐
ntohs:																;//│
		xsll		%r12, 16		;//%r12 := hhhhhhhh llllllll 00000000 00000000				;//│
htonl:																;//├htonl,ntohlは途中から実行する工夫で、全部まとめて8バイトで実装出来ました。
ntohl:																;//│
		ret.d					;//									;//│
		swap		%r10, %r12		;//%r10 := 00000000 00000000 llllllll hhhhhhhh		*delay*		;//┘
");
#endif//PIECE

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

// * Mon Aug 21 21:39:56 JST 2017 Naoyuki Sawa
// - cacheflush()を追加しました。
//   現在の所、どのヘッダファイルでも、宣言していません。
//   当関数を追加した目的は、トランポリンコードを生成した時のリンクエラーを回避するためだけであり、アプリケーションが明示的に呼び出す事は想定していないからです。
//   トランポリンコードの詳細については、/clip/tool/dpcc33/app.c の同日のコメントを参照して下さい。

//□「Linux Programmer's Manual - CACHEFLUSH」(https://linuxjm.osdn.jp/html/LDP_man-pages/man2/cacheflush.2.html)
//│cacheflush - 命令キャッシュやデータキャッシュの内容をフラッシュする
//│説明
//│  cacheflush()はaddrから(addr+nbytes-1)の範囲のユーザーアドレスに対応する指定されたキャッシュをフラッシュする。
//│  cacheには以下のいずれかを指定する:
//│  ICACHE  命令(instruction)キャッシュをフラッシュする。
//│  DCACHE  変更があったキャッシュラインをメモリーに書き戻し、無効にする。
//│  BCACHE  (ICACHE|DCACHE)と同じ。
//│返り値
//│  cacheflush()は成功した場合は0を、失敗した場合は-1を返す。
//│  エラーが検出された場合はerrnoにエラーが指示される。
//│エラー
//│  EFAULT  addrから(addr+nbytes-1)の範囲のアドレスの全てまたは一部がアクセス可能ではない。
//│  EINVAL  cacheパラメーターがICACHE,DCACHE,BCACHEのどれでもない。
//│準拠
//│  このLinux特有のシステムコールはMIPSベースのシステムでのみ有効である。	←←←←←←←←←←←←←←←←←←←←←←←←【注目】
//│  移植を意図したプログラムで使用すべきではない。
//│バグ
//│  現在の実装では、引き数addrとnbytesは無視される。
//│  そのため、常に全てのキャッシュがフラッシュされる。
//{{linux/include/asm-mips/cachectl.hより
#define ICACHE (1<<0)		//flush instruction cache		┐
#define DCACHE (1<<1)		//writeback and flush data cache	├ここで定義しても無意味だが、備忘録として定義しておく事にした。
#define BCACHE (ICACHE|DCACHE)	//flush both caches			┘
//}}linux/include/asm-mips/cachectl.hより
int cacheflush(char* addr, int nbytes, int cache) {
	/** no job **/
	return 0;
}

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

// * Sun Aug 27 21:28:26 JST 2017 Naoyuki Sawa
// - strerror()を追加しました。
//   P/ECE環境でのみ使用し、EPSONライブラリ版のstrerror()を置き換えます。
//   EPSONライブラリ版のstrerror()を置き換える事にした理由は、以下の通りです。
// - EPSONライブラリ版のstrerror()は、動作は問題無いのですが、コードサイズが大き過ぎる(400バイトも占めている)という問題が有ります。
//   「C:\usr\PIECE\docs\datasheet\EPSON\cc33\UTILITY\lib_src\ansilib33\string\src\strerror.c」を見ると、一見小さな関数なのですが、
//   微妙に飛び飛びのエラー番号に対してswitchで処理分けしており、密度が低く無駄の多いジャンプテーブルが生成されてしまっている事が、コードサイズが大きい理由です。
//   strerror()は、clipio.c内のperror()から呼び出されており、clipio.cは常にリンクされるので、strerror()も常にリンクされるので、コードサイズが大きい事は問題です。
//   そこで、動作は同じままで、コードサイズを節約した自前のstrerror()を実装して、EPSONライブラリ版のstrerror()を置き換える事にしました。
//   コードサイズを節約した方法については、下記の実装を参照して下さい。(データ構造を工夫しただけで、さほど複雑な事はしていません。)
//   コードサイズ節約の効果は、400バイト→188バイトになり、212バイトも節約出来ました。
// - 今回の対応は、P/ECE環境のみであり、P/ECE以外の環境(Windowsやその他一般)には適用しないように注意して下さい。	←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←【重要】
//   P/ECE環境のエラー番号は「(前述の)\strerror.c」及び「C:\usr\PIECE\include\errno.h」定義されている7種類だけですが、その他の環境ではもっと多いかも知れないからです。
//   万一、P/ECE以外の環境にも適用してしまうと、それらの環境で定義されているエラー番号に対するエラーメッセージが"unknown error"と表示されてしまう問題が生じます。
#ifdef  PIECE	//P/ECE環境でのみ使用し、EPSONライブラリ版のstrerror()を置き換えます。
static_assert( EPERM == 0001,  "EPERMの値が想定と異なります。");
static_assert(ENOENT == 0002, "ENOENTの値が想定と異なります。");
static_assert(ENOMEM == 0014, "ENOMEMの値が想定と異なります。");
static_assert(EINVAL == 0026, "EINVALの値が想定と異なります。");
static_assert(ESPIPE == 0035, "ESPIPEの値が想定と異なります。");
static_assert(  EDOM == 0041,   "EDOMの値が想定と異なります。");
static_assert(ERANGE == 0042, "ERANGEの値が想定と異なります。");
#ifndef PIECE
char* strerror(int errnum) {
	const char* ptr =
	"\0\000no error"
	"\0\001operation not permitted"		// 1 EPERM
	"\0\002no such file or directory"	// 2 ENOENT
	"\0\014cannot allocate memory"		//12 ENOMEM
	"\0\026invalid argument"		//22 EINVAL
	"\0\035illegal seek"			//29 ESPIPE
	"\0\041domain error"			//33 EDOM
	"\0\042range error"			//34 ERANGE
	"\0\377unknown error";
	int i;
	do {
	    //{{最適化。strchr()を呼び出さない方がレジスタ退避が不要でコードサイズを節約出来る。
	    //	ptr = strchr(ptr, '\0') + 1;
	    //↓最適化。strchr()を呼び出さない方がレジスタ退避が不要でコードサイズを節約出来る。
		while(*ptr++) { /** no job **/ }
	    //}}最適化。strchr()を呼び出さない方がレジスタ退避が不要でコードサイズを節約出来る。
		i = *ptr++;
	} while((i != errnum) && (i != -1));
	return (char*)ptr;
}
#else //PIECE
asm("
		.code
		.align		1
		.global		strerror
strerror:
		xld.w		%r10, strerror_DATA	;//%r10 := ptr
strerror_LOOP:						;//do {
		ld.b		%r11, [%r10]+		;//  while(*ptr++) {
		 cmp		%r11, 0			;//    /** no job **/
		jrne		-2			;//  }
		ld.b		%r11, [%r10]+		;//  %r11 := i = *ptr++
		cmp		%r11, %r12		;//} while(
		jreq		3			;//        (i != errnum)
		 cmp		%r11, -1		;//        &&
		 jrne		strerror_LOOP		;//        (i != -1))
		ret					;//return ptr
strerror_DATA:
		.ascii		\"\\0\\000no error\"
		.ascii		\"\\0\\001operation not permitted\"	;// 1 EPERM
		.ascii		\"\\0\\002no such file or directory\"	;// 2 ENOENT
		.ascii		\"\\0\\014cannot allocate memory\"	;//12 ENOMEM
		.ascii		\"\\0\\026invalid argument\"		;//22 EINVAL
		.ascii		\"\\0\\035illegal seek\"		;//29 ESPIPE
		.ascii		\"\\0\\041domain error\"		;//33 EDOM
		.ascii		\"\\0\\042range error\"			;//34 ERANGE
		.ascii		\"\\0\\377unknown error\"
		.ascii		\"\\0\"
");
#endif//PIECE
#if 0
//□テストプログラム
int app_main(int argc, char* argv[]) {
  for(;;) {
    schedule();
    render_string(&render, 0, 6*0, strerror(0     ), 2, 3); //no error
    render_string(&render, 0, 6*1, strerror(EPERM ), 2, 3); //operation not permitted
    render_string(&render, 0, 6*2, strerror(ENOENT), 2, 3); //no such file or directory
    render_string(&render, 0, 6*3, strerror(ENOMEM), 2, 3); //cannot allocate memory
    render_string(&render, 0, 6*4, strerror(EINVAL), 2, 3); //invalid argument
    render_string(&render, 0, 6*5, strerror(ESPIPE), 2, 3); //illegal seek
    render_string(&render, 0, 6*6, strerror(EDOM  ), 2, 3); //domain error
    render_string(&render, 0, 6*7, strerror(ERANGE), 2, 3); //range error
    render_string(&render, 0, 6*8, strerror(12345 ), 2, 3); //unknown error
  }
  return 0;
}
#endif
#endif//PIECE	//P/ECE環境でのみ使用し、EPSONライブラリ版のstrerror()を置き換えます。
