如何使用 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 $0x16"
: "=a" (output)
: "a" (output)
:
);
return (char)output;
}
当调用此代码时,核心立即崩溃。(我在 VirtualBox 上运行它,我觉得没有必要在真实硬件上尝试这种基本的东西。)
现在我实际上有几个问题。没有人能够告诉我(因为我的代码是从 GRUB 启动的)我目前是否在实模式或保护模式下运行。我还没有以某种方式进行跳跃,我计划在实模式下运行,直到设置了进程处理程序。
那么,假设我在实模式下运行,我做错了什么,以及如何修复它?我只需要一个基本的 getc 例程,最好是非阻塞的,但如果谷歌在这个例程上提供帮助,我就该死了。一旦我能做到这一点,我就可以从那里做剩下的事情。
我想我在这里要问的是,我是否接近正确的轨道?在这一级别上,人们通常如何获得键盘输入?
编辑:噢...所以我在保护模式下运行。这无疑解释了尝试访问实模式函数时发生的崩溃。
所以我想我正在寻找如何从保护模式访问键盘 IO。我也许可以自己找到,但如果有人碰巧知道,请随意。再次感谢。
解决方案
如果您正在使用gcc进行编译,除非您使用的是疯狂的“.code16gcc”。 linux内核使用的技巧(我非常怀疑),你不能处于实模式。如果您正在使用GRUB多引导规范,GRUB本身将切换到保护模式。因此,正如其他人所指出的那样,您将不得不直接与8042兼容的键盘/鼠标控制器进行通信。除非它是USB键盘/鼠标且禁用8042仿真,否则你需要一个USB堆栈(但你可以使用键盘/鼠标的“启动”协议,这更简单)。
没有人说编写操作系统内核很简单。
其他提示
您获得的代码正在尝试访问实模式 BIOS 服务。如果您在保护模式下运行,这可能考虑到您正在编写内核,那么中断将不起作用。您需要执行以下操作之一:
- 将 CPU 切换到实模式,确保中断向量表正确,并使用您拥有的实模式代码或
- 编写您自己的保护模式键盘处理程序(即使用输入/输出指令)。
第一个解决方案将涉及运行时性能开销,而第二个解决方案将需要一些有关键盘 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中使用英特尔保留的中断这一事实)时,你有机会设置16-和32位代码段。这可用于运行16位代码。所以你可以用它来访问getchar中断!
......不太好。要使此中断起作用,您实际上需要键盘缓冲区中的数据,该缓冲区由不同的ISR放置在那里 - 按下键时由键盘触发的ISR。有许多问题几乎阻止您使用BIOS ISR作为保护模式下的实际硬件ISR。因此,BIOS键盘程序没用。
另一方面,BIOS视频通话很好,因为没有硬件触发的组件。你必须准备一个16位的代码段,但是如果它在控制之下,那么你可以通过使用BIOS中断来切换视频模式等等。回到键盘:你需要的东西(再次假设你正在编写所有代码)是写一个键盘驱动程序。除非你是受虐狂(我是其中一个),否则不要去那里。
建议:尝试在Real模式下编写多任务内核。 (这是16位模式。)您可以使用所有BIOS中断!你没有获得内存保护,但你仍然可以通过挂钩定时器中断来获得先发制人的多任务处理。
只是一个想法:查看 GRUB for DOS 来源(asm.s),<代码> console_checkkey 函数正在使用BIOS INT 16H Function 01
,而不是函数00,正如您尝试的那样。也许你想检查一个键是否等待输入。
console_checkkey
代码将CPU设置为实模式以便使用BIOS,如 @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