Printing To Screen: Difference between revisions

m
Bot: Replace deprecated source tag with syntaxhighlight
[unchecked revision][unchecked revision]
No edit summary
m (Bot: Replace deprecated source tag with syntaxhighlight)
 
(37 intermediate revisions by 17 users not shown)
Line 1:
==Basics==
 
Working on the assumptionAssuming that you are in [[protected mode]] and not using the [[BIOS]] to dowrite screentext to writesscreen, you will have to do screen writeswrite directdirectly to "video" memory yourself.
 
This is quite easy. to do, theThe text screen video memory for colour monitors resides at <tt>0xB8000</tt>, and for monochrome monitors it is at address <tt>0xB0000</tt> (see [[Detecting Colour and Monochrome Monitors]] for more information).
 
Text mode memory takes two bytes for every "character" on screen. One is the ''ASCII code'' byte, the other the ''attribute'' byte. so the text "HeLlo" would be stored as:
 
Text mode memory takes two bytes for every "character" on the screen. One is the ''ASCII code'' byte and the other the ''attribute'' byte. so <tt>HeLlo</tt> is stored as
<pre>
0x000b8000: 'H', colourforHcolour_for_H
0x000b8002: 'e', colourforecolour_for_e
0x000b8004: 'L', colourforLcolour_for_L
0x000b8006: 'l', colourforlcolour_for_l
0x000b00080x000b8008: 'o', colourforocolour_for_o
</pre>
 
The ''attribute'' byte carries the ''foreground colour'' in its lowest 4 bits and the ''background color'' in its highest 3 bits. The interpretation of bit #7 's interpretation depends on how you (or the BIOS) configured the hardware (see [[VGA Resources]] for additional info).
 
For instance, using <tt>0x00</tt> as attribute byte means black-on-black (you'll see nothing). <tt>0x07</tt> is lightgrey-on-black (dosDOS default), <tt>0x1F</tt> is white-on-blue (Win9x's blue-screen-of-death), <tt>0x2a</tt> is for green-monochrome nostalgics.
 
For colour video cards, you have 16kb32 KB of text video memory to use, and. sinceSince 80x25 mode (80x25x2==4000 bytes per screen) does not use all 16kb,32 youKB have(80 whatx is25 knownx as2 'pages'= and4,000 inbytes 80x25per screen mode), you have 8 display pages to use.
 
When you print to any other page than 0, it will ''not'' appear on screen until that page is ''enabled'' or "''copied"'' into the page 0 memory space.
 
====Color Table====
 
