OSを自作してみる6 ~割り込み経由でのキー入力~



この回では割り込み経由でキー入力できるように自作OSを改良する。

※過去回の記事とソースコードは下記から入手できる
https://github.com/Shadow5523/osdev/blob/master/README.md
また、ここで使用するコードはGitHubからダウンロードできる。
https://github.com/Shadow5523/osdev/releases/tag/version0.5.0

前回の記事では、IDTとPICの初期化までは完了した。次に行うのは実際にIDTへキーボードの割り込み信号を受け取ったときの処理を対応付け、今あるキー入力処理を少し変更を加えていく。

大まかな動きを下のような絵にしてみた。(;´・ω・)

まずはidt.cでIDTへキーボードの割り込み信号を受け取ったときの処理をあらかじめ対応付けさせる。
実際にOSを起動させてキー入力をするとキーを押した瞬間にPICへと信号が行き、今実行している処理より優先度が高いとPICはCPUにINT命令 + 割り込み信号を送る。するとCPUは一旦処理を中断させて、受け取った割り込み信号の値をもとにIDTに対応付けしてある処理を呼び出す。

呼び出された処理で現在のレジスタの状態をスタックへ退避させたあとにキー入力処理(バッファに画面に出力するデータを保存させる)を行う。その後画面に文字を出力させる処理は割り込み完了後に行う。(そうしなければ画面出力までキーボードより低い優先度の割り込みを受け取れなくなる)

以上のような流れでキーボード入力+画面出力を行うよう改造していく。

その前に、ヘッダーファイルが増えてきたので、includeフォルダを作ってその中にヘッダーファイルを移動させることにした。そのため、ヘッダーファイルをインクルードしているソースファイルは適宜変更を加えること。

1.interrupt.hではinterrupt.cで使用する関数のプロトタイプ宣言と、IRQ番号を表す定数マクロを宣言する。キーボード割り込みのIRQは0x61。また、interrupt.cではPICに対してinb/outb関数でコマンドを送ったりするのでpic.hをインクルードする。
// include/interrupt.h

 

2.ここでは、キーボード割り込みがあったらkeyboard_input_int()を呼び出しキー入力処理を行う。また割り込み処理が完了したらPICのマスター/スレーブそれぞれに対してEOIコマンド(0x20)を書き込む。
こうしなければキーボードより優先度の低い割り込み処理を受け取れなくなってしまう。
// interrupt.c

 

3.callに書かれている関数を呼び出す。実際にここの「as_keyboard_interrupt」ラベルをidtへ対応付けする。call命令の前の処理で割り込み直前のレジスタの状態をいったんスタックへ退避させ、call完了後再度それらをもとに戻しiretl命令でもとの処理へと戻る。
// interrupt.s

 

4.interrupt.cの関数を呼び出すためにinterrupt.hをインクルードする。
またアセンブラで作成したinterrupt.sの「as_keyboard_interrupt」ラベルはアセンブラで宣言されているため、externを使用し明示的に外部で宣言されている事を宣言する。
// include/idt.h

 

5.アセンブラで作成したinterrupt.sの「as_keyboard_interrupt」ラベルを前回の記事で作成したIDTに登録を行う。
キーボードの割り込み信号を検知するとこのラベルが実行される。
※set_gate_desc()の引数やIRQベクタ番号についてはこちらを参照。
// idt.c

 

6.keyboard.hを以下のように変更を加える。key_buf構造体で画面へ出力する文字データを保持する。取り出し方はFIFO型式を利用している。
// include/keyboard.h

 

7.実際に出力データをバッファに格納する部分。キー配列はUS配列。また、inb/outb関数のポート番号とコマンドを定数マクロに置き換える。
//keyboard.c (変更を加えた関数/変数のみを表示)

 

8.kernel.hでは出力文字データを受け取る配列を宣言。
// include/kernel.h

9.文字データを取り出し、画面へ出力させる。まずはcli命令で一時的に割り込み命令を受け取らないようにし、バッファーの長さが0以上ならデータを取り出す。その後sti命令で割り込み命令を受け取れるようにした後で画面出力を行う。
//kernel.c

 

10.Makefileもそろそろ行が多くごちゃごちゃして見づらくなったので編集し整理する。
因みに、今更ではあるがMakefileの構文はいかのとおり。コマンド行の先頭の空白は必ずTabでなければならない。 

ターゲット名 : ファイル名1 ファイル名2
    コマンド

また今から改造するMakefileには以下のような内部マクロと呼ばれるものを使用する。

$(マクロ名) 上で宣言したマクロの中身(文字列)に置き換わる
$< ターゲット名の横で宣言した最初のファイル名に置き換わる
$^ ターゲット名の横で宣言したすべてのファイル名に置き換わる

改造したMakefileを以下のように作り直す。
// Makefile

11.あとはmakeコマンドでコンパイルして、エラー等が無ければOK。
次回は英大文字入力等を行えるように改良していく(まだキーボードから抜けられない…)

Leave a Reply

Your email address will not be published. Required fields are marked *