digitalmars.com                      
Last update Sun Mar 4 12:00:58 2018

Expanded Memory Management

This chapter describes: Digital Mars emm_functions use the Expanded Memory Manager (EMM) on expanded memory. The functions are an alternative to the __handle pointer system, giving an application more control of expanded memory. The emm_functions can be used at the same time as the __handle functions.

The current implementation uses the facilities of Expanded Memory Specification (EMS) 3.2 to ensure the widest possible compatibility. Applications written using the EMM functions or the __handle pointer system are therefore compatible with expanded memory managers for both EMS 3.2 and the newer EMS 4.0, as well as with the EMS facilities provided by MS-DOS versions 4.x and 5.0.

What is Expanded Memory

Expanded memory provides real-mode MS-DOS with access to bank-switched memory. It is not required in protected mode programs; these functions are not available when compiling for DOSX. EMS version 3.2 allows a maximum of 8MB of expanded memory to be used whereas version 4.0 allows up to 32MB (2048 logical pages) to be made available to applications or the operating system.

Blocks of the expanded memory are overlaid on a region of conventional memory, known as a page frame, and swapped in and out of the normal addressing space, as needed. When a block of expanded memory is swapped into the page frame it can be used to store and retrieve data just as if it were conventional memory. Data stored within the blocks is retained even when not swapped in.

An expanded memory system normally consists of two components:

However, a number of other methods can implement expanded memory. Some software-only systems are designed to emulate expanded memory in the 16MB address space of 80286 based computers (known as extended memory). Other systems emulate expanded memory using hard disk storage.


Note
The software-only systems do not support some of the facilities in hardware based implementations. For example, data aliasing, that is the mapping of the same expanded memory page to more than one physical page, cannot be supported.

Another system uses extended memory as expanded memory, except that the facility is built into the hardware of the computer. Finally, special software - Qualitas' 386MAX, Quaterdeck's QEMM, and MS-DOS' EMM386 - can use the memory mapping capabilities of the 80386/ i486 chip to manage expanded memory.

How Expanded Memory Works

An expanded memory system normally sets aside a page frame in the nominally "unused" area of 8086 address space that lies between the top of the video display memory (768K) and the 1MB address limit. In version 3.2 of the EMS, this area has a fixed length of 64KB and is arranged to start at a segment boundary.

The Page Frame

Different board manufacturers use different absolute addresses for the start of their page frames, but this address is always fixed at start up either by board switches or by the device driver command line. (EMS version 4.0 allows the page frame to be any length between 64KB and 176KB and allows for the start of the page frame to be changed by software during program execution.)

Although the memory addresses between 768KB and 1MB are normally unused in most systems, certain hardware add-ons, such as network boards and specialist display boards, may use this address space and might clash with EMS boards. Alleviating this situation is sometimes possible by relocating the page frame, provided a sufficiently large (64KB for EMS 3.2) free area in the 768KB to 1MB address space is unused.

The page frame is normally subdivided into 16KB blocks, known as physical pages. Therefore, a 64KB page frame consists of 4 physical pages. Access to expanded memory is then achieved via the Expanded Memory Manager, by the use of hardware registers in the expanded memory board or a software simulation of these registers. These registers are used to "map" 16KB blocks of expanded memory into the address space of the available physical pages. The 16KB expanded memory page can then be accessed by the microprocessor reading and writing to memory addresses which are contained within that physical page.

Using the Page Frame

Let's look at an example, using an Expanded Memory Manager with a 64KB page frame starting at address 0x1e00:0x0000. The EMM might be asked to map the first 16KB block of expanded memory (this is known as a logical page) into the first physical page in the page frame. The net result of this is that the first logical page of expanded memory now has the effective address of 1e00:0000 through 1e00:3fff.

The application can then store and retrieve data from this block of expanded memory, as if it were any other block of memory. However, if the application program then requests the memory manager to map a different block of expanded memory into this physical page, any data stored to the previous page will no longer be found. The data is not lost, however, because any read or write operations are now affecting a totally different block of memory, which can be used independently of the first block.

The application could, for example, ask for the original first logical page to be mapped back not into the first physical page in the page frame, but into the second physical page at memory addresses 1e00: 4000 through 1e00: 7fff. Any data previously stored in this page of expanded memory can then be retrieved using a memory address which is 16KB higher than that to which it was originally written.

