/***************************************************************************
*   emstest.c                                                              *
*   MODULE:  EMSIF                                                         *
*   OS:      DOS                                                           *
*   VERSION: 1.2                                                           *
*   DATE:    06/15/93                                                      *
*                                                                          *
*   Copyright (c) 1991, 1993 James W. Birdsall. All Rights Reserved.       *
*                                                                          *
*   Requires emsif.h, testutil.h, and emstest.h to compile.                *
*   Compiles under Borland C++ 2.0, TC 2.0, MSC/C++ 7.0, and               *
*    MS Visual C++ 1.0.                                                    *
*                                                                          *
*   Regression test and example for EMSIF.                                 *
*                                                                          *
*   This is part one of the regression tester and example for EMSIF.       *
*   The other parts are named EMSTEST2.C and EMSTEST3.C. All three parts   *
*   must be compiled and linked together along with the appropriate EMSIF  *
*   library and a utilities file TESTUTIL.C to produce the tester          *
*   executable. This program compiles under tiny, small, medium, compact,  *
*   large, and huge models. Note that it doesn't quite fit into tiny       *
*   model; the tiny model tester is actually three executables. The        *
*   compact model under MSC 7.0 suffers from the same problem. When        *
*   compiling for these configurations, one of the symbols PASS1, PASS2,   *
*   or PASS3 must be defined. See the example makefiles for further        *
*   details on what needs to be linked with what to produce these          *
*   executables.                                                           *
*                                                                          *
*   To use this tester: just run it. It requires no arguments and produces *
*   output on stdout. It performs nearly 200 tests and parts of it run     *
*   quite fast, even on an original IBM PC/XT (4.77 MHz). If you want to   *
*   actually read the output, you should redirect the output to a file.    *
*   If you just want to see whether the tests all pass, simply run it --   *
*   if a test fails, execution aborts immediately.                         *
*                                                                          *
*   Certain types of failure may cause EMSTEST to not deallocate EMS or    *
*   conventional memory that it has allocated. This should only occur if   *
*   the library itself is malfunctioning (which should never happen to     *
*   you, only to me!) or if you are trying a different compiler or         *
*   unsupported memory model and the compiler and library are therefore    *
*   not communicating properly. It may also happen if you press control-   *
*   break.                                                                 *
*                                                                          *
*   Turbo C and older versions of Turbo C++ do not have the _fmemcmp() and *
*   _fmemset() functions; I don't know about older versions of MSC. If     *
*   your compiler does not have these functions, define the symbol         *
*   NO_FFUNC and functions in the ancillary file TESTUTIL.C will be used   *
*   instead.                                                               *
*                                                                          *
***************************************************************************/

/*
** system includes <>
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/*
** custom includes ""
*/

#include "testutil.h"

#include "emsif.h"
#include "emstest.h"


/*
** local #defines
*/

/*
** misc: copyright strings, version macros, etc.
*/

/*
** typedefs
*/

/*
** global variables
*/

int testno = 1;                     /* number of test currently being done  */
unsigned char far *frameptr[4];     /* pointers to EMS frames               */
char *gblmsg = "";                  /* msg to be printed in test header     */


/*
** static globals
*/

/*
** function prototypes
*/

/*
** functions
*/


/***************************************************************************
*   FUNCTION: MAIN                                                         *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       The master function.                                               *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       None.                                                              *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
main()
{
    int status;                     /* return status from library functions */

    /*
    ** check initialization test
    */
    TESTHEADER();
    printf("Making a call to EMMgetversion() before calling EMMlibinit().\n");
    printf("The call should fail.\n");
    EMMgetversion();
    nofailcheck("EMMgetversion()", (int) _EMMerror, NULL, 0, 0);
    weirdcodechk("EMMgetversion()", EMM_NOINIT, NULL, 0, 0);
    TESTTAILER();


    /*
    ** initialize EMSIF
    */
    TESTHEADER();
    printf("Calling EMMlibinit().\n");
    printf("Should succeed if and only if Expanded Memory Manager for EMS version 3.0 or\n");
    printf(" greater is present.\n");
    status = EMMlibinit();
    switch (status)
    {
        case EMMOOPS:
            printf("EMMlibinit() failed, code 0x%X.\n",
                                                    (unsigned int) _EMMerror);
            exit(3);
            break;

        case NOEMM:
            printf("EMMlibinit() did not find an Expanded Memory Manager.\n");
            exit(3);
            break;

        case 0:
            printf("EMMlibinit() returned OK.\n");
            weirdcodechk("EMMlibinit()", 0, NULL, 0, 0);
            break;

        default:
            printf("EMMlibinit() returned strange value %d.\n", status);
            exit(3);
            break;
    }
    TESTTAILER();

/*
** The following section of code is included only in the first tiny-model
** executable and in non-tiny-model executables.
*/
#ifndef PASS2
#ifndef PASS3

    /*
    ** test version call
    */
    TESTHEADER();
    printf("Testing EMMgetversion().\n");
    printf("Results should match value in _EMMversion.\n");
    status = EMMgetversion();
    weirdcodechk("EMMgetversion()", 0, NULL, 0, 0);
    if (status != (int) _EMMversion)
    {
        printf("EMMgetversion() [0x%X] and _EMMversion [0x%X] differ.\n",
                                           status, (unsigned int) _EMMversion);
        exit(3);
    }
    printf("EMS version %d.%d.\n", ((status >> 4) & 0xF), (status & 0xF));
    TESTTAILER();


    /*
    ** test allocation functions
    */
    do_alloc_tests(1L);
    do_alloc_tests(16384L);
    do_alloc_tests(55555L);
    do_alloc_tests(0L);

