x86ベアメタルアセンブリでキーボード入力を取得する方法は?
-
03-07-2019 - |
質問
私はカーネルの最初の部分を一緒にハックしようとしています。現在、カーネル全体をCコードとしてコンパイルしており、コンソールウィンドウにテキストを表示し、その優れた点すべてを取得することに成功しています。キーボード入力の受け入れを開始して、実際に使用してプロセス管理を行えるようにします。
DJGPPを使用してコンパイルし、GRUBでロードしています。また、基本的にコンパイル済みのCコードに直接ジャンプする小さなアセンブリを使用しています。そこから満足しています。
私が行ったすべての研究は、キーボードバッファーから次の文字を読み込むための$ 0x16のISRを指しているようです。私が知ることができることから、これは、ASCII値をahに、キーコードをalに、またはその効果があるものに格納することになっています。インラインアセンブリで次のルーチンを使用してこれをコーディングしようとしています。
char getc(void)
{
int output = 0;
//CRAZY VOODOO CODE
asm("xor %%ah, %%ah\n\t"
"int <*>x16"
: "=a" (output)
: "a" (output)
:
);
return (char)output;
}
このコードが呼び出されると、コアはすぐにクラッシュします。 (VirtualBoxで実行しています。実際のハードウェアでこの基本的なことを試してみる必要はありませんでした。)
今、実際にいくつか質問があります。 (私のコードはGRUBから起動されているので)現時点でリアルモードまたは保護モードで実行されているかどうかを誰も教えてくれません。何らかの方法でジャンプしたことはありません。プロセスハンドラーをセットアップするまで、リアルモードで実行することを計画していました。
では、リアルモードで実行していると仮定すると、何が間違っているのでしょうか?また、どうすれば修正できますか?基本的なgetcルーチンが必要なだけで、できればノンブロッキングですが、Googleがこれをまったく助けているのなら、私は大胆です。それができると、そこから残りの作業を行うことができます。
ここで尋ねているのは、正しい道の近くにいるのでしょうか?一般に、このレベルでキーボード入力を取得するにはどうすればよいですか?
編集:OOhh ...だから保護モードで実行しています。これは、実際にリアルモード機能にアクセスしようとするクラッシュを説明しています。
だから、保護モードからキーボードIOにアクセスする方法を探していると思います。私は自分でそれを見つけることができるかもしれませんが、もし誰かが偶然知っていたら気軽に。どうもありがとう。
解決
gccを使用してコンパイルする場合、クレイジーな&quot; .code16gcc&quot;を使用していない限り、 Linuxカーネルが使用するトリック(これは非常に疑わしい)で、リアルモードにすることはできません。 GRUBマルチブート仕様を使用している場合、GRUB自体が保護モードに切り替わります。したがって、他の人が指摘したように、8042互換のキーボード/マウスコントローラーと直接話をする必要があります。 USBキーボード/マウスでなく、USBスタックが必要な8042エミュレーションが無効になっている場合を除きます(ただし、キーボード/マウスには「ブート」プロトコルを使用できます)。
OSカーネルの記述が簡単だと言う人はいませんでした。
他のヒント
そこにあるコードは、リアルモードBIOSサービスにアクセスしようとしています。カーネルを作成していることを考慮している可能性が高いプロテクトモードで実行している場合、割り込みは機能しません。次のいずれかを実行する必要があります。
- CPUをリアルモードにサンクし、割り込みベクターテーブルが正しいことを確認し、持っているリアルモードコードを使用するか、
- 独自のプロテクトモードキーボードハンドラを作成します(つまり、in / out命令を使用します)。
最初のソリューションでは、実行時のパフォーマンスオーバーヘッドが発生し、2番目ではキーボードIOに関する情報が必要になります。
GeekOSの一部を実行しているようです
In_Byte(KB_CMD);
そして
In_Byte(KB_DATA);
スキャンコードを取得します。私はそれを置きます: keyboard.c および keyboard.h 。 KB_CMD
および KB_DATA
はそれぞれ0x64および0x60です。これはおそらくintr:1の割り込みハンドラーで行われることも指摘できます。
あなたは正しいことをしていますが、djgppはプロテクトモード出力のみを生成し、割り込みを呼び出すことはできないことを思い出すようです。他の人が提案したようなリアルモードにドロップできますか、またはハードウェアに直接対処したいですか?
説明のために、自分でアセンブリ言語でブートローダーとカーネルをすべて書いていたとしましょう(*咳*これをやった)。
リアルモードでは、BIOSからの割り込みルーチンを使用できます。割り込みベクトルを独自のものに置き換えることもできます。ただし、すべてのコードは16ビットコードであり、32ビットコードとはバイナリ互換性がありません。
保護モード(割り込みコントローラーの再プログラミング、IBMがPCでIntelが予約した割り込みを使用したという事実を回避することを含む)に到達するためにいくつかの燃え上がる輪を飛び越すと、16- 32ビットコードセグメント。これを使用して、16ビットコードを実行できます。したがって、これを使用してgetchar割り込みにアクセスできます!
...まったくそうではありません。この割り込みが機能するためには、実際には、異なるISR(キーが押されたときにキーボードによってトリガーされるISR)によってそこに置かれたキーボードバッファーのデータが必要です。保護モードで実際のハードウェアISRとしてBIOS ISRを使用することを妨げる多くの問題があります。したがって、BIOSキーボードルーチンは役に立たない。
一方、ハードウェアでトリガーされるコンポーネントがないため、BIOSビデオコールは問題ありません。 16ビットのコードセグメントを準備する必要がありますが、それが制御されている場合は、BIOS割り込みを使用してビデオモードなどを切り替えることができます。
キーボードに戻る:必要なのは(再びすべてのコードを書いていると仮定して)キーボードドライバーを書くことです。あなたがマゾ(私は1人)でない限り、そこに行かないでください。
提案:マルチタスクカーネルをリアルモードで作成してみてください。 (これは16ビットモードです。)すべてのBIOS割り込みを使用できます!メモリ保護は得られませんが、タイマー割り込みをフックすることにより、プリエンプティブマルチタスクを取得できます。
アイデア: GRUB for DOS ソース(asm.s)、< code> console_checkkey 関数はBIOS INT 16H Function 01
を使用していますが、実行しようとしている機能00は使用していません。キーが入力されるのを待っているかどうかを確認したいかもしれません。
console_checkkey
コードは、 @ skizzが提案されました。
GRUB関数を直接使用することもできます(リアルモードでマップされている場合)。
アセンブリソースの読み取りに関する注意:このバージョンでは
movb /*
* int console_checkkey (void)
* if there is a character pending, return it; otherwise return -1
* BIOS call "INT 16H Function 01H" to check whether a character is pending
* Call with %ah = 0x1
* Return:
* If key waiting to be input:
* %ah = keyboard scan code
* %al = ASCII character
* Zero flag = clear
* else
* Zero flag = set
*/
ENTRY(console_checkkey)
push %ebp
xorl %edx, %edx
call EXT_C(prot_to_real) /* enter real mode */
.code16
sti /* checkkey needs interrupt on */
movb <*>x1, %ah
int <*>x16
DATA32 jz notpending
movw %ax, %dx
//call translate_keycode
call remap_ascii_char
DATA32 jmp pending
notpending:
movl <*>xFFFFFFFF, %edx
pending:
DATA32 call EXT_C(real_to_prot)
.code32
mov %edx, %eax
pop %ebp
ret
x1, %ah
は、定数バイト(0x1)をレジスタ%ah
GRUB asm.sの console_checkkey
:
キーボードコントローラーのポーリングの例:
Start:
cli
mov al,2 ; dissable IRQ 1
out 21h,al
sti
;--------------------------------------
; Main-Routine
AGAIN:
in al,64h ; get the status
test al,1 ; check output buffer
jz short NOKEY
test al,20h ; check if it is a PS2Mouse-byte
jnz short NOKEY
in al,60h ; get the key
; insert your code here (maybe for converting into ASCII...)
NOKEY:
jmp AGAIN
;--------------------------------------
; At the end
cli
xor al,al ; enable IRQ 1
out 21h,al
sti