I'm in the process of trying to hack together the first bits of a kernel. I currently have the entire kernel compiled down as C code, and I've managed to get it displaying text in the console window and all of that fine goodness. Now, I want to start accepting keyboard input so I can actually make some use of the thing and get going on process management.
我正在试图破解内核的第一部分。我目前已将整个内核编译为C代码,并且我设法让它在控制台窗口中显示文本以及所有这些良好的优点。现在,我想开始接受键盘输入,这样我就可以实际使用这个东西并开始进行流程管理。
I'm using DJGPP to compile, and loading with GRUB. I'm also using a small bit of assembly which basically jumps directly into my compiled C code and I'm happy from there.
我正在使用DJGPP进行编译,并使用GRUB加载。我也使用了一小部分程序集,它基本上直接跳转到我编译的C代码中,我很高兴。
All the research I've done seems to point to an ISR at $0x16 to read in the next character from the keyboard buffer. From what I can tell, this is supposed to store the ASCII value in ah, and the keycode in al, or something to that effect. I'm attempting to code this using the following routine in inline assembly:
我所做的所有研究似乎都指向一个$ 0x16的ISR来读取键盘缓冲区中的下一个字符。据我所知,这应该存储在ah中的ASCII值,以及al中的keycode或者那种效果。我正在尝试使用内联汇编中的以下例程对此进行编码:
char getc(void)
{
int output = 0;
//CRAZY VOODOO CODE
asm("xor %%ah, %%ah\n\t"
"int $0x16"
: "=a" (output)
: "a" (output)
:
);
return (char)output;
}
When this code is called, the core immediately crashes. (I'm running it on VirtualBox, I didn't feel the need to try something this basic on real hardware.)
调用此代码时,核心会立即崩溃。 (我在VirtualBox上运行它,我觉得不需要在真实硬件上尝试这个基本功能。)
Now I have actually a couple of questions. No one has been able to tell me if (since my code was launched from GRUB) I'm running in real mode or protected mode at the moment. I haven't made the jump one way or another, I was planning on running in real mode until I got a process handler set up.
现在我实际上有几个问题。没有人能够告诉我(因为我的代码是从GRUB启动的)我现在正在以实模式或保护模式运行。我没有以这种或那种方式进行跳转,我计划以实模式运行,直到我设置了进程处理程序。
So, assuming that I'm running in real mode, what am I doing wrong, and how do I fix it? I just need a basic getc routine, preferably non-blocking, but I'll be darned if google is helping on this one at all. Once I can do that, I can do the rest from there.
所以,假设我在实模式下运行,我做错了什么,我该如何解决?我只需要一个基本的getc例程,最好是非阻塞的,但是如果谷歌正在帮助这个,我会被愚弄。一旦我能做到这一点,我可以从那里做其余的事情。
I guess what I'm asking here is, am I anywhere near the right track? How does one generally go about getting keyboard input on this level?
我想我在这里问的是,我是否在正确的轨道附近?人们通常如何在这个级别上获得键盘输入?
EDIT: OOhh... so I'm running in protected mode. This certainly explains the crash trying to access real mode functions then.
编辑:哦......所以我在保护模式下运行。这无疑解释了试图访问实模式功能的崩溃。
So then I guess I'm looking for how to access the keyboard IO from protected mode. I might be able to find that on my own, but if anyone happens to know feel free. Thanks again.
那么我想我正在寻找如何从保护模式访问键盘IO。我或许可以自己找到,但如果有人碰巧知道自由。再次感谢。
4
If you are compiling with gcc, unless you are using the crazy ".code16gcc" trick the linux kernel uses (which I very much doubt), you cannot be in real mode. If you are using the GRUB multiboot specification, GRUB itself is switching to protected mode for you. So, as others pointed out, you will have to talk to the 8042-compatible keyboard/mouse controller directly. Unless it's a USB keyboard/mouse and 8042 emulation is disabled, where you would need a USB stack (but you can use the "boot" protocol for the keyboard/mouse, which is simpler).
如果你正在使用gcc编译,除非你使用Linux内核使用的疯狂“.code16gcc”技巧(我非常怀疑),你不能处于实模式。如果您使用GRUB多引导规范,GRUB本身将切换到保护模式。因此,正如其他人所指出的那样,您将不得不直接与8042兼容的键盘/鼠标控制器进行通信。除非它是USB键盘/鼠标且禁用8042仿真,否则您需要一个USB堆栈(但您可以使用键盘/鼠标的“启动”协议,这更简单)。
Nobody said writing an OS kernel was simple.
没有人说编写操作系统内核很简单。
4
The code you've got there is trying to access a real mode BIOS service. If you're running in protected mode, which is likely considering that you're writing a kernel, then the interrupt won't work. You will need to do one of the following:
您在那里获得的代码是尝试访问实模式BIOS服务。如果您正在保护模式下运行,这可能考虑到您正在编写内核,那么中断将无法工作。您需要执行以下操作之一:
将CPU重置为实模式,确保中断向量表正确,并使用您拥有的实模式代码或
编写自己的保护模式键盘处理程序(即使用输入/输出指令)。
The first solution is going to involve a runtime performance overhead whist the second will require some information about keyboard IO.
第一个解决方案将涉及运行时性能开销,第二个解决方案将需要有关键盘IO的一些信息。
1
I've a piece of GeekOS that seems to do
我有一块似乎正在做的GeekOS
In_Byte(KB_CMD);
and then
In_Byte(KB_DATA);
to fetch a scancode. I put it up: keyboard.c and keyboard.h. KB_CMD
and KB_DATA
being 0x64 and 0x60 respectively. I could perhaps also point out that this is done in an interrupt handler for intr:1.
获取扫描码。我把它放了:keyboard.c和keyboard.h。 KB_CMD和KB_DATA分别为0x64和0x60。我或许也可以指出,这是在intr:1的中断处理程序中完成的。
1
You're doing the right thing, but I seem to recall that djgpp only generates protected mode output, which you can't call interrupts from. Can you drop to real mode like others have suggested, or would you prefer to address the hardware directly?
你做的是正确的,但我似乎记得djgpp只生成保护模式输出,你无法调用中断。您是否可以像其他人建议的那样进入实模式,或者您更愿意直接寻址硬件?
1
For the purposes of explanation, let's suppose you were writing everything in assembly language yourself, boot loader and kernel (*cough* I've done this).
出于解释的目的,让我们假设您自己用汇编语言编写所有内容,启动加载程序和内核(*咳嗽*我已经这样做了)。
In real mode, you can make use of the interrupt routines that come from the BIOS. You can also replace the interrupt vectors with your own. However all code is 16-bit code, which is not binary compatible with 32-bit code.
在实模式下,您可以使用来自BIOS的中断例程。您也可以用自己的中断向量替换中断向量。但是,所有代码都是16位代码,与32位代码不是二进制兼容的。
When you jump through a few burning hoops to get to protected mode (including reprogramming the interrupt controller, to get around the fact that IBM used Intel-reserved interrupts in the PC), you have the opportunity to set up 16- and 32-bit code segments. This can be used to run 16-bit code. So you can use this to access the getchar interrupt!
当你跳过几个燃烧的箍到达保护模式(包括重新编程中断控制器,以解决IBM在PC中使用Intel保留的中断这一事实)时,你有机会设置16位和32位代码段。这可用于运行16位代码。所以你可以使用它来访问getchar中断!
... not quite. For this interrupt to work, you actually need data in a keyboard buffer that was put there by a different ISR - the one that is triggered by the keyboard when a key is pressed. There are various issues which pretty much prevent you using BIOS ISRs as actual hardware ISRs in protected mode. So, the BIOS keyboard routines are useless.
... 不完全的。要使此中断起作用,您实际上需要键盘缓冲区中的数据,该缓冲区由不同的ISR放置在那里 - 按下键时由键盘触发的ISR。有许多问题几乎阻止您在保护模式下使用BIOS ISR作为实际硬件ISR。所以,BIOS键盘程序是没用的。
BIOS video calls, on the other hand, are fine, because there's no hardware-triggered component. You do have to prepare a 16-bit code segment but if that's under control then you can switch video modes and that sort of thing by using BIOS interrupts.
另一方面,BIOS视频通话很好,因为没有硬件触发的组件。你必须准备一个16位代码段,但如果这是在控制之下,那么你可以通过使用BIOS中断切换视频模式和那种东西。
Back to the keyboard: what you need (again assuming that YOU'RE writing all the code) is to write a keyboard driver. Unless you're a masochist (I'm one) then don't go there.
回到键盘:你需要的东西(再次假设你写的所有代码)是写一个键盘驱动程序。除非你是一个受虐狂(我是一个),否则不要去那里。
A suggestion: try writing a multitasking kernel in Real mode. (That's 16-bit mode.) You can use all the BIOS interrupts! You don't get memory protection but you can still get pre-emptive multitasking by hooking the timer interrupt.
建议:尝试在Real模式下编写多任务内核。 (这是16位模式。)您可以使用所有BIOS中断!您没有获得内存保护,但您仍然可以通过挂钩定时器中断来获得先发制人的多任务处理。
0
Just an idea: looking at GRUB for DOS source (asm.s), the console_checkkey
function is using BIOS INT 16H Function 01
, and not function 00, as you are trying to do. Maybe you'd want to check if a key is waiting to be input.
只是一个想法:查看GRUB for DOS源(asm.s),console_checkkey函数正在使用BIOS INT 16H Function 01,而不是函数00,正如您尝试的那样。也许你想检查一个键是否等待输入。
The console_checkkey
code is setting the CPU to real mode in order to use the BIOS, as @skizz suggested.
console_checkkey代码将CPU设置为实模式以便使用BIOS,如@skizz建议的那样。
You can also try using GRUB functions directly (if still mapped in real mode).
您也可以尝试直接使用GRUB函数(如果仍然以实模式映射)。
A note on reading assembly source: in this version
关于阅读汇编来源的说明:在此版本中
movb $0x1, %ah
means move constant byte (0x1) to register %ah
意味着将常量字节(0x1)移动到寄存器%ah
The console_checkkey
from GRUB asm.s:
来自GRUB asm.s的console_checkkey:
/*
* 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 $0x1, %ah
int $0x16
DATA32 jz notpending
movw %ax, %dx
//call translate_keycode
call remap_ascii_char
DATA32 jmp pending
notpending:
movl $0xFFFFFFFF, %edx
pending:
DATA32 call EXT_C(real_to_prot)
.code32
mov %edx, %eax
pop %ebp
ret
0
Example for polling the keyboard controller:
轮询键盘控制器的示例:
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