#endif /* !PASS3 */
#endif /* !PASS2 */

    /*
    ** test frame functions -- included in all executables
    */
    do_frame_tests();

/*
** The following section of code is included only in the first tiny-model
** executable and in non-tiny-model executables.
*/
#ifndef PASS2
#ifndef PASS3

    /*
    ** test name functions
    */
    do_name_tests();

    /*
    ** do mapping tests
    */
    do_map_tests();

    /*
    ** do realloc tests
    */
    do_realloc_tests();

    /*
    ** test the save/restore facility
    */
    do_sr_tests();

#endif /* !PASS3 */
#endif /* !PASS2 */

/*
** The following section of code is included only in the second tiny-model
** executable and in non-tiny-model executables.
*/
#ifndef PASS1
#ifndef PASS3

    /*
    ** test copies of <= one page, frame caching on
    */
    gblmsg = "  SHORT COPY TESTS";
    do_shortcopy_tests();
    gblmsg = "";

    /*
    ** test frame cache control
    */
    TESTHEADER();
    printf("Testing frame caching enable/disable.\n");
    if (_EMMframecache != 0)
    {
        printf("Frame caching is supposed to be off by default, seems to ");
        printf("be on.\n");
        exit(3);
    }
    _EMMenc();
    weirdcodechk("_EMMenc()", 0, NULL, 0, 0);
    if (_EMMframecache == 0)
    {
        printf("_EMMenc() did not enable frame caching.\n");
        exit(3);
    }
    _EMMdisc();
    weirdcodechk("_EMMdisc()", 0, NULL, 0, 0);
    if (_EMMframecache != 0)
    {
        printf("_EMMdisc() did not disable frame caching.\n");
        exit(3);
    }
    printf("Flag and function calls correspond OK.\n");
    TESTTAILER();

    /*
    ** test copies of <= one page, frame caching on
    */
    _EMMenc();
    gblmsg = "  SHORT COPY TESTS WITH FRAME CACHING ON";
    do_shortcopy_tests();
    _EMMdisc();

#endif /* !PASS3 */

/*
** The following section of code is included only in the third tiny-model
** executable and in non-tiny-model executables.
*/
#ifndef PASS2

    /*
    ** test copies of > 1 page, frame caching off
    */
    _EMMdisc();
    gblmsg = "  LONG COPY TESTS";
    do_longcopy_tests();

    /*
    ** test copies of > 1 page, frame caching on
    */
    _EMMenc();
    gblmsg = "  LONG COPY TESTS WITH FRAME CACHING ON";
    do_longcopy_tests();
    gblmsg = "";
    _EMMdisc();

#endif /* !PASS2 */
#endif /* !PASS1 */

    /* end and cleanup */
    printf(">>>END\n");
    printf("All tests succeeded.\n");

    exit(0);
} /* end of main() */


/***************************************************************************
*   FUNCTION: DO_FRAME_TESTS                                               *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function tests EMSIF calls EMMgetnumframe(),                  *
*       EMMgetframeaddr(), and EMMgetsinfraddr().                          *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
*       Fills in the frameptr[] global array.                              *
*                                                                          *
***************************************************************************/
void do_frame_tests(void)
{
    frameinfo *framebuf;
    int frames;
    int status;
    int loop;
    unsigned int segaddr;

    /* set header */
    gblmsg = "  FRAME TESTS";

    /* get number of frames */
    TESTHEADER();
    printf("Getting number of frames with EMMgetnumframe().\n");
    if (_EMMversion < 0x40)
    {
        printf("Should be exactly 4.\n");
    }
    else
    {
        printf("Should be 4 or more.\n");
    }
    frames = EMMgetnumframe();
    weirdcodechk("EMMgetnumframe()", 0, NULL, 0, 0);
    if (frames < 4)
    {
        printf("EMMgetnumframe() returned OK but indicates only %d frames.\n",
                                                                       frames);
        exit(3);
    }
    if ((_EMMversion < 0x40) && (frames != 4))
    {
        printf("EMMgetnumframe() returned OK but indicates wrong number of\n");
        printf("   frames (%d) for this version.\n", frames);
        exit(3);
    }
    printf("EMMgetnumframe() returned OK, %d frames.\n", frames);
    TESTTAILER();

    /* allocate memory for frame address buffer */
    framebuf = (frameinfo *) calloc(frames, sizeof(frameinfo));
    if (framebuf == (frameinfo *) NULL)
    {
        printf("OOPS! Couldn't allocate a buffer. Aborting.\n");
        exit(3);
    }

    /* get frame segment addresses */
    TESTHEADER();
    printf("Getting frame address info with EMMgetframeaddr().\n");
    printf("Should succeed.\n");
    status = EMMgetframeaddr(framebuf);
    TRIPLECHECK("EMMgetframeaddr()", status, 0, framebuf, 0, 0);
    printf("EMMgetframeaddr() returned OK, checking info returned...\n");
    for (loop = 0; loop < frames; loop++)
    {
        /* check for valid frame number */
        if (framebuf[loop].frameno >= frames)
        {
            printf("Frame number in slot %d is bad (%u).\n", loop,
                                                       framebuf[loop].frameno);
            free(framebuf);
            exit(3);
        }
        /* check that frame segment address is on a page (16K) boundary */
        if ((framebuf[loop].segaddr & 0x3FF) != 0)
        {
            printf(
             "Frame segment %u, slot %d, frame number %u, not page aligned.\n",
             framebuf[loop].segaddr, loop, framebuf[loop].frameno);
            free(framebuf);
            exit(3);
        }
        /* if one of frames 0-3, save the address */
        if (framebuf[loop].frameno < 4)
        {
            frameptr[framebuf[loop].frameno] = (unsigned char far *)
                                              MK_FP(framebuf[loop].segaddr, 0);
        }
    }
    printf("Info returned checks out OK.\n");
    TESTTAILER();

    /* now test EMMgetsinfraddr() */
    TESTHEADER();
    printf("Testing EMMgetsinfraddr() against data from EMMgetframeaddr().\n");
    printf("Should succeed.\n");
    /* loop through framebuf, calling EMMgetsinfraddr() on each */
    for (loop = 0; loop < frames; loop++)
    {
        segaddr = EMMgetsinfraddr(framebuf[loop].frameno);
        weirdcodechk("EMMgetsinfraddr()", 0, framebuf, 0, 0);
        if (segaddr != framebuf[loop].segaddr)
        {
            printf("EMMgetsinfraddr(%u) succeeded but returned %u, not %u.\n",
                      framebuf[loop].frameno, segaddr, framebuf[loop].segaddr);
            free(framebuf);
            exit(3);
        }
    }
    printf("EMMgetsinfraddr() returned all addresses OK.\n");
    TESTTAILER();

    /* clean up */
    free(framebuf);
    gblmsg = "";

    return;
} /* end of do_frame_tests() */


