Why does passing a char to a function change it's value in c?

梦想与她 提交于 2019-11-27 16:19:32
Michael Petch

I can reproduce your exact output if the code is modified to be:

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);

The difference being in the last line ('B', GREY_ON_BLACK, 1, 0);. Originally you had ('B', GREY_ON_BLACK, 0, 1); . This is in line with what you described you were trying to do when you said:

I've taken a screen shot of the output that I am getting. I expect 'A' to appear in the 0th column of the 1st row and for 'B' to appear on the 1st column of the 0th row.

I gather you may have posted the wrong code in this question. This is the output I get:


It seems you are new to OS development. Your bootloader code only places the CPU into 32-bit protected mode, but to run a 64-bit kernel you need to be in 64-bit longmode. If you are just getting started I'd suggest falling back to writing a 32-bit kernel for purposes of learning at this early stage. At the bottom I have a 64-bit long mode section with a link to a longmode tutorial that could be used to modify your bootloader to run 64-bit code.


Primary Issue Causing Unusual Behaviour

You are experiencing an issue primarily related to the fact that you are generating 64-bit code with GCC but you are running it in 32-bit protected mode according to your bootloader code. 64-bit code generation running in 32-bit protected mode may appear to execute, but it will do it incorrectly. In simple OSes where you are simply displaying to the video display you may often see unexpected output as a side effect. Your program could triple fault the machine, but you got unlucky that the side effect seemed to display something on the video display. You may have been under the false impression that things were working as they should when they really weren't.

This question is somewhat similar to another Stackoverflow question. After the original poster of that question made available a complete example it became clear that it was his issue. Part of my answer to him to resolve the issue was as follows:

Likely Cause of Undefined Behavior

After all the code and the make file were made available in EDIT 2 it became clear that one significant problem was that most of the code was compiled and linked to 64-bit objects and executables. That code won't work in 32-bit protected mode.

In the make file make these adjustments:

  • When compiling with GCC you need to add -m32 option
  • When assembling with GNU Assembler (as) targeting 32-bit objects you need to use --32
  • When linking with LD you need to add the -melf_i386 option
  • When assembling with NASM targeting 32-bit objects you need to change -f elf64 to -f elf32

With that in mind you can alter your Makefile to generate 32-bit code. It could look like:

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

I gather when you started having issues with your code you ended up trying 0xb8002 as the address for your video memory. It should be 0xb8000. You'll need to modify:

#define FRAME_BUFFER_ADDRESS 0xb8002

To be:

#define FRAME_BUFFER_ADDRESS 0xb8000

Making all these changes should resolve your issues. This is what the output I got looked like after the changes mentioned above:


Other observations

In write_memory you use:

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

Since you are using 0xb8000 that is memory mapped to the video display you should mark it as volatile since a compiler could optimize things away not knowing that there is a side effect to writing to that memory (namely displaying characters on a display). You might wish to use:

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

In your bootloader.asm You really should explicitly set the A20 line on. You can find information about doing that in this OSDev Wiki article. The status of the A20 line at the point a bootloader starts executing may vary between emulators. Failure to set it on could cause issues if you try to access memory areas on an odd numbered megabyte boundary (like 0x100000 to 0x1fffff, 0x300000 to 0x1fffff etc). Accesses to the odd numbered megabyte memory regions will actually read data from the even numbered memory region just below it. This is usually not behaviour you want.


64-bit long mode

If you want to run 64-bit code you will need to place the processor into 64-bit long mode. This is a bit more involved than entering 32-bit protected mode. Information on 64-bit longmode can be found in the OSDev wiki. Once properly in 64-bit longmode you can use 64-bit instructions generated by GCC.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!