                   ACCESSING EXPANDED MEMORY WITH C

                            Matthew Probert
                           Servile  Software


Memory (RAM) in an IBM PC comes in three flavours; Conventional, 
Expanded and Extended. Conventional memory is the 640K of RAM which 
the operating system DOS can access. This memory is normally used. 
However, it is often insufficient for todays RAM hungry systems. 
Expanded memory is RAM which is addressed outside of the area of 
conventional RAM not by DOS but by a second program called a LIM EMS 
driver (eg: EMM386.EXE or QEMM). Access to this device driver is made 
through interrupt 67h. 

The main problem with accessing expanded memory is that no matter how 
much expanded memory is fitted to the computer, it can only be 
accessed through 16K blocks refered to as pages. So for example. If 
you have 2mB of expanded RAM fitted to your PC then that is comprised 
of 128 pages (128 * 16K = 2mB). 

A program can determine whether a LIM EMS driver is installed by 
attempting to open the file 'EMMXXXX0' which is guarranteed by the LIM 
standard to be present as an IOCTL device when the device driver is 
active (lets just say it's another file!). 

The following source code illustrates some basic functions for testing 
for and accessing expanded memory. 

/*
Various functions for using Expanded memory
*/

#include <dos.h>
#define    EMM    0x67

char far *emmbase;

emmtest()
{
    /*
    Tests for the presence of expnaded memory by attempting to
    open the file EMMXXXX0.
    */

    union REGS regs;
    struct SREGS sregs;
    int error;
    long handle;

    /* Attempt to open the file device EMMXXXX0 */
    regs.x.ax = 0x3d00;
    regs.x.dx = (int)"EMMXXXX0";
    sregs.ds = _DS;
    intdosx(&regs,&regs,&sregs);
    handle = regs.x.ax;
    error = regs.x.cflag;

    if (!error)
    {
        regs.h.ah = 0x3e;
        regs.x.bx = handle;
        intdos(&regs,&regs);
    }
    return error;
}

emmok()
{
    /*
    Checks whether the expanded memory manager responds correctly
    */

    union REGS regs;

    regs.h.ah = 0x40;
    int86(EMM,&regs,&regs);

    if (regs.h.ah)
        return 0;

    regs.h.ah = 0x41;
    int86(EMM,&regs,&regs);

    if (regs.h.ah)
        return 0;

    emmbase = MK_FP(regs.x.bx,0);
    return 1;
}

long emmavail()
{
   /*
   Returns the number of available (free) 16K pages of expanded memory 
   or -1 if an error occurs.
   */

   union REGS regs;

    regs.h.ah = 0x42;
    int86(EMM,&regs,&regs);
    if (!regs.h.ah)
        return regs.x.bx;
    return -1;
}

long emmalloc(int n)
{
    /*
    Requests 'n' pages of expanded memory and returns the file handle
    assigned to the pages or -1 if there is an error
    */

    union REGS regs;

    regs.h.ah = 0x43;
    regs.x.bx = n;
    int86(EMM,&regs,&regs);
    if (regs.h.ah)
        return -1;
    return regs.x.dx;
}

emmmap(long handle, int phys, int page)
{
    /*
    Maps a physical page from expanded memory into the page frame in 
    the conventional memory 16K window so that data can be transfered 
    between the expanded memory and conventional memory.
    */

    union REGS regs;

    regs.h.ah = 0x44;
    regs.h.al = page;
    regs.x.bx = phys;
    regs.x.dx = handle;
    int86(EMM,&regs,&regs);
    return (regs.h.ah == 0);
}

void emmmove(int page, char *str, int n)
{
    /*
    Copy 'n' bytes from conventional memory to the specified expanded 
    memory page
    */

    char far *ptr;

    ptr = emmbase + page * 16384;
    while(n-- > 0)
        *ptr++ = *str++;
}

void emmget(int page, char *str, int n)
{
    /*
    Copy 'n' bytes from the specified expanded memory page into 
    conventional memory 
    */

    char far *ptr;

    ptr = emmbase + page * 16384;
    while(n-- > 0)
        *str++ = *ptr++;
}

emmclose(long handle)
{
    /*
    Release control of the expanded memory pages allocated to 'handle'
    */

    union REGS regs;

    regs.h.ah = 0x45;
    regs.x.dx = handle;
    int86(EMM,&regs,&regs);
    return (regs.h.ah == 0);
}

/*
Test function for the EMM routines
*/

void main()
{
    long emmhandle;
    long avail;
    char teststr[80];
    int i;

    if(!emmtest())
    {
        printf("Expanded memory is not present\n");
        exit(0);
    }

    if(!emmok())
    {
        printf("Expanded memory manager is not present\n");
        exit(0);
    }

    avail = emmavail();
    if (avail == -1)
    {
        printf("Expanded memory manager error\n");
        exit(0);
    }
    printf("There are %ld pages available\n",avail);

    /* Request 10 pages of expanded memory */
    if((emmhandle = emmalloc(10)) < 0)
    {
        printf("Insufficient pages available\n");
        exit(0);
    }

    for (i = 0; i < 10; i++)
    {
        sprintf(teststr,"%02d This is a test string\n",i);
        emmmap(emmhandle,i,0);
        emmmove(0,teststr,strlen(teststr) + 1);
    }

    for (i = 0; i < 10; i++)
    {
        emmmap(emmhandle,i,0);
        emmget(0,teststr,strlen(teststr) + 1);
        printf("READING BLOCK %d: %s\n",i,teststr);
    }

    emmclose(emmhandle);
}
