C语言操作0xb8000显存地址写入字符无输出问题求解
问题原因
- 地址存储类型用错:
int长度随编译环境变化,在16位实模式(写引导程序、DOS程序的常见环境)下int只有16位,最大只能存储0xFFFF,而显存起始地址0xb8000是20位地址,存进int会直接截断为低16位的0x8000,访问的根本不是显存区域,自然没有输出。就算是32位环境,也不应该用int存储地址,必须用指针类型或者专门的地址类型。 - 颜色字节逻辑认知错误:VGA文本模式下每个字符占2字节空间,偶数偏移位置存储ASCII字符编码,奇数偏移位置存储属性控制字节,你之前往属性位写字符
'O'(ASCII值为0x4F)只是刚好凑出了一个合法属性值,不是正确的颜色设置方法。 - 编译器优化丢弃写入操作:你后面用
char*变量的版本没有输出,绝大多数情况是没有加volatile修饰指针,开启编译器优化后,编译器会判断你往这个指针写入的值从来没有被读取过,属于无效代码,直接把赋值语句删除了。你之前直接写硬编码地址*(char*)0xb8000 = 'O'能生效,是因为低优化等级下编译器不会随便删除硬编码地址的写入操作。 - 实模式寻址逻辑错误:如果是在16位实模式环境下运行,普通
char*是16位近指针,默认依赖DS段寄存器的值计算物理地址,如果DS没有设置为0xb800,哪怕指针值设为0,访问的也不是显存对应的物理地址。
VGA属性字节规则
属性字节共8位,从高位到低位的定义如下:
- 位7(最高位):字符闪烁开关,值为1时开启闪烁,为0时关闭
- 位6位4:背景色,取值07分别对应黑、蓝、绿、青、红、紫、棕、浅灰8种基础色
- 位3:前景高亮开关,值为1时使用亮色调前景,为0时使用普通色调
- 位2~位0(最低位):前景色,取值规则和背景色完全一致
常用属性参考:0x0F对应二进制00001111,即不闪烁、黑色背景、高亮白色前景,是最常用的默认显示样式。
正确实现
如果是32位保护模式环境(写32位内核的常见场景,段基址为0,32位指针可以直接访问全部物理地址),参考代码如下:
#include <stdint.h> #define VRAM_BASE 0xb8000 #define DEFAULT_ATTR 0x0F // 黑底高亮白字 void vga_print(const char* str) { // 加volatile关键字,禁止编译器优化显存写入操作 volatile uint8_t* vram_ptr = (volatile uint8_t*)VRAM_BASE; while (*str != '\0') { // 写入字符编码 *vram_ptr++ = (uint8_t)*str++; // 写入颜色属性 *vram_ptr++ = DEFAULT_ATTR; } }
如果是16位实模式环境,需要先把段寄存器设置为显存对应的段地址0xb800,再通过远指针访问显存,GCC 16位编译环境参考写法:
#include <stdint.h> void vga_init() { // 把ES段寄存器指向显存段 __asm__ volatile ("movw $0xb800, %%ax; movw %%ax, %%es" ::: "ax"); } void vga_print(const char* str) { // 远指针:高16位存段地址0xb800,低16位存起始偏移0 volatile uint8_t far* vram_ptr = (volatile uint8_t far*)0xb8000000; while (*str != '\0') { *vram_ptr++ = (uint8_t)*str++; *vram_ptr++ = 0x0F; } }
内容的提问来源于stack exchange,提问作者nico_qwer
相关产品推荐
相关产品推荐

