键盘输入 - 常见错误

错误 1:按键无反应

症状

键盘输入没有任何效果,屏幕无回显。

排查步骤

  1. 确认中断系统正常(时钟中断是否在工作?)
  2. 确认IRQ1没有被PIC掩码屏蔽
  3. 确认处理程序注册在向量33(而非向量1)
  4. 使用QEMU的 -d int 选项查看中断是否触发

常见原因

// 错误:注册到了向量1(CPU异常)
register_interrupt_handler(1, keyboard_handler);
 
// 正确:IRQ1 → 向量33(PIC重映射后32+1=33)
register_interrupt_handler(33, keyboard_handler);

错误 2:每个键都显示相同字符

症状

无论按什么键,都显示同一个字符或乱码。

原因

未读取扫描码,或映射表有误。

解决方案

// 确保从正确端口读取
uint8_t scancode = inb(0x60);  // 数据端口
 
// 确保映射表大小足够
if (scancode < sizeof(scancode_ascii)) {
    char c = scancode_ascii[scancode];
}

错误 3:按一次键出现两个字符

症状

按一次键显示两次字符。

原因

没有区分Make Code(按下)和Break Code(释放)。

解决方案

if (scancode & 0x80) {
    // 这是释放事件,不要处理为字符
    return;
}

错误 4:Shift键不工作

症状

Shift键按下后字符没有变成大写。

原因

只处理了左Shift但忘了右Shift,或者在释放时没有清除标志。

解决方案

// Make Code
case 0x2A: shift_pressed = true; break;  // 左Shift
case 0x36: shift_pressed = true; break;  // 右Shift
 
// Break Code
case 0xAA: shift_pressed = false; break; // 左Shift释放
case 0xB6: shift_pressed = false; break; // 右Shift释放

错误 5:缓冲区溢出

症状

快速输入后丢失字符。

原因

环形缓冲区已满但没有检查。

解决方案

bool buffer_put(char c) {
    uint32_t next = (head + 1) % BUFFER_SIZE;
    if (next == tail) return false;  // 满了就丢弃
    buffer[head] = c;
    head = next;
    return true;
}

调试技巧

# 在QEMU中查看键盘中断是否触发
qemu-system-x86_64 -fda os-image.bin -d int 2>&1 | grep "v=21"
# v=21 是十六进制33,即IRQ1的向量号