//      Author:  Chris Somers

AJR:
  This file contains 3 .h files and two .c files, and some notes
  all glued together. you'll have to cut them apart to compile.

//      Author:  Chris Somers

To use memory.c, a few notes are in order. This file was originally used
in an event-driven windowing environment, so there are several headers you
won't need (so I didn't include 'em).

AJR - I nuked all uneeded references from the code

This code was written for Borland C/C++ v3.1 and has _not_ been tested
under any other version. The first set of conventional memory calls
(those flanked by #ifndef USEBORLIB) are to replace Borland's malloc
library when using 3rd party libraries or APIs which called DOS's malloc.
Add a -DUSEBORLIB to the compile command line to remove this code.

The file, tasks.h (application specific - not included), optionally
#define'd EMM_SUPPORT and TCPCOM. #define the first and not the second.

AJR - I modified these sources so this isn't neccessary - all
      references to TCPCOM have been deleted.

Add memory.p to your application to have access to the EMM calls. Don't
include memory.h - it's got static calls and variables you don't want
direct access to.

AJR - I added memory.p to memory.c, and cleaned up memory.h so it can
      be included.

      Please see emm.c for an example.

returns.h and sizes.h just have the #defines for (duh!) return codes
and size constants. Unfortunately, the values used for some of the
return codes may not be safely changed. If they clash with codes in your
program, it might be safer to rename them than to change their values
and try to see where the EMM code got broken.

AJR: I renamed returns.h to emmret.h and sizes.h to emmsize.h
     to avoid file name clashes.

Here are some notes on the calls:

OpenEMM()  - call this first, period.
CloseEMM() - call this last. It'll even free any EMS you forgot to free
             in your program (this isn't like conventional memory, y'know)
EMMInstalled() - returns TRUE or FALSE. Use only after OpenEMM() is called.
EMMalloc() - returns a pointer to the first EMM page (EMMSeg:0000 hex).
             Also stuffs a new handle into *Handle (*Handle == NULL is not
             checked for, so be careful or fix the code). You gotta supply
             Pages (i.e. number of 16K blocks).
EMMRealloc() - increases/decreases number of pages allocated to your handle.
EMMFree()  - frees all the pages and voids the handle.
MapEMM()   - places 1 to 4 Pages into the page frame starting with page, Start.
UnmapEMM() - removes the pages from the page frame so nothing else trashes
             your data.
UseEMM()   - same as MapEMM() but saves the frame state before mapping.
SaveEMM()  - same as UnmapEMM() but restores a saved frame state after unmapping.
EMMCoreLeft() - returns the number of bytes of EMM left to allocate.

The comments in the MapEMM() and UseEMM() (and UnmapEMM() and SaveEMM()) speak
of 'applications' and 'devices'. These refer to layers in the development
environment using this .c file. The application layer could use EMS as long
as it kept track of it's own usage. So it wouldn't have to keep track of the
environment's usage, 'devices' (i.e. lower layers) saved the page frame
state to use EMS themselves, so as not to trash the application layer's data,
and restored the original state upon returning.



More notes:
Alexander J Russell wrote:

>How do you USE emm memory?
>With xms it is obvious - you copy mem back and forth.
>What the parameters for mapemm() etc... mean isn't documented well enough
>for a newbie to use it.

EMM is alot like XMS except that the EMM driver does the copying for you,
without really copying (the 386's LDTs are used to point to appropriate
parts of physical memory when mapping EMM). Here's how it works.
EMM has a 64K (typically) page frame in conventional memory, often at
E000-EFFF. The page frame consists of four 16K pages. When you call
EMMalloc(), you supply the number of 16K pages you need and it
returns 1) a handle, and 2) a pointer to the page frame (i.e. E000:0000).
This memory is allocated somewhere in extended memory but still
unavailable. To use it, you must 'map' it into the page frame so
conventional memory pointers can access it. The MapEMM() function
maps the pages you need (up to four) associated with that handle 
into the page frame. You can now use your alloc'd memory at E000.
When you're finished accessing the memory, you can unmap it using
UnmapEMM(). This removes the mapping from the page frame so you can
map other allocations or just protect what you've already written
(i.e. writing to the page frame segment won't touch your data).
N.B. You never *have to* unmap your pages. Mapping new pages
automagically unmaps existing mappings - but it's good practice
to unmap when you're finished.

Another way to look at EMS is to consider the page frame as a window
over the extended memory area. Mapping is like moving the window to
some area of XMS and making it available with a conventional memory
pointer - at the page frame address, which never changes. 
In other words, EMS 'bank switches' segments of XMS memory into
conventional memory.

>I think I call openemm() the use map and unmap to utilize the mem.
>what does map do, what do the parms mean? Once mapped how is the mem used.

Pretty close. OpenEMM() makes sure the driver is loaded and gets the
page frame address (EMMSeg:0000). Then EMMalloc() reserves the specified
number of 16K pages, associates them to a handle, and gives your app the
page frame address. Then MapEMM() lets you use up to four pages at a time
(at the address returned by EMMalloc()). The parameters for MapEMM() are:
- the handle you got from EMMalloc()
- the memory page # to map to the first page frame page.
- the number of pages to map to the page frame (max. of 4)
BTW, all numbering is zero-based.

>Could you send a quick overview of how you actually use emm mem in a dos
>program.

 -------------------- see emm.c for example ---------------


>Can I re-post your code and notes on my page?
Yup. Leaving my name in the comments at the top would be much appreciated.

I learnt most of this from DOS Programmer's Reference by Terry Dettmann
from Que publishing (it's (C)1989 and I *still* use it!). You can check out
other references dealing with int67h (the EMS API) too.

Hope I didn't leave anything out. Email me again if any points aren't clear.

Chris S.

AJR adds:

memory.c, and the h files all you need to use emm!
emm.c is just a test program to show how it is used.


/*
   *********************************************************************
   *********************************************************************

                               next file

   *********************************************************************
   *********************************************************************
*/

// Cut this out as emmsize.h

/*      File:       EmmSize.h
 *      Module:     All Modules
 *      Author(s):  Chris Somers
 *      Date:       May 8, 1992
 *      Version:    V.1.0
 */

#ifndef _EMMSIZES_DEF
#define _EMMSIZES_DEF 1


/* Module size constants */

#define     MAXACTIONS          29
#define     MAXCHANNELS          4
#define     MAXFOCUS             3
#define     MAXBUFTYPES         64
#define     MAXEMHANDLES        64      /* up to a max of 255 */
#define     MAXTIMERS           32
#define     MAXTITEMS          128
#define     MAXQUEUES           24
#ifdef PRINTER
#define     MAXPRNS              4
#else
#define     MAXPRNS              0
#endif
#ifdef UTSCOM
#define     MAXUTSSCRNS          2
#else
#define     MAXUTSSCRNS          0
#endif
#ifdef CAPTURE
#define     MAXCAPTURE           1
#else
#define     MAXCAPTURE           0
#endif
#define     MAXIRQS             10
#define     MAXTCPDSCPTITEMS    16     /*max # of TCP connections*/

#define FREEBUFF        1   /* CallIODevice flag for BuffSize arrays */
#define DSCONLY         2   /* allocate a BufDscpt, only - no buffer */

#define     EMMPAGESIZE     0x4000     /* 16K EMM page size */
#define     SETUPLEN            32
#define     NAMELEN             40
#define     UNIXLEN             32
#define     ADDRLEN             16
#define     TITLELEN           128
#define     TSLEN               80
#define     COMMENTLEN          65
#define     PALSIZE            768
#define     QSTRLEN            254
#define     PRNBUFSIZ         2048
#define     TXTSCRNSIZE       4096
#define     UTSBUFSIZ         4096
#define     UTSSCRNSIZ        1920
#define     QMEMSIZE            15
#define     KBUFLEN             16
#define     GXBUFFSIZE      0x4000
#define     TCPNDBUFFSIZE   0x1000      /*router auto-allocation buff size*/
                                        /* graphics printing scale values */
#define     SCALE_HALF           1      /* value must not change */
#define     SCALE_ONE            2
#define     SCALE_QUART          3      /* value must not change */

#define     SIXTH_SECOND         3L     /* shade over 1/6 of a second*/
#define     HALF_SECOND          9L
#define     ONE_SECOND          18L
#define     TWO_SECONDS         36L
#define     FIVE_SECONDS        91L
#define     TEN_SECONDS        182L
#define     HALF_MINUTE      182*3L     /* same as 18.2*30, right? */
#define     ONE_MINUTE       182*6L     /* same as 18.2*60, right? */
#define     TWO_MINUTES     182*12L     /* same as 18.2*120, right? */
#define     FIVE_MINUTES    182*30L     /* same as 18.2*300, right? */
#define     TEN_MINUTES     182*60L     /* same as 18.2*600, right? */
#define     HALF_HOUR      182*180L     /* same as 18.2*1800, right? */
#define     ONE_HOUR       182*360L     /* same as 18.2*3600, right? */

#define MAXROUTMOVES            24      /*max # of routing moves at one time*/
                                        /*also max # of Move Complete Events*/
/* Event Channel Defines */

#define MAXRECALLEVENTS    1   /*max nm of Recall Event Channels*/
#define MAXKBDEVENTS       1   /*max nm of Kbd Event Channels*/
#define MAXPRNEVENTS       4   /*max nm of Prt Prog & Prt Cmplte Event Chan*/
#define MAXUTSRCVEVENTS    MAXUTSSCRNS       /*max nm of Uts Rx Event Chans*/
#define MAXUTSXMTEVENTS    MAXUTSSCRNS       /*max nm of Uts Tx Event Chans*/
#define MAXCAPEVENTS       2   /* max number of capture event channels */
#define MAXOP1CMPLTEVENTS  1   /*max nm of Operation 1 Cmplt Event Channels*/
#define MAXOP2CMPLTEVENTS  1   /*max nm of Operation 2 Cmplt Event Channels*/
#define MAXOP3CMPLTEVENTS  MAXTCPDSCPTITEMS  /*max nm of Op 3 Event Chans*/
#define MAXTCPEVENTS       MAXTCPDSCPTITEMS  /* max nm of TCP Event types */

#endif

/*
   *********************************************************************
   *********************************************************************

                               next file

   *********************************************************************
   *********************************************************************
*/

// AJR notes: some of these are not used by the emm code

// Cut this out as emmret.h

/*      File:       EmmRet.h
 *      Module:     All Modules
 *      Author(s):  Chris Somers
 *      Date:       May 8, 1992
 *      Version:    V.1.0
 */

#ifndef _EMM_RETURNS_DEF
#define _EMM_RETURNS_DEF 1

/* Module return values */

/* Return Values */

#define     START                8
#define     END                  7
#define     MOVABORT             6
#define     PRN_COMPLETE         5
#define     PRN_PROGRESS         4
#define     INCOMPLETE           3
#define     HAVEROOM             2
#define     SUCCESS              1
#define     TRUE                 1
#define     YES                  1
#define     FALSE                0
#define     NO                   0
#define     NOTREADY             0
#define     NO_DATA              0
#define     NONE                -1

/* Start of Recoverable error codes */

#define     NO_SPACE            -1001
#define     NOTPOLLING          -1002
#define     ALREADYDONE         -1003
#define     NO_PRN_DEV          -1004
#define     OUTF_PAPER          -1005
#define     NO_VIDEO            -1006
#define     TIMEOUT             -1007
#define     FILENOPEN           -1008
#define     ABORT_REQ           -1009
#define     DEV_IOERR           -1010

#define     MAXRERR                10
#define     RECOVERABLE         -1999   /* all above errors are recoverable */

/* Start of Threadfatal error codes */

#define     NOT_OPEN            -2001
#define     NOT_ATTACHED        -2002
#define     NO_CONNECTION       -2003
#define     INSUFF_MEM          -2004
#define     NR_TIMEOUT          -2005

#define     MAXTERR                 5
#define     THREADFATAL         -2999   /* above errors force task to cancel */

/* Start of Systemfatal error codes */

#define     BAD_TASKNUM         -3001
#define     BAD_HANDLE          -3002
#define     BAD_HARDWARE        -3003
#define     INVALIDACTION       -3004
#define     NOFREEITEMS         -3005
#define     NO_MEMORY           -3006
#define     NO_EMS              -3007
#define     VALUE_OUTF_RANGE    -3008
#define     BAD_MODE            -3009
#define     NO_PALETTE          -3010
#define     BAD_DISPPAGE        -3011
#define     NO_TSR              -3012
#define     BUFTOOSMALL         -3013
#define     BAD_NAME            -3014
#define     BAD_DISPHW          -3015
#define     NO_FLOPPY           -3016

#define     MAXSERR                16
#define     SYSTEMFATAL         -3999   /* above errors are fatal to system */

#endif

/*
   *********************************************************************
   *********************************************************************

                               next file

   *********************************************************************
   *********************************************************************
*/

// Cut this out as memory.h

/*      File:       Memory.h
 *      Module:     All Modules
 *      Author(s):  Chris Somers
 *      Date:       August 5, 1993
 *      Version:    V.1.1

        modified by Alex Russell to simplify.
 */


#ifndef _MEMORY_DEF
#define _MEMORY_DEF 1

#include "emmret.h"
#include "emmsize.h"

#define     CMM                  0
#define     EMM                  1

extern int      OpenEMM(void);
extern void     CloseEMM(void);
extern int      EMMInstalled(void);
extern void far *EMMalloc(int *Handle, int Pages);
extern int      EMMRealloc(int Handle, int Pages);
extern void     EMMFree(int Handle);
extern int      MapEMM(int Handle, int Start, int Pages);
extern void     UnmapEMM(int Handle, int Start, int Pages);
extern int      UseEMM(int Handle, int Start, int Pages);
extern void     SaveEMM(int Handle, int Start, int Pages);
extern unsigned long EMMCoreLeft(void);

#endif

/*
   *********************************************************************
   *********************************************************************

                               next file

   *********************************************************************
   *********************************************************************
*/


/*      File:       Memory.c
 *      Module:     All Modules
 *      Author(s):  Chris Somers
 *      Date:       December 1, 1992
 *      Version:    V.1.1

        minor mods by Alex Russell to simplify

        Must use memory model with FAR code

 */



#if !defined(__LARGE__) && !defined(__COMPACT__) && !defined(__HUGE__)
#error Invalid memory model for compiling MEMORY.C
#endif

#include <stdio.h>
#include <dos.h>
#include <mem.h>

#include "memory.h"

// static globals --------------------------------

static int  ActiveEMList[MAXEMHANDLES];
static unsigned int  EMMSeg;

// forward declarations ---------------------------------

static int  EMPresent(void);
static int  EMReady(void);
static unsigned int  GetEMMSeg(void);
static int  GetEMHandle(int NumPages);
static int  EMMap(int Handle, int LogPg, int PhyPg);
static int  FreeEMHandle(int Handle);
static int  GetNumPages(int Handle);
static int  EMStateSave(int Handle);
static void EMStateRestore(int Handle);


/********************************************************************/
int
OpenEMM(void)
{
    if (!EMPresent() || !EMReady()) return(NOTREADY);
    if (!(EMMSeg = GetEMMSeg())) return(NOTREADY);  /*lint !e720 */
    return(SUCCESS);
}               /* End of OpenEMM() */

/********************************************************************/

void
CloseEMM(void)
{
    int     i;

    if (!EMMSeg) return;
    for (i = 0; i < MAXEMHANDLES; i++) {
        if (ActiveEMList[i]) {
            FreeEMHandle(ActiveEMList[i]);
            ActiveEMList[i] = 0;
        }
    }
    EMMSeg = 0;
}               /* End of CloseEMM() */

/********************************************************************/

int
EMMInstalled(void)
{
    return((EMMSeg) ? TRUE : FALSE);        /* successfully opened? */
}               /* End of EMMInstalled() */

/********************************************************************/

unsigned long
EMMCoreLeft(void)
{
    unsigned      Pages;
    unsigned long RtnVal = 0UL;

    _asm {
        mov     ah,0x42             /* get EMM free page count */
        int     0x67
        or      ah,ah
        js      InternalError       /* returns 80, 81, or 84 hex on error */
        mov     Pages,bx            /* number of unallocated 16K pages */
    }
    RtnVal = ((unsigned long)Pages << 14);  /* Pages * 16K rtns bytes*/

InternalError:

    return(RtnVal);
}               /* End of EMMCoreLeft() */

/********************************************************************/

void far *
EMMalloc(int *Handle, int Pages)
{
    int     i;
    char    *RtnPtr = NULL;

    if (!EMMSeg) {
        *Handle = NOTREADY;
        return(NULL);
    }
    if ((Pages < 1) || (Pages > 1020)) {
        *Handle = VALUE_OUTF_RANGE;
        return (NULL);
    }
    for (i = 0; (i < MAXEMHANDLES) && (ActiveEMList[i]); i++) ;
    if (i == MAXEMHANDLES) {
        *Handle = NOFREEITEMS;
        return (NULL);
    }
    if ((ActiveEMList[i] = GetEMHandle(Pages)) > 0) {
        RtnPtr = MK_FP(EMMSeg, 0);
    }
    *Handle = ActiveEMList[i];
    return((void far *)RtnPtr);
}               /* End of EMMalloc() */

/********************************************************************/

int
EMMRealloc(int Handle, int Pages)
{
    int     RtnCode = FALSE;

    if (!EMMSeg || (Pages < 0) || (Pages > 1020)) {
        return (FALSE);
    }
    _asm {
        mov     ah,0x51             /* change # of pages */
        mov     bx,Pages
        mov     dx,Handle
        int     0x67
        or      ah,ah
        js      NoGo                /* returns 80 to 88 hex on error */
    }
    RtnCode = TRUE;

NoGo:

    return(RtnCode);
}               /* End of EMMRealloc() */

/********************************************************************/

void
EMMFree(int Handle)
{
    int     i, j;

    if (!EMMSeg) return;
    for (i = 0; (i < MAXEMHANDLES) && (ActiveEMList[i] != Handle); i++) ;
    if (i >= MAXEMHANDLES) return;
    j = 16;
    while (j--) {
        if (FreeEMHandle(ActiveEMList[i])) break;
    }
    ActiveEMList[i] = 0;
}               /* End of EMMFree() */

/********************************************************************/

int                                         /* EMM map for application */
MapEMM(int Handle, int Start, int Pages)
{
    int     i;

    if (!EMMSeg) return(NOTREADY);
    for (i = 0; (i < MAXEMHANDLES) && (ActiveEMList[i] != Handle); i++) ;
    if (i == MAXEMHANDLES) return (NO_DATA);
    if ((GetNumPages(Handle) < Pages) || (Pages < 1) || (Pages > 4)) {
        return (VALUE_OUTF_RANGE);
    }
    for (i = Start; i < Start + Pages; i++) {
        if (!EMMap(Handle, i, i - Start)) return(NO_DATA);
    }
    return(SUCCESS);
}               /* End of MapEMM() */

/********************************************************************/

void                                        /* EMM unmap for application */
UnmapEMM(int Handle, int Start, int Pages)
{
    int     i, j;

    if (!EMMSeg) return;
    for (i = 0; (i < MAXEMHANDLES) && (ActiveEMList[i] != Handle); i++) ;
    if (i == MAXEMHANDLES) return;
    j = Start + Pages;
    if ((Pages < 1) || (j > 4)) return;

    for (i = Start; i < j; i++) {
        EMMap(Handle, NONE, i);
    }
}               /* End of UnmapEMM() */

/********************************************************************/

int                     /* EMM map for devices - saves EMM state */
UseEMM(int Handle, int Start, int Pages)
{
    EMStateSave(Handle);
    return(MapEMM(Handle, Start, Pages));
}               /* End of UseEMM() */

/********************************************************************/

void                    /* EMM unmap for devices - restores EMM state */
SaveEMM(int Handle, int Start, int Pages)
{
    UnmapEMM(Handle, Start, Pages);
    EMStateRestore(Handle);
}               /* End of SaveEMM() */

/********************************************************************/

static int
EMPresent(void)
{
    int     i, Segment;
    char    EMName[] = "EMMXXXX0";
    char    *s, *t;

    _asm {                      /* can be replaced with getvect() */
        push    es
        mov     ax,0x3567       /* get vector for int 67h */
        int     0x21
        mov     ax,es
        mov     Segment,ax
        pop     es
    }
    t = MK_FP(Segment, 0x0A);   /* point to driver name */
    s = EMName;
    for (i = 0; (i < 8) && (*s++ == *t++); i++) ;   /* strncmp equivalent */

    if (i == 8) return(TRUE);
    return(FALSE);
}               /*End of EMPresent() */

/********************************************************************/

static int
EMReady(void)
{
    _asm {
        mov     ah,0x40             /* get EM Manager Status */
        int     0x67
        or      ah,ah
        jns     Ready               /* returns 80, 81, or 84 hex on error */
    }
    return(FALSE);

Ready:
    return(TRUE);
}               /* End of EMReady() */

/********************************************************************/

static unsigned int
GetEMMSeg(void)
{
    unsigned int     EMSegment;

    _asm {
        mov     ah,0x41             /* get EMM page frame segment */
        int     0x67
        or      ah,ah
        js      NotReady            /* returns 80, 81, or 84 hex on error */
        mov     EMSegment,bx
    }
    return(EMSegment);              /*lint !e530 */

NotReady:
    return(NOTREADY);
}               /* End of GetEMMSeg() */

/********************************************************************/

static int
GetEMHandle(int NumPages)
{
    int     NewHandle;

    _asm {
        mov     ah,0x43             /* get handle and allocate EM */
        mov     bx,NumPages         /* number of 16K pages to allocate */
        int     0x67
        or      ah,ah               /* returns 80 to 89 hex on error */
        js      NoHandle
        mov     NewHandle,dx        /* retrieve handle */
    }
    return(NewHandle);

NoHandle:
    return(NO_DATA);
}               /* End of GetEMHandle() */

/********************************************************************/

static int
EMMap(int Handle, int LogPg, int PhyPg)
{
    int     RtnCode = NO_DATA;

    _asm {
        mov     ax,PhyPg            /* physical page: 0 - 3 in AL only */
        mov     ah,0x44             /* map logical to physical page */
        mov     bx,LogPg            /* logical page: 0 - 1020 */
        mov     dx,Handle
        int     0x67
        or      ah,ah               /* returns 80 to 8B hex on error */
        js      NoMapping
    }
    RtnCode = SUCCESS;

NoMapping:
    return(RtnCode);
}               /* End of EMMap() */

/********************************************************************/

static int
FreeEMHandle(int Handle)
{
    _asm {
        mov     ah,0x45             /* free handle and deallocate EM */
        mov     dx,Handle
        int     0x67
        or      ah,ah               /* returns 80 to 86 hex on error */
        js      NotFreed
    }
    return(SUCCESS);

NotFreed:                           /* must retry if unsuccessful */
    return(NO_DATA);
}               /* End of FreeEMHandle() */

/********************************************************************/

static int
GetNumPages(int Handle)
{
    int     NumPages = 0;

    _asm {
        mov     ah,0x4C             /* get allocated pages for Handle */
        mov     dx,Handle
        int     0x67
        or      ah,ah               /* returns 80 to 84 hex on error */
        js      BadHandle
        mov     NumPages,bx
    }
BadHandle:

    return(NumPages);
}               /* End of GetNumPages() */

/********************************************************************/

static int
EMStateSave(int Handle)
{
    int     RtnCode = NO_MEMORY;
    _asm {
        mov     ah,0x47             /* save page map under Handle */
        mov     dx,Handle
        int     0x67
        or      ah,ah
        js      Unsaved             /* out of save space error */
    }
    RtnCode = SUCCESS;

Unsaved:
    return(RtnCode);
}               /* End of EMStateSave() */

/********************************************************************/

static void
EMStateRestore(int Handle)
{
    _asm {
        mov     ah,0x48             /* restore page map for Handle */
        mov     dx,Handle
        int     0x67                /* ignore error */
    }
}               /* End of EMStateRestore() */

/********************************************************************/

/*
   *********************************************************************
   *********************************************************************

                               next file

   *********************************************************************
   *********************************************************************
*/

// Cut this out as emm.c

/*      File:       emm.c
 *      Module:     All Modules
 *      Author(s):  Chris Somers
 *      Date:       December 1, 1992
 *      Version:    V.1.1

        minor mods by Alex Russell to simplify

        Must use memory model with FAR code

 */


#include <stdio.h>
#include <stdlib.h>
#include <mem.h>

#include "memory.h"

void TransformData(char *pEmmData, unsigned int len)
{
   while ( len )
      {
      (*pEmmData)++;
      pEmmData++;

      len--;
      }
}

void main(void)
{
   char    *pEmmData;
   int     hEData;

   if ( OpenEMM() != SUCCESS )
      {     // make sure we got EMM
      printf("EMM unavailable.\n");
      exit(1);
      }
   else
      printf("Emm available\n");

   pEmmData = EMMalloc(&hEData, 6);  // get 6 * 16K bytes - 96K
   if ( pEmmData == NULL )
      {
      printf("Not enough EMM or out of handles.\n");
      exit(2);
      }
   else
      printf("emm alloced OK\n");


   printf("Map 1st 4 pages\n");
   MapEMM(hEData, 0, 4);   // load 1st 4 pages into page frame: 0-3

   memset(pEmmData, 0x0e, 64000u);
   UnmapEMM(hEData, 0, 4);          // not absolutely necessary
   
   printf("Map next 2 pages\n");
   MapEMM(hEData, 4, 2);            // map last 2 pages: 4-5
   memset(pEmmData, 0x0e, 32768u);

   MapEMM(hEData, 0, 4);
   // do some stuff with the first 64K of file data.
   printf("Transform data\n");
   TransformData(pEmmData, 64000UL);
   MapEMM(hEData, 4, 2);  // only unmaps 1st two pages of prior 64k mapping
   // do stuff with remaining 32K of data
   TransformData(pEmmData, 32768UL);
   UnmapEMM(hEData, 0, 4);  // should unmap before freeing

   printf("Close emm\n");
   EMMFree(hEData);     // finished with the file data
   CloseEMM();
}

