16.1.1 Standard Mode

Windows uses the standard-mode memory configuration by default on systems that meet the following criteria:

An 80286-based system with at least 1 megabyte of memory.

An 80386-based system with at least 1 megabyte of memory, but less than 2 megabytes. On 80386-based systems with 2 megabytes or more, Windows uses the 386 enhanced-mode memory configuration by default. For a description of this memory configuration, see Section 16.1.2, “386 Enhanced Mode.”

The Windows heap is made up of at least two memory objects, one in conventional (MS-DOS) memory and one in extended memory. Additional conventional or extended memory objects may be present.

The memory object that Windows uses for the global heap is in conventional memory. This area begins above any terminate-and-stay-resident (TSR) programs, device drivers, MS-DOS, and so on, and extends to the top of conventional memory. This conventional memory is usually 640K, but can be less on some systems.

The second required memory object for the Windows standard-mode configuration is in extended memory. Windows allocates the object in extended memory through an extended-memory device driver and then accesses the object directly, without using the driver. The size and location of this object can vary, depending on what the user loaded into extended memory before starting Windows.

Windows links the two or more memory objects to form the Windows global heap. The beginning (bottom) of the conventional memory object is the beginning (bottom) of the global heap, and the end (top) of the extended-memory object is the end (top) of the global heap.

The following figure shows a typical Windows standard-mode memory configuration:

As with other memory configurations, Windows allocates discardable code segments from the top of the heap, fixed segments from the bottom of the heap, and movable code and data segments above fixed segments.

16.1.1.1 Using Huge Memory Objects in Standard Mode

A far address is created from 16-bit segment address and a 16-bit offset. The segment address is a selector, similar to a Windows handle, that points to an entry in a local or global descriptor table (LDT or GDT). The table entry indicates whether the segment referred to by the selector currently resides in memory. If the segment resides in memory, the table entry provides the linear address of the segment.

If you allocate a huge memory object (larger than 64K), the Microsoft C Optimizing Compiler (CL) generates huge-pointer code that performs segment arithmetic to advance a far pointer across segment 64K boundaries. However, CL does this only if the object is explicitly declared as huge or if the module was compiled with the huge memory model. Do not directly change the segment address portion of a far pointer. Attempting to increment the segment address with the intent of advancing the physical paragraph address will only result in an invalid selector. When the invalid selector is subsequently used to read or write to the memory location, either Windows will report a general-protection (GP) fault, or possibly worse, the invalid selector might inappropriately point to unintended data or code.

If you are programming in assembly language, the proper technique for incrementing a far pointer is to use the external variable __ahincr. Windows fixes up __ahincr with the correct constant to increment the segment selector. This is possible because when Windows allocates the huge memory object, it assigns related selector values to the related memory segments that are 64K (0x1000 paragraphs) in size. This is called selector tiling. The following example illustrates the proper method for incrementing a far pointer by 64K (the only increment provided):

extrn     __ahincr:abs
        .
        .
        .

mov     ax, es      ; es is the segment address you
                    ; wish to increment
add     ax, __ahincr
mov     es, ax

The largest memory object Windows can allocate on an 80286 processor is 1 megabyte less 16 bytes. The largest memory object on an 80386 is 16 megabytes less 64K. If your application requires a memory object larger than 16 megabytes less 64K, see the DOS Protected-Mode Interface (DPMI) specification in the Microsoft Windows Device Driver Kit. All parts of an application (code and data) are normally movable in linear memory.

16.1.1.2 Using Global Selectors

To perform memory-mapped input and output, you can use the following global-selector constants in an assembly-language application to access the corresponding locations in memory:

__A000H

__B000H

__B800H

__C000H

__D000H

__E000H

__F000H

The following example illustrates how to use these selectors properly:

mov ax, __A000H
mov es,ax

Do not use these selectors except to support hardware devices that perform memory-mapped input and output.

16.1.1.3 Code-Segment and Data-Segment Aliasing

Usually, you cannot execute code stored in a data segment. In standard mode, an attempt to execute code in a data segment results in a GP fault. In rare cases, however, such execution may be necessary, and can be performed by aliasing the data segment in question. Aliasing involves copying a segment selector and then changing the TYPE field of the copy so that an operation that is not normally permitted can be performed on the segment.

Windows provides two functions that perform segment aliasing:

AllocDStoCSAlias

ChangeSelector

AllocDStoCSAlias accepts a data-segment selector and returns a code-segment selector. This permits you to write machine instructions on your data stack, create an alias for the stack segment, and then execute the code on the stack.

This function allocates a new selector; after calling AllocDStoCSAlias, you must call the FreeSelector function when you no longer need the selector.

You must be careful not to use a selector returned by AllocDStoCSAlias if it is possible that the segment has moved. The only way to prevent a segment from moving is by calling the GlobalFix function to fix it in linear address space before aliasing the segment.

You can also be sure that a segment has not moved if your application does not yield to another task and does not take any action that could result in memory being allocated. Typically, this would require you to allocate and free a new selector each time your application yields or allocates memory. To avoid allocating and freeing a selector so frequently, you can use a temporary selector. ChangeSelector provides a convenient method for “aliasing” a temporary selector

(generating a code selector corresponding to a given data selector, or vice versa). This function accepts two selectors: a temporary selector, and the selector you want to convert. To convert the selector repeatedly, you would perform the following steps:

1.Call AllocateSelector to create a temporary selector.

2.As often as necessary, call ChangeSelector, passing it the temporary selector and the selector you want to convert. Because ChangeSelector uses a previously allocated selector, you need not free the selector each time you convert it. Instead, you call ChangeSelector each time you need the converted selector after the converted segment might have moved.

3.When you no longer need the converted selector, call FreeSelector to free the temporary selector.