/*
** The following section of code is included only in the first tiny-model
** executable and in non-tiny-model executables.
*/
#ifndef PASS2
#ifndef PASS3


/***************************************************************************
*   FUNCTION: DO_ALLOC_TESTS                                               *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function tests EMSIF calls EMMcoreleft(), EMMalloc(), and     *
*       EMMfree(). Don't bother testing EMMallocpages() -- if EMMalloc()   *
*       works, it's only because EMMallocpages() (used internally) works.  *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       bytes - number of bytes of EMS to try to allocate.                 *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void do_alloc_tests(unsigned long bytes)
{
    unsigned long emsfree, emsfree2;
    unsigned long pagelen;
    int handle;

    /* set header */
    gblmsg = "  ALLOCATION TESTS";

    /* get bytes value rounded up to nearest page, or 1 page if bytes == 0 */
    if (bytes != 0L)
    {
        pagelen = bytes + 16383L;
        pagelen &= 0xFFFFC000L;
    }
    else
    {
        pagelen = 16384L;
    }

    /* test coreleft */
    TESTHEADER();
    printf("Testing EMMcoreleft().\n");
    printf("Result should be multiple of 16384.\n");
    emsfree = test_EMMcoreleft();
    printf("EMMcoreleft() returned OK, shows %lu bytes (%lu pages) free.\n",
                                                  emsfree, (emsfree / 16384L));
    TESTTAILER();

    /* make sure enough free */
    if (emsfree < (MINFREE * 16384L))
    {
        printf("Insufficient free EMS to perform all tests. Aborting.\n");
        exit(1);
    }

    /* test allocation */
    TESTHEADER();
    printf("Testing EMMalloc(%lu).\n", bytes);
    printf("Should succeed. Free EMS should drop by %lu bytes (%lu pages).\n",
                                                  pagelen, (pagelen / 16384L));
    handle = test_EMMalloc(bytes);
    printf("EMMalloc() returned OK.\n");
    emsfree2 = test_EMMcoreleft();
    printf("EMMcoreleft() returned OK, shows %lu bytes (%lu pages) free.\n",
                                                emsfree2, (emsfree2 / 16384L));
    if ((emsfree - emsfree2) != pagelen)
    {
        printf("EMMalloc(%lu) caused free to drop weirdly from %lu to %lu.\n",
                                                     bytes, emsfree, emsfree2);
        EMMfree(handle);
        exit(3);
    }
    TESTTAILER();

    /* test free */
    TESTHEADER();
    printf("Testing EMMfree() on handle just returned by EMMalloc().\n");
    printf(
        "Should succeed. Free EMS should increase by %lu bytes (%lu pages).\n",
        pagelen, (pagelen / 16384L));
    test_EMMfree(handle);
    printf("EMMfree() returned OK.\n");
    emsfree2 = test_EMMcoreleft();
    printf("EMMcoreleft() returned OK, shows %lu bytes (%lu pages) free.\n",
                                                emsfree2, (emsfree2 / 16384L));
    if (emsfree2 != emsfree)
    {
        printf("Freeing handle returned by EMMalloc() did not restore\n");
        printf("   free EMS count -- was %lu originally, now %lu.\n", emsfree,
                                                                     emsfree2);
        exit(3);
    }
    TESTTAILER();

    gblmsg = "";

    return;
} /* end of do_alloc_tests() */