The Mapping Context

With a 64KB page frame, it is possible to have more than one page of expanded memory mapped into the microprocessor's address space at any one time. The term used to describe the relationship between physical pages within the page frame and the logical pages which are using the address space of those physical pages (mapped in) is known as the mapping context. The mapping context is maintained for you by the Expanded Memory Manager once you have established it with the appropriate expanded memory function calls; the context is modified by your application.

Installing Expanded Memory

As mentioned previously, applications gain access to expanded memory through the Expanded Memory Manager. This is an MS-DOS device driver which is normally supplied with the expanded memory hardware, or with the computer if the expanded memory hardware is built in. The usual way of installing such a device driver is to place it in the config. sys file.

The EMM installs itself onto interrupt 0x67, and provides the application software with access to the Expanded Memory Manager via the 8086 instruction INT. From the point of view of the programmer, access to the EMM can be achieved through the standard library functions int86 and int86x. This requires the programmer to know exactly the register format required by the Expanded Memory Manager for each EMS function; use of the int86 functions also incurs a speed penalty, since int86 and int86x are general-purpose functions. Any loss of speed can have a detrimental effect on the performance of an application. For these reasons the pre-written access functions provided in the EMM functions are all hand-coded in assembler.

Terminating Use of Expanded Memory

When an application allocates expanded memory pages, by using emm_allocpages, it must also deallocate the pages, and terminate the use of expanded memory before it exits so other applications can use the pages. Therefore, find all routes by which the program can exit, and add calls to deallocate and terminate use of expanded memory. A program can exit by any of the following methods.

If a program does not deallocate pages and terminate use of expanded memory before it exits, other applications will not be able to find available expanded memory pages to allocate. The problem will persist until you run a program to deallocate the pages, or until the system is rebooted. If you are using EMM via handles, this is already taken care of by the run-time library.

How an Application Uses Expanded Memory

To use expanded memory, an application might perform the following steps:

  1. Test whether an Expanded Memory Manager is installed.
  2. Determine if enough expanded memory pages are available for the application's purposes.
  3. Request EMM to allocate the required number of expanded memory (logical) pages.
  4. Use a unique EMM handle, supplied by EMM, to refer to the application's allocated pages.
  5. Obtain, from EMM, the base address of the various physical pages in order to know which memory addresses to use to access the expanded memory.
  6. Map some of the expanded memory (logical) pages into the physical pages within the page frame.
  7. Write data to and read data from these logical pages, just as if it were using conventional memory.
  8. Repeat steps 6 and 7 as necessary.
  9. Return (deallocate) the expanded memory pages, when it finishes using expanded memory, to the EMM pool.
  10. Terminate the use of EMM so other applications can use expanded memory.

Sample Program

The following example calls some of the EMM functions.

#include <stdio.h>
#include <stdlib.h>
#include <EMM.h>

#define HANDLES 4

