如何使用 x86 裸机组件获取键盘输入?

发布于 2024-07-06 18:51:29 字数 1041 浏览 15 评论 0原文

我正在尝试将内核的第一部分拼凑在一起。 目前我已经将整个内核编译为 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。 我也许可以自己找到,但如果有人碰巧知道,请随意。 再次感谢。

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.

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.

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:

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.)

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.

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

懷念過去 2024-07-13 18:51:29

如果你使用 gcc 进行编译,除非你使用 Linux 内核使用的疯狂的“.code16gcc”技巧(我非常怀疑),否则你不能处于实模式。 如果您使用 GRUB 多重引导规范,GRUB 本身会为您切换到保护模式。 因此,正如其他人指出的那样,您必须直接与 8042 兼容的键盘/鼠标控制器对话。 除非它是 USB 键盘/鼠标并且禁用了 8042 仿真,否则您将需要 USB 堆栈(但您可以使用键盘/鼠标的“引导”协议,这更简单)。

没有人说编写操作系统内核很简单。

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).

Nobody said writing an OS kernel was simple.

旧话新听 2024-07-13 18:51:29

您获得的代码正在尝试访问实模式 BIOS 服务。 如果您在保护模式下运行,这可能考虑到您正在编写内核,那么中断将不起作用。 您将需要执行以下操作之一:

  • 将 CPU 转换为实模式,确保中断向量表正确,并使用您拥有的实模式代码或
  • 编写自己的保护模式键盘处理程序(即使用 in/out 指令) )。

第一个解决方案将涉及运行时性能开销,而第二个解决方案将需要一些有关键盘 IO 的信息。

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:

  • Thunk the CPU into real mode, making sure the interrupt vector table is correct, and use the real mode code you have or
  • Write your own protected mode keyboard handler (i.e. use the in/out instructions).

The first solution is going to involve a runtime performance overhead whist the second will require some information about keyboard IO.

过潦 2024-07-13 18:51:29

我有一个 GeekOS 似乎可以做到这一点

In_Byte(KB_CMD);

,然后

In_Byte(KB_DATA);

获取扫描码。 我把它放在: keyboard.ckeyboard.hKB_CMDKB_DATA 分别为 0x64 和 0x60。 我或许还可以指出,这是在 intr:1 的中断处理程序中完成的。

I've a piece of GeekOS that seems to do

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.

像你 2024-07-13 18:51:29

你做的是正确的事情,但我似乎记得 djgpp 只生成保护模式输出,你不能从中调用中断。 您可以像其他人建议的那样进入实模式,还是更愿意直接寻址硬件?

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?

最单纯的乌龟 2024-07-13 18:51:29

为了便于解释,我们假设您自己用汇编语言编写了一切、引导加载程序和内核(*咳*我已经这样做了)。

在实模式下,您可以使用来自 BIOS 的中断例程。 您还可以用自己的中断向量替换中断向量。 然而,所有代码都是 16 位代码,与 32 位代码不二进制兼容

当您跳过一些困难进入保护模式(包括重新编程中断控制器,以避开 IBM 在 PC 中使用 Intel 保留中断的事实)时,您有机会设置 16 位和 32 位代码段。 这可用于运行 16 位代码。 所以你可以用它来访问getchar中断!

... 不完全的。 为了使该中断正常工作,您实际上需要键盘缓冲区中的数据,该数据由不同的 ISR 放置在那里 - 当按下某个键时由键盘触发的 ISR。 有多种问题几乎会阻止您在保护模式下将 BIOS ISR 用作实际的硬件 ISR。 所以,BIOS键盘例程是没有用的。

另一方面,BIOS 视频通话则很好,因为没有硬件触发的组件。 您确实必须准备一个 16 位代码段,但如果它受到控制,那么您可以通过使用 BIOS 中断来切换视频模式等。

回到键盘:您需要(再次假设您正在编写所有代码)是编写键盘驱动程序。 除非你是一个受虐狂(我就是一个),否则不要去那里。

建议:尝试在实模式下编写多任务内核。 (这是 16 位模式。)您可以使用所有 BIOS 中断! 您没有获得内存保护,但仍然可以通过挂钩定时器中断来获得抢占式多任务处理。

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.

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!

... 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.

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.

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.

胡大本事 2024-07-13 18:51:29

只是一个想法:查看 GRUB for DOS 源代码 (asm.s),console_checkkey 功能正在使用 BIOS INT 16H Function 01,而不是您尝试执行的功能 00。 也许您想检查是否有某个键正在等待输入。

console_checkkey 代码将 CPU 设置为实模式以便使用 BIOS,如 @skizz 建议

您还可以尝试直接使用 GRUB 函数(如果仍映射到实模式)。

关于读取汇编源代码的注意事项:在此版本中

movb    $0x1, %ah

意味着将常量字节 (0x1) 移动到寄存器 %ah

来自 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

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.

The console_checkkey code is setting the CPU to real mode in order to use the BIOS, as @skizz suggested.

You can also try using GRUB functions directly (if still mapped in real mode).

A note on reading assembly source: in this version

movb    $0x1, %ah

means move constant byte (0x1) to register %ah

The console_checkkey from GRUB asm.s:

/*
 * 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
偷得浮生 2024-07-13 18:51:29

轮询键盘控制器的示例:

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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文