0

I decided to try and write a C Hello world protected mode Kernel, and although directly accessing the video memory works to print characters manually, I decided to try to write a string, and there are no warnings or errors but it boots to a blinking cursor, but after about half a millisecond it black screens. Here is the code:

#include <stdint.h>

void kmain() {
    // Startup
    int i = 0;
    
    uint16_t* vidmem = (uint16_t*) 0xB8000;
    
    char string[] = "Hello, kernel world!";
    while (string[i]) {
        vidmem[i] = string[i];
        vidmem[i+1] = 0;
        i+=2;
    }

    // Hang the system
    for (;;) {
        
    }
}

Compiled with: gcc -m32 -c kernel.c -o kernel.o -std=gnu99 -ffreestanding -O1 -Wall -Wextra

And then linked with a generic boot.o and then made into an ISO with grub

I tested it in qemu on linux, any help would be appreciated!

Before I attempted the printstring funciton, this was able to print characters:

vidmem[0] = "C";
vidmem[1] = 1;

Which would print the letter C. Nothing fancy though.

After trying to fix some erros I came up with this:

#include <stdint.h>

void kmain() {
    // Startup
    int i = 0;
    int j = 0;
    
    uint16_t* vidmem = (uint16_t*) 0xB8000;
    
    char string[] = "Hello, kernel world!";
    while (string[i]) {
        vidmem[i] = string[i];
        vidmem[j] = 0;
        i+=1;
        j=i*2;
    }

    // Hang the system
    for (;;) {
        
    }
}

Which still blackscreens.

8
  • Try to debug your loop in user space... Commented Jul 28, 2020 at 19:03
  • If you defined uint16_t* vidmem how many bytes are written by vidmem[i] = string[i]; and how many bytes are added by vidmem[i+1], and how many bytes advanced by i+=2;? Commented Jul 28, 2020 at 19:05
  • I think 40 bytes were written in total, is that a problem?
    – A user
    Commented Jul 28, 2020 at 19:08
  • uint16_t is 2 bytes and so vidmem[i] writes to 2 bytes, and vidmem[i+1] is 2 bytes further on. i+=2 used as an index advances by 4 bytes in the video memory and 2 bytes in your source string. Commented Jul 28, 2020 at 19:09
  • 1
    You know you are skipping characters of string? You can easily miss the \0 this way.
    – Eugene Sh.
    Commented Jul 28, 2020 at 19:12

2 Answers 2

3

You are trying too hard to do arithmetic that isn't needed. Remember that pointer arithmetic and array indexing in C are always in units of the size of the type pointed to. If you want to copy the bytes of string into vidmem with a zero byte after each one, then thanks to the fact that integers on x86 are little-endian, it should be sufficient to do

int i = 0;
uint16_t* vidmem = (uint16_t*) 0xB8000;

unsigned char string[] = "Hello, kernel world!";
while (string[i]) {
    vidmem[i] = string[i];
    i++;
}

Or if you prefer

for (i = 0; string[i]; i++)
    vidmem[i] = string[i];

That's it.

If you want a different color that is not black-on-black, you could change this to vidmem[i] = string[i] | 0x7000;.

0

Both versions are incorrect;

    uint8_t* vidmem = (uint8_t*) 0xB8000;
    
    char string[] = "Hello, kernel world!";
    for(size_t i = 0; string[i]; i++) {
        vidmem[i*2] = string[i];
        vidmem[i*2 + 1] = 0;
    }
3
  • 1
    Shouldn't that be vidmem[i*2 + 1] = 0x70; or somesuch. 0 is "black on black", which is a bit tough on the eyes.
    – mevets
    Commented Jul 28, 2020 at 20:17
  • @mevets it can be. Who knows. Commented Jul 28, 2020 at 20:20
  • Your cast on the first line is incorrect, though it will probably just give you a warning -- should cast to uint8_t * to match the pointer type...
    – Chris Dodd
    Commented Jul 28, 2020 at 20:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.