/***************************************************************************
*   FUNCTION: DO_NAME_TESTS                                                *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function tests EMSIF calls EMMgetname() and EMMsetname().     *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void do_name_tests(void)
{
    char name[9];
    int handle, status;

    /* set header */
    gblmsg = "  NAME TESTS";

    TESTHEADER();
    printf("Testing EMMsetname() and EMMgetname().\n");
    if (_EMMversion < 0x40)
    {
        printf(
              "EMMsetname() should fail, EMMgetname() return empty buffer.\n");
    }
    else
    {
        printf("Should succeed.\n");
    }
    /* allocate some EMS to assign a name to */
    handle = test_EMMalloc(16384);
    printf("Calling EMMsetname() with name ABCDEFGH.\n");
    status = EMMsetname(handle, "ABCDEFGH");
    if (_EMMversion < 0x40)
    {
        nofailcheck("EMMsetname()", status, NULL, handle, 0);
        weirdretchk("EMMsetname()", status, NULL, handle, 0);
        weirdcodechk("EMMsetname()", EMM_BADVERS, NULL, handle, 0);
        printf("EMMsetname() failed OK.\n");
    }
    else
    {
        TRIPLECHECK("EMMsetname()", status, 0, NULL, handle, 0);
        printf("EMMsetname() succeeded.\n");
    }
    printf("Now calling EMMgetname().\n");
    status = EMMgetname(handle, name);
    TRIPLECHECK("EMMgetname()", status, 0, NULL, handle, 0);
    if (_EMMversion < 0x40)
    {
        if (farmemcheck((unsigned char far *) name, 9, '\0') != 0)
        {
            printf("A character in name is not null.\n");
            EMMfree(handle);
            exit(3);
        }
    }
    else
    {
        if (strcmp(name, "ABCDEFGH") != 0)
        {
            printf("Got name %s back.\n", name);
            EMMfree(handle);
            exit(3);
        }
    }
    printf("EMMgetname() succeeded.\n");
    test_EMMfree(handle);
    TESTTAILER();

    gblmsg = "";

    return;
} /* end of do_name_tests() */


