VGA 0xb8000 文件 (Printing To Screen OSDev Wiki)
Printing To Screen - OSDev Wiki
Printing To Screen
Contents[hide] |
Basics
Assuming that you are inprotected modeand not using the
This is quite easy. The text screen video memory for colour monitors resides at0xB8000, and for monochrome monitors it is at address0xB0000(seeDetecting Colour and Monochrome Monitorsfor more information).
Text mode memory takes two bytes for every "character" on screen. One is theASCII code
0x000b8000: 'H', colour_for_H 0x000b8002: 'e', colour_for_e 0x000b8004: 'L', colour_for_L 0x000b8006: 'l', colour_for_l 0x000b8008: 'o', colour_for_o
Theattributebyte carries theforeground colourin its lowest 4 bits and thebackground colorin its highest 3 bits. The interpretation of bit #7 depends on how you (or the BIOS) configured the hardware (see
For instance, using0x00as attribute byte means black-on-black (you'll see nothing).0x07is lightgrey-on-black (DOS default),0x1Fis white-on-blue (Win9x's blue-screen-of-death),0x2ais for green-monochrome nostalgics.
For colour video cards, you have 32 KB of text video memory to use. Since 80x25 mode does not use all 32 KB (80 x 25 x 2 = 4,000 bytes per screen), you have 8 display pages to use.
When you print to any other page than 0, it willnotappear on screen until that page isenabledorcopiedinto the page 0 memory space.
Color Table
Color number | Color name | RGB value | Hex value |
---|---|---|---|
0 | Black | 0 0 0 | 00 00 00 |
1 | Blue | 0 0 170 | 00 00 AA |
2 | Green | 0 170 0 | 00 AA 00 |
3 | Cyan | 0 170 170 | 00 AA AA |
4 | Red | 170 0 0 | AA 00 00 |
5 | Purple | 170 0 170 | AA 00 AA |
6 | Brown | 170 85 0 | AA 55 00 |
7 | Gray | 170 170 170 | AA AA AA |
8 | Dark Gray | 85 85 85 | 55 55 55 |
9 | Light Blue | 85 85 255 | 55 55 FF |
10 | Light Green | 85 255 85 | 55 FF 55 |
11 | Light Cyan | 85 255 255 | 55 FF FF |
12 | Light Red | 255 85 85 | FF 55 55 |
13 | Light Purple | 255 85 255 | FF 55 FF |
14 | Yellow | 255 255 85 | FF FF 55 |
15 | White | 255 255 255 | FF FF FF |
Printing Strings
If you have a pointer to video memory and want to write a string, here is how you might do it;
// note this example will always write to the top
// line of the screen
void write_string( int colour, const char *string )
{
volatile char *video = (volatile char*)0xB8000;
while( *string != 0 )
{
*video++ = *string++;
*video++ = colour;
}
}
This simply cycles through each character in the string, and copies it to the appropriate place in video memory.
For a more advanced print function, you need to store variables for x and y, as the display controller will not print a newline. This involves a switch statement or similar construct. You also have to test for x>80 or y>25 and in the case of x>80 setting x to 0 and incrementing y, or in the case of y>25 scrolling.
Printing Integers
Just like in any environment, you repeatedly divide the value by the base, the remainder of the division giving you the least significant digit of the value.
For example, since 1234 = 4 + 3* 10 + 2 * 100 + 1* 1000, if you repeatedly divide "1234" by ten and use the remainder of the division, you get the digits:
1234 = 123*10 + 4 123 = 12*10 + 3 12 = 1*10 + 2 1 = 1
As this algorithm retrieves the digits in the "wrong" order (last-to-first), you have to either work recursively, or invert the sequence of digits afterwards. If you know the numerical value ofnumber% 10, you simply have to add this to the character '0' to have the correct character (e.g. '0'+4 == '4')
Here is an example implementation of the itoa() function (which is not standard, but provided by many libraries):
char * itoa( int value, char * str, int base )
{
char * rc;
char * ptr;
char * low;
// Check for supported base.
if ( base < 2 || base > 36 )
{
*str = '\0';
return str;
}
rc = ptr = str;
// Set '-' for negative decimals.
if ( value < 0 && base == 10 )
{
*ptr++ = '-';
}
// Remember where the numbers start.
low = ptr;
// The actual conversion.
do
{
// Modulo is negative for negative value. This trick makes abs() unnecessary.
*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz"[35 + value % base];
value /= base;
} while ( value );
// Terminating the string.
*ptr-- = '\0';
// Invert the numbers.
while ( low < ptr )
{
char tmp = *low;
*low++ = *ptr;
*ptr-- = tmp;
}
return rc;
}
Troubleshooting
Nothing is Displayed
Keep in mind that this way of writing to video memory willonlywork if the screen has been correctly set up for 80x25 video mode (which is mode 03). You can do this either by initializing every VGA register manually, or by calling theSet Video Modeservice of the BIOS Int10h while you're still in real mode (in your bootsector, for instance). Most BIOS's do that initialization for you, but some other (mainly on laptops) do not. Check outRalf Brown's Interrupt Listfor details. Note also that some modes that are reported as "both text & graphic" by mode lists are actually graphic modes with BIOS functions that plot fonts when you call char/message output through Int10h (which means you'll end up with plain graphic mode once inProtected Mode).
(GRUBdoes this setup for you.)
Another common mistake, e.g. in numerous tutorials spread across the net, is to link the .text section of your kernel/OS to the wrong memory address. If you don't have memory management in place yet, make sure you're using physical memory locations in the linker script.
Printing a Character
While in Protected Mode, try a simple command like:
// C *((int*)0xb8000)=0x07690748; // NASM mov [0xb8000], 0x07690748 // GAS movl $0x07690748,0xb8000
which should display 'Hi' in grey-on-black on top of your screen. If this does not work, check your paging / segmentation setup correctly maps your assumed video memory address to 0xB8000 (or 0xB0000).
Missing Strings
Sometimes printing individual characters works, but printing strings fails. This is usually due to the.rodatasection missing in the linker script.
Previously, GCC had an option-fwritable-stringswhich could be used as a workaround for this, but it was deprecated in version 3.0 and removed in 4.0 and later, which was released in 2005. Even when the option was available, it was a kludge; the real solution was, and still is, to add.rodatato the script.