为什么Stage 2在QEMU中正常运行,在真实硬件上却无响应?
问题排查与修复方案
1. 驱动器号硬编码错误
QEMU常将USB镜像模拟为软盘(驱动器号0x00),但真实硬件中绝大多数BIOS会把USB盘识别为硬盘,驱动器号从0x80开始。你代码里硬写dl=0x00,导致真实硬件上读的是软盘而非USB盘——虽然USB灯闪可能是BIOS在尝试访问软盘,但实际并未读取到USB盘上的Stage2扇区。
修复:
删除硬编码的mov dl, 0x00,BIOS启动引导扇区时会将当前驱动器号存入dl寄存器,直接使用这个值即可:
mov ah, 0x02 mov al, 1 mov ch, 0 mov cl, 2 mov dh, 0 ; 移除 mov dl, 0x00,保留BIOS传入的驱动器号 mov bx, stage_two int 13h
2. CHS磁盘几何参数不兼容
代码中使用的是1.44M软盘的CHS参数(每磁道18扇区、2磁头),但真实USB盘的CHS参数通常与软盘差异极大,依赖CHS模式的int 13h (ah=0x02)会无法正确定位Stage2扇区。
修复:
优先改用LBA模式的磁盘读取(ah=0x42),该模式兼容性更强,无需依赖硬件特定的CHS参数,支持绝大多数存储设备。若需兼容老BIOS,可先检测LBA支持性,再选择对应读取方式。
3. 段寄存器与跳转地址可靠性问题
部分BIOS会将引导扇区加载到0x07C0:0000而非0x0000:7C00,此时org 0x7C00的段内跳转可能出错;另外,代码未显式设置es寄存器,部分BIOS默认es值不为0,会导致读盘目标地址错误。
修复:
- 显式设置
es=0,确保读盘地址正确:xor ax, ax mov ds, ax mov es, ax ; 添加此行,将es寄存器置0 - 使用远跳转指定段地址,避免CS寄存器值不确定的问题:
; 替换原有的 jmp stage_two jmp 0x0000:stage_two
4. INT 10h输出参数不全
Stage2中调用int 10h (ah=0xE)时未设置bh(视频页号)和bl(字符颜色),QEMU默认值可正常工作,但部分真实硬件BIOS需要显式指定这些参数才能输出字符。
修复:
在Stage2中补充必要参数:
stage_two: mov ah, 0Eh mov bh, 0x00 ; 指定视频页0 mov bl, 0x07 ; 黑底白字(标准文本颜色) mov al, 'A' int 10h jmp $
5. 未验证扇区加载结果
即使int 13h未置CF标志,也不代表数据正确写入目标地址。可添加简单校验,确保Stage2确实被加载。
修复:
- 在Stage2开头添加特征字节:
stage_two: db 0xAA ; 加载校验用特征字节 mov ah, 0Eh ... - 读盘后检查特征字节:
int 13h jc on_error ; 验证Stage2是否正确加载 cmp byte [stage_two], 0xAA jne on_error
修改后的完整代码
org 0x7c00 bits 16 boot: jmp main TIMES 3-($-$$) DB 0x90 ; Support 2 or 3 byte encoded JMPs before BPB. ; Dos 4.0 EBPB 1.44MB floppy OEMname: db "mkfs.fat" ; mkfs.fat is what OEMname mkdosfs uses bytesPerSector: dw 512 sectPerCluster: db 1 reservedSectors: dw 1 numFAT: db 2 numRootDirEntries: dw 224 numSectors: dw 2880 mediaType: db 0xf0 numFATsectors: dw 9 sectorsPerTrack: dw 18 numHeads: dw 2 numHiddenSectors: dd 0 numSectorsHuge: dd 0 driveNum: db 0 reserved: db 0 signature: db 0x29 volumeID: dd 0x2d7e5a1a volumeLabel: db "NO NAME " fileSysType: db "FAT12 " main: xor ax, ax mov ds, ax mov es, ax ; 显式设置es为0 ; Pause for making stable the reading... mov ah, 0x00 int 16h mov ah, 0x02 mov al, 1 mov ch, 0 mov cl, 2 mov dh, 0 ; 保留BIOS传入的驱动器号,不硬编码dl mov bx, stage_two int 13h jc on_error ; 验证Stage2是否正确加载 cmp byte [stage_two], 0xAA jne on_error ; 远跳转到Stage2,确保段地址正确 jmp 0x0000:stage_two printer: mov ah, 0x0E mov bh, 0x00 ; 补充视频页参数 mov bl, 0x07 ; 补充颜色参数 .prntloop: lodsb or al,al jz .done int 10h jmp .prntloop .done: ret on_error: mov si, errorstr call printer jmp $ errorstr db "Error.", 0 times 510-($-$$) db 0 dw 0xAA55 stage_two: db 0xAA ; 加载校验特征字节 mov ah, 0Eh mov bh, 0x00 mov bl, 0x07 mov al, 'A' int 10h jmp $ times 1024-($-$$) db 0
内容的提问来源于stack exchange,提问作者Saveriokarol

