Expanded Memory Management
This chapter describes:- What the Expanded Memory Manager is
- How expanded memory works
- How an application uses expanded memory
- How the sample program uses emm_functions
- Expanded Memory 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:
- An expansion board containing extra memory and some special control chips
- An MS-DOS device driver known as the Expanded Memory Manager (EMM).
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.
- Returning from main(). Set up a static destructor in the program.
- Calling exit(). Set up a static destructor in the program.
- Calling _exit(), _assert(), abort() or raise(SIGABRT).
- Receiving a Ctrl-C or Ctrl-Break. The program must intercept control break interrupt (0x23).
- Receiving heap corruption or "floating point not loaded" errors from the runtime library.
How an Application Uses Expanded Memory
To use expanded memory, an application might perform the following steps:
- Test whether an Expanded Memory Manager is installed.
- Determine if enough expanded memory pages are available for the application's purposes.
- Request EMM to allocate the required number of expanded memory (logical) pages.
- Use a unique EMM handle, supplied by EMM, to refer to the application's allocated pages.
- 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.
- Map some of the expanded memory (logical) pages into the physical pages within the page frame.
- Write data to and read data from these logical pages, just as if it were using conventional memory.
- Repeat steps 6 and 7 as necessary.
- Return (deallocate) the expanded memory pages, when it finishes using expanded memory, to the EMM pool.
- 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