[an error occurred while processing this directive]

Comments, suggestions, questions or anything? Please contact the author, Bruno Barberi Gnecco, via email: brunobg@geocities.com.

Introduction

One of the most frequent questions in comp.os.msdos.djgpp is how to work with far pointers in DJGPP, so I'll try to make a brief explanation.

Some compilers have a keyword far. When one declares a far pointer, like:

char far *ptr;

it means: create a pointer to a memory address. Far pointers let you access RAM memory using DOS memory address type (SEGMENT:OFFSET). But, as DJGPP uses DPMI and protected mode, you can't directly access memory like in others compilers.

There's a way to do it, however; in fact, many of them. You can read about in FAQ section 18, specially in question 4. We'll discuss just one of them: using _far*.

Basic memory operations

DJGPP has a library, sys/farptr.h to deal with direct memory operations. The functions of the library can be divided basically in two: peek and poke. Peek functions are used to get information from memory, while poke functions move information to memory.

Any color video adapter has its video RAM at B800:0000. Monochrome adapter has it at B000:0000. As monochrome adapters are really hard to find nowadays, I'll use B800 as the beginning of video RAM. You, can however, write a function to know which adapter is in the system: interrupt 16, function 15 returns the current video mode; if the mode is 7, it's a monochrome adaptor. We'll be using the default video mode, 3, which is 80x25 and 16 colors.

You should know that video memory requires two bytes for each character: one is the character to be displayed, the second is the attribute. So, it takes 160 bytes for each line, and 4000 bytes to display a whole screen.

It's important also to know how addresses will be used in the _far* functions: you should use the formula

address = segment*16+offset

Let's start with an example: we want to print 'Godzilla', beginning at 0,0 (the top-left of the screen). Let's write:

#
include <go32.h>
#include <sys/farptr.h>
#include <string.h>
#define NORMAL 7

int
main()
{
    int i, limit;
    char *st = "Godzilla";
    limit = strlen(st);
    for ( i = 0; i < limit; i++ )
    {
        _farpokeb(_dos_ds, 0xB800*16+2*i, *st++ );
        _farpokeb(_dos_ds, 0xB800*16+2*i+1, NORMAL );
    }
    return 0;
}

Let's see this code. First, we must include sys/farptr.h, which contains the _far* functions, and go32.h, which contains _dos_ds (which is just a shortcut to _go32_info_block.selector_for_linear_memory. We'll talk about his later. In main(), we create two ints, a string, set the value of limit to the length of the string. Next, we open a for, which will count, one by one, from zero to limit. Now, the interesting part.

_farpokeb is the function which copies a byte to a specific position in memory. Its syntax is: (unsigned short selector, unsigned long offset, unsigned char val). The selector we will use is _dos_ds, which let's use DOS memory and use the formula above for calculating the offset, which is the second parameter. Last, but not least, the third parameter is what you want to print. In the example, we print each one of the characters. And the second _farpokeb? Remember, you have to set the attribute of the character: it's the next position in memory.

The colorful world of video memory

Now you know the basics of direct video memory manipulation. Let's continue using the attributes. Here's a table of them:

- - - -  - - - 1     1   blue foreground
- - - -  - - 1 -     2   green foreground
- - - -  - 1 - -     4   red foreground
- - - -  1 - - -     8   bright foreground
- - - 1  - - - -    16   blue background
- - 1 -  - - - -    32   green background
- 1 - -  - - - -    64   red background
1 - - -  - - - -   128   blinking

Combining it, you can display 16 colors (low intensity creates a 'light' color). The value passed to _farpokeb is usually in hexadecimal or decimal. Example: we want a green letter in a purple background. Summing up, it's: 5A, or 90. We can use then:

_farpokeb(_dos_ds, 0xB800*16+2*i, *st++ );
_farpokeb(_dos_ds, 0xB800*16+2*i+1, 0x5A );

Exercise: create a program that displays the number of attribute in decimal, and set each number's attribute to the number's value.

More about addresses: row, column, and _farsetsel

You don't need to type _dos_ds every time you use _farpoke* or _farpeek*. If you write:

_farsetsel(_dos_ds);

you can use _farns*. Example:

_farsetsel(_dos_ds);
for ( i = 0; i < limit; i++ )
{
   _farnspokeb( 0xB800*16+2*i, *st++ );
   _farnspokeb( 0xB800*16+2*i+1, NORMAL );
}

Using _farsetsel(_dos_ds); is also faster than set it every time you use _far*.

Now, supposing you want to write 'B' at position 50,18 (51th column, 19th row). How to do? Well, if a line has 80 characters, and each character is 2 bytes long in video memory, each line is 80*2=160 characters long. So, we can use:

_farnspokeb( 0xB800*16+160*18+2*50, character );

or, generally:

offset = 0xB800*16+160*line+2*column;

Peeking the memory

Now that we now how to get an specific position in memory given a pair (x,y), we have an use for _farpeek* functions. They are used almost the same way their brothers:

char ch = _farpeekb( _dos_ds, 0xB800*16+2*x+160*y );

The differences are: as peek gets from memory, the return value should be put in a variable; and there's no third parameter.

Peek functions are used to save the screen (or some area of it). Let's do an example: we want to print "This is a phrase" with reverse attribute (or white background) at (26,10), wait for a key, and then restore whatever was in that position.

#include <go32.h>
#include <sys/farptr.h>
#include <string.h>

void
pause(void)
{
    while( !bioskey(1) )
    {
    }
}

int
main()
{
    int i, length;
    char *st = "This is a phrase";
    char buffer[(2 * strlen(st))];
    length = strlen(st);
    _farsetsel(_dos_ds);
    for ( i = 0; i < length; i++ )
    {
        buffer[2*i] = _farnspeekb( 0xB800*16+160*Y+2*(X+i) );
        buffer[2*i+1] = _farnspeekb( 0xB800*16+160*Y+2*(X+i)+1 );
        _farnspokeb( 0xB800*16+160*Y+2*(X+i), *st++ );
        _farnspokeb( 0xB800*16+160*Y+2*(X+i)+1, 0x70 );
    }
    pause();
    for ( i = 0; i < (2*length); i++ )
    {
        _farnspokeb( 0xB800*16+160*Y+2*(X)+i, buffer[i] );
    }
    return 0;
}

Text by Bruno Barberi Gnecco < brunobg@geocities.com>

[an error occurred while processing this directive]