4. 读取键盘输入并回显
任务目标
在BIOS/UEFI固件开发环境中,实现从键盘读取按键输入并将字符回显到屏幕的功能。本案例聚焦于最底层的键盘控制器轮询方式,不依赖操作系统服务,直接通过端口I/O操作获取键盘扫描码并转换为ASCII字符。
环境与依赖
- 开发环境:任何支持x86实模式汇编的编译器(如NASM、MASM)
- 运行环境:x86模拟器(如QEMU、Bochs)或真实硬件
- 无需额外库,直接使用端口I/O指令
核心实现原理
键盘控制器使用Intel 8042芯片,通过两个I/O端口访问:0x60(数据端口,读取扫描码)和0x64(状态端口,检查缓冲区状态)。读取流程为:先轮询0x64端口的状态字节,检查bit0(输出缓冲区满标志)是否为1;若为1,则从0x60端口读取扫描码。本案例仅处理按键按下事件(扫描码最高位为0),并将扫描码通过查表转换为ASCII字符后调用BIOS中断0x10的0x0E号功能回显。
关键完整代码
; keyboard_echo.asm
; 汇编:nasm -f bin keyboard_echo.asm -o keyboard_echo.bin
; 运行:qemu-system-x86_64 -drive format=raw,file=keyboard_echo.bin
[org 0x7c00]
; 初始化段寄存器
mov ax, 0
mov ds, ax
mov es, ax
main_loop:
; 等待键盘输出缓冲区满
call wait_for_key
; 读取扫描码
in al, 0x60
; 检查是否为按下事件(bit7=0)
test al, 0x80
jnz main_loop ; 释放事件,忽略
; 扫描码转ASCII
movzx bx, al
mov al, [scancode_table + bx]
test al, al
jz main_loop ; 无对应字符,忽略
; 调用BIOS中断回显字符
mov ah, 0x0e
int 0x10
; 检查是否为Esc键(扫描码0x01)
cmp byte [scancode_table + bx], 27
jne main_loop
; 退出到BIOS
int 0x19
; 等待键盘缓冲区有数据
wait_for_key:
push ax
.wait:
in al, 0x64
test al, 0x01
jz .wait
pop ax
ret
; 扫描码到ASCII转换表(仅常用键)
scancode_table:
db 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 0x08
db 0x09, 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', 0x0d
db 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', "'", '`', 0, '\'
db 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' '
times 510-($-$$) db 0
dw 0xaa55
运行与验证
- 编译:使用NASM编译上述代码生成引导扇区镜像:
nasm -f bin keyboard_echo.asm -o keyboard_echo.bin - 运行:在QEMU中执行:
qemu-system-x86_64 -drive format=raw,file=keyboard_echo.bin - 验证方法:系统启动后,依次按下字母键、数字键和符号键,观察屏幕是否回显对应字符。按Esc键应退出并重新启动。
- 通过条件:所有按下的可打印字符均正确显示在屏幕上,无乱码或重复;按Esc键能正常触发重启。
常见问题
- 按键无反应:检查QEMU窗口是否获得焦点,或确认编译后的bin文件大小是否为512字节(引导扇区要求)。
- 回显乱码:确认扫描码表索引正确,部分扩展键(如方向键)会产生两字节扫描码(0xE0前缀),本案例未处理,属预期行为。
- 大小写问题:本案例未处理Shift键状态,因此字母始终显示为小写。如需支持大写,需额外跟踪Shift键的按下/释放状态。