如何在自研C/C++操作系统中实现用户输入获取与按键响应功能
实现方案
核心逻辑
x86架构下PS/2键盘的输入可通过IO端口轮询的方式实现,不需要先配置中断,适合入门阶段快速跑通功能:
- 通过
0x64端口读取键盘控制器状态,判断是否有输入数据就绪 - 输入就绪后从
0x60端口读取键盘扫描码,忽略按键释放事件(扫描码最高位为1) - 建立扫描码到ASCII字符的映射表,完成码值转换
- 修复现有打印函数的逻辑问题,实现输入字符的屏幕回显
- 匹配特定按键扫描码触发对应操作
代码修改
1. 新增/修改 includes/types.h
添加基础类型定义和IO端口操作函数:
#ifndef TYPES_H #define TYPES_H typedef unsigned int uint32_t; typedef unsigned short uint16_t; typedef unsigned char uint8_t; // 读8位端口 static inline uint8_t inb(uint16_t port) { uint8_t ret; __asm__ __volatile__ ("inb %1, %0" : "=a"(ret) : "Nd"(port)); return ret; } // 写8位端口 static inline void outb(uint16_t port, uint8_t val) { __asm__ __volatile__ ("outb %0, %1" : : "a"(val), "Nd"(port)); } #endif
2. 新增 includes/keyboard.h 及实现
添加扫描码映射和键盘读取逻辑:
#ifndef KEYBOARD_H #define KEYBOARD_H #include "types.h" // 扫描码转ASCII映射表(仅包含常用可打印字符,索引为扫描码值) const char scancode_map[] = { 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', 0, 0, 0, '+', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // 读取扫描码 uint8_t keyboard_read_scancode() { // 等待键盘控制器数据就绪 while((inb(0x64) & 0x1) == 0); return inb(0x60); } // 读取ASCII字符 char keyboard_read_char() { uint8_t scancode = keyboard_read_scancode(); // 忽略按键释放事件(最高位为1) if(scancode & 0x80) return 0; return scancode_map[scancode]; } // 等待指定按键按下(参数为对应ASCII值) void wait_key_press(char target) { char c; do { c = keyboard_read_char(); } while(c != target); } #endif
3. 修复 functions.h 打印逻辑
修复原有单字符打印函数的错误,添加光标位置管理:
#ifndef FUNCTIONS_H #define FUNCTIONS_H #include "keyboard.h" #include "colors.h" #include "types.h" static unsigned short* VideoMemory = (unsigned short*)0xb8000; // 全局光标偏移,单位为字符,一行80字符,总共25行 int cursor_offset = 0; void print_char(unsigned char colour, char c) { if(c == '\n') { // 换行:光标移动到下一行开头 cursor_offset = (cursor_offset / 80 + 1) * 80; return; } if(c == '\b') { // 退格:光标前移一位,替换为空格 if(cursor_offset > 0) { cursor_offset--; VideoMemory[cursor_offset] = (0x0F << 8) | ' '; } return; } // 普通字符打印 VideoMemory[cursor_offset] = (colour << 8) | c; cursor_offset++; // 到达行尾自动换行 if(cursor_offset >= 80 * 25) { // 简单滚屏实现:将前24行内容上移一行,清空最后一行 for(int i = 0; i < 80 * 24; i++) { VideoMemory[i] = VideoMemory[i + 80]; } for(int i = 80 * 24; i < 80 * 25; i++) { VideoMemory[i] = (0x0F << 8) | ' '; } cursor_offset = 80 * 24; } } void print_string(unsigned char colour, const char* str) { while(*str != '\0') { print_char(colour, *str); str++; } } // 保留原有接口兼容现有代码 void print_title(unsigned char colour, const char *string) { print_string(colour, string); } void setChar(char character, short col, short row, unsigned char attr) { int offset = row * 80 + col; VideoMemory[offset] = (attr << 8) | character; } void print_line(unsigned char colour, const char *string, int line) { int old_offset = cursor_offset; cursor_offset = line * 80; print_string(colour, string); cursor_offset = old_offset; } // 兼容原有print接口,单字符版本 void print(unsigned char colour, char c) { print_char(colour, c); } // 新增字符串版本print void print(unsigned char colour, const char* str) { print_string(colour, str); } #endif
4. 修改 kernel.cpp 实现输入回显和按键触发逻辑
#include "includes/types.h" #include "includes/functions.h" #include "includes/colors.h" #include "includes/keyboard.h" using namespace std; typedef void (*constructor)(); extern "C" constructor* start_ctors; extern "C" constructor* end_ctors; extern "C" void callConstructors() { for(constructor* i=start_ctors;i!=end_ctors;i++){ (*i)(); } } // 特定按键触发的操作示例:按下回车后执行 void on_enter_press() { print(COLOR_RED, "\n你按下了回车键!触发自定义操作\n"); } extern "C" void kernelMain(void* multiboot_structure, uint32_t magicnumber) { print(COLOR_RED, "Hello! 请输入内容,按回车触发操作:\n"); while(1) { char c = keyboard_read_char(); if(c == 0) continue; // 忽略无效输入 // 回显输入字符 print(0x0F, c); // 检测特定按键触发操作 if(c == '\n') { on_enter_press(); } // 可添加其他按键判断逻辑,比如按下ESC退出等 } }
功能验证
编译运行后可直接输入字符,屏幕会实时回显输入内容,按下回车键会触发预设的提示打印逻辑,其他按键可自行扩展匹配。
内容的提问来源于stack exchange,提问作者elona
相关产品推荐
相关产品推荐

