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

编写简易OS时Bootloader执行int 13h指令卡住及栈配置疑问

Bootloader卡在int 13h指令的问题排查与修复

问题背景

正在编写简易操作系统的Bootloader,在Bochs或Qemu中运行时,程序卡在int 0x13指令处;同时不清楚如何正确设置栈。

相关代码

main.asm

org 0x7c00
bits 16

; 定义BOOT_DRIVE变量,存储启动盘号
BOOT_DRIVE db 0
KERNEL_OFFSET equ 0x2000    ; 内核加载地址

start:
    ; 设置数据段
    mov ax, 0
    mov ds, ax
    mov es, ax
    
    ; 正确设置栈(推荐方案)
    cli                     ; 关中断,避免设置ss/sp时触发异常
    mov ax, 0x0000
    mov ss, ax
    mov sp, 0x7c00          ; 栈顶指向Bootloader起始地址,向下增长
    sti                     ; 开中断
    
    ; 修正BIOS可能的加载地址差异(07c0:0000 vs 0000:7c00)
    push es
    push word .after
    retf

.after:
    mov [BOOT_DRIVE], dl
    call load_kernel
    jmp $                        ; 临时循环,后续改为跳转到内核

load_kernel:
    ; 加载内核到内存
    push bx
    push dx

    mov bx, KERNEL_OFFSET  ; 缓冲区地址(ES:BX)
    mov al, 1              ; 要读取的扇区数
    mov dl, [BOOT_DRIVE]
    call disk_load

    pop dx
    pop bx
    ret

disk_load:
    push ax
    push bx
    push cx
    push dx
    push di

    mov ah, 02h     ; AH=02h:读磁盘功能
    ; AL已由调用者传入,为要读取的扇区数
    mov cl, 02h     ; 起始扇区号(从第2扇区开始,第1扇区是Bootloader)
    mov ch, 0x00    ; 柱面号
    mov dh, 0x00    ; 磁头号

    mov di, 3       ; 重试次数

.loop:
    pusha
    stc             ; 设置进位标志,模拟可能的错误状态(可选)

    int 0x13        ; 调用磁盘BIOS中断
    jnc .done       ; 无进位则读取成功

    ; 读取失败,重置磁盘并重试
.fail:
    popa
    call disk_reset

    dec di
    test di, di
    jnz .loop
    jmp floppy_error

.done:
    popa

    pop di
    pop dx
    pop cx
    pop bx
    pop ax
    ret

; 磁盘重置函数
disk_reset:
    mov ah, 00h
    int 0x13
    ret

; 磁盘错误处理
floppy_error:
    mov si, error_msg
    call print_string
    jmp $

; 字符串打印函数
print_string:
    pusha
    mov ah, 0Eh     ; BIOS teletype输出功能
.loop:
    lodsb
    test al, al
    jz .done
    int 10h
    jmp .loop
.done:
    popa
    ret

; 错误提示字符串
error_msg db "Disk read error!", 0

; 填充Bootloader到512字节,并添加引导标志
times 510-($-$$) db 0
dw 0xaa55

Makefile(镜像制作方式)

# 假设BUILD_DIR是编译后的二进制文件目录
IMAGE = os.img
BUILD_DIR = build

$(IMAGE):
    dd if=/dev/zero of=$@ bs=512 count=2880  ; 创建1.44MB软盘镜像
    dd if=$(BUILD_DIR)/main.bin of=$@ conv=notrunc bs=512 count=1 seek=0  ; 写入Bootloader到第1扇区
    dd if=$(BUILD_DIR)/kernel_entry.bin of=$@ conv=notrunc bs=512 count=1 seek=1  ; 写入内核到第2扇区

问题分析与修复

一、int 0x13卡住的核心原因及解决

  1. 磁盘参数混淆
    原代码中错误地将"要读取的扇区数"存入dh,随后又将dh设为磁头号覆盖了该值,导致al=dh变为0,要求BIOS读取0个扇区,引发无响应。修复时将扇区数存入al(符合BIOS中断参数规范),并保留al的值不被覆盖。

  2. 未定义关键变量与函数

    • 缺失BOOT_DRIVE变量,导致mov [BOOT_DRIVE], dl写入随机内存,破坏数据;添加变量定义即可解决。
    • 未实现disk_reset和floppy_error标签,调用/跳转时会指向错误地址,引发死机;添加基础实现即可。
    • KERNEL_OFFSET引用错误,原代码用[KERNEL_OFFSET]会当作内存地址取值,改为直接使用KERNEL_OFFSET作为立即数传递缓冲区地址。

二、正确设置栈的方法

栈需要设置在不会被Bootloader或内核覆盖的安全区域,推荐两种方案:

  • 方案一:栈顶指向Bootloader起始地址
    将栈设置在物理地址0x7c00下方(向下增长),完全避开Bootloader的内存区域(0x7c00-0x7dff),代码如上述start段中的栈设置部分。
  • 方案二:独立内存区域栈
    可将栈设置到物理地址0x70000(段地址0x7000,偏移0x0000),但需确保该区域未被其他数据占用:
    cli
    mov ax, 0x7000
    mov ss, ax
    mov sp, 0x0000
    sti
    
    注意:设置ss和sp时建议先关中断,避免在修改过程中触发中断导致栈异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 14:04:54