[Home]
[Search]
[Contents]
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;
}