
The
EMS TOOLKIT
for C developers











Copyright (c) 1989 Intel Corporation.  All rights reserved.
Intel Corporation, 5200 NE Elam Young Parkway, Hillsboro, OR  97124


First Edition            December, 1989


DISCLAIMER
Intel Corporation assumes no responsibility for errors that may appear in
this manual.  Nor does Intel make any commitment to update the
information contained in this manual.

Intel and Above are trademarks of Intel Corporation.




LIMITED WARRANTY
Intel Corporation excludes any and all implied warranties, including 
warranties of merchantability and fitness for a particular purpose. Intel 
makes no warranty of representation, either express or implied, with 
respect to this software, its quality, performance, merchantability, or 
fitness for a particular purpose.  Intel shall not have any liability for 
special, incidental, or consequential damages arising out of or resulting 
from the use or modification of this software. This software is provided as 
is.


USE OF PROGRAM PRODUCT You may use the Program Product on any one IBM or 
compatible personal computer, and copy the Object Code into any 
machine-readable form for your use of the Program Product; You may modify 
the Program Product material and/or merge or incorporate it into any 
general use software program of your development except for a utility 
program of similar nature to the Program Product.  You may freely reproduce 
any such program of your development; however, the merged or incorporated 
part of the Program Product will continue to be subject to all other 
provisions of this agreement.



CONTENTS


CHAPTER 1.  GETTING STARTED
How to use this book      1-2
What's on the disks?      1-3
Which library should I use?    1-5
Requirements    1-7
Installing the software   1-7

CHAPTER 2.  MEMLIB:  LIBRARY FOR C DEVELOPERS
Compiling and linking MEMLIB   2-3
Suggested sequence for using MEMLIB functions      2-4
MEMLIB functions list     2-5 -- 2-31

CHAPTER 3.  EMMLIB:  LIBRARY FOR ASSEMBLY LANGUAGE AND C DEVELOPERS
EMMLIB functions grouped by operation    3-3
EMMLIB functions in alphabetical order   3-7

APPENDIX A.  EXAMPLE PROGRAM

APPENDIX B.  ERROR MESSAGES

APPENDIX C.  TECHNICAL INFORMATION ABOUT MEMLIB
How MEMLIB provides access to expanded memory      C-1
How MEMLIB keeps track of expanded memory     C-2
How MEMLIB allocates expanded memory     C-2
Algorithms      C-4



Chapter 1
GETTING STARTED

This manual describes the toolkit for the Lotus-Intel-Microsoft Expanded 
Memory Specification (EMS).  The toolkit offers  experienced Microsoft C 
developers a quick and easy way to use expanded memory in their application 
programs.

If you are writing programs in some other Microsoft language (Fortran, 
Pascal, etc.), you may still be able to use the toolkit.  Read the 
mixed-language programming section in the Microsoft manual to find out how 
tomake calls to C from your language.

The toolkit includes the following libraries:

MEMLIB    A set of C functions that perform the "housekeeping"
          necessary to access and store data in expanded memory.  When you
          use MEMLIB, you won't need to worry about page frame size, 
          16K-byte boundaries, or interfacing with an assembly language 
          device driver.  See Chapter 2.

EMMLIB    A set of C-callable assembly language functions that call
          the EMS driver directly.  See Chapter 3.

These libraries allow you to use the full functionality of EMS without
spending countless hours writing assembly language programs.

This manual supplements the Lotus-Intel-Microsoft Expanded Memory
Specification manual (also referred to as the EMS manual).  The LIM
specification defines the software interface between an application
program and the expanded memory used by the program.

How to use this book
--------------------
This manual explains the features and operation of the EMS Toolkit
libraries.  Once you are familiar with the libraries and how to use
them, you can use this book as a quick reference for function names,
parameters, and error messages.

Here's what you'll find in each chapter of the manual: Chapter 1 -- Getting 
Started:   This is the chapter you're reading now.  Besides introducing you 
to the EMS Toolkit, this chapter lists the contents of the Intel diskettes, 
offers suggestions for which library to use, and tells you how to install 
the software.

Chapter 2 -- MEMLIB: Library for C Developers     This chapter describes 
how the MEMLIB library works, how to write code to use MEMLIB, and how to 
compile and link MEMLIB.  The last section (which makes up the bulk of this 
chapter) describes each of the MEMLIB functions in detail.

Chapter 3 -- EMMLIB:  Library for Assembly Language and C Developers 
Chapter 3 describes the EMMLIB library and how it works, and describes how 
to compile and link EMMLIB.  This chapter also includes lists of EMMLIB 
functions, and tells you how to get more information about each one.

Appendix A -- Example Progra:  This short example program
demonstrates how to use MEMLIB to store and access data in expanded memory.

Appendix B -- Error Messages:  This appendix lists the error messages
you may incur from MEMLIB, and suggests corrective actions.

Appendix C -- Technical Information About MEMLIB:  This appendix explains 
the more technical aspects of how MEMLIB allocates and frees expanded 
memory.  You don't need to know this information to use MEMLIB.

