我正在尝试将内核的第一部分拼凑在一起。目前我已经将整个内核编译为 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
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top