无法从带有变量的 C 代码写入视频内存

问题描述

所以我一直在尝试编写一个基本的内核,并且达到了写入屏幕的程度。

我可以很容易地从 Assembly 中做到这一点,但不能从 C 中做到。以下是从 Assembly 写入屏幕的代码

[bits 32]

VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x10

print_string_pm:
  pusha
  mov edx,VIDEO_MEMORY

print_string_pm_loop:
  mov al,[ebx]
  mov ah,WHITE_ON_BLACK

  cmp al,0
  je print_string_pm_done

  mov [edx],ax

  add ebx,1
  add edx,2

  jmp print_string_pm_loop

print_string_pm_done:
  popa
  ret

这工作得很好,但是从 C 代码开始尝试:

#define VIDEO_MEMORY 0xb8000
#define WHITE_ON_BLACK 0x0f

void main(void) {
    char* video_memory = (char*) VIDEO_MEMORY;

    *video_memory = 'H';
    *(video_memory + 2) = WHITE_ON_BLACK; 
    *(video_memory + 3) = 'e';
    *(video_memory + 5) = 'l';
    *(video_memory + 7) = 'l';
    *(video_memory + 9) = 'o';
    *(video_memory + 11) = ' ';
    *(video_memory + 13) = 'W';
    *(video_memory + 15) = 'o';
    *(video_memory + 17) = 'r';
    *(video_memory + 19) = 'l';
    *(video_memory + 21) = 'd';
}

这有效但效率不高,然后当我尝试声明一个字符串时:

void main(void) {
    char* video_memory = (char*) VIDEO_MEMORY;
    char* str = "Hello World!";

    *video_memory = str[0];
    *(video_memory + 2) = WHITE_ON_BLACK;
    video_memory += 3;

    for (int i = 0; str[i] != '\0'; i++) {
        *(video_memory) = str[i];
        video_memory += 2;
    }
}

它只会给我一些奇怪的东西(比如空格或希腊字母)。

qemu output

我的Makefile

C_SOURCES = $(wildcard src/*.c)
HEADERS = $(wildcard headers/*.h)

OBJ = ${C_SOURCES:.c=.o}

all: build run

boot_sect.bin:bootloader/main.asm bootloader/gdt.asm bootloader/print_string_pm.asm bootloader/printf.asm bootloader/switch_to_pm.asm bootloader/readdisk.asm 
    nasm -i./bootloader/ -f bin bootloader/main.asm -o $@

clean:
    rm -f *.bin **/*.o

clean_src: 
    rm -f src/*.o

run: image 
    qemu-system-x86_64 $< 

%.o: src/%.c ${HEADERS}
    gcc -I../headers/ -ffreestanding  -c $< -o build/$@

%.o: bootloader/%.asm
    nasm $<  -f elf64 -o build/$@

kernel.bin: ${OBJ} build/kernel_entry.o
    ld -o $@ -T link.ld $^ --oformat binary 

image: boot_sect.bin kernel.bin
    cat $^ > image

build: clean boot_sect.bin kernel.o kernel_entry.o kernel.bin image

我尝试过的事情:

这会为字母 H 创建一个空白空间,但该字母不存在并且该空间是 红色

typedef struct char_with_attribute {
    char ch,attribute;
} char_with_attribute;

char_with_attribute* convert_to_struct(char,char);

void main(void) {
    volatile char_with_attribute *video_memory = (void*)VIDEO_MEMORY;
    char* str = "Hello World!";

    *video_memory = *convert_to_struct('H',(char)0x0f);
}

char_with_attribute* convert_to_struct(char ch,char attr) {
    char_with_attribute* combination;

    combination->ch = ch;
    combination->attribute = attr;

    return combination;
}

解决方法

看起来您正在关注 cfenollosaos-tutorial,或者至少偶然发现了其内容的副本,因为您的汇编代码片段来自 chapter 08。>

简单地说,你一个一个字节地写你的角色和属性,每次增加你的视频内存偏移量。一个粗略的例子:

const char *s = "Hello world";
char *base = (void *) 0xb8000;

while (*s) {
    *base++ = *s++;
    *base++ = 0x0f;
}

Chapter 15 涵盖使用处理光标位置的 VGA I/O 端口。

Chapter 16 引入了许多在 VGA 文本模式下写入屏幕的高级函数,其中许多专门用于处理 80x25 boundaries