What's on the disks?
--------------------
The EMS Toolkit package includes four diskettes.  You'll need disks 1, 2 
and 3 to use either of the toolkit libraries.  If you are an assembly 
language developer and want to use expanded memory to execute code, you may 
want to use disk 4.  Disk 4 contains examples for assembly language 
programmers only.  (You don't need disk 4 to use MEMLIB or EMMLIB.)

The Intel EMS Toolkit diskettes contain the following files:

 Disk 1
Filename            Description
 MEMLIB.C            MEMLIB source code (functions)
 MEMLIB.H            MEMLIB include file (function prototypes)
 MEMINTRL.C          MEMLIB source code (internal MEMLIB utilities)
 ERRORS.H            MEMLIB error messages
 EMMLIB.LIB          EMM large-model library of C interface routines
 EMMLIB.H            EMMLIB include file

 Disk 2
Filename            Description
 SAMPLE.C            Short sample program using MEMLIB functions
 SAMPLE.MAK          "make" file for use with SAMPLE.C program
 ROLODEX.C           More detailed sample program using MEMLIB and EMMLIB
 ROLODEX.H           Header file for use with ROLODEX.C
 ROLODEX.DAT         Data file for ROLODEX.C
 ROLODEX.MAK         "make" file for use with ROLODEX.C program
 EMMLIB   <DIR>      Directory containing EMMLIB function files

 Disk 3
Filename            Description
 EMMLIB   <DIR>      Directory containing the rest of the EMMLIB function
                     files and other EMMLIB support files

 Disk 4
Filename            Description
 LOADCODE.ASM        Sample assembly language program that demonstrates how
                     to execute code from expanded memory
 HELLO.ASM           Sample code the LOADCODE.ASM will execute
 LOADCODE.MAK        "make" file for LOADCODE.ASM


Which library should I use?
---------------------------
The way in which your application will use expanded memory determines
which library you should use.
o    If you are writing code in C and you want to store data in
     expanded memory, you can use MEMLIB; it offers routines to do this 
     quickly and easily.
o    If you understand the Lotus-Intel-Microsoft Expanded Memory
     Specification and want to make expanded memory calls directly from C,
     you must use EMMLIB.  For example, if you want to use named handles for
     the blocks of expanded memory you allocate, you need to use EMMLIB.
o    If you want to use expanded memory for more complex uses such as
     executing code, you must write your program in assembly language and
     make EMS function calls directly.

Requirements
------------
The functions in the EMS Toolkit libraries assume a few things about
your computer's environment.
+
o    Both MEMLIB and EMMLIB assume that Microsoft C version 5.1 and its
     "include" files are available on your system.
o    MEMLIB assumes that you are using a C "large-memory-model."  EMMLIB
     allows you to use any size memory model.
o    The sample "make" files assume that the Microsoft utility MAKE.EXE
     is available; that the source code, header files, and EMMLIB.LIB are
     in one directory; and that the EMMLIB function files are in a 
     subdirectory named \EMMLIB.  Intel recommends that you install the 
     MEMLIB files and your application's source code in the same 
     subdirectory.


Installing the software
-----------------------
The following sequence is the way we suggest you install the EMS
Toolkit software.  For this example, suppose the application source code is
kept a subdirectory named \APP.
1    Insert Intel Toolkit diskette 1 into drive A.

2    At the DOS prompt, type the following:
          XCOPY  A:\*.*  C:\APP  /s

3    Repeat the first two steps with diskettes 2 and 3.

This command will copy the contents of the Intel disks to the \APP
directory on drive C.

/s  means that all files in subdirectories below A:\ will be copied,
and will keep the same directory structure.  (If you'd like more
information about the XCOPY command, refer to a DOS manual.)



Chapter 2
MEMLIB:  LIBRARY FOR C DEVELOPERS


MEMLIB is a set of functions that allow developers to manage expanded
memory similar to the way they manage conventional memory with C.
MEMLIB functions look and act like some of the more familiar C functions
(malloc and free, for example).

MEMLIB's internal structure does the "housekeeping" necessary to access and 
store data in expanded memory.  When you use MEMLIB, you won't need to 
worry about page frame size, 16K-byte boundaries, or interfacing with an 
assembly language device driver.

MEMLIB lets you allocate and access any size block of memory up to 64K
bytes.  It tracks available free memory, and uses a "best fit"
algorithm to allocate new blocks.

When you use MEMLIB, you just make a few C calls, and the libraries do
the rest.
Your application makes a call to MEMLIB, and MEMLIB in turn calls
the EMMLIB library to access expanded memory.  EMMLIB then translates
the C calls into assembly language, and passes the calls on to the EMS
driver.


Compiling and linking MEMLIB
----------------------------
Because MEMLIB uses EMMLIB calls to perform its functions, you must
link and compile both libraries when you use MEMLIB.  (The toolkit includes
a previously made EMMLIB.LIB file to use with MEMLIB.)  You can compile
and link either with "make" (a Microsoft C utility) or manually.

Using the "make" file
---------------------
The easiest way to compile and link a number of interdependent modules
is to use a "make" file.  A "make" file lists all of the modules and
their dependencies in a single file.

A "make" file for a program called "SAMPLE" to use MEMLIB should
contain the following lines.

(contents of the file sample.mak)
memlib.obj: memlib.c memlib.h errors.h emmlib.h
            CL /C /AL memlib.c

sample.obj: sample.c memlib.h errors.h
            CL /C /AL sample.c

sample.exe: sample.obj memlib.obj
            link sample memlib,,, emmlib.lib

To use the "make" file, type the following line at the DOS prompt:
                make sample.mak

If you change any file, just run the "make" file again to recompile
and relink.


Compile and link manually
-------------------------
Use the following command to compile the MEMLIB library:
                 CL  /C /AL memlib.c

Use the following command to link MEMLIB to a program called "SAMPLE":
                 LINK sample memlib, sample, nul, emmlib.lib


Suggested sequence for using MEMLIB functions
---------------------------------------------
MEMLIB functions provide a convenient way for your application to
store data in expanded memory.  To use expanded memory (and MEMLIB) most
effectively, your application should follow this sequence of actions:

 1.  Allocate memory     efmalloc allocates a block of expanded memory
                         and returns a token (a block ID) to the application.

 2.  Access memory       seteptrs uses the token returned by efmalloc to
                         access the blocks you allocated.  Also set1eptr, 
                         set2eptrs, set3eptrs.

 3.  Free memory         effree frees previously allocated blocks of memory.
                         Also effreeall. 

The other MEMLIB functions are provided for more specialized uses.
You don't need to use them to store data in expanded memory.

**CAUTION**  Be sure to check the error codes that MEMLIB returns.  If
             you try to allocate and use expanded memory when EMM isn't 
             installed, you may lose data or overwrite some other area of 
             memory.  To learn more about MEMLIB's error codes, read 
             Appendix B.

The next section describes all of the MEMLIB functions in detail. Each 
description includes the purpose of the function, the calling sequence to 
use, and a short example program.

The functions are listed in alphabetical order for easy reference. Below is 
a complete list of the functions grouped according to the operations they 
perform.  To learn about a particular function, turn to the specified page.


  Function               Page
Allocating Memory         
  efmalloc               2-10

Accessing Memory
  seteptrs               2-24
  set1eptr               2-28
  set2eptrs              2-30
  set3eptrs              2-32

Freeing Memory
  effree                 2-6
  effreeall              2-8

Other
  ememavl                2-12
  ememmax                2-14
  emsize                 2-16
  push_context           2-21
  pop_context            2-18

This is all you need to begin using MEMLIB to get expanded memory for your 
application program.  If you would like more detailed information about how 
MEMLIB works, read Appendix C, Technical Information about MEMLIB.


EFFREE
------

PURPOSE
The effree function frees a block of memory that efmalloc had allocated.  
Effree is analogous to the standard C function free.

**CAUTION**  The expanded memory manager (MEMLIB) is NOT a part of DOS.
             MEMLIB will not automatically free your allocated blocks when
             you exit your application.  Your application should free all
             blocks during any kind of exit, including <CTL><BREAK> or 
             other error conditions.  (See Function 6, effreeall.)
         
CALLING SEQUENCE
unsigned int status;
status = effree (token);

       unsigned int  token    The token that efmalloc returned when it 
                              allocated that block of memory. (input)

STATUS
PASSED (zero)           The block is now free.
error (non-zero value)  See Appendix B for descriptions of error codes.

EXAMPLE
#include "memlib.h"
#include "errors.h"

unsigned int token;
unsigned int status;

status = efmalloc (100, &token);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
    ...
status = effree (token);
if (status == PASSED)
    /* Token is free, continue normal code */
else
    /* error condition */



EFFREEALL
---------

PURPOSE
The effreeall function frees all expanded memory blocks, pages, and
handles effreeallallocated by your application.  Effreeall is a convenient 
function to use when you are ending your application.

**CAUTION** The expanded memory manager (MEMLIB) is NOT a part of DOS.
            MEMLIB will not automatically free your allocated blocks when 
            you exit your application.  Your application should free all 
            blocks during any kind of exit, including <CTL><BREAK> or
            other error conditions.

CALLING SEQUENCE
unsigned int status;
status = effreeall ( );

STATUS
PASSED (zero)           All blocks, handles, and pages are now free.
error (non-zero value)  See Appendix B for descriptions of error codes.

EXAMPLE
#include "memlib.h"
#include "errors.h"

unsigned int status;
    ...
status = effreeall();
if (status == PASSED)
    printf ("effreeall() successful");
else
    /* error condition */



EFMALLOC
-------- 

PURPOSE
The efmalloc function is analogous to the standard C function malloc.
In the same way that malloc allocates conventional memory, the efmalloc
function allocates a block of memory from expanded memory pool.  You can
allocate up to 64K bytes with a call to efmalloc.

There is one significant difference between malloc and efmalloc.  A
call to malloc allocates memory and returns a pointer, giving you access to
that memory.  Efmalloc allocates memory, but doesn't return a pointer.

Here's why:  You can't access expanded memory the same way you access
conventional memory.  You have to map a block of expanded memory into
the page frame before you can access it.  Once you've mapped in a block,
you get a pointer to that block.   With MEMLIB, you access a block using
the seteptrs function (also set1eptr, set2eptrs, and set3eptrs).  See the
seteptrs function for more information about accessing expanded memory.

**NOTE**  You must use the effree (or effreeall) function to free a block of
          memory allocated with efmalloc.

CALLING SEQUENCE
unsigned int status;
status = efmalloc (size, &token);

     unsigned int  size       The size (in bytes) desired for the memory 
                              block.  The size must be greater than zero.
                              (input)
     unsigned int  token      An identifier assigned to the memory block
                              you've allocated.  The token identifies that 
                              memory block in subsequent calls to other 
                              functions such as set1eptr.  (output)

STATUS
PASSED (zero)           The memory manager has allocated the block as
                        expanded memory.
error (non-zero value)  See Appendix B for descriptions of error
codes.

EXAMPLE
#include "memlib.h"
#include "errors.h"

unsigned int size;
unsigned int status;
unsigned int token;

size = 100 * sizeof(int);
status = efmalloc (size, &token);
if (status == PASSED)
    /* Continue normal code.  We have allocated */
    /* space to store 100 integer values.        */
else
    /* error condition */


EMEMAVL
-------

PURPOSE
The ememavl function computes the amount (in bytes) of expanded memory
available in your computer.  The amount returned by ememavl is the total of
all expanded memory available -- it is not the largest contiguous block of
expanded memory (see ememmax).

CALLING SEQUENCE
unsigned int status;
status = ememavl (&size)
     unsigned long  size      The amount of expanded memory (in bytes)
                              ememavl returns (output)

STATUS
PASSED (zero)           The memory manager has returned the total amount of 
                        expanded memory.

error (non-zero value)  See Appendix B for descriptions of the error codes.

EXAMPLE
#include "memlib.h"
#include "errors.h"

unsigned int  status;
unsigned long size;

status = ememavl (&size);
if (status == PASSED)
    printf ("Exp. memory avail. = %lu bytes \n",
             size);
else
    /* error condition */


EMEMMAX
-------

PURPOSE
The ememmax function returns the size (in bytes) of the largest usable
ememmaxblock of expanded memory.  Because blocks of free memory can be 
fragmented, this amount may be smaller than the total amount of free 
expanded memory.

CALLING SEQUENCE
unsigned int status;
status = ememmax (&size);

     unsigned int  size  The number of bytes in the largest usable block of
                         expanded memory.  (output)

STATUS
PASSED (zero)           The memory manager has returned the size of the 
                        largest usable block.
error (non-zero value)  See Appendix B for descriptions of the error codes.

EXAMPLE
#include "memlib.h"
#include "errors.h"

unsigned int status;
unsigned int size;

status = ememmax (&size);
if (status == PASSED)
    printf ("Largest block available = %u bytes \n",
             size);
else
    /* error condition */


EMSIZE
------

PURPOSE
The emsize function computes the size of a previously allocated block
of expanded memory.  Identify the block using the token that efmalloc
returned emsizeafter allocating it.

CALLING SEQUENCE
unsigned int status;
status = emsize (token, &size);

     unsigned int  token      Identifier for the block. (input)
     unsigned int  size       The size of the block (in bytes).  (output)

STATUS
PASSED (zero)           The token was valid; the size is computed.
error (non-zero value)  See Appendix B for descriptions of error
codes.

EXAMPLE
#include "memlib.h"
#include "errors.h"

unsigned int status;
unsigned int token;
unsigned int size;

status = efmalloc (200, &token);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
    ...
status = emsize (token, &size)
if (status == PASSED)
    printf ("Size of block = %u bytes \n", size);
else
    /* error condition */


POP_CONTEXT
-----------

PURPOSE
The pop_context function restores the context saved by the
push_context function.

**NOTE**  When you call pop_context, you lose access to the blocks that
          were mapped in at the time of the call.

CALLING SEQUENCE
unsigned int status;
status = pop_context( );

STATUS
PASSED (zero)           The context of block ( ) is restored.
error (non-zero value)  See Appendix B for descriptions of error codes.

EXAMPLE
#include "memlib.h"
#include "errors.h"

unsigned int status;
unsigned int token1;
unsigned int token2;
void         *pointer1;
void         *pointer2;

status = efmalloc (100, &token1);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
   ...
status = efmalloc (200, &token2);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
    ...
status = set1eptr (token1, &pointer1);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
    ...
status = push_context();
if (status != PASSED)
    /* error condition */
else
    /* save context for block 1 */
status = set1eptr (token2, &pointer2);
if (status != PASSED)
    /* error condition */
else
    /* context for block 2 is active -- */
    /* can't access block 1             */
   ...
status = pop_context();
if (status != PASSED)
    /* error condition */
else
    /* context for block 1 is active -- */
    /* can't access block 2             */


PUSH_CONTEXT
------------

PURPOSE
The push_context function provides a convenient way to make the context of 
a mapped block available for later use.  The context tells MEMLIB which 
blocks are mapped into which physical pages. Push_context saves the 
context, making it easy to restore access to whatever block(s) were mapped 
in at the time of the call to push_context.

Restore the context you've "pushed" using the pop_context function
(see Function 12).

CALLING SEQUENCE
unsigned int status;
status = push_context( );

STATUS
PASSED (zero)           The memory manager has pushed the context of the 
                        block onto a stack.
error (non-zero value)  See Appendix B for descriptions of error codes.

**NOTE**  To save a context, MEMLIB needs to allocate a small amount of
          conventional memory.  MEMLIB allocates this memory dynamically--
          allocating just enough memory to save the context at the time of
          the call (usually less than 20 bytes).

EXAMPLE
#include "memlib.h"
#include "errors.h"

unsigned int status;
unsigned int token1;
unsigned int token2;
void         *pointer1;
void         *pointer2;

status = efmalloc (100, &token1);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
    ...
status = efmalloc (200, &token2);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
   ...
status = set1eptr (token1, &pointer1);
   ...
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
    ...
status = push_context();
if (status != PASSED)
    /* error condition */
else
    /* save context for block 1 */
    ...
status = set1eptr (token2, &pointer2);
if (status != PASSED)
    /* error condition */
else
    /* context for block 2 is active -- */
    /* can't access block 1             */
   ...
status = pop_context();
if (status != PASSED)
    /* error condition */
else
    /* context for block 1 is active -- */
    /* can't access block 2             */


SETEPTRS
--------

PURPOSE
The seteptrs function attempts to gain access to the blocks you specify.  
Seteptrs then returns pointers to those blocks. Use this function if you 
need access to more than three blocks at once.   Use set1eptr, set2eptrs, 
or set3eptrs to access 1, 2, or 3 blocks of memory.

You can access all allocated blocks, but not necessarily all at the same 
time.  You can access only that memory which is mapped into the page frame.  
Each expanded memory page is 16K, so if your page frame is 64K bytes, it 
will hold four pages, and any four blocks of 16K or less can be mapped in 
at one time.  (EMS page frames are guaranteed to be at least 64K bytes.)

If the combined size of all memory blocks you allocate during your 
program's execution is less than the size of your page frame, you can 
access all of the blocks at once.

For a more detailed description of page frames, read Chapter 1 in the EMS 
manual.

**NOTE**  Seteptrs will unmap any pages that were mapped in before the call
to seteptrs.  If you want to call seteptrs, but want to regain access to a 
block that had been mapped in before the call, use the push_context 
function (before you call seteptrs).  See push_context.

CALLING SEQUENCE
unsigned int status;
status = seteptrs (num_blocks, tokens, pointers);

       unsigned int num_blocks          The number of blocks you want to
                                        access. (input)
       unsigned int tokens[num_blocks]  An array of tokens that efmalloc
                                        returned when it allocated the 
                                        memory blocks.  (input)
       void    *pointers[num_blocks]    The array of pointers seteptrs
                                        returns.  Pointer[i] will point to 
                                        the block of memory identified by 
                                        token[i]. (output)

STATUS
PASSED (zero)           You can gain access to all of the specified blocks.
error (non-zero value)  See Appendix B for descriptions of error codes.

EXAMPLE
#include "memlib.h"
#include "errors.h"

unsigned int tokens[2];
      /* Set array size for the number of blocks */
      /* you wish to allocate (in this case, 2)  */
void far     *pointers[2];
unsigned int status;

status = efmalloc (200, &token[0]);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
    ...
status = efmalloc (600, &token[1]);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
    ...
status = seteptrs (2, tokens, pointers)
if (status == PASSED)
    /* blocks are mapped in -- use */
    /* pointers to reference them  */
else
    /* error condition */


SET1EPTR
--------

PURPOSE
The set1eptr function provides access to a single block of memory by 
calling seteptrs.  (This is a convenience routine for programmers who don't 
want to declare the arrays required by seteptrs.)

CALLING SEQUENCE
unsigned int status;
status = set1eptr (token, &pointer);

     unsigned int  token      The token that efmalloc returned when it
                              allocated that block of memory. (input)
     void          *pointer   The pointer that set1eptr returns to the 
                              application. (output)

STATUS
PASSED (zero)           You have access to the block.
error (non-zero value)  See Appendix B for descriptions of error codes.

EXAMPLE
#include "memlib.h"
#include "errors.h"

void         *pointer;
unsigned int token;
unsigned int status;

status = efmalloc (500, &token);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
    ...
status = set1eptr (token, &pointer);
if (status == PASSED)
    /* You have access to that block.  Use   */
    /* the pointer as the block's reference. */
else
    /* error condition */


SET2EPTRS
---------

PURPOSE
The set2eptrs function provides access to two blocks of expanded memory by 
calling seteptrs.  (This is a convenience routine for programmers who don't
want to declare the arrays required by seteptrs.)

CALLING SEQUENCE
unsigned int status;
status = set2eptrs (token1, token2, &pointer1, &pointer2);

     unsigned int  token1     The token efmalloc returned when it allocated
                              the first block of memory. (input)
     unsigned int  token2     The token efmalloc returned when it allocated
                              the second block of memory. (input)
     void          *pointer1  The pointer to the first block that set2eptrs
                              returns. (output)
     void          *pointer2  The pointer to the second block that set2eptrs
                              returns. (output)

STATUS
PASSED (zero)           You have access to these two blocks.
error (non-zero value)  See Appendix B for descriptions of error codes.

EXAMPLE
#include "memlib.h"
#include "errors.h"

void         *pointer1;
void         *pointer2;
unsigned int token1;
unsigned int token2;
unsigned int status;

status = efmalloc (500, &token1);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
    ...
status = efmalloc (900, &token2);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
   ...
status = set2eptrs (token1, token2, &pointer1, &pointer2);
if (status == PASSED)
    /* You have access to these blocks. */
    /* Use the pointers as references.  */
else
    /* error condition */


SET3EPTRS
---------

PURPOSE
The set3eptrs function provides access to three blocks of expanded memory 
by calling seteptrs.  (This is a convenience routine for programmers who 
don't want to declare the arrays required by seteptrs.)

CALLING SEQUENCE
unsigned int status;
status = set3eptrs (token1, token2, token3, &pointer1, &pointer2, &pointer3);

     unsigned int  token1     The token efmalloc returned when it allocated
                              the first block of memory. (input)
     unsigned int  token2     The token efmalloc returned for the second 
                              block. (input)
     unsigned int  token3     The token efmalloc returned for the third 
                              block. (input)
     void          *pointer1  The pointer to the first block that set3eptrs 
                              returns. (output)
     void          *pointer2  The pointer to the second block that set3eptrs
                              returns. (output)
     void          *pointer3  The pointer to the third block. (output)

STATUS
PASSED (zero)            You can gain access to these three blocks.
error (non-zero value)   See Appendix B for descriptions of error codes.

EXAMPLE
#include "memlib.h"
#include "errors.h"

void         *pointer1;
void         *pointer2;
void         *pointer3;
unsigned int token1;
unsigned int token2;
unsigned int token3;
unsigned int status;

status = efmalloc (500, &tok1);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
    ...
status = efmalloc (900, &tok2);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
   ...
set3eptrs status = efmalloc (300, &tok3);
if (status != PASSED)
    /* error condition */
else
    /* continue normal code */
    ...
status = set3eptrs (token1, token2, token3,
                     &pointer1, &pointer2, &pointer3);
if (status == PASSED)
    /* Blocks are allocated; use pointers as references */
else
    /* error condition */




Chapter 3
EMMLIB:  LIBRARY FOR ASSEMBLY LANGUAGE
         AND C DEVELOPERS


EMMLIB is a collection of assembly language functions that call EMS 
functions directly. This library provides full EMS capability to C 
programmers; EMMLIB performs all of the functions listed in the 
Lotus-Intel-Microsoft Expanded Memory Specification.

If you want to use expanded memory for more complex uses such as named 
handles, alternate register sets, or page aliasing, then you will need the 
advanced functions available from EMMLIB.

**NOTE**  LIM-EMS is a powerful and complex specification.  The EMMLIB 
routines are designed to allow high level languages to use the full power 
of EMM directly; the routines are not designed to simplify the 
specification.


The LIM specification has approximately 30 functions, and most of these 
functions have subfunctions.  EMMLIB provides C-callable routines for all 
of the functions and subfunctions (a total of 59).  The EMMLIB functions 
are described in comment headers in the .ASM files on the Intel diskettes.  
Each filename includes the number of the EMS function corresponding to the 
EMMLIB function.

Table 3-1 is a list of the functions available in EMMLIB, grouped according 
to the operations they perform.  The table includes the name of the 
function, the file containing the code and description, and the 
corresponding function number from EMS.

Table 3-2 lists the functions in alphabetical order, and includes cross 
references to the filename and corresponding EMS function.

For a detailed description of a function, read the <Filename> file using 
any text editor, or read the description of the corresponding assembly 
language function <EMS Function No.> in the EMS manual.

For example, to map in a couple of pages of expanded memory, you could use 
the map_unmap_pages function.  To learn how to use this function, you would 
read either file EMMLIB23.ASM or the description of Function 17 in the EMS 
manual.

**NOTE**  EMMLIB.H is a header file that provides your C code with all of 
the "typedefs", function prototypes, and "#defines" that you need to call 
the EMMLIB functions.  Be sure to include this file when you use any EMMLIB 
function.


Table 3-1      EMMLIB Functions grouped by operation

______________________________________________________________

Function Name                       Filename    EMS Function No.
_____________________________________________________________
presence, status, and version
  EMM_installed                   EMM01-B.ASM      1
  get_EMM_status                  EMM01-A.ASM      1
  get_EMM_version                 EMM07-A.ASM      7

memory allocation
  get_unalloc_page_count          EMM03-A.ASM      3
  get_alloc_page_count            EMM03-B.ASM      3
  get_total_page_count            EMM03-C.ASM      3
  alloc_pages                     EMM04-A.ASM      4
  dealloc_pages                   EMM06-A.ASM      6
  realloc_pages                   EMM18-A.ASM      18
  get_unalloc_raw_page_count      EMM26-A.ASM      26
  get_alloc_raw_page_count        EMM26-B.ASM      26
  get_total_raw_page_count        EMM26-C.ASM      26
  alloc_std_pages                 EMM27-A.ASM      27
  alloc_raw_pages                 EMM27-B.ASM      27

mappable memory region information
  get_page_frame_seg              EMM02-A.ASM      2
  get_mappable_conv_regions       EMM25-A.ASM      25
  get_mappable_exp_regions        EMM25-A.ASM      25
  get_mappable_regions            EMM25-A.ASM      25
  get_mappable_conv_region_count  EMM25-A.ASM      25
  get_mappable_exp_region_count   EMM25-A.ASM      25
  get_page_frame_count            EMM25-A.ASM      25
  get_mappable_region_count       EMM25-C.ASM      25

memory mapping
  map_unmap_page                  EMM05-A.ASM      5
  map_unmap_pages                 EMM17-A.ASM      17

memory mapping context
  save_context                    EMM08-A.ASM      8
  restore_context                 EMM09-A.ASM      9
  get_context                     EMM15-A.ASM      15
  set_context                     EMM15-B.ASM      15
  get_set_context                 EMM15-C.ASM      15
  get_context_size                EMM15-D.ASM      15
  get_partial_context             EMM16-A.ASM      16
  get_partial_context_size        EMM16-C.ASM      16
  set_partial_context             EMM16-B.ASM      16

memory movement and exchange
  move_memory_region              EMM24-A.ASM      24
  xchg_memory_region              EMM24-B.ASM      24

handle management
  get_handle_count                EMM12-A.ASM      12
  get_handle_pages                EMM13-A.ASM      13
  get_all_handles_pages           EMM14-A.ASM      14
  get_handle_attrib               EMM19-A.ASM      19
  set_handle_attrib               EMM19-B.ASM      19
  get_attrib_capability           EMM19-C.ASM      19
  get_handle_name                 EMM20-A.ASM      20
  set_handle_name                 EMM20-B.ASM      20
  get_handle_dir                  EMM21-A.ASM      21
  search_handle_name              EMM21-B.ASM      21 
  get_total_handles               EMM21-C.ASM      21

program flow control
  alter_map_jump                  EMM22-A.ASM      22
  alter_map_call                  EMM23-A.ASM      23
  get_alter_map_call_stack_size   EMM23-B.ASM      23

Operating System only
  enable_OS_fcns                  EMM30-A.ASM      30
  disable_OS_fcns                 EMM30-B.ASM      30
  return_OS_access_key            EMM30-C.ASM      30
  get_hw_info                     EMM26-D.ASM      26
  get_alt_reg_set                 EMM28-A.ASM      28
  set_alt_reg_set                 EMM28-B.ASM      28
  get_alt_context_size            EMM28-C.ASM      28
  alloc_alt_reg_set               EMM28-D.ASM      28
  dealloc_alt_reg_set             EMM28-E.ASM      28
  alloc_DMA_reg_set               EMM28-F.ASM      28
  enable_DMA_reg_set              EMM28-G.ASM      28
  disable_DMA_reg_set             EMM28-H.ASM      28
  dealloc_DMA_reg_set             EMM28-I.ASM      28
  prep_EMM_warmboot               EMM29-A.ASM      29
  _____________________________________________





Table 3-2 EMMLIB Functions in alphabetical order
________________________________________________________________

Function Name                        Filename    EMS Function No.
_________________________________________________________________

alloc_alt_reg_set                    EMM28-D.ASM      28
alloc_DMA_reg_set                    EMM28-F.ASM      28
alloc_pages                          EMM04-A.ASM      4
alloc_std_pages                      EMM27-A.ASM      27
alloc_raw_pages                      EMM27-B.ASM      27
alter_map_jump                       EMM22-A.ASM      22
alter_map_call                       EMM23-A.ASM      23

dealloc_alt_reg_set                  EMM28-E.ASM      28
dealloc_DMA_reg_set                  EMM28-I.ASM      28
dealloc_pages                        EMM06-A.ASM      6
disable_DMA_reg_set                  EMM28-H.ASM      28
disable_OS_fcns                      EMM30-B.ASM      30

EMM_installed                        EMM01-B.ASM      1
enable_DMA_reg_set                   EMM28-G.ASM      28
enable_OS_fcns                       EMM30-A.ASM      30

get_all_handles_pages                EMM14-A.ASM      14
get_alloc_page_count                 EMM03-B.ASM      3
get_alloc_raw_page_count             EMM26-B.ASM      26
get_alt_context_size                 EMM28-C.ASM      28
get_alter_map_call_stack_size        EMM23-B.ASM      23
get_alt_reg_set                      EMM28-A.ASM      28
get_attrib_capability                EMM19-C.ASM      19

get_context                          EMM15-A.ASM      15
get_context_size                     EMM15-D.ASM      15
get_EMM_status                       EMM01-A.ASM      1
get_EMM_version                      EMM07-A.ASM      7
get_handle_count                     EMM12-A.ASM      12
get_handle_pages                     EMM13-A.ASM      13
get_handle_attrib                    EMM19-A.ASM      19
get_handle_name                      EMM20-A.ASM      20
get_handle_dir                       EMM21-A.ASM      21
get_hw_info                          EMM26-D.ASM      26

get_mappable_conv_regions            EMM25-A.ASM      25
get_mappable_conv_region_count       EMM25-A.ASM      25
get_mappable_exp_regions             EMM25-A.ASM      25
get_mappable_exp_region_count        EMM25-A.ASM      25
get_mappable_regions                 EMM25-B.ASM      25
get_mappable_region_count            EMM25-C.ASM      25
get_page_frame_count                 EMM25-A.ASM      25
get_page_frame_seg                   EMM02-A.ASM      2

get_partial_context                  EMM16-A.ASM      16
get_partial_context_size             EMM16-C.ASM      16
get_set_context                      EMM15-C.ASM      15
get_total_handles                    EMM21-C.ASM      21
get_total_page_count                 EMM03-C.ASM      3
get_total_raw_page_count             EMM26-C.ASM      26
get_unalloc_page_count               EMM03-A.ASM      3
get_unalloc_raw_page_count           EMM26-A.ASM      26

map_unmap_page                       EMM05-A.ASM      5
map_unmap_pages                      EMM17-A.ASM      17
move_memory_region                   EMM24-A.ASM      24

prep_EMM_warmboot                    EMM29-A.ASM      29

realloc_pages                        EMM18-A.ASM      18
restore_context                      EMM09-A.ASM      9
return_OS_access_key                 EMM30-C.ASM      30

save_context                         EMM08-A.ASM      8
search_handle_name                   EMM21-B.ASM      21
set_alt_reg_set                      EMM28-B.ASM      28
set_context                          EMM15-B.ASM      15
set_handle_attrib                    EMM19-B.ASM      19
set_handle_name                      EMM20-B.ASM      20
set_partial_context                  EMM16-B.ASM      16

xchg_memory_region                   EMM24-B.ASM      24
_________________________________________________________




Appendix A
EXAMPLE PROGRAM

/* The intent of this example program is to show how to store and         */
/* manipulate data in expanded memory using the C Memory Manager --       */
/* MEMLIB.                                                                */
/*                                                                        */
/* This example shows what developers need to do in order to manipulate   */
/* expanded memory in their own applications by using MEMLIB routines.    */
/* For a more detailed program that uses a doubly linked list data        */
/* structure, see the ROLODEX.C program.                                  */
/*                                                                        */
/* This program follows a seven-step algorithm:                           */
/*   1.  Check the total amount of expanded memory available.             */
/*   2.  Find the largest contiguous block of free expanded memory.       */
/*   3.  Allocate expanded memory for two strings.                        */
/*   4.  Calculate the size of the block allocated for the first string.  */
/*   5.  Print the two strings.                                           */
/*   6.  Manipulate the data by appending one string to the other.        */
/*   7.  Print the new string.                                            */
/**************************************************************************/

#include <stdio.h>
#include <string.h>

/**************************************************************************/
/* The two header files below contain the function prototypes for         */
/* MEMLIB routines and the error messages specific to MEMLIB.  Keep in    */
/* mind that MEMLIB calls EMMLIB functions, so you may get an error code  */
/* relating to EMS.  (EMS codes are explained in Table A-2 in the         */
/* EMS manual.)  You must include these two headers for any application   */
/* that will use MEMLIB.  If you are going to use any EMMLIB calls        */
/* directly, you must also include "emmlib.h."                            */
/**************************************************************************/

#include "memlib.h"
#include "errors.h"
#define PASSED          0           /* If the MEMLIB call was successful */
                                    /* it will return a zero.            */
#define MAX_STRING_SIZE 255
void abort (unsigned short);        /* If we encounter any errors, we    */
                                    /* call this function to "clean up." */
void main()
{
   unsigned short status;           /* The status after a MEMLIB call.   */
   char           *string1;         /* Our first data item.              */
   char           *string2;         /* Our second data item.             */
   unsigned int   token1;           /* The identifier to our first       */
                                    /* block of expanded memory.         */
   unsigned int   token2;           /* The identifier to our second      */
                                    /* block of expanded memory.         */
   unsigned int   max_block_size;   /* The size of the largest           */
                                    /* allocatable contiguous block.     */
   unsigned int   size;             /* The size of one of our blocks.    */
   unsigned long  max_exp_mem;      /* The amount of expanded mem avail. */

   /***********************************************/
   /* See how much total expanded memory we have. */
   /***********************************************/

   status = ememavl (&max_exp_mem);
   if (status != PASSED)
   {
     printf ("Unable to obtain total expanded memory size.\n");
     abort (status);
   }
   else
     printf ("Total expanded memory available: %lu\n", max_exp_mem);

   /*************************************************/
   /* Get the size of the largest contiguous block. */
   /*************************************************/

   status = ememmax (&max_block_size);
   if (status != PASSED)
   {
     printf ("Unable to obtain the largest contiguous block.\n");
     abort (status);
   }
   else
     printf ("Largest contiguous block: %u\n\n", max_block_size);

   /*********************************************/
   /* Allocate the memory and check for errors. */
   /*********************************************/

   status = efmalloc (MAX_STRING_SIZE, &token1);
   if (status != PASSED)
   {
     printf ("Unable to allocate first block.\n");
     abort (status);
   }
   else
     printf ("Allocated space for the first block.\n");

   status = efmalloc (MAX_STRING_SIZE, &token2);
   if (status != PASSED)
   {
     printf ("Unable to allocate second block.\n");
     abort (status);

   else
     printf ("Allocated space for the second block.\n\n");

   /********************************/
   /* Check the size of block one. */
   /********************************/

   status = emsize (token1, &size);
   if (status != PASSED)
   {
     printf ("Unable to obtain size for block %u\n", token1);
     abort (status);
   }
   else
     printf ("Size of block %u is %u\n\n", token1, size);

   /***********************************************/
   /* Map in the two blocks, checking for errors. */
   /***********************************************/

   status = set2eptrs (token1, token2, & (char *) string1, & (char *)string2);
   if (status != PASSED)
   {
     printf ("Unable to map in the two blocks.\n");
     abort (status);
   }
   else
     printf ("Blocks mapped in.\n\n");

    printf ("Putting data in expanded memory.\n\n");

   /*********************************************/
   /* Store values in expanded memory using the */
   /* pointers given to us from set2eptrs().    */
   /*********************************************/

   strcpy (string1, "'What if life is an illusion and nothing exists?'");
   strcpy (string2, "'In that case, I definitely overpaid for my carpet.' 
                      --Woody Allen");

   printf ("String1: %s\n", string1);
   printf ("String2: %s\n\n", string2);

   /**************************************************/
   /* Still using the pointers, manipulate the data. */
   /**************************************************/

   printf ("Manipulating data in expanded memory.\n");

   strcat (string1, string2);

   printf ("String1: %s\n\n", string1);

   /**************************************************************/
   /* Free all expanded memory we've allocated.  If effreeall()  */
   /* is unsuccessful, we don't want to call abort() because     */
   /* abort() would just call effreeall() again.                 */
   /**************************************************************/

   status = effreeall();
   if (status != PASSED)
   {
     printf ("Unable to free memory.\n");
     printf ("ERROR %X\n", status);
     exit (2);
   }
   else
     printf ("Expanded memory freed.\n");
}

/****************************************************************************/
/* Aborts the program with an error message.  Effreeall() frees all         */
/* pages, blocks, and handles associated with this application.  It is      */
/* imperative that you release all the expanded memory you've allocated     */
/* before exiting, so that other applications can use this memory.  If      */
/* you don't, you'll have to reboot to recover the lost memory.  Notice     */
/* also that a call to effreeall() checks the EMM status.  In case that     */
/* call fails,  you'll get an error condtion back to let you know the       */
/* memory was not freed.                                                    */
/*                                                                          */
/* You should always call effreeall() before aborting your program, even    */
/* if you get an error before allocating any memory.  Effreeall() will free */
/* the handles and memory that MEMLIB allocated when it was initialized.    */
/****************************************************************************/

void abort (status)
unsigned short status;
{
   printf ("ERROR %X\n", status);
   status = effreeall();
   if (status != PASSED)
   {
      printf ("ERROR %X from effreeall().\n");
      printf ("Expanded memory not freed.\n");
   }
exit (1);
}




Appendix B
MEMLIB ERROR MESSAGES

This appendix lists the MEMLIB error codes that may appear when you are 
running your application.  The codes are listed in numerical order.  Each 
error code description includes:
 o   the hexadecimal number of the error code
 o   a one-line description of the error
 o   a short explanation of why the error message appeared
 o   what you should do to correct the error

Because MEMLIB makes calls to EMMLIB functions, you may see messages other 
than those listed here.  Each EMMLIB error message includes a hexadecimal 
code number used by the Expanded Memory Specification (EMS).  To find out 
more about these errors, look up the code number in Table A-2, "Status and 
Function Code Cross Reference," in the EMS manual.

Error:

    0xD0     INVALID_TOKEN

cause        The token that the MEMLIB routine received did not represent
             an allocated block of memory.

action       Each time efmalloc allocates a block of memory, it returns a 
             token unique to that block.  Use this token when referring to
             a particular block.


     0xD1     NOT_ENOUGH_UNALLOCATED_PAGES

cause        Your computer does not have enough free expanded memory.  
             MEMLIB requires at least 1 page (16K bytes) for its internal 
             directory, and assumes that you'll need at least 1 more page 
             to store data for your application.

action       You need more expanded memory.  Add more memory to your Above
             Board, buy a Piggyback Option, or buy another Above Board.


     0xD2     TOO_MANY_DIRECTORY_ENTRIES

cause        You requested more separate blocks of memory than the MEMLIB
             directory can handle.  The maximum number of directory 
             entries available is 65535.

action       Try requesting a few larger blocks instead of many small blocks.

     0xD3     REQUEST_FOR_ZERO_LENGTH_BLOCK

cause        You tried to allocate a 0K-byte block of memory.

action       When you use efmalloc, make sure the size parameter is greater
             than zero.

     0xD4     CANNOT_MAP_ALL_BLOCKS

cause        The blocks you attempted to access can't all be mapped into the 
             page frame at the same time.

action       Try accessing fewer blocks at a time.  See the NOTE at the 
             beginning of the seteptrs function for more information on 
             the number of memory blocks you can access.

     0xD5     MAX_PUSH_CONTEXTS_EXCEEDED

cause        MEMLIB has a set maximum number of contexts it will "push" 
             onto a stack, and you have exceeded that maximum.  
             (See Function 11 for more information about contexts.)

action       You can either pop some of the contexts before pushing any 
             more, or you can change the maximum default.  In the 
             MEMINTRL.C file you'll find a #define called 
             MAX_CONTEXTS_AVAILABLE.  Increase this number, then recompile
             MEMLIB.

      0xD6     MALLOC_FAILURE

cause        When you tried to push a context, MEMLIB could not find enough
             open conventional memory to hold the context.

action       Use the pop_context function to release the contexts you have 
             already pushed.  
             If you haven't called push_context previously but get this 
             error, you don't have enough free conventional memory to use 
             the push_context function.  Unless you can free some of your
             computer's conventional memory, don't call push_context or
             pop_context.


       0xD7     NO_CONTEXT_AVAILABLE_TO_POP

cause        You attempted to call pop_context before calling push_context,
             or you tried to pop a context you had popped already.

action       Use the push_context function before using pop_context.


       0xD8     ERROR_REALLOCATING_PAGES

cause        Efmalloc could not allocate the block of memory you requested 
             because your computer doesn't have enough free expanded memory.

action       Allocate a smaller block of memory, add more memory to your 
             Above Board, or buy another.


        0xD9     NOT_INITIALIZED

cause        You called a function that was designed to be called by another
             function, not by your application.

action       Limit your MEMLIB function calls to the functions listed in 
             this manual.



Appendix C
TECHNICAL INFORMATION ABOUT MEMLIB

This appendix explains in detail how MEMLIB provides access to expanded 
memory, how it keeps track of expanded memory, how it allocates memory, and 
the algorithms it uses.  You don't need this information to use MEMLIB with 
your application program. It's provided for your information only.

If you'd like more information about how expanded memory works, read 
Chapter 1 in the EMS manual.


How MEMLIB provides access to expanded memory
---------------------------------------------

You can have up to 32M bytes of expanded memory in your computer, and 
MEMLIB can provide access to all of it.

Expanded memory is divided into logical pages.  Each logical page is 16K 
bytes.

To access a block of expanded memory, MEMLIB needs to map in the logical 
page that the block uses.  MEMLIB can map only a limited number of these 
pages into the EMS page frame at one time.

For example, if your page frame is 64K bytes, four logical pages can be 
mapped into it.  You can access four memory blocks at the same time if the 
size of each block is 16K bytes or less.  However, accessing five blocks at 
once may be impossible, because the blocks might use five different logical 
pages (five pages won't fit in the page frame).

How MEMLIB keeps track of expanded memory
-----------------------------------------

The expanded memory manager, MEMLIB, is based on a directory structure.  
Each directory entry consists of four fields:

   unsigned int     token    (0xFFFF = free, otherwise = allocated)
   unsigned int     size     (size [in bytes] of this block)
   unsigned short  offset    (the offset into the first logical page of 
                             this block)
   int     logical_page[4]   (pages used)

Blocks less than or equal to 16K bytes will use one logical page; 16 - 32K, 
two pages; 32 - 48K, three pages; and 48 - 64K, four pages.

This directory is stored in expanded memory that MEMLIB allocates when 
needed.


How MEMLIB allocates expanded memory
------------------------------------

To allocate memory, MEMLIB attempts to reuse as much deallocated memory as 
possible before allocating any new blocks.

When a block of memory is freed, MEMLIB checks all other freed blocks to 
see if they fall immediately before or after that block.  If there are any 
adjacent free blocks, MEMLIB combines the blocks into one larger block.  
When a user requests a block of memory, MEMLIB checks all free blocks and 
gives the user the smallest free block bigger than the size requested (best 
fit).  Any leftover memory is put into another free block.

EXAMPLE:
       A user makes an efmalloc request for a 6K block.  MEMLIB allocates 
       logical page 1 from EMM, and creates two directory entries:  one to 
       the 6K block and one to the 10K free block.

       Next, the user makes another efmalloc request, this time for an 11K 
       block.  11K won't fit in the 10K free block in logical page 1, and 
       will overlap into the next logical page if assigned to page 1 at all.  
       MEMLIB allocates an entirely new logical page, page 2. Again, it 
       creates one directory entry to point to the allocated block, and 
       another entry to point to the 5K of free memory (4).



Algorithms
----------

When a user requests a block of memory (efmalloc), Intel uses the following
algorithm:

           (C pseudo code)

do
    if (exact fit)
        allocate block
    else
        check for best fit
while ((not exact fit) and (not last directory entry))
if (not exact fit)
    if (best fit)
        allocate block
        set directory entry for remainder
    else
        request page from expanded memory manager
        if request = OK
             allocate block
        else
             return NOT_ENOUGH_UNALLOCATED_PAGES error


To free a block of memory (effree), we use this algorithm:

do
    if (contiguous free block)
        combine into one free block
while (not last directory entry)
    if (no allocated pages found)
        deallocate all pages      /* prepare for program termination */


When mapping in blocks of memory (seteptrs), we use the following algorithm:

for (i = 0, i < number of requested blocks; i++)
    if (logical pages needed for block i are already mapped)
        OK = true
    else
        if (pages need for block i will fit)
             OK = true
             map in pages needed
        else
             return CANNOT_MAP_ALL_BLOCKS error





















