键盘功能

目前,键盘控制器芯片大多数采用Intel 8042芯片,键盘控制器芯片通过PS/2接口或USB接口与外部设备相连。键盘舍内通常会包含一个Intel 8048或兼容芯片,这个芯片会时刻扫描键盘设备的每个按键,并将扫描到的按键进行编码,每个按键的编码是唯一的。8042芯片还负责控制系统的其他功能,如鼠标、A20地址线等。

Screenshot_20220820_195522

当8048芯片检测到按键被按下时,它会将按键对应的编码值通过PS/2接口发送到8042键盘控制器芯片中。8042键盘控制器在接收到编码值后,会将其解析并转换成统一键盘扫描码(第1套XT扫描码集)。并存放到输出缓冲区等待处理器读取。如果此时还有新键被按下,8042芯片将不再接收新的数据,直到输出缓冲区被清空后,8042芯片才会继续接收编码数据。

键盘扫描码共三套,第1套为原始XT扫描码;第2套为AT扫描码;第三套为PS/2扫描码。现在键盘默认使用第二套AT扫描码,出于兼容性考虑,第二套扫描码最终都会转换为第一套XT扫描码供处理器使用。

第1套扫描码,每个按键扫描码由1B数据组成,这1B数据的低7位代表按键的扫描码,最高位代表按键状态(0:按下,1:松开)。当某个按键被按下时,键盘控制器输出的扫描码叫做Make Code,松开按键时的扫描码叫做Break Code码。如按下字母b是0x30,松开是0xb0

此外,还有一些扩展按键使用2B键盘扫描码,当扩展按键被按下后,键盘编码将相继产生两个中断请求,第一个中断请求将向处理器发送1B的扩展码前缀0xe0,第二个中断请求将向处理器发送1B的扩展Break Code码。当松开按键后,键盘编码器依然后相继产生两个中断请求,第一个中断请求向处理器发送1B的扩展前缀0xe0,第二个中断请求向处理器发送1B的扩展Break Code码。

还有两个特殊的按键,PrtScn和Pause/Break。当PrtScn按键被按下时,处理器共会收到两组含有扩展码前缀的Make Code码,它们的字节顺序依次是0xe00x2a0xe00x37;当松开PrtScn按键时,会依次产生0xe00xb70xe00xaa四个键盘扫描码。而在按下Pause/Break键时,键盘编码器产生扩展码字节顺序0xe10x1d0x450xe10x9d0xc5,与其他按键不同的是,松开此按键并不会产生键盘扫描码。

键盘控制器的寄存器地址同样采用I/O地址映射方式,使用IN和OUT汇编指令可对寄存器进行访问。键盘控制器的I/O端口地址时60h和64h,其中60h地址处的寄存器时读写缓冲区,64h地址处的寄存器是用于读取寄存器状态或者向芯片发送控制命令。

键盘中断捕捉

先屏蔽键盘中断请求以外的请求:

1
2
3
4
5
6
7
void interrupt_init() {
...
io_out8(0x21, 0xfd);
io_out8(0xa1, 0xff);

sti();
}

do_irq添加键盘扫描码的接收代码:

1
2
3
4
5
6
7
void do_irq(unsigned long regs, unsigned long nr) { // regs: rep, nr
unsigned char x;
color_printk(RED, BLACK, "do_irq:%#08x\t", nr);
x = io_in8(0x60);
color_printk(RED, BLACK, "key code: %#08x\n", x);
io_out8(0x20, 0x20);
}

借助汇编指令IN从I/O端口地址60h处读取键盘扫描码,并打印到屏幕上

Screenshot_20220820_202406

如图是依次打印按下然后松开键盘b、PrtScn、Pause的扫描码。