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

int 0x16键盘输入在286 PC-AT实模式OS下无法读取故障排查

故障根因定位

你的代码存在两处兼容性问题,虚拟机BIOS容错性强可以正常运行,286实机的老版BIOS无容错逻辑,直接触发故障:

  1. badChar按键过滤循环存在越界死循环问题
    你实现的非法扫描码过滤逻辑有明显缺陷:
    • 加载bx为badChar表首地址后,进入循环先执行inc bx,直接跳过了表中第一个待过滤的扫描码
    • 循环未做边界判断,一旦因为内存内容偏差没有遍历到badChar表末尾的0终止符,会持续向后遍历随机内存,永远不会退出循环,代码直接卡死在该循环内,根本不会回到int 0x16读按键的逻辑,表现为完全不响应键盘输入。
      虚拟机启动后内存大多被初始化为全0,越界遍历很快就能碰到0值退出循环;286实机上电后内存内容是随机的,很容易出现长时间遍历不到0的情况,直接触发死循环。
      这也解释了键盘锁定灯可正常触发、Ctrl+Alt+Del可正常重启的现象:这些功能是BIOS的键盘硬件中断(IRQ1对应int 0x9)服务程序直接处理的,你的代码死循环时没有关闭中断,硬件中断可正常响应,只是用户层代码卡死,永远执行不到读键盘缓冲区的逻辑。
  2. 调用int 0x10电传输出功能时未设置BH寄存器
    int 0x10的ah=0x0e功能明确要求传入bh = 目标显示页码,bl = 图形模式下的前景色。你的代码仅设置了bl为颜色值,bh保留了之前bx(输入缓冲区指针)的高8位,在286实机搭载的老EGA/CGA显卡BIOS上,如果传入不存在的显示页码,调用会直接返回不输出字符,部分老BIOS甚至会破坏寄存器状态,导致后续流程跑飞。
修复步骤
  • 第一步:代码开头增加远跳转固化cs:ip,手动初始化段寄存器和栈,避免不同BIOS跳转MBR的方式差异导致地址计算错误:
[org 0x7c00]
jmp 0x0000:entry  ; 强制cs=0,ip=entry,和org 0x7c00的地址约定完全匹配
entry:
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7c00    ; 栈设置在0x7c00以下,不会和后续加载的程序内容冲突
sti
  • 第二步:重写badChar过滤循环逻辑,避免跳过首元素,增加终止判断彻底杜绝死循环:
waitForKey:
    xor ah, ah
    int 0x16
    
    push bx
    push cx
    mov bx, badChar
cmpLoop:
    mov cl, [bx]
    test cl, cl    ; 碰到0终止符立刻退出循环
    jz endCmp
    cmp ah, cl
    je popBack
    inc bx
    jmp cmpLoop
endCmp:
    pop cx
    pop bx 
    jmp continue
popBack:
    pop cx
    pop bx
    jmp waitForKey
  • 第三步:调用int 0x10输出字符前,手动将bh设为0(第0显示页),不要依赖BIOS的默认值:
mov ah, 0x0e
push bx
mov bh, 0         ; 固定使用第0显示页,兼容老显卡BIOS
mov bl, [colourByte]
int 0x10
pop bx
额外兼容性建议
  • 调用int 0x13读扇区后必须检查CF标志,读盘失败时要重置磁盘控制器重试,老286的软盘控制器容错性极差,不做错误处理很容易加载到错误的内存内容。
  • 实模式开发不要假设BIOS初始化的段寄存器、栈指针符合预期,所有段寄存器、栈指针必须手动初始化,不能依赖BIOS默认值。

内容的提问来源于stack exchange,提问作者Skeledog

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.11 16:16:23