OS自作入門 -Advent23-

NO IMAGE

第二部開始、Step7 割り込み処理を実装する

さて、これまでのstepを通してブートローダが完成したので、プログラムをシリアル経由でロードし実行できるようになった。ようやく折り返しである。全然アドベントカレンダー内に終わらなかった。。。あと一ヶ月くらいかかるということか。大変だ。とはいえめげずにやっていこう。いよいよOS部分の作成である。

まずは割込み処理の実装から開始する。

割込み処理

実行中のプログラムの実行を一時停止して、特定の処理を行う

という風な説明がなされることが多い。CPUが割込みを受け付けると現在のプログラムの実行を中断して、特定の処理に実行を移すこと。と言うこともできる。割込み発生時に実行される処理のことを 割込みハンドラ と呼ぶ。この割込みハンドラは前もって登録しておく必要がある。

ポーリング という、一定周期で状態をチェックして必要なら処理を実行する。というやり方もあるが定期的な確認であるため即時反応ではないし、あまり効率的ではない。そのため例えばシリアルからの文字受信時の処理を割込みハンドラとして実装しておき必要に応じて実行する。というような形をとる。これを シリアル受信割込み と呼ぶ。

またCPUリソースの話も出てきている。ある一定の時間内にCPUが実行できる命令数は決まっているのでCPUは有限のリソースである。シリアル送信処理を例にとると、SSRレジスタのTDREビットを見て送信可能かどうかを判断しているが、ここでのコントローラのビットの切り替えを待っている間はビジーループであるため「待ち」となりCPUのリソースが無駄となってしまう。なので割込みを用いて送信完了時に割込みによって通知してもらい、割込みハンドラの中で次に送信すべき文字の送信処理を行う。ということをすれば良い。

通常のCPUでは複数の割込み要因を受け付けることができ、割込みの受付は割込み要因ごとにON/OFFができ、割込みハンドラも複数用意できるようになっている。

タイマ割込みやシリアル受信割込みは、CPUに外付けされれる周辺I/Oであるタイマコントローラやシリアルコントローラが割込みを発生させている。コントローラーは出力線、CPUは入力ピンを持っており、ここに電圧がかかることによってCPU側に割込みが通知されることになる。この割込み入力ピンには IRQ という名称が付いている。

割込みベクタと割込みハンドラ

割込み発生時のCPUの動作は

  1. 特定のアドレスから実行する
  2. どのアドレスから実行するのか特定のアドレスに設定しておく

これは

  1. 特定のアドレスに強制ジャンプする
  2. 特定のアドレスからジャンプ先アドレスを読み込んで、そこにジャンプする

ということになる。この処理プログラムを割込みハンドラと呼ぶ。2.の方法は、割込みハンドラを適当な位置に配置し、その配置されたアドレスをメモリ上の特定のアドレスに書いておく。というものであり、このアドレスを書いた配列を割込みベクタと呼んでいる。なので準備としては以下のようなものが必要となる

  • 組込みハンドラを用意する(処理の実体)
  • 割込みハンドラの先頭アドレスを割込みベクタに書き込む

割込み処理で行われること

組込みハンドラの最後に割込み復帰命令が必要である。これは割込み発生時に退避しておいた情報を復帰することであり、具体的には「プログラムカウンタ」と「モードレジスタ」の二つの値を退避しておけば良い。割込みが発生した時点でプログラムカウンタの値は書き換わってしまうため、CPUの仕組みとして割込み発生時にそれまで実行していたプログラムカウンタの値をどこかに保持するようになっている。また、モードレジスタというのは「割込み禁止」などを管理しているレジスタであり、多重割込み(割込み中の割込み)を防いでくれる仕組みなのだが、これも退避しておかないとプログラムカウンタの復帰が正常にできなくなる。という問題を抱えてしまうことになるため退避しておく必要がある。

割込みのクリア

シリアル受信を例にとると、シリアルコントローラがシリアルケーブル経由で文字を受信した時に、割込みハンドラでは

  • シリアルコントローラのレジスタを参照し、文字を受け取る
  • シリアルコントローラのレジスタを操作し、割込みフラグを落とす

ということを行う必要がある。コントローラは割込み発生時に割込み出力をアサートし続ける必要があり、この値を読み込むことにより割込みの発生状態を確認することができるようになっている。CPUは割込みを検知したらモードレジスタを書き換えて割込み禁止にし、割込みハンドラを実行する。その後割込み復帰命令が実行されるとモードレジスタが元の値に戻り割込みが有効化される。割込みハンドラでは各コントローラのレジスタを確認し発生元となるコントローラを特定し、そのコントローラのレジスタを操作して「割込みが発生したことを示すためのフラグ」を落とす必要がある。

これをやらないと、コントローラから割込み出力のアサートが消えずに割込み復帰命令を呼び出した直後に、再度同じ割込みがかかってしまうことになる。いわゆる無限ループになる。


さて今日は割込みに関する一般的な話が出てきたところだ。レジスタとかコントローラとかI/O周りの話が結構出てくる。また最初の方から読んだり知識を再度補完しながら読み進めないと大変だ。

次回はH8における割込み処理とその実装について。になるだろう。