/***************************************************************************
*   FUNCTION: DO_MAP_TESTS                                                 *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function tests EMSIF call EMMmappage().                       *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void do_map_tests(void)
{
    int handle1, handle2;
    int loop;
    int status;

    /* set header */
    gblmsg = "  MAPPING TESTS";

    /* first, allocate some EMS to map */
    handle1 = test_EMMallocpages(6);
    handle2 = test_EMMallocpages(2);

    /* initial test of EMMmappage() */
    TESTHEADER();
    printf("Calling EMMmappage(frameno = 0, first handle, logpage = 0).\n");
    printf("Should succeed.\n");
    test_EMMmappage(0, handle1, 0);
    printf("EMMmappage() returned OK.\n");
    TESTTAILER();

    /* calling EMMmappage() with a bad logical page number */
    TESTHEADER();
    printf("Calling EMMmappage(frameno = 0, first handle, logpage = 6).\n");
    printf("Should fail.\n");
    status = EMMmappage(0, handle1, 6);
    nofailcheck("EMMmappage()", status, NULL, handle1, handle2);
    weirdretchk("EMMmappage()", status, NULL, handle1, handle2);
    weirdcodechk("EMMmappage()", EMM_BADLOGPAGE, NULL, handle1, handle2);
    printf("EMMmappage() failed OK.\n");
    TESTTAILER();

    TESTHEADER();
    printf("Writing data pattern to page 0, first handle... ");
    FMEMSET(frameptr[0], 0, 16384);
    printf("Verifying... ");
    if (farmemcheck(frameptr[0], 16384, 0) != 0)
    {
        printf("Verify failed!\n");
        test_EMMfree(handle1);
        test_EMMfree(handle2);
        exit(3);
    }
    printf("OK.\n");
    printf("Calling EMMmappage(frameno = 0, first handle, logpage = 1).\n");
    test_EMMmappage(0, handle1, 1);
    printf("EMMmappage() returned OK.\n");
    printf("Writing data pattern to page 1, first handle... ");
    FMEMSET(frameptr[0], 1, 16384);
    printf("Verifying... ");
    if (farmemcheck(frameptr[0], 16384, 1) != 0)
    {
        printf("Verify failed!\n");
        test_EMMfree(handle1);
        test_EMMfree(handle2);
        exit(3);
    }
    printf("OK.\n");
    printf("Now mapping page 0 back, see if it's still OK.\n");
    test_EMMmappage(0, handle1, 0);
    printf("EMMmappage() returned OK, verifying contents... ");
    if (farmemcheck(frameptr[0], 16384, 0) != 0)
    {
        printf("Verify failed!\n");
        test_EMMfree(handle1);
        test_EMMfree(handle2);
        exit(3);
    }
    printf("OK.\n");
    printf("EMMmappage() looks like it's doing something.\n");
    TESTTAILER();

    TESTHEADER();
    printf("Testing mapping of all frames...\n");
    printf("Filling rest of allocated pages with unique patterns... ");
    for (loop = 2; loop < 6; loop++)
    {
        test_EMMmappage(0, handle1, loop);
        FMEMSET(frameptr[0], loop, 16384);
        if (farmemcheck(frameptr[0], 16384, (unsigned char) loop) != 0)
        {
            printf("Verify failed on handle 1, page %d.\n", loop);
            test_EMMfree(handle1);
            test_EMMfree(handle2);
            exit(3);
        }
    }
    test_EMMmappage(0, handle2, 0);
    FMEMSET(frameptr[0], 0x10, 16384);
    if (farmemcheck(frameptr[0], 16384, 0x10) != 0)
    {
        printf("Verify failed on handle 2, page 0!\n");
        test_EMMfree(handle1);
        test_EMMfree(handle2);
        exit(3);
    }
    test_EMMmappage(0, handle2, 1);
    FMEMSET(frameptr[0], 0x11, 16384);
    if (farmemcheck(frameptr[0], 16384, 0x11) != 0)
    {
        printf("Verify failed on handle 2, page 1!\n");
        test_EMMfree(handle1);
        test_EMMfree(handle2);
        exit(3);
    }
    printf("Done.\n");
    printf("Mapping handle 1 pages 0-3 in frames 0-3 respectively... ");
    for (loop = 0; loop < 4; loop++)
    {
        test_EMMmappage(loop, handle1, loop);
    }
    printf("Done.\n");
    printf("Verifying contents... ");
    for (loop = 0; loop < 4; loop++)
    {
        if (farmemcheck(frameptr[loop], 16384, (unsigned char) loop) != 0)
        {
            printf("Verify failed for frame %d.\n", loop);
            test_EMMfree(handle1);
            test_EMMfree(handle2);
            exit(3);
        }
    }
    printf("OK.\n");
    printf("Mapping handle 1 pages 0-3 in frames 0-3 in reverse... ");
    for (loop = 0; loop < 4; loop++)
    {
        test_EMMmappage(loop, handle1, (3 - loop));
    }
    printf("Done.\n");
    printf("Verifying contents... ");
    for (loop = 0; loop < 4; loop++)
    {
        if (farmemcheck(frameptr[loop], 16384, (unsigned char)(3 - loop)) != 0)
        {
            printf("Verify failed for frame %d.\n", loop);
            test_EMMfree(handle1);
            test_EMMfree(handle2);
            exit(3);
        }
    }
    printf("OK.\n");
    printf(
      "Mapping handle 1 pages 0-3 in frames 0-3 except page 5 in frame 2... ");
    for (loop = 0; loop < 4; loop++)
    {
        test_EMMmappage(loop, handle1, loop);
    }
    test_EMMmappage(2, handle1, 5);
    printf("Done.\n");
    printf("Verifying contents... ");
    for (loop = 0; loop < 4; loop++)
    {
        if (farmemcheck(frameptr[loop], 16384,
                                 (unsigned char)((loop != 2) ? loop : 5)) != 0)
        {
            printf("Verify failed for frame %d.\n", loop);
            test_EMMfree(handle1);
            test_EMMfree(handle2);
            exit(3);
        }
    }
    printf("OK.\n");
    TESTTAILER();

    TESTHEADER();
    printf("Final mapping test... two handles at once!\n");
    printf("Mapping handle2 page 1 in frame 0.\n");
    printf("        handle1 page 4 in frame 1.\n");
    printf("        handle1 page 0 in frame 2.\n");
    printf("        handle2 page 0 in frame 3.\n");
    test_EMMmappage(0, handle2, 1);
    test_EMMmappage(1, handle1, 4);
    test_EMMmappage(2, handle1, 0);
    test_EMMmappage(3, handle2, 0);
    printf("Mapping done. Verifying... ");
    if (farmemcheck(frameptr[0], 16384, 0x11) != 0)
    {
        printf("Verify failed for frame 0.\n");
        test_EMMfree(handle1);
        test_EMMfree(handle2);
        exit(3);
    }
    if (farmemcheck(frameptr[1], 16384, 4) != 0)
    {
        printf("Verify failed for frame 1.\n");
        test_EMMfree(handle1);
        test_EMMfree(handle2);
        exit(3);
    }
    if (farmemcheck(frameptr[2], 16384, 0) != 0)
    {
        printf("Verify failed for frame 2.\n");
        test_EMMfree(handle1);
        test_EMMfree(handle2);
        exit(3);
    }
    if (farmemcheck(frameptr[3], 16384, 0x10) != 0)
    {
        printf("Verify failed for frame 3.\n");
        test_EMMfree(handle1);
        test_EMMfree(handle2);
        exit(3);
    }
    printf("OK.\n");
    TESTTAILER();

    /* clean up */
    test_EMMfree(handle1);
    test_EMMfree(handle2);
    gblmsg = "";

    return;
} /* end of do_map_tests() */


