编写简易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卡住的核心原因及解决
磁盘参数混淆
原代码中错误地将"要读取的扇区数"存入dh,随后又将dh设为磁头号覆盖了该值,导致al=dh变为0,要求BIOS读取0个扇区,引发无响应。修复时将扇区数存入al(符合BIOS中断参数规范),并保留al的值不被覆盖。未定义关键变量与函数
- 缺失
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 stiss和sp时建议先关中断,避免在修改过程中触发中断导致栈异常。
内容的提问来源于stack exchange,提问作者neta cohen
相关产品推荐
相关产品推荐

