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).
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:
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
Headeremm.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
Headeremm.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
Headeremm.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
Headeremm.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
Headeremm.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
Headeremm.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
Headeremm.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
Headeremm.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
Headeremm.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
Headeremm.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
See Also
emm_init
Headeremm.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
See Also
emm_maphandle
Headeremm.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
See Also
emm_physpage
Headeremm.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
See Also
emm_restorepagemap
Headeremm.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
Headeremm.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
Headeremm.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
Headeremm.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











