You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在自研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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 23:39:03