テキストモードのコンソールへキーボード入力(スキャンコード)を表示するようになった。– 自作OS

ブートモニター側で、ブートセクターで設定したテキストモードの画面をそのまま引き継いで、文字列表示や入力コンソール位置を操作するモジュールを作成した。そのためにブートセクターから設定値を伝えてもらうBOOT_INFO領域を確保し、VRAMアドレスや行数などのハード的な情報を得られるようにした。その時にドツボに嵌ったのが、文字のアトリビュートの設定で、文字色は4bit、背景色は3bit、文字の点滅(ブリンク)は1bitで指定するのだが、文字色と背景色でビット数が違うのは変に思っていたのだが、カーソル表示で文字を反転表示で点滅させようとして、文字色は黒、背景色は明るいグレー、ブリンク=ONで設定していたが、点滅をしないどころか、反転表示にもならず、四角い白になってしまった。いろいろプログラムの仕方や条件を変えて試したところ、ブリンクと背景色の設定は両立しないらしいという結論になった。ネット上のどこにもそれを明確に書いた情報はなかったが、条件を変えて試した限りでは、そうなっている。私の誤解なのかもしれないが、現時点ではブリンクを諦め、白黒反転させるようにした。これの試行錯誤だけで四日も掛かってしまった。

次はキーボード入力をFIFOへデータ格納していって、Enterキーでコンソール実行ルーチンを呼び出す、という感じに作成してみることにした。これが良いとか効率的とかいうわけではなく、ただ、こんな感じ? みたいな思い付きのままに作成している。学びながら遊ぶ感じだ。

キーボード入力を可能にするには、プロテクトモードのためBIOSが使えず、割り込み処理ハンドラーとそれに対応するIDTを作成する必要がある。また、外部割込みのため、PICの設定をするのだが、書籍やネットでもPS/2キーボード入力となっていて、今時、PS/2キーボードとかPS/2マウスとか誰も使ってないと思うが、USBドライバを実装しないといけないのだろうか? と心配になったが、互換性のためにマザーボード内で、エミュレーションしてくれる、と書籍の余り目立たない所に、書いてあった。

PICにPS/2キーボードの割り込み設定をして、試してみたが、IDTのキーボード割り込みハンドラーのディスクリプタの設定ができず、またドツボへ嵌ってしまった。
割り込みハンドラーのアドレスは、上位2バイトと下位2バイトを分離して、別々のフィールドの情報としてディスクリプタへ設定しないといけない。ほとんど全てのサンプルはC言語で記述しているが、私は軽い気持ちでGNUアセンブラで記述したのだが、「invalid operand: (中略)”>>”」と同じく「invalid operand: (中略)”&”」、というエラーが出て、原因が全く分からなかった。問題となっていた個所は、割り込み処理ハンドラーのラベルから上位アドレスと下位アドレスを計算している個所で、「ivt_adr_H=.>>16」と「ivt_adr_L=.&0x0000ffff」だった。二日試行錯誤して悩んだ挙句、「ivt_adr_H=./(0x10000)」と「ivt_adr_L=.」に変更したら、アセンブルエラーが出なくなった。明確な原因は分からないが、C言語で普通に何気なく記述する算術式は、アセンブラでは同じようにはサポートされてないようだった。右ビットシフトは2で割るのと同じだから、2の16乗の値で割るという代替案だ。上位ビットをゼロマスクする代替方法は思いつかなかったので、.wordで2バイト領域に設定する時、自動で下位2バイトだけ格納してくれるのでは? と思ってそうしたが、思ったようにはアドレスをIDTへ格納してくれなかった。
ラベルはアセンブルの時点では値が不定で、リンカーで計算する。アセンブルの時点で値が決定できている場合にしか”&”とか”>>”は使えないらしい。しかし、上記のように除算”/”はできる(後になって分かったが、エラーにならないだけで除算した値は設定されなかった)のだから、一貫性がないようにも思えるのだが、仕方ない。ネット上のサンプルプログラムや書籍もC言語(あるいは、nasm)で記述しているのは、GNUアセンブラの機能が貧弱というか、C言語なら簡単にできることができないという制限があるからではないかと思う。
仕方がないから、固定値部分だけ設定してアドレスは0x0000のIDTの領域だけ確保し、わざわざアセンブラのサブルーチンを作って、ラベルアドレスのLowとHighをそれぞれ下位2バイト、次にshrl $16で右シフトした値を、IDTディスクリプタに書き込むようにした。

PICはIBM-PC互換機のマザーボード内部でエミュレートしていることが分かり、キーボードとマウスについては、昔ながらのレジスタ操作で問題なく設定できるはずなのだが、ここでかなり思考がこんがらがり、悪戦苦闘した。その結果、私はPICの設定とキーボードコントローラーの操作を繋がりのあるハード操作だと思い込んでいたことに気付いた。それで、PIC回路のプログラミングとキーボード回路のプログラミングは完全に別のこととして割り切って、それぞれで理解と作り込みをすることにした。書籍でもネットでもPICとキーボードコントローラーの説明が入れ替わりに交互に絡み合い、それが、私の場合、理解の妨げになった。
一旦、時間をおいて、まずPICの初期化処理を作りIRQ1だけ許可し、割り込みハンドラはhltしてIRETするだけのもので、IDTを作成し、CPUへ再設定(lidt)した。この時点で、それまで作っていた画面への文字表示の後、hltで停止した状態で、何かキーを押すと、回復不可能なエラーでクラッシュしたが、とりあえずキーを押すと割込みが発生することだけは確認できた。IDTや割込みハンドラの間違いをチェックして、割込みハンドラ内でhltするようにしたら、とりあえずクラッシュしなくなった。次にキーボードからの入力(スキャンコード)を画面への文字表示をさせるために、バイナリを16進数文字列へ変換するサブルーチンを作成し、テキスト画面へ文字表示するようにした。キーを押すごとに16進文字列が表示されたので、少なくともPICと割り込みハンドラーの処理は正しいことは確認できた。
ちなみに、PICの設定でIRQ1だけ許可したら、初期化処理中にクラッシュした。思い付きで、カスケード接続されているIRQ2も許可したら、クラッシュしなかった。理由は不明だが、PS/2マウスはスレーブ(IRQ2)へ接続されているので、その関連の仕様で、過去の負のレガシーみたいなものかもしれない。

ここまでで十日間近く掛かっているので、一休みして、FIFOへスキャンコードを入力していって、メイン関数でアスキーコードの文字列に変換してから、テキスト画面へ表示するようにする予定だ。その先は、FIFOからの文字列から、コマンド解析を行って、コマンドの実行を行う機能を作ることになる。テスト的にechoコマンドの実行ルーチンを作ってみようと思う。

カテゴリー: Linux, VirtualBox, パソコン, 自作OS パーマリンク

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です