スタックをさかのぼる
例外やハードウェアエラー、スプリアス割り込みが発生した場合、プログラムはそこで止めるしかありません。止めた場合、当然問題発生時のレジスタの状態および問題発生アドレスを知りたいという要求が出てきます。問題発生アドレスはretxあるいはretiレジスタを読めば分かります。問題は発生時のレジスタの内容です。
問題発生時のレジスタの内容は、当然スタックに保存してあります。その保存位置はスタック・フレームをさかのぼっていけば分かります。ここで疑問がわきます。どうやってフレームをさかのぼればいいのでしょうか。
スタック・フレームをさかのぼるには、unlink命令と同じ操作を行います。unlink命令は
- SPにFPをコピーする
- 古いフレームアドレスをFPにポップする
- 戻り番地をRETSにポップする
という動作を行います。これを繰り返していけば、ネストしている呼び出しの底から呼び出し元まで次々にさかのぼっていけます。
ではスタックフレームをどれだけさかのぼればよいのでしょうか?残念ながら、すべての例外、割り込みに対して適切なさかのぼりを実現するのは、現在のTOPPERS/JSP for blackfinでは不可能です。なぜならば、呼び出したアドレスから呼び出した関数をきちんと特定することができないためです。
割り込みの場合、interrupt_dispatcherの入り口で割り込まれたコンテキストを保存し、device_dispatcher()を呼び出します。同じように例外ではexpEntryの中でexecVector[0]に登録されている関数を呼び出します。したがって、あるフレームを解析したときに、戻り番地からinterrupt_dispatcher、あるいはexpEntryから呼ばれたことを特定できれば、ひとつ上のフレーム*1がまさに求めているフレームであるとわかります。この特定を、常に成功させる方法が現在の実装には存在しないのです。
対策は実は簡単です。interrupt_dispatcherの場合、以下のようにラベルを打ってそれをglobalにしておけば十分です。
LOADLABEL( p0, _device_dispatcher) sp += -12; // ダミー引数確保 (A) call (p0); // C言語で書かれたデバイスディスパッチャを呼ぶ interrupt_dispatcher_return_point: sp += 12; // ダミー引数開放 (A) astat = r7; // ccを復帰
こうすると、retsレジスタにはinterrupt_dispatcher_return_pointが格納されてcallが発生します。このレジスタは呼ばれた側のlink命令でスタックに積まれます。ですので、スタックから取り出した戻り番地が上のアドレスであるかを調べればよいということになります。
そのうち実験しようと思っています。
*1:割り込み、或いは例外の処理のときには、最初にlink命令を実行している