For the 16-bit compiler, a based pointer operates as a 16-bit offset from a base you specify. For the 32-bit compiler, a based pointer is a 32-bit offset from a 32-bit pointer base. In this respect, based addressing differs from near, far, or huge addressing because you are responsible for naming the base.
Based addressing is useful for exercising control over segments where objects are allocated and for referencing far objects with 16-bit addresses, thereby decreasing the size of the executable file and increasing execution speed.
The __based keyword specifies that a pointer is a 16-bit value interpreted as an offset from a specified base, or that a data object resides in the segment given by a specified base. In general, the form for specifying a based pointer is
type __based( base ) declarator
The following code shows some examples of based pointer declarations:
typedef struct tree BTree;
struct tree /* Binary tree */
{
char *szSymbolName;
BTree *btLeft;
BTree *btRight;
};
/* Pointer to data that resides in segment SYM_DATA: */
BTree __based( __segname( "SYM_DATA" ) ) *btSymTable1;
/* Pointer to data that resides in same segment
as the pointer btSymTable3: */
BTree __based( (__segment) __self ) *btSymTable3;
Based pointers can address any location in memory but are only two bytes in size for 16-bit targets (4 bytes for 32-bit targets), because they contain only the offset portion of an address. The segment portion of the address is stored separately and is combined with the offset when needed. Multiple based pointers can share the same segment value, so they require less memory than far pointers and allow the compiler to generate better code.
You also can use the __based keyword to specify the segment in which data resides.
A based pointer can be based on a fixed segment, a variable segment, self, void, or a pointer. The following sections explain these topics. See Chapter 4 in Programming Techniques for additional information on using based addressing.
The __segname and __segment Keywords (Microsoft Specific)
The built-in function __segname accepts a quoted string and returns a value of type __segment. This built-in function can be used to initialize variables of type __segment or in declarations of based identifiers or pointers. A __segname declaration is not an l-value, and its address cannot be taken. The primary use for __segname is in initializing identifiers of type __segment or in declarations of pointers or identifiers of based type. It enables specification of a segment name instead of a segment value when declaring a based type.
The __segment keyword is a type that contains a segment value. Variables of such type can contain a segment value. A variable or value of that type is used either at the point of declaration of a based type or at the point of a based dereference to specify the segment in which the based identifier resides.
An identifier of type __segment can be initialized with the following:
An expression that evaluates to an integral constant value.
The result of the built-in function __segname. This allows specification of a segment by name, causing the linker to insert segment fixups in the executable file, to be resolved at load time. This kind of initialization can be performed only on static and external identifiers, not identifiers with block scope.
Another expression of type __segment. For example:
main()
{
__segment sgCustomerData = 0x7000;
__segment sgCurrent = sgCustomerData;
}
Another expression explicitly cast to type __segment.
If specified at the point of declaration, the segment value is implicit and need not be respecified at the point of dereference (based pointers). Otherwise, the segment must be explicitly specified.
The __segment type and the built-in function __segname are not supported by the 32-bit compiler.¨
Pointers Based on a Constant (Microsoft Specific)
Pointers based on a constant are restricted to accessing one segment of memory. By making assignments to the based pointer, you change only the offset portion of the address. To specify a pointer based on a fixed segment, you can use the __segname built-in function or the __segment keywords to supply the segment name.
Pointers based on a named segment are specified as
__segname( string-literal )
The string-literal can be the name of one of four predefined segments (_CODE, _CONST, _DATA, or _STACK), or it can be the name of a new segment you define. A based pointer declared this way can address locations in only the specified segment.
Pointers based on the segment of a variable are specified as
(__segment)&var
A based pointer declared this way uses the segment of the address of var as its base. It can address locations only in the same segment as var.
In the following example, sgConst is explicitly declared as a constant. The pointer bp1, therefore, is based on a constant.
const __segment sgConst = 0x3000;
char __based( sgConst ) *bp1;
In the following example, the __segname function returns a constant value of type __segment. The pointer bp3, therefore, is based on a constant.
char __based( __segname("INFO_STRINGS") ) *bp3;
Pointers Based on a Segment Variable (Microsoft Specific)
Pointers based on a nonfixed segment have access to locations in any segment simply by changing the value of the base. Changing a single segment value causes all pointers based on that segment to address new locations. You also can make assignments to the based pointers themselves to change their offset values.
Pointers based on a segment variable require that a variable of type __segment be declared. This variable determines what segment the based pointer refers to. The segment variable can be changed at run time.
type __based(__segment) * ptr
A based pointer declared this way uses the segment portion of ptr as its base. If ptr is a near pointer, the declared pointer uses the DS register as its base. If ptr is a far pointer, the declared pointer uses the segment of the ptr as its base. Changing the segment value of ptr causes the based pointer to address a new location.
The base for pointers based on a segment variable is specified as
__segment segvar
A based pointer declared this way uses segvar as its base. Assigning a new value to segvar causes the based pointer to address a different location. This type of based pointer can be used for dynamic allocation of based identifiers.
The form of base of pointers based on another pointer is
type * ptr
A based pointer declared this way acts as an offset from ptr. Assigning a new value to ptr causes the based pointer to address a different location.
Pointers Based on Pointers (Microsoft Specific)
The “based on pointer” variant of based addressing enables specification of a pointer as a base. The based pointer, then, is an offset into the segment starting at the beginning of the pointer on which it is based.
One use for pointers based on pointers is for persistent identifiers that contain pointers. A linked list that consists of pointers based on a pointer can be saved to disk, then reloaded to another place in memory, with the pointers remaining valid.
The following example shows a pointer based on a pointer.
void *vpBuffer;
struct llist_t
{
void __based( vpBuffer ) *vpData;
llist_t __based( vpBuffer ) *llNext;
};
The pointer vpBuffer is assigned the address of memory allocated at some later point in the program. The linked list is relocated relative to the value of vpBuffer.
Pointers based on pointers are the only form of the __based keyword valid in 32-bit compilations. In such compilations, they are 32-bit displacements from a 32-bit base.¨
Pointers Based on void (Microsoft Specific)
Pointers based on void defer actual address calculation until the pointer is dereferenced. A pointer based on void acts as an offset into any segment. The form of a pointer based on void is
type __based( void ) * ptr
A pointer based on void has no implied segment as its base. The segment specified at the point of dereference can be a constant or a segment variable. The segment and offset are combined using the base operator (:>) to form an address that can be dereferenced using the indirection operator (*).
struct BiosEquipList /* Structure for the BIOS Equipment List
{ * that starts at 000:0410 (hex)
. */
. /* structure fields */
.
};
/* Declare ROM data as const and supply the offset, hex 410: */
const BiosEquipList __based( void ) *bpelROM = 0x410;
int main()
{
BiosEquipList elLocal; /* Local copy of equipment list */
elLocal = *(0x000 :> bpelROM); /* Segment and offset combined */
This example shows how to declare and dereference a pointer based on void.
Pointers Based on the __self keyword (Microsoft Specific)
Pointers based on self can access data anywhere in the segment in which the pointer resides. They are declared using the __self keyword cast to the __segment type as the base. You can only base on (__segment)__self; not on __self only. Basing a pointer on (__segment)__self can improve program performance by requiring that the segment register be the same for addressing both the pointer and the data it addresses. Functions cannot return pointers based on self.
The __segment keyword must be used to cast to a segment value, as in the example below:
typedef struct tree TREE;
struct tree
{
int name;
TREE __based( (__segment)__self ) *left;
TREE __based( (__segment)__self ) *right;
};
TREE __based( __segname( "MYSEGMENT" ) ) t1;
The example above declares a structure called tree and then declares t1 to be such a structure. The pointers within the structure are self-based, meaning that they point within the segment in which the tree structure is located.
Pointers based on (__segment)__self are particularly useful for optimizing access in self-referencing data structures such as linked-lists and trees.
Any based declarations that are based on (__segment)__self must apply to pointers only. Ordinary data identifiers cannot be self-based.
Pointers based on __self are not available for 32-bit targets.¨