为什么将字符传递给函数会改变它';用c表示的s值?

为什么将字符传递给函数会改变它';用c表示的s值?,c,assembly,x86,qemu,osdev,C,Assembly,X86,Qemu,Osdev,我目前正在开发一个操作系统 我的意图是编写一个64位内核。我已经加载了“内核”代码,并在文本模式下将单个字符写入帧缓冲区 当我通过将代码包装到函数中向帧缓冲区写入单个字符时,我的问题出现了。传递到函数中的char值似乎以某种方式被损坏 我有三个文件: bootloader.asm bootkernel.asm kernel.c text部分链接到从0x1000开始,引导加载程序希望内核从这里开始 linker.ld脚本为 SECTIONS { . = 0x1000; .text

我目前正在开发一个操作系统

我的意图是编写一个64位内核。我已经加载了“内核”代码,并在文本模式下将单个字符写入帧缓冲区

当我通过将代码包装到函数中向帧缓冲区写入单个字符时,我的问题出现了。传递到函数中的char值似乎以某种方式被损坏

我有三个文件:

bootloader.asm bootkernel.asm kernel.c text部分链接到从0x1000开始,引导加载程序希望内核从这里开始

linker.ld脚本为

SECTIONS
{
    . = 0x1000;

    .text : { *(.text) } /* Kernel is expected at 0x1000 */
}
将这一切放在一起的Make文件是:

bootloader.bin: bootloader.asm
    nasm -f bin bootloader.asm -o bootloader.bin

bootkernel.o: bootkernel.asm
    nasm -f elf64 bootkernel.asm -o bootkernel.o

kernel.o: kernel.c
    gcc-6 -Wextra -Wall -ffreestanding -c kernel.c -o kernel.o

kernel.bin: bootkernel.o kernel.o linker.ld
    ld -o kernel.bin -T linker.ld bootkernel.o kernel.o --oformat binary

os-image: bootloader.bin kernel.bin
    cat bootloader.bin kernel.bin > os-image

qemu: os-image
    qemu-system-x86_64 -d guest_errors -fda os-image -boot a
我已经拍摄了我得到的输出的屏幕截图。我希望“A”出现在第1行的第0列中,“B”出现在第0行的第1列中。出于某种原因,我得到了另一个角色

gcc-6-S kernel.c的输出
如果代码修改为:

unsigned int offset = frame_buffer_offset(0, 1);
write_memory(FRAME_BUFFER_ADDRESS, offset, 'A');
write_memory(FRAME_BUFFER_ADDRESS, offset + 1, GREY_ON_BLACK);

write_frame_buffer_cell('B', GREY_ON_BLACK, 1, 0);
差异出现在最后一行
('B',黑色上的灰色,1,0)。最初你有
('B',灰色,黑色,0,1)。这与你所描述的你当时所说的是一致的:

我已经拍摄了我得到的输出的屏幕截图。我希望“A”出现在第1行的第0列中,“B”出现在第0行的第1列中

我想你可能在这个问题上发布了错误的代码。这是我得到的输出:


看来你对操作系统开发还不熟悉。引导加载程序代码仅将CPU置于32位保护模式,但要运行64位内核,需要处于64位长模式。如果您刚刚开始,我建议您回到编写32位内核,以便在早期阶段进行学习。在底部,我有一个64位长模式部分,其中有一个到长模式教程的链接,可以用来修改引导加载程序以运行64位代码


导致异常行为的主要问题 您遇到的问题主要与以下事实有关:您正在使用GCC生成64位代码,但您正在根据引导加载程序代码以32位保护模式运行它。在32位保护模式下运行的64位代码生成看起来可能会执行,但它不会正确执行。在简单的操作系统中,如果您只是向视频显示器显示,您可能会经常看到意外的输出作为副作用。您的程序可能会使机器出现三重故障,但不幸的是,副作用似乎会在视频显示器上显示出来。你可能有一种错误的印象,认为事情本来就应该正常运转,而实际上却没有

这个问题与另一个问题有些相似。在这个问题的原始海报提供了一个完整的例子之后,很明显这是他的问题。我对他解决问题的部分答复如下:

未定义行为的可能原因

在Edit2中提供了所有代码和make文件之后,一个明显的问题是大部分代码都被编译并链接到64位对象和可执行文件。该代码在32位保护模式下无法工作

在make文件中进行以下调整:

  • 使用GCC编译时,需要添加
    -m32
    选项
  • 当使用GNU汇编程序(as)针对32位对象进行汇编时,您需要使用
    --32
  • 与LD链接时,需要添加
    -melf_i386
    选项
  • 当以32位对象为目标装配NASM时,需要将
    -f elf64
    更改为
    -f elf32
记住这一点,您可以更改
Makefile
以生成32位代码。它可能看起来像:

bootloader.bin: bootloader.asm
    nasm -f bin bootloader.asm -o bootloader.bin

bootkernel.o: bootkernel.asm
    nasm -f elf32 bootkernel.asm -o bootkernel.o

kernel.o: kernel.c
    gcc-6 -m32 -Wextra -Wall -ffreestanding -c kernel.c -o kernel.o

kernel.bin: bootkernel.o kernel.o linker.ld
    ld -melf_i386 -o kernel.bin -T linker.ld bootkernel.o kernel.o --oformat binary

os-image: bootloader.bin kernel.bin
    cat bootloader.bin kernel.bin > os-image

qemu: os-image
    qemu-system-x86_64 -d guest_errors -fda os-image -boot a
我推测,当您的代码出现问题时,您最终尝试使用0xb8002作为视频内存的地址。它应该是0xb8000。您需要修改:

#define FRAME_BUFFER_ADDRESS 0xb8002
将是:

#define FRAME_BUFFER_ADDRESS 0xb8000
进行所有这些更改应该可以解决您的问题。这就是我在上述更改后得到的输出:


其他意见 在
写入内存中
使用:

unsigned char * memory = (unsigned char *) address;
由于您使用的0xb8000是映射到视频显示的内存,因此您应该将其标记为
volatile
,因为编译器可能会在不知道写入该内存(即在显示器上显示字符)有副作用的情况下进行优化。您可能希望使用:

volatile unsigned char * memory = (unsigned char *) address;

bootloader.asm
中,确实应该显式地将A20行设置为on。您可以在下面的列表中找到有关这样做的信息。在引导加载程序开始执行时,A20行的状态可能因模拟器而异。如果您试图访问奇数MB边界上的内存区域(如0x100000到0x1FFFF、0x300000到0x1FFFF等),如果设置失败,可能会导致问题。对奇数MB内存区域的访问实际上将从其正下方的偶数内存区域读取数据。这通常不是你想要的行为


64位长模式
如果要运行64位代码,需要将处理器置于64位长模式。这比进入32位保护模式要复杂一些。有关64位longmode的信息,请参阅。一旦正确进入64位长模式,您就可以使用GCC生成的64位指令。

在这两种情况下,当您将
i
0
传递到
col
row
时,为什么您希望看到A在(0,1)和B在(1,0)处?@WeatherVane是。最后,OP没有发布足够的代码来复制问题。真的,我会认为代码太多了,但也许它不是复制问题的正确代码?除非我误读了这一点,否则你的主要问题实际上是
#define FRAME_BUFFER_ADDRESS 0xb8002
#define FRAME_BUFFER_ADDRESS 0xb8000
unsigned char * memory = (unsigned char *) address;
volatile unsigned char * memory = (unsigned char *) address;