void IntModInit()
	SetIntHandler(AddrOff(gIdtInfo.entry, 0x0D), (uint)SegmentFaultHandlerEntry);
	SetIntHandler(AddrOff(gIdtInfo.entry, 0x0E), (uint)PageFaultHandlerEntry);
    SetIntHandler(AddrOff(gIdtInfo.entry, 0x20), (uint)TimerHandlerEntry);
    SetIntHandler(AddrOff(gIdtInfo.entry, 0x21), (uint)KeyboardHandlerEntry);
    SetIntHandler(AddrOff(gIdtInfo.entry, 0x80), (uint)SysCallHandlerEntry);
    InitInterrupt();

loader.asm

RunTask:
    push ebp
    mov ebp, esp
    mov esp, [ebp + 8]
    lldt word [esp + 96]
    ltr word [esp + 98]
    pop gs
    pop fs
    pop es
    pop ds
    popad
    add esp, 4  
    mov dx, MASTER_IMR_PORT
    in ax, dx  
    %rep 5
    %endrep 
    and ax, 0xFC    ; Enable Timer and Keyboard Interrupt
    out dx, al  
    %rep 5
    %endrep
    mov eax, PageDirBase    ; Open Page Mode
	mov cr3, eax
	mov eax, cr0
	or  eax, 0x80000000
	mov cr0, eax    

kentry.asm

KeyboardHandlerEntry: BeginISR call KeyboardHandler EndISR

ihandler.c

void KeyboardHandler()
	PrintChar('*');
	SendEOI(MASTER_EOI_PORT);

我们在中断向量中注册了 0x21 号中断 (键盘中断),并将中断屏蔽寄存器的值设置为 0xFD,时钟中断和键盘中断分别为 IRQ0 和 IRQ1,这两个中断现在可以被处理器接收。

在键盘中断服务程序中,我们打印 '*' 字符。

当第一次按下键盘的一个按键时,屏幕上显示了 '*' 字符,但之后在按下键盘时,屏幕上就不会再显示 '*' 字符了。

为什么键盘中断只发生一次?如何获取键盘按键的值?

键盘工作原理简介

当我们按下字符 ‘a’ 时,8048把按键信息发送给8042,8042把键位信息存储在缓冲区中,发送中断请求;然后 8259A 通知处理器来处理8042的中断。当处理器读取8042的缓冲区时,8042的缓冲区会被清空;而我们的中断处理函数并没有读取8042缓冲区的信息,所以第二次按下按键的时候,8042就接收不了8048的键位信息了。

键盘扫描码 (键位信息)

处理器接收到按键中断后,从 0x60 端口读取键盘扫描码

扫描码指的是硬件电路对键位的编码

  • Make Code - 按下键时产生的扫描码
  • Break Code - 释放键时产生的扫描码
  • Break Code = Make Code + 0x80

C语言中的函数是怎么返回值的?

汇编程序可以通过将该函数的返回值写入 AX  寄存器,AX  寄存器会作为这个函数的返回值。

端口数据读取

读取键盘扫描码

kentry.asm

; byte ReadPort(ushort port) ReadPort: push ebp mov ebp, esp xor eax, eax mov dx, [esp + 8] in al, dx leave ; void WritePort(ushort port, byte value) WritePort: push ebp mov ebp, esp xor eax, eax mov al, [esp + 8] mov dx, [esp + 12] out dx, al leave
void KeyboardHandler()
	byte kc = ReadPort(0x60);
	PrintIntHex(kc);
	PrintString("  ");
	SendEOI(MASTER_EOI_PORT);

在键盘中断服务程序中,我们读取 0x60 端口上的键盘扫描码,以便可以接收到下一次按键中断。

当按下一个按键时,屏幕上会显示两个数字,一个是按下键时产生的扫描码,另一个是松开键时产生的扫描码,二者相差 0x80。并且我们每按下一次按键,都会读到两个按键信息。

在本篇中,pyos 实现了类似windows 的一个消息系统,换句话说,此处的pyos 是由消 息驱动的,如果你能windows 的消息驱动很好奇,但又不知它是怎样实现的,那么,本篇 中所介绍的pyos 的消息驱动或许能对你理解windows 的消息戏动有些许帮助:) 本篇与前几篇实验报告是一脉相承的,特别是同《保护模式下中断编程》一篇有较为 紧密的相关性,你可以在我的主页上(http://xieyubo.com)找到它们,上面的内容对理解本 篇会具有较大帮助。 中断”的意思是即使处理器正在执行其他工作,当它收到周边设备传来的中断信号时,处理器也会停下来,优先处理这个信号代表的工作,完成后再继续 处理之前未完成的工作。因此当系统自动分配IRQ时,若声卡被分配与其他设备共用一个IRQ的话,发生IRQ冲突的可能性极大,而解决之道就是手动分配 IRQ,在BIOS内进行设置。1、查看主板说明书,找出哪一根PCI插槽是不与其他插槽共用IRQ的(一般是第三根插槽),然后将声卡插到第三根PCI插槽中。IRQ”,它就是第三根PCI插槽所分配的IRQ位置。 在上面的代码中,我们使用了Linux内核提供的GPIO和中断相关的函数来实现键盘驱动程序。在初始化函数中,我们通过gpio_request函数请求GPIO资源,并使用gpio_direction_input函数将GPIO引脚配置为输入模式。接下来,我们使用request_irq函数注册中断处理函数(keyboard_irq_handler),并指定触发中断的条件为下降沿(IRQF_TRIGGER_FALLING)。通过使用GPIO和中断相关的函数,我们能够实现与硬件设备的交互,从而实现对键盘输入的响应。 自己设定或由系统自动分配驱动设备的主设备号; 编设备操作函数(drv_open、drv_read、drv_write、drv_close等); 构造file_operations结构体,将上一步编的操作函数赋值给结构体内的.open、.read、.write、.poll、.fasync等 注册驱动程序,调用register_chrdev(major, name, fops); 编入口函数和出口函数。 但输入子系统驱动框架将以上步骤分开了,它是   按键在我们的项目中是经常使用到的组件。一般来说,我们都是在用到按键时直接针对编码,但这样每次都做很多重复性的工作。所以在这里我们考虑做一般性抽象得到一个可应用于按键操作的通用性驱动程序。 1、功能概述   按键操作在我们的产品种经常用到,一般都是在特定的应用环境中直接有针对性的操作。但这些按键的操作往往有很多的共性,这就为代码复用提供了可能。 1.1、按键的定义   在开始考虑按键操作之前,我们先来分析一下究竟什么是按键。按键一般来讲就是用于信号输入的按钮,通过响应它的操作我们可以实现想要的功能。但我们