/*
* Get data from the keyboard. If we finish a character, return it. Else 0.
* Return -1 if no data.
*/
static int
kbd_proc_data(void)
{
int c;
uint8_t data;
static uint32_t shift; // 数据存在bss段中,这部分数据初始化为0,下次调用该函数时shift里的值还保存着
// 读键盘控制状态寄存器0x64,如果第一位为1则表示有数据;KBSTATP,KBS_DIB在inc/kbdreg.h中定义
if ((inb(KBSTATP) & KBS_DIB) == 0)
return -1;
//读键盘数据寄存器0x60
data = inb(KBDATAP);
/* 如果想知道键被按下和被释放时的键盘扫描码,可以加上一条输出语句输出码值,很直观!
cprintf(" %02x ",data);
*/
if (data == 0xE0) {
// E0 escape character,扩展按键,扫描码为2字节;E0后面还跟有一个字节值
shift |= E0ESC;
return 0;
} else if (data & 0x80) {
// Key released,除去E0,最高位为1(+0x80)的,应该是键释放的扫描码
// 如果该扫描码是E0 escape的,那么保持值不变;否则清除最高位的1,其值
// 变为建按下是的码值。这么做的目的就是为了处理左右ctrl,alt和shift键,
// 即在建释放时清除其shiftcode值,因为它们的shiftcode值会影响其他字符
// 的解析
data = (shift & E0ESC ? data : data & 0x7F);
shift &= ~(shiftcode[data] | E0ESC); // 清除之前的shiftcode,表示
// 释放了ctrl,alt或者shift
// 如果该扫描码是E0 escape的
// 清除E0 escape标记
return 0;
} else if (shift & E0ESC) {
// Last character was an E0 escape; or with 0x80
// 上一个字节值为E0,那么该字节应该是键被按下时扫描码的第二字节,
// 让其与上0x80,用以后面处理时区分单字节扫描码
data |= 0x80;
shift &= ~E0ESC; // 清除E0 escape
}
shift |= shiftcode[data]; // 键被按下时,置ctrl,alt和shift键被按下的状态
shift ^= togglecode[data]; // CAPSLOCK,NUMLOCK,SCROLLLOCK这三个键没按一次,
// 它们的状态就改变(0,1变化),这里用异或巧妙的实现
// 它们的状态也会影响后面按键字符的解析
// static uint8_t *charcode[4] = {normalmap,shiftmap,ctlmap,ctlmap};
// CTL = 1 (01); SHIFT = 2 (10). CTL | SHIFT = 3 (11)
// CTL和SHIFT二者组合值为0~3, 对应charcode里面的四个不同字符值map
c = charcode[shift & (CTL | SHIFT)][data];
if (shift & CAPSLOCK) { // CAPSLOCK 影响字母字符解析
if ('a' <= c && c <= 'z') // 小写->大写
c += 'A' - 'a';
else if ('A' <= c && c <= 'Z') // 大写(shift键按下时)->小写
c += 'a' - 'A';
}
// Process special keys
// Ctrl-Alt-Del: reboot
if (!(~shift & (CTL | ALT)) && c == KEY_DEL) {
cprintf("Rebooting!\n");
outb(0x92, 0x3); // courtesy of Chris Frost
// PS/2 POS, 0092: bit 1 = 1 indicates A20 active
// bit 0 = 0 system reset or write
// 1 pulse alternate reset pin (alternate CPU reset)
}
return c;
}
|
阅读(1057) | 评论(0) | 转发(0) |