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

编写二级引导加载器时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

火山引擎 最新活动