/***************************************************************************
*   FUNCTION: DO_REALLOC_TESTS                                             *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function tests EMSIF calls EMMrealloc() and EMMreallocpages().*
*       Note that EMMreallocpages() is not tested directly; if EMMrealloc()*
*       works, it is only because EMMreallocpages() (which it uses         *
*       internally) is working.                                            *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void do_realloc_tests(void)
{
    unsigned long emsfree, emsfree2;
    int handle;

    /* set header */
    gblmsg = "  REALLOC TESTS";
    TESTHEADER();

    /* allocate original block */
    handle = test_EMMalloc(55555);

    /* map and fill */
    test_EMMmappage(0, handle, 0);
    test_EMMmappage(1, handle, 1);
    test_EMMmappage(2, handle, 2);
    test_EMMmappage(3, handle, 3);
    FMEMSET(frameptr[0], 0, 16384);
    FMEMSET(frameptr[1], 1, 16384);
    FMEMSET(frameptr[2], 2, 16384);
    FMEMSET(frameptr[3], 3, 16384);

    /* get current value for free EMS */
    emsfree = test_EMMcoreleft();

    /* realloc */
    printf("Testing EMMrealloc() to make block shorter.\n");
    handle = test_EMMrealloc(handle, 48000);

    /* check free -- should be 16K higher */
    emsfree2 = test_EMMcoreleft();
    if ((emsfree + 16384L) != emsfree2)
    {
        printf("EMMrealloc() should have freed up one page. Old: %lu, new: %lu\n",
               emsfree, emsfree2);
        EMMfree(handle);
        exit(3);
    }

    /* check page contents */
    test_EMMmappage(0, handle, 0);
    test_EMMmappage(1, handle, 1);
    test_EMMmappage(2, handle, 2);
    if ((farmemcheck(frameptr[0], 16384, 0) != 0) ||
        (farmemcheck(frameptr[1], 16384, 1) != 0) ||
        (farmemcheck(frameptr[2], 16384, 2) != 0))
    {
        printf("EMMrealloc() did not preserve contents.\n");
        EMMfree(handle);
        exit(3);
    }
    TESTTAILER();

    TESTHEADER();
    printf("Testing EMMrealloc() to make block longer.\n");
    handle = test_EMMrealloc(handle, 55555);

    /* check free -- should be same as original */
    emsfree2 = test_EMMcoreleft();
    if (emsfree != emsfree2)
    {
        printf("EMMrealloc() did not allocate as expected.\n");
        EMMfree(handle);
        exit(3);
    }

    /*
    ** check page contents -- contents of new page are undefined so
    ** don't bother checking it.
    */
    test_EMMmappage(0, handle, 0);
    test_EMMmappage(1, handle, 1);
    test_EMMmappage(2, handle, 2);
    if ((farmemcheck(frameptr[0], 16384, 0) != 0) ||
        (farmemcheck(frameptr[1], 16384, 1) != 0) ||
        (farmemcheck(frameptr[2], 16384, 2) != 0))
    {
        printf("EMMrealloc() did not preserve contents.\n");
        EMMfree(handle);
        exit(3);
    }
    TESTTAILER();

    /* clean up */

    test_EMMfree(handle);
    gblmsg = "";

    return;
} /* end of do_realloc_tests() */


/***************************************************************************
*   FUNCTION: DO_SR_TESTS                                                  *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function tests the EMSIF mapping save/restore functions:      *
*       EMMsrinit(), EMMsave(), EMMrestore().                              *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void do_sr_tests(void)
{
    void *savehandle;
    int handle1, handle2;
    int status;
    int loop;

    /* set header */
    gblmsg = "  SAVE/RESTORE TESTS";

    /* first, allocate some EMS and fill with unique patterns */
    handle1 = test_EMMallocpages(6);
    handle2 = test_EMMallocpages(2);
    for (loop = 0; loop < 6; loop++)
    {
        test_EMMmappage(0, handle1, loop);
        FMEMSET(frameptr[0], loop, 16384);
        if (farmemcheck(frameptr[0], 16384, (unsigned char) loop) != 0)
        {
            printf("Verify failed on handle 1, page %d.\n", loop);
            test_EMMfree(handle1);
            test_EMMfree(handle2);
            exit(3);
        }
    }
    test_EMMmappage(0, handle2, 0);
    FMEMSET(frameptr[0], 0x10, 16384);
    if (farmemcheck(frameptr[0], 16384, 0x10) != 0)
    {
        printf("Verify failed on handle 2, page 0!\n");
        test_EMMfree(handle1);
        test_EMMfree(handle2);
        exit(3);
    }
    test_EMMmappage(0, handle2, 1);
    FMEMSET(frameptr[0], 0x11, 16384);
    if (farmemcheck(frameptr[0], 16384, 0x11) != 0)
    {
        printf("Verify failed on handle 2, page 1!\n");
        test_EMMfree(handle1);
        test_EMMfree(handle2);
        exit(3);
    }

    TESTHEADER();
    printf(
     "Testing save/restore facility. Calling EMMsave() before EMMsrinit().\n");
    printf("Should fail.\n");
    savehandle = EMMsave();
    nofailcheck("EMMsave()", (int) _EMMerror, NULL, handle1, handle2);
    weirdcodechk("EMMsave()", EMM_NOSR, NULL, handle1, handle2);
    printf("EMMsave() failed OK.\n");
    TESTTAILER();

    TESTHEADER();
    printf("Calling EMMsrinit().\nShould %s.\n",
                                 ((_EMMversion >= 0x32) ? "succeed" : "fail"));
    status = EMMsrinit(malloc);
    if (_EMMversion >= 0x32)
    {
        TRIPLECHECK("EMMsrinit()", status, 0, NULL, handle1, handle2);
        printf("EMMsrinit() succeeded; save/restore facility initialized.\n");
    }
    else
    {
        weirdretchk("EMMsrinit()", status, NULL, handle1, handle2);
        nofailcheck("EMMsrinit()", status, NULL, handle1, handle2);
        weirdcodechk("EMMsrinit()", EMM_BADVERS, NULL, handle1, handle2);
        printf("EMMsrinit() failed OK. Skipping other save/restore tests.\n");
    }
    TESTTAILER();

    if (_EMMversion >= 0x32)
    {
        TESTHEADER();
        printf("Testing EMMsave().\n");
        printf("Mapping handle 1 pages 0-3 in frames 0-3 respectively.\n");
        for (loop = 0; loop < 4; loop++)
        {
            test_EMMmappage(loop, handle1, loop);
        }

        printf("Calling EMMsave() to save current configuration.\n");
        printf("Should succeed.\n");
        savehandle = (void *) NULL;
        savehandle = EMMsave();
        if (savehandle == (void *) NULL)
        {
            printf("EMMsave() did not return anything, code 0x%X.\n",
                                                     (unsigned int) _EMMerror);
            test_EMMfree(handle1);
            test_EMMfree(handle2);
            exit(3);
        }
        weirdcodechk("EMMsave()", 0, NULL, handle1, handle2);
        printf("EMMsave() succeeded.\n");
        TESTTAILER();

        printf("Changing configuration.\n");
        test_EMMmappage(0, handle2, 1);
        test_EMMmappage(1, handle1, 4);
        test_EMMmappage(2, handle1, 0);
        test_EMMmappage(3, handle2, 0);

        TESTHEADER();
        printf("Calling EMMrestore() to restore previous configuration.\n");
        printf("Should succeed.\n");
        status = EMMrestore(savehandle);
        TRIPLECHECK("EMMrestore()", status, 0, NULL, handle1, handle2);
        printf("EMMrestore() returned OK, verifying contents... ");
        for (loop = 0; loop < 4; loop++)
        {
            if (farmemcheck(frameptr[loop], 16384, (unsigned char) loop) != 0)
            {
                printf("Verify failed for frame %d.\n", loop);
                test_EMMfree(handle1);
                test_EMMfree(handle2);
                exit(3);
            }
        }
        printf("OK.\n");
        TESTTAILER();
    }

    printf("Cleaning up.\n");
    test_EMMfree(handle1);
    test_EMMfree(handle2);
    if (_EMMversion >= 0x32)
    {
        free(savehandle);
    }
    gblmsg = "";

    return;
} /* end of do_sr_tests() */

