简体   繁体   English

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

[英]How to get keyboard input with x86 bare metal assembly?

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. 我目前已将整个内核编译为C代码,并且我设法让它在控制台窗口中显示文本以及所有这些良好的优点。 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. 我正在使用DJGPP进行编译,并使用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. 我也使用了一小部分程序集,基本上直接跳转到我编译的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. 我所做的所有研究似乎都指向一个$ 0x16的ISR来读取键盘缓冲区中的下一个字符。 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. 据我所知,这应该存储在ah中的ASCII值,以及al中的keycode,或者那种效果。 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.) (我在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. 没有人能告诉我(因为我的代码是从GRUB启动的)我现在正在以实模式或保护模式运行。 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. 我只需要一个基本的getc例程,最好是非阻塞的,但是如果谷歌正在帮助这个,我会被愚弄。 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. 那么我想我正在寻找如何从保护模式访问键盘IO。 I might be able to find that on my own, but if anyone happens to know feel free. 我也许可以自己找到,但如果有人碰巧知道自由。 Thanks again. 再次感谢。

The code you've got there is trying to access a real mode BIOS service. 您在那里获得的代码是尝试访问实模式BIOS服务。 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 将CPU重置为实模式,确保中断向量表正确,并使用您拥有的实模式代码或
  • Write your own protected mode keyboard handler (ie 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. 第一个解决方案将涉及运行时性能开销,第二个解决方案将需要有关键盘IO的一些信息。

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. 如果你正在使用gcc编译,除非你使用Linux内核使用的疯狂的“.code16gcc”技巧(我非常怀疑),你不能处于实模式。 If you are using the GRUB multiboot specification, GRUB itself is switching to protected mode for you. 如果您正在使用GRUB多引导规范,GRUB本身将切换到保护模式。 So, as others pointed out, you will have to talk to the 8042-compatible keyboard/mouse controller directly. 因此,正如其他人所指出的那样,您将不得不直接与8042兼容的键盘/鼠标控制器进行通信。 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). 除非它是USB键盘/鼠标且禁用了8042仿真,否则你需要USB堆栈(但你可以使用键盘/鼠标的“启动”协议,这更简单)。

Nobody said writing an OS kernel was simple. 没有人说编写操作系统内核很简单。

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 . 我把它放了: keyboard.ckeyboard.h KB_CMD and KB_DATA being 0x64 and 0x60 respectively. KB_CMDKB_DATA分别为KB_DATA和0x60。 I could perhaps also point out that this is done in an interrupt handler for intr:1. 我或许也可以指出这是在intr: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. 你做的是正确的,但我似乎记得djgpp只生成保护模式输出,你无法调用中断。 Can you drop to real mode like others have suggested, or would you prefer to address the hardware directly? 您是否可以像其他人建议的那样进入真实模式,或者您更愿意直接解决硬件问题?

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. 在实模式下,您可以使用来自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. 但是,所有代码都是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. 当你跳过几个燃烧的箍到达保护模式(包括重新编程中断控制器,以解决IBM在PC中使用Intel保留的中断这一事实)时,你有机会设置16位和32位代码段。 This can be used to run 16-bit code. 这可用于运行16位代码。 So you can use this to access the getchar interrupt! 所以你可以使用它来访问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. 要使此中断起作用,您实际上需要键盘缓冲区中的数据,该缓冲区由不同的ISR放置在那里 - 按下键时由键盘触发的ISR。 There are various issues which pretty much prevent you using BIOS ISRs as actual hardware ISRs in protected mode. 有许多问题几乎阻止您使用BIOS ISR作为保护模式下的实际硬件ISR。 So, the BIOS keyboard routines are useless. 所以,BIOS键盘程序是没用的。

BIOS video calls, on the other hand, are fine, because there's no hardware-triggered component. 另一方面,BIOS视频通话很好,因为没有硬件触发的组件。 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. 你必须准备一个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. 建议:尝试在Real模式下编写多任务内核。 (That's 16-bit mode.) You can use all the BIOS interrupts! (这是16位模式。)您可以使用所有BIOS中断! You don't get memory protection but you can still get pre-emptive multitasking by hooking the timer interrupt. 您没有获得内存保护,但您仍然可以通过挂钩定时器中断来获得先发制人的多任务处理。

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. 只是一个想法:查看GRUB for DOS源(asm.s), console_checkkey函数正在使用BIOS INT 16H Function 01 ,而不是函数00,正如您尝试的那样。 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 . 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

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM