Setting Up Segments

Within a customized model, you can choose to make the stack segment (SS) equal the data segment (DS), in which case they overlap:

Option Effect

/Axxd SS == DS
/A[[xx]]u SS != DS; DS reloaded on function entry
/A[[xx]]w SS != DS; DS not reloaded on function entry

Segment Setup Option /Ad

The option /Ad tells the compiler that the segment addresses stored in the SS and DS registers are equal. The stack segment and the default data segment are combined into a single segment. This is the default for all standard-model programs. In small- and medium-model programs, the stack plus all data must occupy less than 64K; thus, any data item is accessed with only a 16-bit offset from the segment address in the SS and DS registers.

In compact-, large-, and huge-model programs, initialized global and static data are placed in the default data segment up to a certain threshold. The address of this segment is stored in the DS and SS registers. All pointers to data, including pointers to local data (the stack), are full 32-bit addresses. This is important to remember when passing pointers as arguments in multiple-segment programs. Although you may have more than 64K of total data in these models, no more than 64K of data can occupy the default segment. The /Gt and /ND options control allocation of items in the default data segment if a program exceeds this limit.

Segment Setup Option /Au

The option /Au tells the compiler that the stack segment does not necessarily coincide with the data segment. In addition, it adds the __loadds attribute to all functions within a module, forcing the compiler to generate code to load the DS register with the correct value prior to entering the function body. Combine the /ND option with /Au to name data segments other than the default. When /Au is combined with /ND, the address in the DS register is saved upon entry to each function, and the new DS value for the module in which the function was defined is loaded into the register. The previous DS value is restored on exit from the function. Therefore, only one data segment is accessible at any given time. The /ND option lets you combine these segments into a single segment.

If a standard memory-model option precedes it on the command line, the /Au option can be specified without any letters indicating data pointer or code pointer sizes. The program uses a standard memory model, but different segments are set up for the stack and data segments.

The /Au option is useful for Microsoft Windows dynamic-link libraries (DLLs), since it forces DS to be loaded on entry to each function. It is also useful for writing extensions to the Programmer's WorkBench. This is a costly operation, however, so consider using the /Aw option.

Segment Setup Option /Aw

The option /Aw, like /Au, causes the compiler to assume that the stack segment is separate from the data segment. The compiler does not automatically load the DS register at each function entry point. The /Aw option is useful in creating applications that interface with an operating system or with a program running at the operating-system level. The operating system or the program running under the operating system actually receives the data intended for the application program and places that data in a segment; then the operating system or program must load the DS register with the segment address for the application program.

As with the /Au option, the /Aw option can be specified without data pointer and code pointer letters if a standard memory-model option precedes it on the command line. In such a case, the program uses the specified memory model just as with /Au, but the DS register is not reloaded at each function entry point.

Even though /Au and /Aw indicate that the stack may be in a separate segment, the stack's size is still fixed at the default size unless this is overridden with the /F compiler option or the /STACK linker option.

Summary: Use caution when writing Windows DLLs with /Aw.

The /Aw option is useful for writing Windows dynamic-link libraries (DLLs), but exercise caution when using it. Declare all entry points to the dynamic-link library as __loadds to force DS to be loaded on entry to the function (exactly like the /Au option). This adds a costly operation to each function that acts as an entry point, but not to any of the functions that are private to the DLL. This is more efficient than using the /Au option, because most of the DLL's functions do not have to perform redundant loads of the DS register. For example,

void __export __loadds __far __pascal LibFunc( void )

{

.

.

.

HelperFunc();

}

void HelperFunc( void )

{

.

.

.

}

The library entry point, LibFunc, is declared as __loadds to force the DS register to be loaded on entry. The function HelperFunc, which is private to the dynamic-link library, is declared as a normal C function. Since it cannot be called from outside of the module, HelperFunc does not need to reload DS.

If you choose one of the options specifying that the stack segment is not equal to the data segment (SS != DS), you cannot pass the address of frame variables as arguments to functions that take near pointers. That is, in tiny, small, and medium models, you cannot pass the address of a local variable (which is allocated on the stack) as an argument, because the receiving function will assume the pointer is relative to the data segment. However, the receiving function could solve this problem by declaring the pointer to be the following:

based(__segname(“_STACK”))

Another solution would be to cast the pointer to a far pointer in both locations as follows:

/* Call func with an explicit cast to far */

func( (char far *)frame_var );

.

.

.

void func( char far *formal_var )