如何修复自定义操作系统中PS/2键盘输入字符乱屏问题
解决LainOS中PS/2键盘输入乱显示的问题
1. 修正扫描码到ASCII的映射逻辑
PS/2键盘发送的是扫描码而非直接ASCII字符,必须区分通码(按键按下,最高位为0)和断码(按键松开,最高位为1),仅处理按下事件:
- 误将断码当成通码处理会映射出错误字符,比如
0x9E(A键断码)不能直接映射为'A' - 初始化时清空键盘缓冲区,避免残留扫描码干扰:
void keyboard_init() { // 清空缓冲区残留数据 while (inb(0x64) & 0x01) { inb(0x60); } // 启用键盘中断(主PS/2端口) outb(0x64, 0xAE); uint8_t status = inb(0x60); status |= 0x01; outb(0x64, 0x60); outb(0x60, status); } void keyboard_handler() { uint8_t scancode = inb(0x60); // 忽略断码 if (scancode & 0x80) { // 发送中断结束信号 outb(0x20, 0x20); return; } char c = scancode_to_ascii[scancode]; if (c != 0) { terminal_putchar(c); } // 发送中断结束信号 outb(0x20, 0x20); }
2. 确保终端光标与显存写入同步
乱显示大多是软件跟踪的光标位置(terminal_row/terminal_column)与实际显存写入位置不一致导致:
- 写入字符后必须正确更新行列值,处理换行和屏幕滚动,同步硬件光标:
#define VGA_WIDTH 80 #define VGA_HEIGHT 25 uint16_t* terminal_buffer = (uint16_t*)0xB8000; size_t terminal_row; size_t terminal_column; uint8_t terminal_color; void terminal_putchar(char c) { cli(); // 关闭中断避免被打断 if (c == '\n') { terminal_row++; terminal_column = 0; } else { size_t index = terminal_row * VGA_WIDTH + terminal_column; terminal_buffer[index] = (uint16_t)c | ((uint16_t)terminal_color << 8); terminal_column++; } // 处理屏幕滚动 if (terminal_row >= VGA_HEIGHT) { // 向上滚动一行 memcpy(terminal_buffer, terminal_buffer + VGA_WIDTH, (VGA_HEIGHT - 1) * VGA_WIDTH * sizeof(uint16_t)); // 清空最后一行 size_t last_row_idx = (VGA_HEIGHT - 1) * VGA_WIDTH; for (size_t i = 0; i < VGA_WIDTH; i++) { terminal_buffer[last_row_idx + i] = (uint16_t)' ' | ((uint16_t)terminal_color << 8); } terminal_row = VGA_HEIGHT - 1; } // 更新硬件光标位置 size_t cursor_pos = terminal_row * VGA_WIDTH + terminal_column; outb(0x3D4, 0x0F); outb(0x3D5, (uint8_t)(cursor_pos & 0xFF)); outb(0x3D4, 0x0E); outb(0x3D5, (uint8_t)((cursor_pos >> 8) & 0xFF)); sti(); // 恢复中断 }
3. 修复中断处理的竞态问题
键盘中断(IRQ1)处理不当会导致重复触发或寄存器混乱:
- 中断处理函数必须完整保存/恢复寄存器,发送EOI(中断结束)信号:
keyboard_interrupt: pusha ; 保存所有通用寄存器 call keyboard_handler mov al, 0x20 out 0x20, al ; 向主PIC发送EOI信号 popa ; 恢复寄存器 iret ; 中断返回
4. 保证显存操作的原子性
如果系统支持多任务或中断可能打断显存写入,会出现字符拆分显示:
- 在
terminal_putchar中用cli()和sti()包裹写入逻辑,确保操作不被中断打断(如上面代码示例)
内容的提问来源于stack exchange,提问作者Tessa312
相关产品推荐
相关产品推荐

