Home > Software design >  How to Write a newline in VGA memory
How to Write a newline in VGA memory

Time:04-22

I am trying to create a kernel in c, and I am having difficult time creating the new line. For example, after writing a sentence to the screen, I would add a new line.

Here is my sample code. I would like to write these strings on two separate lines. So how do I move the cursor to a new line? Your help is highly appreciated

static char* const VGA_MEMORY = (char*) 0xb8000;
static const int VGA_WIDTH = 80;
static const int VGA_HEIGHT = 25;
static unsigned int cursor_pos = 0;

unsigned int i = 0;
char* str = "HELLO WORLD";
char* str1 = "TEST !";

while (str[i]!='\0')
  {
    char ch = str[i];
    VGA_MEMORY[cursor_pos] = ch;
    VGA_MEMORY[cursor_pos 1] = 0x07;
    cursor_pos  = 2;
    i  = 1;
  }

i = 0;
while (str[i]!='\0')
  {
    char ch = str1[i];
    VGA_MEMORY[cursor_pos] = ch;
    VGA_MEMORY[cursor_pos 1] = 0x07;
    cursor_pos  = 2;
    i  = 1;
  }

Expected output is:

HELLO WORLD

TEST !

CodePudding user response:

You just go to the first character of the new line so: VGA_WIDTH * line_number.

VGA memory is just a grid of characters. Control codes (so new lines) makes no sense, they are used on an higher level protocol, to move into the correct position.

Note: instead of new lines and other control code, usually there are other symbols. Really the characters in VGA memory are not characters, but character index of the active font.

Note: in your program, you must have a cursor position _x and _y (or _col/_row), not just one integer.

CodePudding user response:

If you write to a screen buffer, there is no such thing as a new line to write. Instead you just need to move the cursor to the next line:

cursor_pos = ((cursor_pos   (VGA_WIDTH-1)*2) / (VGA_WIDTH*2)) * (VGA_WIDTH*2);

This simply rounds up your cursor position to the next multiple of VGA_WIDTH*2

CodePudding user response:

So how do I move the cursor to a new line?

The absolute minimum (worst) approach would be like:

    i = 0;
    while (str[i]!='\0') {
        char ch = str1[i];
        if(ch == '\n') {
            cursor_pos = (cursor_pos / VGA_WIDTH   1) * VGA_WIDTH;
        } else {
            VGA_MEMORY[cursor_pos] = ch;
            VGA_MEMORY[cursor_pos 1] = 0x07;
            cursor_pos  = 2;
        }
        i  = 1;
    }

The first problem with this is that if the cursor is already at the bottom of the screen it will end up as "index out of bounds" and trash memory that is past the end of the frame buffer.

To fix that you need to detect the condition (e.g. if(cursor_pos >= VGA_WIDTH * VGA_HEIGHT * 2) {) and decide what to do - ignore all new characters, wrap around to the top of the screen (e.g. cursor_pos = 0;), or scroll the screen.

Scrolling the screen might look something like:

    i = 0;
    while (str[i]!='\0') {
        char ch = str1[i];
        if(ch == '\n') {
            cursor_pos = (cursor_pos / VGA_WIDTH   1) * VGA_WIDTH;
            if(cursor_pos >= VGA_WIDTH * VGA_HEIGHT * 2) {
                memmove(VGA_MEMORY, &VGA_MEMORY[VGA_WIDTH * 2], (VGA_HEIGHT - 1) * VGA_WIDTH * 2);
                cursor_pos -= VGA_WIDTH * 2;
            }
        } else {
            VGA_MEMORY[cursor_pos] = ch;
            VGA_MEMORY[cursor_pos 1] = 0x07;
            cursor_pos  = 2;
        }
        i  = 1;
    }

This is relatively horrible - reading from VGA memory is slow (better to have a buffer in RAM), and it means there's no way to see any text that disappeared off the top of the screen ever again.

One way to fix these problems is to append the new string to a kernel log in memory, and then display the last (up to) VGA_HEIGHT lines from the log; so that the whole log can be viewed (and/or written to disk) later. That creates the possibility of replacing the slow memmove() with a faster "print everything from the log in memory again, starting from an updated top of screen pointer" approach.

  • Related