从Bootloader跳转到硬盘第二扇区失败的技术问题排查
问题分析与修复方案
1. 核心错误:跳转目标与代码存储位置不匹配
你把outside函数写在了主引导扇区的代码末尾,但BIOS只会加载硬盘第一扇区(主引导扇区)到内存0x7c00,第二扇区需要手动读取到0x9000。当前代码里的jmp outside是跳转到主引导扇区代码内的标签,而这个标签所在的内存区域根本没被加载,跳转后必然跑飞。
修复:
- 把
outside函数从主引导程序中移除,单独放到第二扇区的代码文件中。 - 跳转时直接指向读取第二扇区的内存地址:
jmp 0x9000(对应你设置的es:bx = 0x0000:0x9000物理地址)。
2. 磁盘读取函数的参数混淆问题
disc_read函数中,你用dh传递要读取的扇区数,但之后又用dh设置磁头号,虽然有push dx/pop dx保存恢复,但逻辑易混淆。此外,错误字符串用dw定义会导致显示乱码(print函数按字节读取,dw是按字存储),让你看不到读盘错误提示。
修复后的disc_read.s:
disc_read: pusha push dx ; 保存原始dx(dh=扇区数,dl=BIOS传入的驱动器号) mov ah, 0x02 ; INT 13h读扇区功能号 mov al, dh ; 要读取的扇区数 mov cl, 0x02 ; 起始扇区号(第二扇区) mov ch, 0x00 ; 柱面号 mov dh, 0x00 ; 磁头号 int 0x13 ; 执行读盘 jc disc_error pop dx cmp al, dh ; 校验实际读取扇区数 jne sectors_error popa ret disc_error: mov bx, disc_error_str call print call print_nl jmp $ sectors_error: mov bx, sectors_error_str call print call print_nl jmp $ ; 用db按字节存储字符串,避免乱码 sectors_error_str: db "Incorrect number of sectors read", 0 disc_error_str: db "Disc read error", 0
3. 主引导程序结构修正
主引导扇区必须严格控制在512字节内,末尾是0xaa55签名。移除原代码中outside相关内容,确保签名前的代码不超过510字节。
修改后的主引导程序(boot.asm):
[org 0x7c00] mov bp, 0x8000 mov sp, bp mov bx, 0x9000 mov dh, 1 ; 只读取第二扇区,设为1即可 call disc_read jmp 0x9000 ; 跳转到第二扇区的内存地址 jmp $ %include "src/disc_read.s" %include "src/print.s" hello_World_str: db "Welcome", 0 ; 填充到510字节,加上签名刚好512字节 times 510 - ($-$$) db 0 dw 0xaa55
4. 独立的第二扇区代码(second_sector.s)
创建单独的第二扇区文件,存放outside函数:
[org 0x9000] ; 对应读取到的内存地址 outside: mov ah, 0x0e mov al, 0x41 ; 显示字符'A' int 0x10 jmp $ ; 无限循环 times 512 - ($-$$) db 0 ; 填充到512字节,保证是完整扇区
编译与测试步骤
- 编译主引导程序:
nasm boot.asm -f bin -o boot.bin - 编译第二扇区:
nasm second_sector.s -f bin -o second.bin - 合并成磁盘镜像:
- Windows:
copy /b boot.bin + second.bin disk.img - Linux/macOS:
cat boot.bin second.bin > disk.img
- Windows:
- 用虚拟机或模拟器加载
disk.img测试
修复后,主引导程序会读取第二扇区到0x9000,跳转后执行outside函数,显示字符'A'并进入无限循环,符合预期。
内容的提问来源于stack exchange,提问作者em3rald1
相关产品推荐
相关产品推荐

