Qemu自制BIOS写入0xB8000显存无显示问题排查
问题
作为学术实践,我正在为Qemu编写一个简单的Hello World BIOS,已使Qemu识别显示初始化完成,但启动后仅显示黑屏,向字符视频内存0xB8000写入数据无效果。我已配置PCI插槽、设置VGA寄存器为80x25文本模式并启用显示,请问遗漏了什么?为何显存写入无法在屏幕呈现结果?以下是我目前编写的NASM汇编代码:
[BITS 16] times 4194304 - 2000 db 0x00 _jmpTo: ;Configure the VGA's PCI slot (hardcoded for now) mov eax, 0x80001004 mov dx, 0x0CF8 out dx, eax mov dx, 0x0CFC in eax, dx or eax, 0x0000037F and eax, 0xFFFFFFFE or eax, 0x00000002 mov dx, 0x0CFC out dx, eax mov eax, 0x80001008 mov dx, 0x0CF8 out dx, eax mov dx, 0x0CFC in eax, dx and eax, 0x0000FFFF or eax, 0x03000000 out dx, eax ;map BARS mov eax, 0x80001010 mov dx, 0x0CF8 out dx, eax mov dx, 0x0CFC in eax, dx or eax, 0xe0000000 out dx, eax mov eax, 0x80001018 mov dx, 0x0CF8 out dx, eax mov dx, 0x0CFC in eax, dx or eax, 0xe1000000 out dx, eax mov eax, 0x80001030 mov dx, 0x0CF8 out dx, eax mov dx, 0x0CFC in eax, dx or eax, 0xB0000001 out dx, eax ;Set VGA Config Registers via I/O mov dx, 0x03DA in eax, dx mov dx, 0x03C0 mov al, 0x10 out dx, al mov al, 0x0C out dx, al mov dx, 0x03DA in eax, dx mov dx, 0x03C0 mov al, 0x11 out dx, al mov al, 0x00 out dx, al mov dx, 0x03DA in eax, dx mov dx, 0x03C0 mov al, 0x12 out dx, al mov al, 0x0F out dx, al mov dx, 0x03DA in eax, dx mov dx, 0x03C0 mov al, 0x13 out dx, al mov al, 0x08 out dx, al mov dx, 0x03DA in eax, dx mov dx, 0x03C0 mov al, 0x14 out dx, al mov al, 0x00 out dx, al mov dx, 0x03C2 mov al, 0x67 out dx, al mov dx, 0x03C4 mov al, 0x00 out dx, al mov dx, 0x03C5 mov al, 0x01 out dx, al mov dx, 0x03C4 mov al, 0x01 out dx, al mov dx, 0x03C5 mov al, 0x00 out dx, al mov dx, 0x03C4 mov al, 0x02 out dx, al mov dx, 0x03C5 mov al, 0x03 out dx, al mov dx, 0x03C4 mov al, 0x04 out dx, al mov dx, 0x03C5 mov al, 0x0A out dx, al mov dx, 0x03C4 mov al, 0x00 out dx, al mov dx, 0x03C5 mov al, 0x03 out dx, al mov dx, 0x03CE mov al, 0x05 out dx, al mov dx, 0x03CF mov al, 0x00 out dx, al mov dx, 0x03CE mov al, 0x06 out dx, al mov dx, 0x03CF mov al, 0x03 out dx, al ;disable write protect mov dx, 0x03D4 mov al, 0x11 out dx, al mov dx, 0x03D5 mov al, 0x00 out dx, al ;crct mov dx, 0x03D4 mov al, 0x00 out dx, al mov dx, 0x03D5 mov al, 0x5F out dx, al mov dx, 0x03D4 mov al, 0x01 out dx, al mov dx, 0x03D5 mov al, 0x4F out dx, al mov dx, 0x03D4 mov al, 0x02 out dx, al mov dx, 0x03D5 mov al, 0x50 out dx, al mov dx, 0x03D4 mov al, 0x03 out dx, al mov dx, 0x03D5 mov al, 0x82 out dx, al mov dx, 0x03D4 mov al, 0x04 out dx, al mov dx, 0x03D5 mov al, 0x55 out dx, al mov dx, 0x03D4 mov al, 0x06 out dx, al mov dx, 0x03D5 mov al, 0xB5 out dx, al mov dx, 0x03D4 mov al, 0x07 out dx, al mov dx, 0x03D5 mov al, 0x0D out dx, al mov dx, 0x03D4 mov al, 0x08 out dx, al mov dx, 0x03D5 mov al, 0x00 out dx, al mov dx, 0x03D4 mov al, 0x09 out dx, al mov dx, 0x03D5 mov al, 0x0F out dx, al mov dx, 0x03D4 mov al, 0x0A out dx, al mov dx, 0x03D5 mov al, 0x0E out dx, al mov dx, 0x03D4 mov al, 0x0B out dx, al mov dx, 0x03D5 mov al, 0x0F out dx, al mov dx, 0x03D4 mov al, 0x0C out dx, al mov dx, 0x03D5 mov al, 0x00 out dx, al mov dx, 0x03D4 mov al, 0x0D out dx, al mov dx, 0x03D5 mov al, 0x00 out dx, al mov dx, 0x03D4 mov al, 0x0E out dx, al mov dx, 0x03D5 mov al, 0x00 out dx, al mov dx, 0x03D4 mov al, 0x0F out dx, al mov dx, 0x03D5 mov al, 0x00 out dx, al mov dx, 0x03D4 mov al, 0x10 out dx, al mov dx, 0x03D5 mov al, 0x90 out dx, al mov dx, 0x03D4 mov al, 0x11 out dx, al mov dx, 0x03D5 mov al, 0x8C out dx, al mov dx, 0x03D4 mov al, 0x12 out dx, al mov dx, 0x03D5 mov al, 0xAF out dx, al mov dx, 0x03D4 mov al, 0x13 out dx, al mov dx, 0x03D5 mov al, 0x28 out dx, al mov dx, 0x03D4 mov al, 0x14 out dx, al mov dx, 0x03D5 mov al, 0x07 out dx, al mov dx, 0x03D4 mov al, 0x15 out dx, al mov dx, 0x03D5 mov al, 0x9C out dx, al mov dx, 0x03D4 mov al, 0x16 out dx, al mov dx, 0x03D5 mov al, 0xE3 out dx, al mov dx, 0x03D4 mov al, 0x17 out dx, al mov dx, 0x03D5 mov al, 0xE3 out dx, al ;mode control mov dx, 0x03da in al, dx mov al, 0x10 mov dx, 0x03c0 out dx, al mov al, 0x20 out dx, al mov dx, 0x03C6 mov al, 0xFF out dx, al ;enable display mov dx, 0x03da in al, dx mov al, 0x20 mov dx, 0x03c0 out dx, al mov ax, 0xB800 mov es, ax mov di, 0x0000 mov al, 0x41 mov ah, 0x07 mov [es:di], ax _loop: jmp _loop hlt times (4194304 - ($-$$) - 16) db 0x00 _main: jmp _jmpTo _end: times (4194304 - ($-$$)) db 0x00
分析与解决
你的代码存在几个关键问题,导致显存写入无法显示:
- BIOS入口位置错误:Qemu会从BIOS ROM的最后16字节区域加载执行入口,而你的
_main标签放在了文件末尾的填充区之后,实际执行的是前面的空数据,而非初始化代码。必须将入口跳转指令放在ROM最后16字节的范围内。 - 缺少BIOS ROM签名:标准BIOS ROM的最后两个字节必须是
0x55AA,这是Qemu识别有效BIOS的标志,缺失会导致代码无法正确执行。 - VGA寄存器手动配置不可靠:硬编码所有VGA寄存器容易出现配置冲突或遗漏,更稳妥的方式是调用VGA BIOS中断
INT 0x10来设置80x25文本模式,该中断会自动完成所有必要的寄存器初始化。
修正后的代码
[BITS 16] ORG 0x0000 ; BIOS ROM起始地址 ; 主入口:跳转到初始化代码 _main: jmp _start ; 填充到ROM最后16字节前的位置,确保入口在正确区域 times (4194304 - 16 - ($-$$)) db 0x00 _start: ; 调用VGA BIOS中断设置80x25彩色文本模式 mov ah, 0x00 mov al, 0x03 int 0x10 ; 写入字符'A'到显存起始位置(黑底白字) mov ax, 0xB800 mov es, ax mov di, 0x0000 mov al, 'A' mov ah, 0x07 mov [es:di], ax ; 设置光标到屏幕左上角(0,0) mov ah, 0x02 mov bh, 0x00 ; 第0页 mov dh, 0x00 ; 行号 mov dl, 0x00 ; 列号 int 0x10 ; 无限循环 _loop: jmp _loop hlt ; 填充剩余空间并添加BIOS签名(最后两个字节必须为0x55AA) times (4194304 - 2 - ($-$$)) db 0x00 dw 0xAA55 ; 小端序存储,对应内存最后两字节为0x55AA
内容的提问来源于stack exchange,提问作者Matthias Birkemeier
相关产品推荐
相关产品推荐