/* chkout: make sure an EMM supervisor is
installed. */
void chkout(void)
{
 int i, version;
 float ver_no;

 if (EMM_init()) {
    printf("Unable to initiate EMS driver\n");
    exit(EXIT_FAILURE);
 }

 version = EMM_getversion();
 ver_no = version/16 + version%16/10.0;

 printf("EMS driver detected,
        version %1.1f\n", ver_no);

 printf("\tlogical page\t\tsegment\n");
 for (i = 0; i < 4; i++) {
    printf("\t%d\t\t\t%lp\n", i, EMM_physpage(i));
 }
 printf("\n");
}

/* check that allocation worked */
void check_aloc(void)
{
 int i, noh;
 struct EMM_handle_s *hp;

 noh = EMM_gethandlecount();

 if ((hp = calloc(noh, sizeof(struct
          EMM_handle_s))) == NULL)
 {
   printf("Insufficient Memory: function
          check_aloc\n");
   exit(EXIT_FAILURE);
 }

 EMM_gethandlespages(hp);

 printf("\thandle no.\t\tpages\n");
 for (i = 0; i < noh; i++) {
    printf("\t%d\t\t\t%d\n",
           hp[i].handle,
           hp[i].pages);
 }
 printf("\n");

}

use_EMM(unsigned h, int logical)
{
 char message[128], *s;
 char __far *src, far *dst;

 src = EMM_physpage(0);
 dst = EMM_physpage(1);

 printf("Writing string to physical page 0 at "
      "%lp\n", src);
 sprintf(message,"Hello, from physical page 1 at "
      "%lp", dst);
 EMM_maphandle(h, logical, 0);
 s = message;
 while (*s)
    *src++ = *s++;

 printf("Reading String from EMM buffer:\n");
 EMM_maphandle(h, logical, 1);
 s = message;
 while (* s)
    *s++ = *dst++;
 printf("Handle %d message= '%s'.\n", h, message);
}
int main()
{
 unsigned i, usedp, thandle[HANDLES];

 chkout() ;

 printf("No. of active handles is %d \n",
         EMM_gethandlecount());

 for (i= 0; i< HANDLES; i++) {
    /* Take half of what is available */
    usedp = (EMM_getunalloc()+ 1)>> 1;
    thandle[i] = EMM_allocpages(usedp);
    printf("%d pages allocated to handle
            %d\n", usedp, thandle[i]);
 }

 printf("No. of active handles is %d \n",
          EMM_gethandlecount());
 printf("Total size is %d pages\n",
          EMM_gettotal());
 printf("Free size is %d pages\n",
          EMM_getunalloc());

 check_aloc();
 use_EMM(thandle[0], 0);

 for (i= 0; i< HANDLES; i++) {
    EMM_deallocpages(thandle[i]);
    printf("[%d] H=%d freed\n", i, thandle[i]);
 }
 printf("Total size is %d pages\n",
        EMM_gettotal());
 printf("Free size is %d pages\n",
        EMM_getunalloc());
 printf("Done with EMM test.\n");
 EMM_term();
 return EXIT_SUCCESS;
}

emm_allocpages

Header
emm.h
Prototype
int emm_allocpages(unsigned no_pages)
Description
The emm_allocpages function uses the Expanded Memory Manager (EMM) to allocate the required number of expanded memory (logical) 16KB pages. The number of pages to be allocated should not exceed the number of free pages remaining, otherwise an error will occur. Use emm_getunalloc to check the number of free pages available.

When you use emm_allocpages to allocate expanded memory, you must use EMM functions to manage this memory.

Return Value
A unique handle allocated by the Expanded Memory Manager. If there are no handles available, a fatal error is returned.
See Also
emm_deallocpages emm_getunalloc

emm_deallocpages

Header
emm.h
Prototype
void emm_deallocpages(int handle)
Description
The emm_deallocpages function frees the expanded memory pages associated with the specified Expanded Memory Manager (EMM) handle.
Return Value
None
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32
See Also
emm_allocpages

emm_gethandlecount

Header
emm.h
Prototype
int emm_gethandlecount (void);
Description
The emm_gethandlecount function uses the Expanded Memory Manager (EMM) to return the number of EMM handles currently active. The operating system is allocated handle 0, and this handle is always active. Thus the minimum number of active handles is 1.
Return Value
The number of active handles (minimum 1).
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32
See Also
emm_allocpages

emm_gethandlespages

Header
emm.h
Prototype
int emm_gethandlespages(struct emm_handle_s *p);
Description
The emm_gethandlespages returns the number of pages currently allocated to each of the active Expanded Memory Manager (EMM) handles. It is passed a pointer to an array of structures of type emm_handle_s (defined in emm. h).
Return Value
Returns zero if successful. Otherwise, a non-zero value is returned.
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32
See Also
emm_allocpages

emm_getpagemap

Header
emm.h
Prototype
void emm_getpagemap(void *pmptr);
Description
The emm_getpagemap function uses the Expanded Memory Manager (EMM) to store the current page mapping context into the buffer pointed to by pmptr. No EMM handle is required (unlike emm_savepagemap). To determine the buffer size needed to store the page mapping context, use emm_getpagemapsize.
Return Value
None
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32
See Also
emm_getpagemapsize emm_savepagemap

emm_getpagemapsize

Header
emm.h
Prototype
unsigned emm_getpagemapsize(void);
Description
The emm_getpagemapsize function uses the Expanded Memory Manager (EMM) to obtain the size of the buffer needed to store the current page mapping context. Use this function to obtain the buffer size needed by emm_getpagemap, emm_setpagemap, and emm_getsetpagemap.
Return Value
The size of the buffer required.
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32
See Also
emm_getpagemap emm_getsetpagemap emm_setpagemap

emm_getsetpagemap

Header
emm.h
Prototype
void emm_getsetpagemap(void *dst, void *src);
Description
The emm_getsetpagemap function uses the Expanded Memory Manager (EMM) to store the current page mapping context in the buffer dst, and then set the mapping context using the new values provided in the buffer src. This is the equivalent of doing emm_getpagemap(dst) followed by emm_setpagemap(src). No EMM handle is required (unlike emm_savepagemap). Use emm_getpagemapsize to determine the size of the dst buffer needed to store the current page mapping information.
Return Value
None
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32
See Also
emm_getpagemap emm_getpagemapsize emm_setpagemap

emm_gettotal

Header
emm.h
Prototype
unsigned emm_gettotal(void);
Description
The emm_gettotal function uses Expanded Memory Manager (EMM) to get the total number of existing 16KB pages of expanded memory. Some of these pages may already be allocated. Use emm_getunalloc to get the number of free (unallocated) pages.
Return Value
Returns the total number of 16KB logical pages which are present.
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32
See Also
emm_getunalloc

emm_getunalloc

Header
emm.h
Prototype
unsigned emm_getunalloc(void);
Description
The emm_getunalloc function uses the Expanded Memory Manager (EMM) to obtain the number of 16KB logical pages of expanded memory that are currently free and available for allocation. Use this function to find out how many pages are available before you call emm_allocpages.
Return Value
Returns the number of 16KB pages available for allocation.
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32
See Also
emm_allocpages

emm_getversion

Header
emm.h
Prototype
int emm_getversion(void);
Description
The emm_getversion function obtains the version number of the Expanded Memory Manager (EMM) software.
Return Value
Returns the version number of the Expanded Memory Manager as two hexadecimal digits in the form 0x32 where 3 is the major version and 2 the minor.
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32

emm_init

Header
emm.h
Prototype
int emm_init(void);
Description
The emm_init function tests for the presence of the Expanded Memory Manager (EMM) driver as recommended in the Expanded Memory Specification. The technique of tracing through the interrupt vector to find the name of the device driver is adopted since it can be safely used by interrupt handlers and memory resident software.
Return Value
Returns zero if an EMM is installed. Returns a non-zero value if an EMM is not installed or is not operating correctly.
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32

emm_maphandle

Header
emm.h
Prototype
void emm_maphandle(int handle, unsigned logical, unsigned physical);
Description
The emm_maphandle function uses the Expanded Memory Manager (EMM) to map the logical page logical (which belongs to handle) onto the specified physical page in the EMM page frame.
Return Value
None
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32

emm_physpage

Header
emm.h
Prototype
void __far* emm_physpage(int page);
Description
The emm_physpage function gets the address of the start of the specified Expanded Memory Manager (EMM) page. This page must be in the range 0 to 3.
Return Value
Returns a far pointer to the base address of page, or a NULL pointer if an error occurred.
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32

emm_restorepagemap

Header
emm.h
Prototype
void emm_restorepagemap(int handle);
Description
The emm_restorepagemap function uses the Expanded Memory Manager (EMM) to restore a mapping context for a specified handle that has been saved previously with emm_savepagemap. Only one mapping context can be saved and restored for each handle.
Return Value
None
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32
See Also
emm_savepagemap

emm_savepagemap

Header
emm.h
Prototype
void emm_savepagemap(int handle);
Description
The emm_savepagemap function uses the Expanded Memory Manager (EMM) to save the current logical/ physical page mapping context for the specified handle. The context is restored using emm_restorepagemap. Only one such context can be saved for each handle. There are a limited number of such contexts that can be saved, and therefore any application should strive to require no more than 1. This function generates a fatal error if there is no space to save the mapping context.
Return Value
None
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32

emm_setpagemap

Header
emm.h
Prototype
void emm_setpagemap(void *pmptr);
Description
The emm_setpagemap function uses the Expanded Memory Manager (EMM) to load a new page mapping context from the pmptr buffer. No EMM handle is required (unlike emm_restorepagemap).
Return Value
None
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32
See Also
emm_restorepagemap

emm_term

Header
emm.h
Prototype
void emm_term(void);
Description
The emm_term function terminates the use of the Expanded Memory Manager (EMM).
Return Value
None
Compatibility
DOS Windows 3.x Phar Lap DOSX Win32
See Also
emm_init
Home | Compiler & Tools | IDDE Reference | STL | Search | Download | Forums