最近はまたARM Cortex-Mシリーズのマイコンを使って仕事をしていて、
CPU例外発生後にその情報を引数で渡してmain関数に戻ってくるコードを書いたら、どうもうまく動かない。
ARM Cortex-Mシリーズでは割り込み発生時にハードウェア的にレジスタの退避が行われる。
なので割り込みハンドラでレジスタ退避のことを考えなくてよい。
それは知っていたのだが退避させたレジスタは復帰させなければならない。
この復帰処理に入るための仮想的なアドレス(EXC_RETURN)がリンクレジスタに入る。
リンクレジスタというのは関数の呼び出し元を表すレジスタで、
関数からreturnするというのはここにジャンプすることを指す。
で、例外ハンドラから出るときには何らかのEXC_RETURNにジャンプするべきようですね。
とはいえ例外ハンドラを出るときに例外発生場所に戻っては仕方ないわけで、
そこを変えたければスタックに積まれている例外発生元アドレスを変える必要がある。
sp+0x18が例外発生元アドレス、すなわち復帰先アドレスになっている。
退避されているレジスタの値を変えたければここを変えなきゃいけないんですね。
EXC_RETURNのアドレスもまた例外発生時の状態が保存されている。
復帰先のモードやレジスタ復帰方法がここで決まるんですね。
このあたりの値を変える処理はアセンブラで書かないと無理なので、
例外ハンドラから出るアセンブラのコードを書いて、例外ハンドラの最後から呼び出すようにした。
ただ、これはこれで問題があって例外ハンドラを出ると再度例外が発生するという。
例外ハンドラの中で例外フラグを消しておく必要があったようで、
CFSRレジスタをリードして同じ値をライトして、消去するとうまくいった。
このことからわかるが、例外ハンドラを実行中は例外が起きないんですね。
例外ハンドラからEXC_RETURNにジャンプすると再度例外が起こるようになると。
さらに言えば例外ハンドラの優先度は他の割り込みより高いので、例外ハンドラ実行中は他の割り込みが起きない。
ただ、他にも問題はあったんですよね。
それは浮動小数点レジスタの退避ですね。
普通はあまり意識しなくても効率よくやってくれるのだが、けっこう複雑な仕組みで……
このあたりもまた改めて紹介しようかなと。
これもEXC_RETURNが関係していると言うね。