Wednesday 16 November 2011

Bypassing the Operating System and Writing Directly to the Screenin C programming

Bypassing the Operating System and Writing Directly to the Screen

This action is somewhat frowned on, for many reasons. In fact, it is impossible to perform on some machines and under some operating systems. Furthermore, it is likely to be different for different machines, or even between different compilers on the same computer

Nonetheless, for speed of video output, you simply cannot beat writing bytes directly to the screen. With full-screen text, you might be able to write hundreds of screens per second. If you need this kind of performance (perhaps for a video game), this method of output is probably worth the effort.

Because each computer and operating system handles this concept differently, it is impractical to give code for every operating system here. Instead, you shall see exactly how this concept is carried out under MS-DOS with Borland C. Even if you are not using these systems, you should be able to learn the correct methods from the following text, enabling you to write similar routines for your computer and operating system.

First of all, you need some method to write data to the screen. You can do this by creating a pointer that “points” to the screen memory. Under Borland C for MS-DOS, this task can be accomplished with this line of code:

char far *Screen = MK_FP( 0xb8000 , 0x0000 );
A “far” pointer is one that is not limited to the small data segment that has been reserved for your program;
it can point anywhere in memory. MK_FP generates a far pointer to a specific location. Some other compilers and computers will not require the pointer type differentiation, or they might not have a similar function. You should look in your compiler’s manual for the appropriate information.

Now, you have a pointer that points to the upper-left corner of the screen. You can write bytes to the location of this pointer, and you will see the characters you are writing appear there, as in the following program:

#include <dos.h>
main()
{
int a;
char far *Screen = MK_FP( 0xb800 , 0x0000 );
for( a = 0 ; a < 26 ; ++ a )
Screen[ a * 2 ] = ‘a’ + a;
return( 0 );
}

After running this program, you should see the alphabet printed across the top of your monitor, in lowercaseletters.

You will notice that instead of being written to consecutive locations in video memory, the characters were written only to every other byte of screen memory. Why is that? It is because even though a character occupies only a single byte, a byte is stored immediately after it to hold its color value. Therefore, each character as displayed on-screen is represented by two bytes in the computer’s memory: one byte for the character itself and another byte for its color value.

This means two things: First, you must write characters only into every other byte of memory, or else you will see only every other character, as well as having bizarrely colored text. Second, you need to write the color bytes yourself if you plan to have colored text or overwrite the color that already exists at a location. Unless you do this, your text will be written with the color of what was already there. Every color byte must describe not only the color of the character, but also the color of the background it is written over. There are 16 foreground colors and 16 background colors. The lower four bits of the byte are reserved for the foreground color, and the high four bits are reserved for the background color.

This topic might seem a little complex for the inexperienced programmer, but it is actually pretty easy tounderstand. Just remember that there are 16 colors, ranging from 0 to 16, and that to get the screen byte value,

you add the foreground color to the value of the background color times 16. This is shown by the following
program:
#include <stdio.h>
main()
{
int fc , bc , c;
scanf( “ %d %d” , &fc , &bc );
printf( “ Foreground = %d , Background = %d , Color = %d\n” ,
fc , bc , fc + bc * 16 );
return( 0 );
}

I think the reader will agree that it is impractical in most cases for the programmer to have to explicitly write bytes to the screen throughout his program. Instead, it is better to write a routine for writing text to the display
quickly, and reuse it frequently. Let’s examine the construction of such a routine.
First, you need to ask yourself, “What information will I need to pass to my general-purpose printing
function?” For starters, you want to be able to specify
The text to be written to the screen
The location of the text (two numbers)
The color of the text, as well as the background (also two numbers)
Now that you know what data you need to pass to your function, you can declare it in the following fashion:
void PrintAt( char *Text , int x , int y , int bc , int fc )
{
Now you want to calculate the byte value for the color of the text you will print:
int Color = fc + bc * 16;
You also need to calculate the starting position for the text pointer:
char far *Addr = &Screen[ ( x + y * 80 ) * 2 ];

Pay special attention to the fact that you must multiply the offset by two to write into the correct place. Also, note that this line assumes that somewhere in your code you have already defined the Screen variable. If you haven’t, just insert the line

char far *Screen = MK_FP( 0xb800 , 0x0000 );
somewhere in your code.

Now that the preliminaries are out of the way, it’s time to actually copy the bytes onto the screen. Look at the code that will carry out this task:
while( *Text )
{
*( Addr++ ) = *( Text++ );
*( Addr++ ) = Color;
}

This code loops while there are still characters left to copy, copying each character to the screen along with its corresponding color.

Take a look at this code in its entirety along with a corresponding test program:

#include <dos.h>
/* This is needed for the MK_FP function */
char far *Screen = MK_FP( 0xb800 , 0x0000 );
void PrintAt( char *Text , int x , int y , int bc , int fc )
{
int Color = fc + bc * 16;
char far *Addr = &Screen[ ( x + y * 80 ) * 2 ];
while( *Text )
{
*( Addr++ ) = *( Text++ );
*( Addr++ ) = Color;
}
}
main()
{
int a;
for( a = 1 ; a < 16 ; ++ a )
PrintAt( “This is a test” , a , a , a + 1 , a );
return( 0 );
}

If you time this function as compared to the built-in printing functions, you should find it to be much faster. If you are using some other hardware platform, you might be able to use the concepts presented here to write a similarly quick printing function for your computer and operating system.

Cross Reference:

None.

No comments:

Post a Comment