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

使用BIOS INT 13h ah=42h读取USB在真机失效的问题求助

我来帮你拆解这个真实PC和QEMU差异的坑——我之前做自制OS时也踩过类似的BIOS兼容性雷,咱们一步步捋清楚:

核心问题分析

QEMU对BIOS调用的容错性极高,但真实PC的BIOS(尤其是老型号或USB启动场景下)对参数的严谨性要求远高于模拟器。你遇到的「仅能读512字节、目标内存无写入」,大概率是这几个环节出了问题:

1. LBA数据包(DAP)的参数错误

int 0x13, ah=0x42完全依赖DAP结构的正确性,真实BIOS几乎不会容错:

  • DAP长度必须严格为0x10(16字节):很多新手会不小心把长度写成其他值,QEMU可能忽略,但真实BIOS直接拒绝执行。
  • 内存地址的段:偏移必须正确:如果你的目标内存是0x8000:0x0000,必须把DAP里的dw 0x0000(偏移)和dw 0x8000(段)填对,不能把线性地址直接塞进偏移字段。
  • 扇区数不要超过BIOS限制:部分老BIOS单次最多支持读64扇区,如果你一次性读太多,会直接失败,只能读取前512字节(单扇区)。
  • LBA地址不能越界:要确保你读取的LBA地址在USB磁盘的实际扇区范围内,真实BIOS不会像QEMU那样自动截断。

2. 未检查BIOS扩展功能支持

很多人跳过这一步直接调用ah=0x42,但真实PC的BIOS可能不支持扩展LBA命令:

  • 必须先调用int 0x13, ah=0x41,传入bx=0xaa55验证扩展功能是否可用。
  • 还要检查返回的cx寄存器第0位是否置位,确认LBA模式被支持。如果不支持,就得降级用CHS模式读取。

3. BPB结构参数不正确

你的MBR里包含BPB(BIOS参数块),真实BIOS会读取这些参数识别磁盘类型,QEMU则可能默认使用通用参数:

  • 比如BPB_BytsPerSec必须是512(USB磁盘的标准扇区大小),BPB_TotSec32要填磁盘的总扇区数,BS_DrvNum(驱动器号)在真实PC里可能不是0x80(0x80是本地硬盘,USB盘可能是0x81/0x82等)。

4. USB磁盘的BIOS兼容性问题

真实PC的USB启动场景下,BIOS对int 0x13的处理有特殊情况:

  • 部分老BIOS的扩展LBA命令只支持本地硬盘,不支持USB存储设备。
  • USB设备初始化需要时间,可能要加一段延迟循环再执行读操作,避免BIOS还没识别到设备就调用命令。
修复步骤与示例代码

先给你一个加了完整错误检查的示例片段,你可以对照修改:

[bits 16]
[ORG 0x7c00]

; 正确填充BPB结构(适配USB磁盘的标准参数)
BS_jmpBoot:    jmp short entry
               nop
BS_OEMName:    db "HARIBOTE"
BPB_BytsPerSec:dw 512        ; 扇区大小固定512字节
BPB_SecPerClus:db 1
BPB_RsvdSecCnt:dw 1
BPB_NumFATs:   db 2
BPB_RootEntCnt:dw 224
BPB_TotSec16:  dw 0          ; 用32位总扇区数替代
BPB_Media:     db 0xf0
BPB_FATSz16:   dw 9
BPB_SecPerTrk:dw 63         ; 多数USB磁盘的磁道扇区数
BPB_NumHeads:  dw 255       ; 多数USB磁盘的磁头数
BPB_HiddSec:   dd 0
BPB_TotSec32:  dd 0x100000  ; 示例:1GB磁盘的总扇区数
BS_DrvNum:     db 0x81       ; 真实PC中USB盘常用驱动器号
BS_Reserved1:  db 0
BS_BootSig:    db 0x29
BS_VolID:      dd 0x12345678
BS_VolLab:     db "HARIBOTEOS "
BS_FileSysType:db "FAT12   "

entry:
    mov ax, 0
    mov es, ax
    mov ds, ax
    mov ss, ax
    mov sp, 0x7c00

    ; 第一步:检查BIOS扩展功能支持
    mov ah, 0x41
    mov bx, 0xaa55
    mov dl, [BS_DrvNum]
    int 0x13
    jc ext_not_supported      ; CF置位=不支持
    cmp bx, 0xaa55
    jne ext_not_supported
    test cx, 0x0001
    jz lba_not_supported      ; 无LBA支持

    ; 第二步:设置正确的DAP结构
    mov si, dap
    mov ah, 0x42
    mov dl, [BS_DrvNum]
    int 0x13
    jc read_error             ; CF置位=读取失败

    ; 读取成功,跳转到目标内存执行
    jmp 0x8000:0x0000

; 错误处理分支
ext_not_supported:
    mov si, msg_ext_fail
    call print_str
    jmp $

lba_not_supported:
    mov si, msg_lba_fail
    call print_str
    jmp $

read_error:
    mov al, ah                ; AH中是BIOS返回的错误码
    call print_hex
    jmp $

; 辅助函数:打印字符串
print_str:
    mov ah, 0x0e
.print_loop:
    lodsb
    or al, al
    jz .print_done
    int 0x10
    jmp .print_loop
.print_done:
    ret

; 辅助函数:打印十六进制错误码
print_hex:
    push bx
    mov cx, 2
    mov bx, 16
.hex_loop:
    mov dx, 0
    div bx
    cmp dl, 10
    jl .hex_digit
    add dl, 0x27
.hex_digit:
    add dl, 0x30
    push dx
    dec cx
    jnz .hex_loop
.hex_out:
    pop dx
    mov ah, 0x0e
    int 0x10
    cmp sp, 0x7c00
    jne .hex_out
    pop bx
    ret

; 标准LBA DAP结构
dap:
    db 0x10        ; DAP长度(必须是16)
    db 0x00        ; 保留字段
    dw 0x0002      ; 读取2个扇区(可根据需求调整,建议单次不超64)
    dw 0x0000      ; 目标内存偏移
    dw 0x8000      ; 目标内存段
    dq 0x00000001  ; LBA地址(从第2扇区开始读,LBA从0计数)

; 错误提示字符串
msg_ext_fail: db "BIOS ext not supported", 0
msg_lba_fail: db "LBA not supported", 0

times 510-($-$$) db 0
dw 0xaa55
额外调试建议
  1. 打印BIOS错误码:调用int 0x13后如果CF置位,AH里的错误码能直接告诉你问题(比如0x01=无效命令,0x04=扇区未找到),比只看内存更高效。
  2. 枚举驱动器号:真实PC中USB盘的驱动器号不一定是0x80,可以循环尝试0x80、0x81、0x82等,用int 0x13, ah=0x00重置驱动器,找到能成功的那个。
  3. 缩小测试范围:先尝试只读1个扇区,确认能写入目标内存后,再逐步增加扇区数,排查是否是扇区数限制问题。

内容的提问来源于stack exchange,提问作者石原秀一

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 06:34:11