{| {{wikitable}}
|-
! 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==
Line 26 ⟶ 118:
If you have a pointer to video memory and want to write a string, here is how you might do it;
 
<syntaxhighlight lang="c">
<pre>
/*/ 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;
string++;
}
video++;
*video=colour;
video++;
}
}
</syntaxhighlight>
</pre>
 
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.
This simply cycles through each character in the string, and copies it to the appropriate place in video memory.
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:, convertyou repeatedly divide the numbervalue intoby athe stringbase, andthe remainder of the division giving you the least significant thendigit printof the stringvalue.
 
E.g. since 1234 = 4 + 10*3 + 100*2 + 1000*1, if you recursively divide "1234" by ten and use the result of the division, you get all the digits:
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:
 
<pre>
Line 57 ⟶ 150:
</pre>
 
As this algorithm retrieves the digits in the "wrong" order (last-to-first), beyou have to displayedeither arework '1'recursively,'2','3','4' ..or invert the sequence of digits afterwards. ifIf you know the numerical value of <tt>number % 10</tt>, 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):
(see more on [http://www.osdev.org/phpBB2/viewtopic.php?t=10319 the forum].)
 
<syntaxhighlight lang="c">
== Printf ==
char * itoa( int value, char * str, int base )
 
{
If you're working with C, you may want to print any number of arguments and you may have looked at the <tt>stdarg.h</tt> file from other Operating Systems (e.g. linux 0.1 and Thix 0.3.x). These macro definitions may be a bit weird to understand as they're basically C voodoo using pointers and casts and sizeof. Beware before porting them to 16 bit systems, though.
char * rc;
 
char * ptr;
<tt>va_start()</tt> points to the first variable argument.
char * low;
<tt>va_arg()</tt> advances the pointer, then evaluates to the previous argument (comma operator).
// Check for supported base.
<tt>va_end()</tt> doesn't do anything.
if ( base < 2 || base > 36 )
 
{
The good news are that stdagr.h is part of the required features for freestanding implementations, so you can #include it even from your kernel source files.
*str = '\0';
 
return str;
'''Example:'''
}
 
rc = ptr = str;
To implement <tt>va_start()</tt>, you extract the address of the last 'known' argument and advance (yes, it's a <tt>+</tt> since you're rewinding the stack) to the next stack item. The voodoo comes to the fact that things are automatically aligned on 32bits boundary on the stack, even if just 1 byte. This approach however only works on x86 and also depends on the calling convention. Some calling conventions like fastcall, or on other architectures like x86-64 this approach doesn't work because the parameters are passed in registers and/or stack. Under [GCC], the <tt>_builtin_next_arg()</tt> function may help you and works cross-platform. Versions 3.x even seem to have <tt>_builtin_va_start()</tt>, <tt>_builtin_va_end()</tt> and <tt>_builtin_va_arg()</tt> so no black magic is required at all.
// Set '-' for negative decimals.
 
if ( value < 0 && base == 10 )
<tt>va_arg()</tt> usually require a bit more black magic since you have two things to do:
{
* advance to the next item
*ptr++ = '-';
* return the value of the (previously current) item
}
 
// Remember where the numbers start.
You can get [http://my.execpc.com/~geezer/osd/code/inc/stdarg.h stdarg.h], [http://my.execpc.com/~geezer/osd/code/inc/_printf.h _printf.h] and [http://my.execpc.com/~geezer/osd/code/tinylib/stdio/doprintf.c doprintf.c] from geezer/osd.
low = ptr;
 
// The actual conversion.
Also, Solar's public domain c library has a starg.h implementation as well.
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;
}
</syntaxhighlight>
[http://www.strudel.org.uk/itoa/ And here is a shorter one]
 
== Troubleshooting ==
Line 87 ⟶ 199:
=== Nothing is Displayed ===
 
Keep in mind that this way of writing to video memory will _only_''only'' work 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 the ''Set Video Mode'' service of the BIOS Int10h while you're still in real mode (in your bootsector, for instance). Most BIOS's doesdo that initialization for you, but some other (mainly on laptops) do not. Check out [[Ralf Brown's Interrupt List]] for 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 in [[Protected Mode]]).
 
([[GRUB]] does this setup for you.)
===Printing in Real Mode===
 
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.
''It's '''real''' easy...''
 
While still in [[Real Mode]], try to write directly to Video memory. If this doesn't work either you haven't set up the Video mode properly to 0x03 (check out [[RBIL]]) or you're assuming the wrong video memory address (<tt>0xb8000</tt> instead of <tt>0xb0000</tt>)
 
===Printing a Character===
 
While in Protected Mode, try a simple command like:
 
<pre>
// C
*((int*)0xb8000)=0x07690748;
</pre>
 
// NASM
which should display 'Hi' in grey-on-black on top of your screen. If the previous step worked and not this one, check your paging / segmentation setup correctly maps your assumed video memory address onto 0xB8000 (or 0xB0000). NASM-only developers may use
<pre>
mov [0xb8000], 0x07690748
</pre>
 
// GAS
and GAS-guys will have
<pre>
movl $0x07690748,0xb8000
</pre>
 
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 ===
 
ThatSometimes mayprinting soundindividual stupidcharacters works, but it'sprinting astrings commonfails. mistakeThis is usually due to forget the <tt>.rodata</tt> section missing in the linker script. <tt>-fwritable-strings</tt> can be a substitute, but still if you had
 
<pre>
Previously, GCC had an option <tt>-fwritable-strings</tt> which 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 <tt>.rodata</tt> to the script.
kprint("Hello World");
 
</pre>
==See Also==
and that no "Hello World" string appear in your kernel.bin (or whatever), don't search any further
*[[Printing to the screen without a db]]
 
[[Category:Tutorials]]
[[Category:Video]]
[[Category:Text UI]]