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

前端 未结 1 1789
悲&欢浪女
悲&欢浪女 2020-12-04 01:11

I am currently following this workbook on build an operating system.

My intention is to write a 64-bit kernel. I have got as far as loading the \"kernel\" code and w

相关标签:
1条回答
  • 2020-12-04 01:21

    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.

    0 讨论(0)
提交回复
热议问题