#endif /* !PASS3 */
#endif /* !PASS2 */


/*
** The following group of functions {test_EMM*()} are wrapper functions
** that call the EMSIF function named and perform preliminary checks on
** the return values. This keeps code size down since the check only has
** to be coded in one place.
*/

/***************************************************************************
*   FUNCTION: TEST_EMMCORELEFT                                             *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function calls EMSIF function EMMcoreleft() and checks the    *
*       return value to see if it is a multiple of 16384 (the EMS page     *
*       size).                                                             *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Returns the value returned by EMMcoreleft().                       *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
unsigned long test_EMMcoreleft(void)
{
    unsigned long emsfree;

    /* call EMMcoreleft and check error code */
    emsfree = EMMcoreleft();
    weirdcodechk("EMMcoreleft()", 0, NULL, 0, 0);

    /* check if free byte count is multiple of 16384 */
    if ((emsfree % 16384L) != 0)
    {
        printf("EMMcoreleft() returned strange number %lu.\n", emsfree);
        exit(3);
    }

    return emsfree;
} /* end of test_EMMcoreleft() */


/***************************************************************************
*   FUNCTION: TEST_EMMALLOC                                                *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function calls EMSIF function EMMalloc() and checks the       *
*       return codes.                                                      *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       bytes - bytes of EMS to allocate                                   *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Returns the handle returned by EMMalloc().                         *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
int test_EMMalloc(unsigned long bytes)
{
    int handle;

    /* call EMMalloc() and check the return */
    handle = EMMalloc(bytes);
    weirdcodechk("EMMalloc()", 0, NULL, 0, 0);

    return handle;
} /* end of test_EMMalloc() */


/***************************************************************************
*   FUNCTION: TEST_EMMALLOCPAGES                                           *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function calls EMSIF function EMMallocpages() and checks the  *
*       return codes.                                                      *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       pages - pages of EMS to allocate                                   *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Returns the handle returned by EMMallocpages().                    *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
int test_EMMallocpages(int pages)
{
    int handle;

    /* call EMMallocpages() and check the return */
    handle = EMMallocpages(pages);
    weirdcodechk("EMMallocpages()", 0, NULL, 0, 0);

    return handle;
} /* end of test_EMMallocpages() */


/***************************************************************************
*   FUNCTION: TEST_EMMREALLOC                                              *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function calls EMSIF function EMMrealloc() and checks the     *
*       return codes.                                                      *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       handle - EMS handle to be resized                                  *
*       bytes  - new size for handle                                       *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Returns the handle returned by EMMrealloc().                       *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
int test_EMMrealloc(int handle, unsigned long bytes)
{
    int newhan;

    /* call EMMrealloc() and check the return */
    newhan = EMMrealloc(handle, bytes);
    weirdcodechk("EMMrealloc()", 0, NULL, handle, newhan);
    if (((_EMMversion >= 0x40) && (newhan != handle)) ||
        ((_EMMversion < 0x40) && (newhan == handle)))
    {
        printf("Handle problem in EMMrealloc(). New: %d, old: %d\n", newhan,
               handle);
        EMMfree(handle);
        EMMfree(newhan);
    }

    return newhan;
} /* end of test_EMMrealloc() */


/***************************************************************************
*   FUNCTION: TEST_EMMFREE                                                 *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function calls EMSIF function EMMfree() and checks the        *
*       return codes.                                                      *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       handle - EMS handle to be freed                                    *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void test_EMMfree(int handle)
{
    int status;

    /* call EMMfree() and check the return */
    status = EMMfree(handle);
    TRIPLECHECK("EMMfree()", status, 0, NULL, 0, 0);

    return;
} /* end of test_EMMfree() */


/***************************************************************************
*   FUNCTION: TEST_EMMMAPPAGE                                              *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function calls EMSIF function EMMmappage() and checks the     *
*       return codes.                                                      *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       frameno - frame number to map page into                            *
*       handle  - handle of page to be mapped                              *
*       logpage - page number to map                                       *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void test_EMMmappage(int frameno, int handle, int logpage)
{
    int status;

    /*
    ** do sanity check on frame number, just in case -- we shouldn't be
    ** trying to map into frames other than standard 0 through 3.
    */
    if ((frameno < 0) || (frameno > 3))
    {
        printf("Sanity check failed, trying to map to frame %d, aborting.\n",
                                                                      frameno);
        EMMfree(handle);
        exit(3);
    }

    /* call EMMmappage() and check return */
    status = EMMmappage(frameno, handle, logpage);
    TRIPLECHECK("EMMmappage()", status, 0, NULL, handle, 0);

    return;
} /* end of test_EMMmappage() */


