键盘输入 - 练习题

练习 1:扩展扫描码表

当前映射表只覆盖了主键盘区域。添加以下按键支持:

  • F1-F12 功能键(扫描码 0x3B-0x46, 0x57-0x58)
  • 方向键(扫描码 0x48上, 0x50下, 0x4B左, 0x4D右)

提示:功能键和方向键不产生ASCII字符,可以用特殊编码表示。

练习 2:实现简单命令行

在回显基础上实现行编辑功能:

#define LINE_BUFFER_SIZE 128
static char line[LINE_BUFFER_SIZE];
static uint32_t line_pos = 0;
 
void process_input(void) {
    char c = keyboard_getchar();
    if (c == '\n') {
        line[line_pos] = '\0';
        process_command(line);  // 处理命令
        line_pos = 0;
    } else if (c == '\b' && line_pos > 0) {
        line_pos--;
    } else if (line_pos < LINE_BUFFER_SIZE - 1) {
        line[line_pos++] = c;
    }
}

实现一个能识别 “help”、“clear”、“version” 命令的简单shell。

练习 3:LED控制

PS/2键盘有3个LED灯(Num Lock、Caps Lock、Scroll Lock)。 通过端口0x60发送命令控制它们:

void keyboard_set_leds(bool scroll, bool num, bool caps) {
    uint8_t leds = (scroll) | (num << 1) | (caps << 2);
 
    // 等待键盘准备好
    while (inb(0x64) & 0x02);
    outb(0x60, 0xED);  // LED控制命令
 
    while (inb(0x64) & 0x02);
    outb(0x60, leds);  // LED状态
}

实现并测试:按Caps Lock时切换Caps Lock LED。

练习 4:按键速率控制

实现按键重复(长按时持续产生输入)的速率控制:

提示:使用定时器中断(IRQ0)配合键盘状态,当检测到持续按下同一个键时,按固定间隔重复产生字符。

思考题

  1. 为什么QEMU中的键盘使用的是PS/2协议而非USB?
  2. 如果两个键同时按下(非修饰键),键盘控制器会如何处理?
  3. 环形缓冲区在中断上下文中使用是否需要加锁?为什么?