Printing To Screen: Difference between revisions

Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content deleted Content added
No edit summary
 
No edit summary
Line 1: Line 1:
{{Convert}}
{{Convert}}


!! Basics
==Basics==


Working on the assumption that you are in protected mode and not using the BIOS to do screen writes, you will have to do screen writes direct to "video" memory yourself.
Working on the assumption that you are in [[protected mode]] and not using the [[BIOS]] to do screen writes, you will have to do screen writes direct to "video" memory yourself.


This is quite easy to do, the text screen video memory for colour monitors resides at =0xB8000=, and for monochrome monitors it is at address =0xB0000=.
This is quite easy to do, the 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 the screen. One is the _ASCII code_ byte and the other the _attribute_ byte. so =~HeLlo= is 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>
<pre>
0x000b8000: 'H', colourforH
0x000b8000: 'H', colourforH
Line 16: Line 16:
</pre>
</pre>


The _attribute_ byte carries the _foreground colour_ in its lowest 4 bits and the _background color_ in its highest 3 bits. The bit #7 's interpretation depends on how you (or the BIOS) configured the hardware (see VgaResources for additionnal info).
The ''attribute'' byte carries the ''foreground colour'' in its lowest 4 bits and the ''background color'' in its highest 3 bits. The bit #7 's interpretation depends on how you (or the BIOS) configured the hardware (see [[VGA Resources]] for additional info).


