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

时间:2008-10-20 07:43:18

标签: c assembly keyboard x86 bare-metal

我正在试图破解内核的第一部分。我目前已将整个内核编译为C代码,并且我设法让它在控制台窗口中显示文本以及所有这些良好的优点。现在,我想开始接受键盘输入,这样我就可以实际使用这个东西并开始进行流程管理。

我正在使用DJGPP进行编译,并使用GRUB加载。我也使用了一小部分程序集,它基本上直接跳转到我编译的C代码中,我很高兴。

我所做的所有研究似乎都指向一个$ 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;
}

调用此代码时,核心会立即崩溃。 (我在VirtualBox上运行它,我觉得不需要在真实硬件上尝试这种基本功能。)

现在我实际上有几个问题。没有人能告诉我(因为我的代码是从GRUB启动的)我现在正在以实模式或保护模式运行。我没有以这种或那种方式进行跳转,我计划在实际模式下运行,直到我设置了进程处理程序。

所以,假设我在实模式下运行,我做错了什么,我该如何解决?我只需要一个基本的getc例程,最好是非阻塞的,但是如果谷歌正在帮助这个,我会被愚弄。一旦我能做到这一点,我就可以从那里完成剩下的工作。

我想我在这里问的是,我是否在正确的轨道附近?人们通常如何在这个级别上获得键盘输入?

编辑:哦......所以我在保护模式下运行。这无疑解释了试图访问实模式功能的崩溃。

那么我想我正在寻找如何从保护模式访问键盘IO。我也许可以自己找到,但如果有人碰巧知道自由。再次感谢。

7 个答案:

答案 0 :(得分:4)

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

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

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

答案 1 :(得分:4)

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

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

答案 2 :(得分:1)

我有一块似乎正在做的GeekOS

In_Byte(KB_CMD);

然后

In_Byte(KB_DATA);

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

答案 3 :(得分:1)

你做的是正确的,但我似乎记得djgpp只生成保护模式输出,你无法调用中断。您是否可以像其他人建议的那样进入真实模式,或者您更愿意直接解决硬件问题?

答案 4 :(得分:1)

出于解释的目的,让我们假设您自己用汇编语言编写所有,启动加载程序和内核(*咳嗽*我已经这样做了)。

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

当你跳过几个燃烧的箍到达保护模式(包括重新编程中断控制器,以解决IBM在PC中使用英特尔保留的中断这一事实)时,你有机会设置16-和32位代码段。这可用于运行16位代码。所以你可以用它来访问getchar中断!

......不太好。要使此中断起作用,您实际上需要键盘缓冲区中的数据,该缓冲区由不同的ISR放置在那里 - 按下键时由键盘触发的ISR。有许多问题几乎阻止您使用BIOS ISR作为保护模式下的实际硬件ISR。因此,BIOS键盘程序没用。

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

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

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

答案 5 :(得分:0)

只是一个想法:查看GRUB for DOS源(asm.s),console_checkkey函数正在使用BIOS INT 16H Function 01,而不是函数00,正如您尝试的那样。也许你想检查一个键是否等待输入。

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

您也可以尝试直接使用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

答案 6 :(得分:0)

轮询键盘控制器的示例:

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