Unexpected output when printing directly to text video memory

With the changes suggested in other answer and comments, your problem doesn't seem to be reproducible for me. The following code works for me. I've tried to maintain how you coded it just so it makes sense to you:

#define VIDEO_MEM 0xb8000

void write_string( unsigned char colour, const char *string );
void write_string_line( unsigned char colour, const char *string, int pos );
void putChar(char character, short col, short row, unsigned char attr);

/* Place this at top of file as first code in kernel.o */
__asm__ ("call main\r\n" \
         "cli\r\n" \
         "hlt\r\n"
         );

void main()
{
    volatile unsigned char *vid = (unsigned char*) VIDEO_MEM;
    int i=0;
    for (i = 0; i < 2000; i++)
    {
        *vid = ' ';
        *(vid+1) = 0x1f;
        vid += 2;
    }
    write_string(0x1f,"The Kernel has been loaded successfully!!");
    write_string_line(0x1f,"Testing Here!!",1);
    putChar('Z',3,3,0xf3);
}

void write_string( unsigned char colour, const char *string ) {
    volatile unsigned char *vid = (unsigned char*) VIDEO_MEM;
    while(*string != 0)
    {
        *(vid) = *string;
        *(vid+1) = colour;
        ++string;
        vid+=2;
    }
}

void write_string_line( unsigned char colour, const char *string, int pos ) {
    volatile unsigned char *vid = (unsigned char*) VIDEO_MEM;
    vid+=pos*160;
    while(*string != 0)
    {
        *vid = *string;
        *(vid+1) = colour;
        ++string;
        vid+=2;
    }

}

void putChar(char character, short col, short row, unsigned char attr) {
    volatile unsigned char* vid_mem = (unsigned char *) VIDEO_MEM;
    int offset = (row*80 + col)*2;
    vid_mem += offset;
    if(!attr) {
        attr = 0x0f;
    }
    *(unsigned short int *)vid_mem = (attr<<8)+character;
    /* This would do the same as line above
    *vid_mem     = character;
    *(vid_mem+1) = attr;
    */
}

I've added the __asm__ at the beginning to make sure that code is the first to appear in the generated object file. It likely works without it. I've modified all your *vid pointers to be volatile . Since video is memory mapped IO you don't want to have the compiler potentially remove screen writes when it optimizes. Likely your code will work without volatile, but it is proper to add it here to avoid potential problems.

When run BOCHS this code produces this screen output:

enter image description here

If you use the code provided here and it doesn't work that would suggest the issue you are having is likely related to the a code you write in your bootloader that read the disk, enabled A20, set the GDT, entered protected mode, and then called into your C code. It is also possible problems could occur depending on how you compile and link your kernel.


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

A preferable option to using a 64-bit compiler and tool chain from the host environment is to create a cross compiler toolchain for i686 or i386.


This should work. Each VGA cell is of 2 bytes long, First byte stores Character while the second byte stores the color. Also make sure you make marked the pointer volatile. To avoid any type of unexpected changes(or optimizations) made by the compiler on that local field.

void write_string( int colour, const unsigned char *string )
{
    volatile unsigned char *vid = (unsigned char*) VIDEO_MEM;
    while( *string != 0 )
    {
        *vid++ = *string++;
        *vid++ = colour;
    }
}