You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何处理实模式(16位)下的中断?——32位保护模式操作系统切换至实模式调用BIOS中断故障排查

问题分析与解决方案

我来帮你拆解下这个问题——从Bochs的报错日志和你的切换代码来看,你其实已经成功进入实模式了(日志里明确显示real mode,且CR0的PE位已经被清除),问题出在中断设置时机BIOS中断调用的错误使用上,具体如下:

核心问题点

1. 中断调用完全错误

看Bochs日志最后一行:>> int 0x21 : CD21,但你的代码里写的是:

mov ax,4c00h
int 10h

这是典型的错误搭配:ax=4c00h是DOS中断int 0x21的程序退出功能,你却用它调用视频中断int 10h,完全不匹配。而且实模式下没有DOS环境,调用int 0x21会触发未定义的中断向量,直接导致报错。

2. IDT加载时机滞后

你在切换到实模式后先开了中断(sti),再加载实模式IVT(中断向量表)。如果在stilidt之间有中断触发,CPU会使用旧的保护模式IDT,而实模式下的中断向量范围(0-255)会超出保护模式IDT的限制,直接抛出interrupt(real mode) vector > idtr.limit错误。

3. 段寄存器设置混乱

你在realMode标签下反复修改段寄存器:先把ds/es设为0,又改成0x7c0ss先设为0再改成0x8000。虽然这种操作本身不致命,但会增加调试复杂度,实模式下建议统一设置段寄存器为符合BIOS预期的值(通常ds/es=0,栈设置在高地址比如0xf000)。

修正后的代码示例

global go16
;______________________________________________________________________________________________________
;Switch to 16-bit real Mode
;IN/OUT: nothing
go16:
[BITS 32]
cli ; 全程关闭中断,直到所有实模式设置完成
pop edx ; 保存返回地址
jmp 0x20:PM16 ; 跳转到16位保护模式代码段(确保GDT中0x20是16位代码段,基址0,限长0xFFFF)

[BITS 16]
PM16:
mov ax, 0x28 ; 加载16位数据段选择器(GDT中0x28对应16位数据段,基址0,限长0xFFFF)
mov ss, ax
mov ds, ax
mov es, ax
mov gs, ax
mov fs, ax
mov sp, 0x7c00+0x200 ; 设置保护模式下的临时栈

; 保存并修改CR0,关闭PE位
mov eax, cr0
mov [savcr0], eax
and eax, 0xfffffffe ; 清除PE位(第0位)
mov cr0, eax
jmp 0:realMode ; 远跳冲刷CPU流水线,彻底进入实模式

realMode:
; 初始化实模式段寄存器:段值左移4位为基址,这里设置为0对应基址0
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov sp, 0xf000 ; 设置实模式栈(BIOS通常使用高地址栈)

lidt [idt_real] ; 先加载实模式IVT,再开中断
sti

; 测试视频模式:设置为80x25文本模式(AH=00h是设置视频模式,AL=03h是文本模式)
mov ax, 0x0003
int 0x10

; 暂停系统,观察结果
cli
hlt

; Real mode interrupt vector table definition
idt_real:
dw 0x3ff ; IVT限长:256个中断向量,每个4字节,共1KB
dd 0 ; IVT基址:实模式下默认从0x0000开始

savcr0: dd 0

额外注意事项

  • 确保GDT中的0x20(代码段)和0x28(数据段)是16位保护模式段:段描述符的D/B位设为0,限长设为0xFFFF,基址设为0。
  • 切换过程中必须全程关闭中断,直到实模式IVT加载完成,避免中途触发中断导致错误。
  • 调用BIOS中断时,严格遵循BIOS中断规范:比如int 10h的视频模式设置功能,必须保证AH=00hAL为对应的模式号。

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

火山引擎 最新活动