NOTE This code won't work at the application level. It directly addresses memory at 0xB8000 which will result in a segfault at the application level. This code must be part of an OS or an application in a not-so-secure operating system like DOS, where applications can directly address any part of memory.
This is some simple code which facilitates the printing of strings, without needing to use an interrupt like int 0x10
. This gives you the foundations so that you can better customise aspects of how printing is done, so that you can, say, make special control characters or speed it up by reading and writing 32 or 64 bits at a time. I've pulled this code up from the remnants of the OS project that I ditched.
This code is simple enough (and has enough comments) that it should explain itself, but I'll highlight some quick points about how VGA text memory is organised at 0xB8000. For each of the 2000 characters on a 80x25 text screen, there are two bytes; the first byte is for the ASCII value of the character, and the second byte is the colour code for the character. This snippet uses the classic white-on-black as a hard-coded value for all character colours
This means that for each row on the screen (80 columns), there are 160 bytes allocated. Hence when we encounter a carriage return in the string, we simply add 160 to the offset in VGA memory for the next byte to be printed.
The LF handler is what confuses most people. Here's a numerical example:
- The current VGA pointer is 400.
- We divide it by 160, resulting in an answer of 2 (remember that
div
keeps the remainder in a separate register from the answer) - We multiply 160 by 2 (the answer to our division), giving 320 as the next place to write a byte
So all of a sudden, we've got a memory pointer that points to the start of the current line. Makes sense?
To use this, just point to a null-terminated string in DS:SI, then call this function.