昨日、マイコンのプログラムを3つのプロジェクトに分けてビルドするようにしたという話を書いた。
詳しい事情は書かなかったのだが、背景の1つには割り込みハンドラがある。
割り込みハンドラではレジスタの退避を適切に行う必要があり、
最初はこの退避処理をアセンブラで記述していた。
全ての割り込み要因に対して共通の割り込みハンドラを用意して、
アセンブラで退避処理をした後に、要因ごとの処理に分岐して、
処理が終わったらアセンブラに戻ってきて復帰処理をするという流れである。
で、実はこれを作っているときに __interrupt とか付けて関数を記述すると
コンパイラがレジスタの退避処理などを含めたコードを生成するのでは?
という教えてもらって、コンパイラのマニュアルを見たら、確かにあった。
それで生成された機械語を確認してみたのだが、どうも思ったのと違って、
要因ごとの分岐処理も必要だし、従来通りアセンブラで退避処理を書く方法で行くかと一旦は考えた。
その後、割り込みハンドラについて、いろいろ見直しが入って、
ベクタテーブルを使って割り込みハンドラを振り分ける方がよさそうだとなった。
従来は1つの割り込みハンドラにアセンブラで退避処理を書けばよかったが、
要因ごとに割り込みハンドラを作ると、それぞれに退避処理が必要になる。
アセンブラのマクロで同じ処理を何回も書かせてもよいのだが、
そういえば関数に __interrupt を付けたらコンパイラが生成してくれるんだよなと思い出して、
適用してみたら、振り分け処理がない前提ならそう悪くない気がしたのだが、
浮動小数点レジスタの退避が必ず入る点がうーん……となった。
特に浮動小数点演算とは関係ない処理なので浮動小数点レジスタの退避はいらないのだが、
コンパイラとしては割り込み元で浮動小数点演算をしている中で、
浮動小数点演算を行う割り込みハンドラが入る可能性があると考えてしまうようだ。
浮動小数点レジスタの退避・復帰というのは比較的大きな処理なので、
退避・復帰を省略したり後回しにするという手段がしばしば使われる。
ただ、OSレスだとこういう効率的な手法はやりにくいのかもしれない。
浮動小数点レジスタの退避をなくすだけならば、FPU無効の構成でビルドすればよいのだが、
そうすると別の問題が発生してしまうので、これもNGだった。
その問題を回避する手段として、プロジェクトを分割して、
FPU有効のプロジェクトと、FPU無効のプロジェクトに分けることにしたと。
だいたいそんな感じですね。実際はもっと複雑な事情があるのだけど。
結果として、アセンブラでのレジスタ退避を手で書くところはなくなった。
ただ、全ての割り込みハンドラを __interrupt を付けた関数にしたわけでなく、
特殊な事情があり割り込みハンドラの入口をアセンブラで書いてあるものがある。
ただ、その割り込みハンドラは元に戻れなくてよいというものなので、レジスタ退避そのものが不要である。
最初は元に戻れなくてよい割り込みもレジスタ退避を行っていたのだが、
よく考えたらいらないねというところから見直しが進んでいった事情がある。
いろいろ普通ではない事情が重なって現在に至るという感じはある。