For instance, using =0x00= as attribute means black-on-black (you'll see nothing). =0x07= is lightgrey-on-black (dos default), =0x1F= is white-on-blue (Win9x's blue-screen-of-death), =0x2a= is for green-monochrome nostalgics :P
For instance, using <tt>0x00</tt> as attribute means black-on-black (you'll see nothing). <tt>0x07</tt> is lightgrey-on-black (dos 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 16kb of text video memory to use, and since 80x25 mode (80x25x2==4000 bytes per screen) does not use all 16kb, you have what is known as 'pages' and in 80x25 screen mode you have 8 display pages to use.
For colour video cards, you have 16kb of text video memory to use, and since 80x25 mode (80x25x2==4000 bytes per screen) does not use all 16kb, you have what is known as 'pages' and in 80x25 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.
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.


!! Writing strings
==Printing strings==


If you have a pointer to video memory and want to write a string, here is how you might do it;
If you have a pointer to video memory and want to write a string, here is how you might do it;


<pre>
<verbatim>
/* note this example will always write to the top
/* note this example will always write to the top
line of the screen */
line of the screen */
void write_string(int colour, char *string)
void write_string(int colour, char *string)
{
char *video=(char*)0xB8000;
while(*string!=0)
{
{
char *video=(char*)0xB8000;
*video=*string;
while(*string!=0)
string++;
video++;
{
*video=*string;
*video=colour;
string++;
video++;
video++;
*video=colour;
video++;
}
}
}
}
</verbatim>
</pre>


==Printing numbers==
!! Okay for strings, but how do i print numbers ?


just like in any environment: convert the number into a string, then print the string.
just like in any environment: convert the number into a string, then print the string.
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:
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:


<pre>
<verbatim>
1234 = 123*10 + 4
1234 = 123*10 + 4
123 = 12*10 + 3
123 = 12*10 + 3
12 = 1*10 + 2
12 = 1*10 + 2
1 = 1
1 = 1
</verbatim>
</pre>


digits to be displayed are '1','2','3','4' ... if you know the numerical value of number%10, you simply have to add this to the character '0' to have the correct character (e.g. '0'+4 == '4')
digits to be displayed are '1','2','3','4' ... if you know the numerical value of number%10, you simply have to add this to the character '0' to have the correct character (e.g. '0'+4 == '4')
Line 62: Line 62:
(see more on [the forum|Forum:7462])
(see more on [the forum|Forum:7462])


!! How do i print formatted messages (a la printf) ?
==Printing formatted messages (a la printf)==


If you're working with C, you may want to print any number of arguments and you may have looked at the stdarg.h 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 bits system, though.
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.


va_start() points to the first variable argument.
<tt>va_start()</tt> points to the first variable argument.
va_arg() advances the pointer, then evaluates to the previous argument (comma operator).
<tt>va_arg()</tt> advances the pointer, then evaluates to the previous argument (comma operator).
va_end() doesn't do anything.
<tt>va_end()</tt> doesn't do anything.


E.g. to implement =va_start()=, you extract the address of the last 'known' argument and advance (yes, it's a =+= 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. Under gcc, the =_~_builtin_next_arg()= function may help you. Versions 3.x even seem to have =_~_builtin_va_start()=, =_~_builtin_va_end()= and =_~_builtin_va_arg()= so no black magic is required at all.
E.g. 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. Under gcc, the <tt>_builtin_next_arg()</tt> function may help you. 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.


=va_arg()= usually require a bit more black magic since you have two things to do:
<tt>va_arg()</tt> usually require a bit more black magic since you have two things to do:
- advance to the next item
* advance to the next item
- return the value of the (previously current) item.
* return the value of the (previously current) item


You can get [stdarg.h|http://www.execpc.com/~~geezer/osd/code/inc/stdarg.h], [_printf.h|http://www.execpc.com/~~geezer/osd/code/inc/_printf.h] and [doprintf.c|http://www.execpc.com/~~geezer/osd/code/tinylib/stdio/doprintf.c] from geezer/osd. It seems a spider ate Geezers website, [here's a mirror from archive.org|
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.
http://web.archive.org/web/20050212051530/my.execpc.com/~geezer/osd/index.htm].


!! Uh ? I get nothing displayed at all ...
== Uh ? I get nothing displayed at all ...==


Keep in mind that this way of writing to video memory will _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 BIOSes does that initialization for you, but some other (mainly on laptops) do not. Check out [Ralf Browns Interrupt List | http://www.ctyme.com/rbrown.htm] 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 ProtectedMode)
Keep in mind that this way of writing to video memory will _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 does 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]])


More hints for non-working implementations on [Help! I cannot print to screen !?]
More hints for non-working implementations on [Help! I cannot print to screen !?]

----
Categories: HowTo, HardWareVga


==Troubleshooting==
==Troubleshooting==
Line 92: Line 88:
So, you read everything in [How do I output text to the screen in protected mode?] carefully, but it still doesn't work. Here's a check list with suggestion to find out what's wrong.
So, you read everything in [How do I output text to the screen in protected mode?] carefully, but it still doesn't work. Here's a check list with suggestion to find out what's wrong.


! Can you print in real mode ?
=== Can you print in real mode ?===


While still in RealMode, 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 (=0xb8000= instead of =0xb0000=)
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>)


! Can you print a single character ?
=== Can you print a single character ?===


While in ProtectedMode, try a simple command like
While in Protected Mode, try a simple command like


<pre>
<verbatim>
*((int*)0xb8000)=0x07690748;
*((int*)0xb8000)=0x07690748;
</verbatim>
</pre>


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
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>
<verbatim>
mov [0xb8000], 0x07690748
mov [0xb8000], 0x07690748
</verbatim>
</pre>


and GAS-guys will have
and GAS-guys will have
<pre>
<verbatim>
movl $0x07690748,0xb8000
movl $0x07690748,0xb8000
</verbatim>
</pre>


! Can you find your string in the kernel image ?
=== Can you find your string in the kernel image ?===


That may sound stupid, but it's a common mistake to forget the =.rodata= section in the linker script. =-fwritable-strings= can be a substitute, but still if you had
That may sound stupid, but it's a common mistake to forget the <tt>.rodata</tt> section in the linker script. <tt>-fwritable-strings</tt> can be a substitute, but still if you had
<pre>
<pre>
kprint("Hello World");
kprint("Hello World");
</pre>
</pre>
and that no "Hello World" string appear in your kernel.bin (or whatever), don't search any further
and that no "Hello World" string appear in your kernel.bin (or whatever), don't search any further

[[Category:Video]]