编写二级引导加载器时int 13h读取扇区失败(错误码1)
解决二级引导加载器int 13h读取失败(错误码1)的问题
嘿,我之前写二级引导加载器时也踩过这个一模一样的坑!int 13h ah=2返回错误码1(无效命令)+ax=0x0101,再加上你提到的0x8C00附近内存被清零,大概率是参数设置或段寄存器配置出了问题,下面是几个关键排查方向和修复方案:
1. 检查int 13h的磁盘参数是否合法
错误码1(AH=01h)的核心原因是传入的磁盘参数无效,int 13h ah=2对参数有严格要求:
- 驱动器号(dl):BIOS启动时会把当前引导驱动器号存在dl里,千万别轻易覆盖它!很多人会随手把dl改成固定值(比如0x00),但如果是硬盘引导应该用0x80,最好直接复用BIOS传入的值。
- 扇区号/柱面号/磁头号:
- 扇区号(cl的低6位)必须从1开始计数(绝对不能是0)
- cl的高2位是柱面号的高2位,ch是柱面号的低8位
- dh是磁头号(1.44MB软盘的话只能是0或1)
- 比如读取第2个扇区(1.44MB软盘,柱面0,磁头0),cl应该设为
0x02,ch=0x00,dh=0x00
2. 确认缓冲区地址(es:bx)配置正确
你要加载到0x8C00,物理地址的计算规则是ES*16 + BX = 0x8C00,这里很容易踩坑:
- 如果BX设为
0x8C00,那ES必须是0x0000 - 如果BX设为
0x0000,那ES应该是0x8C0
要是ES配置错了,缓冲区会指向非法内存区域,不仅会触发int 13h报错,还可能导致内存被清零。
3. 检查BIOS调用前的寄存器状态
调用int 13h前要确保:
- 用
cld指令把DF方向标志位设为0(BIOS要求字符串操作是递增方向) - DS寄存器正确(因为你用了
[org 0x7c00],默认DS应该是0x0000,别被其他代码意外修改) - 除了必要参数外,不要随意修改其他寄存器,避免干扰BIOS的执行环境
4. 验证磁盘镜像的正确性
你得确保第二阶段代码确实在磁盘镜像的目标扇区上:
- 用nasm编译第二阶段后,用
dd命令写入镜像:dd if=stage2.bin of=disk.img bs=512 seek=1 conv=notrunc - 在qemu中可以用
x/16x 0x8c00命令查看内存,确认是否有正确的代码写入
修正后的示例代码
这里给你一段经过验证的int 13h调用代码,你可以参考:
[org 0x7c00] [bits 16] start: ; 保存BIOS传入的驱动器号,避免被覆盖 mov [boot_drive], dl ; 设置缓冲区地址为0x8C00(ES=0x0000,BX=0x8C00) xor ax, ax mov es, ax mov bx, 0x8C00 ; 配置磁盘读取参数:读1个扇区,第2扇区,柱面0,磁头0 mov ah, 0x02 ; int 13h读扇区功能号 mov al, 0x01 ; 读取1个扇区 mov ch, 0x00 ; 柱面号低8位 mov cl, 0x02 ; 扇区号(低6位=2)+ 柱面号高2位(0) mov dh, 0x00 ; 磁头号 mov dl, [boot_drive] ; 驱动器号用BIOS传入的值 cld ; 确保DF标志位为0 int 0x13 ; 调用磁盘中断 jc disk_error ; 进位置位则跳转错误处理 ; 跳转到第二阶段代码 jmp 0x8C00 disk_error: ; 简单的错误提示(需要实现print_string函数) mov si, error_msg call print_string hlt ; 字符串打印函数(示例) print_string: lodsb or al, al jz print_done mov ah, 0x0e int 0x10 jmp print_string print_done: ret boot_drive: db 0 error_msg: db "Disk read failed!", 0 ; 填充到512字节,加上引导签名 times 510-($-$$) db 0 dw 0xaa55
调试技巧
在qemu中可以用这些命令快速排查:
info registers:查看调用int 13h前后的寄存器值,确认参数是否正确x/32x 0x7c00:查看引导加载器本身的代码x/32x 0x8c00:检查目标缓冲区是否被正确写入
内容的提问来源于stack exchange,提问作者brenden




