Using Expanded and Extended Memory
(A brief and simplified explanation.)
(This article is intended for "REAL" DOS programmers:
those who don't eat quiche or drive little plastic cars.)
by: Victor E. Cummings
Trademarks contained herein are attributed to their respective owners.
This is a frequently asked question: I have just had two megabytes
of extended memory or expanded memory placed on my system, so what
do I do with it? For those who are not programmers, the answer:
don't worry about it. If your programs use extended or expanded
memory, then the programs will take care of that, all you have to
worry about is having enough in your checking account for that
To an extent, nowadays, this is true even for application program-
mers if you are executing code overlays from your compiler.
Borland's new Vroom overlay linker as well a few others will take
care of that for you without you having to write the overlay
manager. Creating data structures in extended or expanded memory
is still left to the programmer.
This brief explanation is designed for the DOS application
programmer or engineer who needs to set up array processes which
extend beyond the 640 kilo byte boundary. It gives a brief
overview of the topic which will enable them to be able to program
both types of memory and neglect that information which is
necessary for OEMs (original equipment manufacturers) or operating
If you are an operating system designer it is doubtful that you
have downloaded this article in the first place, because the scope
is too small for you.
Wherever possible this article is written at the 7th grade level
so that you may quickly get the information you need without long
winded sentences. Formulae are neglected by adding discrete
values in several examples so that the engineer can do his own
thinking. The endnotes in this article point you to some referen-
ces which may enhance your technical understanding of the topics.
The Real Deal:
What is meant by the real deal: what is real and what is protect-
ed. Simply stated: protected mode allows direct addressing of the
microprocessor's capability by both code and data. DOS was written
during an age of microprocessing when computers used 8 bit
processors. For this reason it is not possible with the use of
8086 or 8088 instructions to access more than one midge byte of
address space. DOS was not an operating system designed for the
multitasking, and you will be programming in real mode if you are
programming for DOS. With the advent of the 80286 microprocessor,
addressing became possible outside of the one midge byte boundary
set up by DOS to a maximum of 32 midge bytes. This additional
memory can be addressed directly using operating systems which
operate in protected mode. The 80286 processor has on board
approximately 130,000 transistors a huge improvement over the
approximately 40,000 provided by the 8088 processor. About 40% of
the additional transistors added to the 80286 are dedicated to
performance enhancement of the 8088 instruction set. If we remain
real, this means that the machine will operate at least 3 times as
fast as the 8088 based machines. The other transistors are not
used by DOS, and they are not of concern to the real mode DOS
programmer. The 386 offers more performance enhancements with data
segments of up to four gigabytes in protected mode. We will
remain real with the exception of the whys and wherefores.
The Whys and Wherefores:
The 80286 offers many features which are not used by OEMs for
compatibility reasons and for others discussed below. Accessing
the capabilities of the new processor chips released needs to be
a coordinated effort between the chip manufactures, OEMs that
produce the machines and the systems programmers who write the
operating system. This takes time, agreements and coordination to
maintain downward compatibility for the application programmer and
end users. Operating systems which use more advanced features of
the chips developed since the 8088 do not remain downward com-
patible, are more expensive and do not enjoy as wide an audience
at the present time for those reasons.
The AT or PS/2 BIOS is a superset of the PC BIOS (Basic Input
Output System) and contains three functions for extended memory.
It contains none for EMS which is always addressed by communication
with a DOS device driver which uses interrupt 67(hex). The XMS
driver recently documented by Microsoft (HIMEM.SYS) now gives the
application programmer the ability to address high memory in a
manner similar to the EMS specification. The three functions:
(Int 15(hex) Function 87(hex)Move Extended Memory Block
Int 15(hex) Function 88(hex)Get Extended Memory Size
Int 15(hex) Function 89(hex)Enter Protected Mode) are not
intended for use by real DOS programmers. So what was?
LIM EMS Specification:
Several companies have created memory specifications which were
designed to overcome the 640 kilo byte memory barrier crunch. The
specification that remains in wide use is the LIM EMS 3.2 or 4.0
EMM(Expanded Memory Specification) developed as a collaborative
effort by Lotus, Intel and Microsoft(LIM). LIM Expanded Memory
Manager makes addressing high expanded memory possible for the
application programmer in real mode for data storage and retrieval.
For the real DOS application programmer, it is best to stick with
calls to the 3.2 specification only since your programs will enjoy
a wider use. The purpose of the specification was to offer OEMs
and application programmers a standard way of addressing high
memory and using it to store data and execute code.
The LIM standard is a cleanly documented and well designed standard
which actually offers some advantages to the application programmer
over extended memory. Among the advantages using LIM EMS over
extended memory is the ability for the assembler programmer to
easily execute code from expanded memory. This is something that
the newer XMS standard does not allow easily. This article will
only cover the use of both specifications for storing and retriev-
ing data from high memory (above one midge byte address space of
the 8088) in real mode. This function is "roughly" similar for
both specifications. The LIM standard is older, more developed and
therefore more well documented than the newer XMS. This will
change, because the reason for machines having the extended memory
in the first place is to take advantage of the more advanced
features of the 80286, 80386 and 80486 microprocessors. In
addition, the LIM driver sets up the page frame for you to map high
memory where the XMS standard will not. If the LIM standard offers
a lot of advantage to the programmer then why use it? The reason:
because it is there.
The XMS Standard:
The industry is migrating slowly to newer and more advanced uses
of the newer processors, but can't do that all at once. Since may
users are still using DOS, they must support it with their
hardware, but extended memory is needed to run protected mode
operating systems. For this reason, many machines which still use
DOS are being sold with extended memory only, and users of LIM
programs must emulate the older LIM standard with emulation
software. Since extended memory is there, and for the most part
is faster then it should be used if available in sufficient
quantity. The reason for the XMS driver is to give the real DOS
programmer a vehicle similar to the LIM EMS specification on newer
computers which have the capability of running operating systems
other than DOS. The specification is needed for machines still
running under DOS. The XMS standard using (HIMEM.SYS) has been
published by Microsoft and is available on bulletin board systems
and directly from them. They even include the source code to the
driver itself for OEMs who wish to write their own XMMs (Extended
Memory Managers). HIMEM.SYS (an Extended Memory Manager) simplif-
ies the chores for the programmer in addressing high memory for
data storage and retrieval.
The GAZINTAS and the GAZOWTAS:
GAZINTA: What GAZINTA here >
Also mathematical term
[origin obscure] >>>
GAZOWTA: What GAZOWTA there
Where ever possible, the GAZINTA and GAZOWTA method of definitions
will be used in the following explanations. This context is used
to add some humor to a dry subject, but not to insult your intel-
ligence. The discussion does not get into a lot of detail, but
attempts to help the reader gain the basic concepts quickly so that
the two specification documentations are painless. It also
attempts to encourage the reader to develop coded routines which
are roughly parallel so that code size and development time can be
optimized. The discussion does not attempt to document all of the
EMS or XMS calls, but points the reader to the ones which are most
important to gain quicker understanding of the specifications
themselves. It assumes a knowledge of lower level programming
practice of addressing microprocessor registers directly from the
language you are using and the execution of software interrupts.
Paralleling the Two Specifications:
Paralleling the two specifications may serve to help the real DOS
application programmer learn the two specifications faster.
EMS Uses Interrupt 67(hex) to for functions calls to the driver.
First put the GAZINTAS into the appropriate register and execute
interrupt 67(hex). Then grab and evaluate the GAZOWTAS.
XMS Uses Multiplex Interrupt 2f(hex) to determine the address of
the XMS control function. Rather than executing a software
interrupt as with EMS, you must execute the code at the XMS control
address after placing the GAZINTAS into the appropriate registers.
Then grab and evaluate the GAZOWTAS. With EMS you know what you
are going to execute (INT 67(hex)), but you must find out where
your data is going. With XMS you must find out what you are going
to execute, but you know where your data is going to go. This is
the only confusing part of these two specifications, and a discus-
sion of this can be found in the section
"Storing Your Data".
The XMS and EMS functions paralleled:
See if the Driver Exists:
(So you know what type of memory you have to store data to.)
GAZOWTAS: AL 80(hex)>if driver is installed
EMS: (two methods) Preferred method is listed which may be used by both
standard and resident programs.
GAZINTAS:>AH 35(hex) The DOS Get Interrupt Function
AL 67(hex) The EMS interrupt function you wish to address
INT 21(hex) Call DOS to ask it where the driver is.
GAZOWTAS: ES> Segment to the device signature.
If the string "EMMXXXX0" is located at offset 0a(hex) in
the segment returned in ES then the XMS is present.
What is the version of the driver?
(So you know that the version of the driver is the same or later than
the one you bargained and or programmed for.)
GAZINTAS:>AH 00(hex) Function 00(hex) Get version
Call the XMS driver. (described later)
GAZOWTAS: AX> XMS version number.
BX> Driver internal revision number.
GAZINTAS: AH 46(hex) Function 46(hex) Get version
INT 67(hex) Call EMS driver
GAZOWTAS: AL> EMM version in binary coded decimal.
How much memory does the computer have?
(So you don't request more than it has.)
GAZINTAS:>AH 08(hex) Function 08(hex). Query Free Extended Memory
Call the XMS driver function (described later)
GAZOWTAS: AX> Size of largest free extended memory block in kilo bytes.
DX> Size of total free extended memory in kilo bytes.
GAZINTAS: AH 42(hex) Get count of unallocated 16 kilo byte pages.
INT 67(hex) Call EMS driver
GAZOWTAS: BX> Number of unallocated 16 kilo byte pages.
(You now know you have enough of the right type and version.)
GAZINTAS:>AH 09(hex) Function 09(hex). Allocate Extended Memory Block
>DX Amount of extended memory requested in kilo bytes.
Call the XMS driver function (described later)
GAZOWTAS: AX> 0001 if allocation is successful 0000 if not successful.
DX> 16 bit handle to the allocated memory block.
This handle should be used in subsequent calls to the driver to address
this allocated block.
GAZINTAS:>AH 43(hex) Allocate Pages
>BX Number of 16 kilobyte pages to request.
INT 67(hex) Call EMS driver
GAZOWTAS: DX> handle to the allocated memory block.
Storing Your Data:
Now here is where the differences between conventional memory show up
and where you want to be. You want to be able to place books full of
information into the computers memory and retrieve that information.
EMS: With EMS you don't know where you are going to send your data
until you ask the driver where it maps its logical pages. So now you
will need to know the difference between a logical page and a physical
one. Below is a representation of your computer's base memory on the
left and EMS memory on the right. The EMS specification specifies
that there must be at least four
<> physical pages mapped above the 640
<>00 kilo byte DOS boundary and the one
<>01 midge byte address space allowable
<>02 by the 8088 microprocessor. For
03 most machines the space is exactly
04 four contiguous 16 kilo byte pages.
640 kb 05 Where the machine maps the memory
06 is machine specific. For instance,
07 some manufacturers use the segment
08 of the hard disk controller used by
09 the XT BIOS at C800. To get the
0a address to the base frame of these
0b four pages use EMM (shown below) Since
0c this 64 kb page frame is within the
0d address space of your program, you
0e may access it directly by using a
0f far pointer. The example shown here
has physical pages 00 through 03 mapped to logical pages 00 through
03. If you need to address more than 64 kilo bytes, you must then
re-map the four physical pages within your program's address space to
four more of your allocated memory pages in LIM EMS. Just think of
the whole pool of EMS being available by a fictional address pointer
the offset of which is your code function which performs your page shift
and the offset from the base of the physical page map as your offset
into your huge pool of EMS memory.
Getting the Base Page Frame Address for EMS or the API Segment for XMS
(For XMS you must find out where the code is that you need to execute to
call the Application Programmer Interface (API).
(For EMS you need to know where you will send your data.)
Now moving your data into EMS is simply a matter of addressing using your
fictional Segment (your page switching function) and your real offset address
which is the offset from the base of the page frame segment address. This
fictional pointer allows you to store up to 32 megabytes of data. Just
move your data into EMS by using this fictional pointer: The segment
being your page switching routine, a 16 bit offset into the 64 kilo byte
page frame. You can now access the base of the physical page map by
setting a pointer to the base page frame address returned by the above
Remapping EMS pages(your fictional segment) or Moving Data Blocks XMS
(For XMS you may allocate a region in base memory where you can move an
extended memory region.)
(For EMS you switch to the mapping "context" or (fictional segment) and
move your data to the offset from the base of the 64 kilo byte page frame.)
> > With XMS you can move memory regions
1 MB 00 of memory to and from conventional memory
Boundary 01 and also within extended memory. More
02 than 64 kilo bytes can be moved, but a
> 03 helpful hint is to use a 64 kilo byte
640 KB 04 segment of memory each time for your
Boundary 05 move. The idea behind this is two fold.
06 First, the transfer rate dramatically
07 increases in memory moves until you
08 reach about 64 kilo bytes. On my 286
09 the transfer rate reaches over 5 midge
64 KB 0a bits per second. In the time it takes
Region 0b to snap your fingers you can move a
To Move 0c 64 kilo byte block to the heap of your
0d program. The second reason for doing
0e this is that you can use the same
0f address routine for both extended and
expanded memory. This time you will form your own fictional 64 kilo
byte page frame in base memory and the physical address is one that you
will supply yourself to a 64 kilo byte array in base memory and move
the memory region from extended memory instead of adjusting the page
frame mapping context as with EMS.
Changing the Context Mapping (EMS) and moving data to it or Moving a
Memory Region (XMS)
(For XMS you may move a memory region (data) to
(your fictional page frame) a pointer you have specified in base memory.
(For EMS you move up to 64k to the page frame segment address returned
by EMM after you perform a page context switch "mapping".)
>DS:SI Pointer to memory move structure
Length dd ? ; 32 bit number of bytes to transfer
SourceHandle dw ? ; Handle of source block
SourceOffset dd ? ; 32 bit offset into source
DestHandle dw ? ; Handle of destination block
DestOffset dd ? ; 32-bit offset into destination block
GAZOWTAS: AX> 0 if successful
If source handle is set to 0000h, the SourceOffset is interpreted as a
standard segment:offset pair which refers to memory accessible by the
GAZINTAS:>AH 41(hex) Get Page Frame Segment Address
INT 67(hex) Call EMS driver
GAZOWTAS: BX> Page Frame Segment Address
GAZINTAS:>AH 44(hex) Map/Unmap Handle Pages.
AL Physical Page Number (refer to diagram for base memory)
BX Logical Page Number (refer to diagram for EMS memory)
-1 to unmap the physical page.
INT 67(hex) Call EMS driver
GAZOWTAS: DX> Handle
You may now move data to and from EMS memory by placing it at an offset
of 0-64 kilo bytes from the base frame address.
To make your data transfers faster, it is suggested that you do not use
a great many handles or pointers. Try to make your structures as large
as possible for EMS or XMS to maximize the use of them. Also here is an
easy way to calculate the offset into your page frame or your "fictional
page frame" for XMS:
0 Let us assume that this is your page frame. You
have defined a fixed length data field which is
1 8 kb or half of a page length. One of then data
fields is hatched at the top. The physical pages
2 are labeled to the right. Your full page frame
will contain 8 of these data fields. Now suppose
3 you are allocating 640 kb of extended or expanded
memory which you fill with these fields. You will
then have 80 fields in memory with 160 pages.
Give each of the page mappings its own number to use as an index to the
array of handles you need to access them. To get the number of your
page mapping to switch to, you can divide the number of field you wish
to access by the total contained in a single page frame context (8). If
I wish to access field number 42 then it will be in page mapping 42 div
8. Then it will be at page mapping number 5. You can use this number
as an indice to the array of handles which you will need to restore this
context or mapping. The offset into the page mapping will be the modula
of the field number (42) and the total number of fields that can be
contained within the page frame (8) times the field length.
In this simple example 42 mod 8 * field length = 2 * field length
= 16 kilo bytes
The 42'nd field 8kb in length would be in page frame context 5 at offset
16 kilo bytes.
There are many other functions available from both specifications. EMS
memory allows execution of code from EMS without understanding the inner
workings of the memory itself which is a nice advantage if you are an
assembler application programmer. On the other hand, XMS does not allow
execution of code in extended memory in real mode. An advantage of XMS
is that it allows data transfers larger than 64 kilo bytes at one time.
XMS does however allow you to execute code in a UMB which is a 64 kilo
byte block above the 640kb DOS boundary and below the one midge byte
boundary. The documentation for the XMS driver assumes a working
knowledge of the architecture of the 8088 on this topic. For a
description of the A20 line see the "iAPX 88 Book Chapter 3 Hardware
Design". This topic is beyond the scope of this article.
Please beware of assuming that this is a complete documentation. This
article is basically intended to make the two specifications easier to
understand and to give the author something to hand people when they ask
him what to do with that new memory that they just purchased. Also
beware that the treatment of errors and error handling has been
neglected. These topics and more are covered in the specifications. It
is hoped that this article will further your understanding of them.