VESA图形模式下绘制像素失败求助:int 10h 0ch函数无效
如何在VESA图形模式下绘制像素?
让我来帮你分析一下代码里的问题,以及如何解决它:
问题根源
你尝试使用传统VGA BIOS的INT 10h AH=0Ch函数来绘制像素,但这个方法在VESA图形模式下几乎行不通,原因有两个:
- BIOS函数兼容性问题:传统的
INT 10h图形函数是为老旧的VGA模式设计的,绝大多数VESA扩展模式(尤其是线性帧缓冲模式)并不支持通过BIOS中断来操作像素。VESA的设计初衷就是让程序直接访问显存,而非依赖BIOS层的封装。 - VESA模式设置的缺失:你的代码中设置VESA模式时,没有启用线性帧缓冲(这是直接操作显存的关键)。即使某些VESA模式支持BIOS像素函数,分段式显存的管理也会带来额外的复杂度,远不如直接操作线性显存高效。
解决方案:直接操作VESA线性帧缓冲
正确的做法是获取VESA模式的显存基地址,然后直接计算像素的显存位置并写入颜色值。下面是修改后的完整代码(NASM语法,可在QEMU中测试):
首先定义VESA模式信息结构,用于获取显存参数:
struc VbeModeInfoBlock .ModeAttrs resw 1 .WinAAttrs resb 1 .WinBAttrs resb 1 .WinGranularity resw 1 .WinSize resw 1 .WinASegment resw 1 .WinBSegment resw 1 .WinFuncPtr resd 1 .BytesPerScanLine resw 1 .XResolution resw 1 .YResolution resw 1 .XCharSize resb 1 .YCharSize resb 1 .NumberOfPlanes resb 1 .BitsPerPixel resb 1 .NumberOfBanks resb 1 .MemoryModel resb 1 .BankSize resb 1 .NumberOfImagePages resb 1 .Reserved0 resb 1 .RedMaskSize resb 1 .RedFieldPos resb 1 .GreenMaskSize resb 1 .GreenFieldPos resb 1 .BlueMaskSize resb 1 .BlueFieldPos resb 1 .RsvdMaskSize resb 1 .RsvdFieldPos resb 1 .DirectColorModeInfo resb 1 .PhysBasePtr resd 1 .Reserved1 resd 1 .Reserved2 resw 1 .Reserved3 resb 1 .Reserved4 resb 39 endstruc
主引导代码:
org 0x7c00 ; 初始化段寄存器和栈 xor ax, ax mov ds, ax mov es, ax mov ss, ax mov sp, 0x7c00 ; 获取VESA模式0x105(1024x768 8位色)的信息 mov ax, 0x4F01 mov cx, 0x105 mov di, vbe_mode_info int 0x10 cmp ax, 0x4F jne error_handler ; 如果VESA不支持该模式,跳转到错误处理 ; 设置VESA模式,启用线性帧缓冲(BX最高位设为0x8000) mov ax, 0x4F02 mov bx, 0x8105 ; 0x105 | 0x8000,启用线性帧缓冲 int 0x10 cmp ax, 0x4F jne error_handler ; 计算(2,3)位置像素的显存地址 mov eax, [vbe_mode_info + VbeModeInfoBlock.PhysBasePtr] ; 显存基地址 mov ecx, 3 ; Y坐标(行) movzx edx, word [vbe_mode_info + VbeModeInfoBlock.BytesPerScanLine] ; 每行字节数 mul edx ; EAX = Y * 每行字节数 add eax, 2 ; 加上X坐标(8位色,每个像素占1字节) mov byte [eax], 0x05 ; 写入颜色值(对应你之前的00000101b) ; 无限循环 jmp $ ; 简单错误处理:显示'E'字符 error_handler: mov ah, 0x0E mov al, 'E' mov bh, 0 int 0x10 jmp $ ; 存储VESA模式信息的缓冲区 vbe_mode_info: istruc VbeModeInfoBlock iend ; 填充引导扇区到512字节 times 510-($-$$) db 0 dw 0xaa55
关键细节说明
- 线性帧缓冲启用:设置VESA模式时,BX的最高位(0x8000)必须置1,这样才能获得一个连续的32位显存地址,无需处理分段显存的银行切换。
- 像素地址计算:必须使用
BytesPerScanLine而非屏幕宽度来计算行偏移,因为某些VESA模式会在扫描线末尾添加填充字节,确保每行字节数对齐到特定边界。 - 颜色值:对于8位色模式(如0x105),颜色值是0-255的索引,对应VESA模式的调色板。你可以通过
INT 10h AH=10h系列函数来设置调色板颜色。
内容的提问来源于stack exchange,提问作者Juan
相关产品推荐
相关产品推荐

