前章で画面描画が行えるようになりました。
この章ではキーボードドライバを作成することで、シリアル通信ではない方法でキー入力を取得できるようにします。
この章では1章を通して「051_get_scancode」のサンプルを作成します。
キーボードのキー入力は、キーボードコントローラ(KBC)というICとやり取りすることで取得できます。KBCもレジスタを持っており、KBCのレジスタを参照することで、入力されたキーの情報や、キーボードに関する現在のステータスを確認できます。
KBCのレジスタはIOアドレス空間に対応付けられています。そのため、in命令/out命令でレジスタを読み書きします。
キー入力(スキャンコード)を取得する流れは、シリアル通信でデータ受信を行ったときと同様で、以下の通りです。
今回は、キーから指を離した(Break)時のスキャンコードを取得してみることにします。
それでは、実装してみます。
KBCのステータスレジスタはIOアドレス空間の0x0064に割り当てられている8ビットのレジスタです。
例によってin命令でレジスタ値を取得します。なお、今回のようにIOアドレスが8ビット(1バイト)に収まる場合、2バイトで書ける構文があります。
アセンブラ | in 即値(8bit), %al |
---|---|
機械語 | e4 即値(8bit) |
今回の場合、以下のようになります。
アセンブラ | in $0x64,%al |
---|---|
機械語 | e4 64 |
なので、「1. ステータスレジスタ値を取得」の部分の機械語コードはリスト5.1の通りです。
ステータスレジスタの最下位ビット(ビット0)でKBCにキー入力値がある(=1)か無い(=0)かが分かります。
「フラグが立つまで待つ」系の処理で、コードブロックの構造としては「シリアル通信」の「受信待ち」の時と使う命令も同じです(リスト5.2)。
and命令によるマスクでALレジスタの最下位ビットだけ抽出し、その結果に応じて以下の挙動となります。
データレジスタのIOアドレスは0x60です。in命令で取得しALレジスタへ格納します。
sample.txtへ追記するとリスト5.3の通りです。
データレジスタの8ビットの内容は以下の通りです。
ビット | 内容 |
---|---|
7 | Make(=0)かBreak(=1)か |
0 - 6 | スキャンコード |
Make時のスキャンコードなら、ビット7は0なので、単にデータレジスタから取得した8ビットをそのままスキャンコードとして使えば良いのですが、今回はBreak時のスキャンコードを取得したいので、ビット7がスキャンコードに含まれないように何らかの対処が必要です。
今回は以下の2命令で実装してみます。
1つ目のsub命令がミソです。ALのビット7がセットされていたか否かに応じて、以下の挙動になります。
こうすることで、「スキャンコード部分の抽出」と「最上位ビットの状態確認」を同時に行うことができます。
なお、「jb
」命令は初出なのでここで紹介します。
条件付きジャンプ命令の一種で、「Jump if Below」の略です。フラグレジスタとしてはキャリーフラグがセットされているとジャンプします。
構文は以下の通りです。
アセンブラ | jb <ラベルあるいは"."> |
---|---|
機械語 | 72 fe+rel_addr |
キャリーフラグに応じてジャンプする命令でありながら「Jump if Below」という名前なのは、cmp命令が内部的には減算によって比較を行っていることに由来していると思いますが、キャリーフラグはオーバーフローした場合にもセットされるので、add命令等で桁溢れが起きてキャリーフラグがセットされた場合も、jb命令はもちろんジャンプします。
sample.txtへ追記するとリスト5.4の通りです。
jb命令2バイト目の相対アドレス表記はその他のjmp命令と同じで、自分自身の命令位置が0xfeです。今回の場合、sub命令含め、jbより上に14バイトあるので、相対アドレス表記としては0xf0になります。
特に意味はないのですが、一応、アウトプット代わりとして、取得したスキャンコードをCLレジスタへコピーした上で、先頭へ戻ります(リスト5.5)。
QEMUのGUI画面でキー入力をしながらQEMUモニターでCLレジスタの値を確認したいので、動作確認の際は、以下のようにQEMUモニターだけ、実行したシェル上で起動するようにするとやりやすいです。そして、別途立ち上がったGUIのウィンドウの方でキー入力(Make&Break)を行います。
$ ../tools/run_qemu ../fs -monitor stdio vvfat ../fs chs 1024,16,63 QEMU 2.8.1 monitor - type 'help' for more information (qemu) info registers RAX=000000000011001c RBX=00000000bef6ab18 RCX=0000000000000000 RDX=0000000000000000 # ↑起動直後、CLは0x00 ... # '1'キーをMake&Break (qemu) info registers RAX=000000000011001c RBX=00000000bef6ab18 RCX=0000000000000002 RDX=0000000000000000 # ↑CLにはスキャンコード0x02が格納された ... # '2'キーをMake&Break (qemu) info registers RAX=000000000011001c RBX=00000000bef6ab18 RCX=0000000000000003 RDX=0000000000000000 # ↑CLにはスキャンコード0x03が格納された ... # '3'キーをMake&Break (qemu) info registers RAX=000000000011001c RBX=00000000bef6ab18 RCX=0000000000000004 RDX=0000000000000000 # ↑CLにはスキャンコード0x04が格納された ... # 'a'キーをMake&Break (qemu) info registers RAX=000000000011001c RBX=00000000bef6ab18 RCX=000000000000001e RDX=0000000000000000 # ↑CLにはスキャンコード0x1eが格納された ...
スキャンコードはASCIIコードとは別で、スキャンコードセットには大きく3つの種類があります。スキャンコードについては例えば以下のページ等が詳しいです。