まずはmain()関数から見てみましょう。
main()関数は、psp/np2.cにあります。
エミュレータソフトウェアはほとんど以下のような構成になっています。
1. 初期化(ハードウェア、ソフトウェア初期化、設定ファイルの読み込み等)
2. メインループ開始
3. キー入力
4. 処理をちょっとだけ実行(CPU命令実行、サウンド再生等)
5. メインループ終了、2.に戻る
6. 終了処理(ハードウェア、ソフトウェア後始末、設定ファイル保存等)
NP2 for PSPも基本は変わりありません。
main()の最初に〜init()とか〜initaialize()とう関数を多数呼び出して初期化を行っています。
以下がメインループですね。
while(taskmng_isavail()) {
(中略)
}
以下の関数でキー入力などを行っています。
taskmng_rol()
以下の関数がエミュレータコアの実行部です。
pccore_exec()
そして、メインループを抜けると、終了処理が走ります。
次にPSP固有の処理について見てみます。
まず特徴的なのは以下の関数です。
int SetupCallbacks(void)
{
int thid;
thid = sceKernelCreateThread("update_thread", CallbackThread,
0x11, 0xFA0, 0, 0);
if (thid >= 0) {
sceKernelStartThread(thid, 0, 0);
}
return thid;
}
スレッドを作成して、実行しています。
スレッドとは、main()から始まる処理から分岐し、平行に動作する、main()の流れとは別の流れのことを言います。スレッドにより処理が平行に動作するように見えますが、実際に処理を行うCPUは一つですので、処理を細かく分けて、main()の流れをちょっと実行しては、別のスレッドの流れをちょっと実行して、ということを繰り返し行っています。
main()の流れ ----+--------------------->
|
別のスレッド +--------------------->
SetupCallbacks()でスレッドを作成し、成功すればそのスレッドを実行します。
実際に起動されるスレッドは、sceKernelCreateThread()の第2引数で指定される関数、つまり以下のCallbackThread()です。
int CallbackThread(void *arg)
{
int cbid;
cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
sceKernelRegisterExitCallback(cbid); //SetExitCallback(cbid);
cbid = sceKernelCreateCallback("Power Callback", power_callback, NULL);
scePowerRegisterCallback(0, cbid);
sceKernelSleepThreadCB(); //KernelPollCallbacks();
return 0;
}
上記CallbackThread()で、二つのcallback関数を登録しています。callback関数登録後、sceKernelSleepThreadCB()を呼び出してこのスレッドはイベント待ちのスリープに入ります。callback関数とは、あらかじめ登録しておき、何かイベントが発生したら、呼び出してもらう関数のことを言います。
CallbackThread()において、sceKernelRegisterExitCallback()でexit_callback()を登録し、scePowerRegisterCallback()でpower_callback()を登録しています。
それぞれ、PSPのホームボタンが押され、終了を選択した場合、電源オフを実行した場合に呼び出されます。
exit_callback()の先では、単に終了処理を実行しますが、パワーオフの場合は、単に終了処理をする場合と、スリープ対応の処理があります。スリープの場合は、一度電源オフのcallbackが実行されてしまったため、再度callback関数を登録して終了し。そのままスリープから再度電源オンを実行されるのを待つようです。
もう一つ、sceKernelWaitSema(), sceKernelSignalSema()を見ておきましょう。
使われているのは、
メインループの直前にsceKernelWaitSema()
メインループの直後にsceKernelSignalSema()
exit_proc()関数の最初、taskmng_exit()の直後にsceKernelWaitSema()
です。exit_proc()の最初に実行されるtaskmng_exit()でtask_avail = FALSE;を実行しているが、実はこれがメインループを抜ける条件となります。
つまりホームボタン等でゲームを終了する場合に、メインループを完全に抜けてから終了処理を走らせたいため、メインループが抜けてsceKernelSignalSema()が実行されるまでは、exit_proc()は、sceKernelWaitSema()で待たされます。
【PSPの最新記事】