/*
** The following group of functions are used to speed up return checking
** and keep code size down, since the return check only has to be coded
** in one place. They are used in various macros to further compact and
** clarify the code.
*/

/***************************************************************************
*   FUNCTION: WEIRDRETCHK                                                  *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function checks to see if the status value passed to it is    *
*       either 0 or EMMOOPS, and assumes something has gone wrong if it    *
*       is not. If something has gone wrong, does some clean up before     *
*       exiting.                                                           *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       function - name of function which may have goofed                  *
*       status   - status value to check                                   *
*       tofree1  - conventional memory block to be freed on exit. Not      *
*                  freed if NULL.                                          *
*       tofree2  - EMS handle to be freed on exit. Not freed if 0.         *
*       tofree2  - another EMS handle to be freed on exit. Not freed if 0. *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void, or may not return.                                           *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void weirdretchk(char *function, int status, void *tofree1, int tofree2,
                                                                   int tofree3)
{
    if ((status != EMMOOPS) && (status != 0))
    {
        printf("%s returned weird value %d, code 0x%X.\n", function, status,
                                                     (unsigned int) _EMMerror);
        if (tofree1 != (void *) NULL)
        {
            free(tofree1);
        }
        if (tofree2 != 0)
        {
            EMMfree(tofree2);
        }
        if (tofree3 != 0)
        {
            EMMfree(tofree3);
        }
        exit(3);
    }

    return;
} /* end of weirdretchk() */


/***************************************************************************
*   FUNCTION: WEIRDCODECHK                                                 *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function checks to see if the EMSIF error code value matches  *
*       the expected value, and assumes something has gone wrong if it     *
*       does not. If something has gone wrong, does some clean up before   *
*       exiting.                                                           *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       function - name of function which may have goofed                  *
*       expected - expected value of _EMMerror                             *
*       tofree1  - conventional memory block to be freed on exit. Not      *
*                  freed if NULL.                                          *
*       tofree2  - EMS handle to be freed on exit. Not freed if 0.         *
*       tofree2  - another EMS handle to be freed on exit. Not freed if 0. *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void, or may not return.                                           *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void weirdcodechk(char *function, int expected, void *tofree1, int tofree2,
                                                                   int tofree3)
{
    if ((int) _EMMerror != expected)
    {
        printf("%s returned unexpected code 0x%X.\n", function,
                                                     (unsigned int) _EMMerror);
        if (tofree1 != (void *) NULL)
        {
            free(tofree1);
        }
        if (tofree2 != 0)
        {
            EMMfree(tofree2);
        }
        if (tofree3 != 0)
        {
            EMMfree(tofree3);
        }
        exit(3);
    }

    return;
} /* end of weirdcodechk() */


/***************************************************************************
*   FUNCTION: FAILCHECK                                                    *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function checks to see if the status value passed to it is    *
*       EMMOOPS and exits if it is. failcheck() is used when a function    *
*       is expected to succeed. Does some clean up before exiting.         *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       function - name of function which may have goofed                  *
*       status   - status value to be checked                              *
*       tofree1  - conventional memory block to be freed on exit. Not      *
*                  freed if NULL.                                          *
*       tofree2  - EMS handle to be freed on exit. Not freed if 0.         *
*       tofree2  - another EMS handle to be freed on exit. Not freed if 0. *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void, or may not return.                                           *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void failcheck(char *function, int status, void *tofree1, int tofree2,
                                                                  int tofree3)
{
    if (status == EMMOOPS)
    {
        printf("%s failed, code 0x%X.\n", function, (unsigned int) _EMMerror);
        if (tofree1 != (void *) NULL)
        {
            free(tofree1);
        }
        if (tofree2 != 0)
        {
            EMMfree(tofree2);
        }
        if (tofree3 != 0)
        {
            EMMfree(tofree3);
        }
        exit(3);
    }

    return;
} /* end of failcheck() */


/***************************************************************************
*   FUNCTION: NOFAILCHECK                                                  *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function checks to see if the status value passed to it is    *
*       0 and exits if it is. nofailcheck() is used when a function is     *
*       expected to fail. Does some clean up before exiting.               *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       function - name of function which may have goofed                  *
*       status   - status value to be checked                              *
*       tofree1  - conventional memory block to be freed on exit. Not      *
*                  freed if NULL.                                          *
*       tofree2  - EMS handle to be freed on exit. Not freed if 0.         *
*       tofree2  - another EMS handle to be freed on exit. Not freed if 0. *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void, or may not return.                                           *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void nofailcheck(char *function, int status, void *tofree1, int tofree2,
                                                                  int tofree3)
{
    if (status == 0)
    {
        printf("%s did not fail.\n", function);
        if (tofree1 != (void *) NULL)
        {
            free(tofree1);
        }
        if (tofree2 != 0)
        {
            EMMfree(tofree2);
        }
        if (tofree3 != 0)
        {
            EMMfree(tofree3);
        }
        exit(3);
    }

    return;
} /* end of nofailcheck() */

