/***************************************************************************
*   xmstest.c                                                              *
*   MODULE:  XMSIF                                                         *
*   OS:      DOS                                                           *
*   VERSION: 1.3                                                           *
*   DATE:    05/09/93                                                      *
*                                                                          *
*   Copyright (c) 1993 James W. Birdsall. All Rights Reserved.             *
*                                                                          *
*   Requires xmsif.h, testutil.h, and xmstest.h to compile.                *
*   Compiles under Borland C++ 2.0, Turbo C 2.0, MSC 6.00A, MSC/C++ 7.0.   *
*                                                                          *
*   Regression test and example for XMSIF.                                 *
*                                                                          *
*   This file is part one of the regression tester and example for XMSIF.  *
*   The other parts are named EMSTEST2.C and EMSTEST3.C. All three parts   *
*   must be compiled and linked together along with the appropriate XMSIF  *
*   library to produce the tester executable. This program compiles under  *
*   tiny, small, medium, compact, large, and huge models.                  *
*                                                                          *
*   To use this tester: in general, just run it. Depending on what XMS     *
*   driver you have, you may need to use the "-q" option. HIMEM.SYS does   *
*   not require this option; QEMM 5.1* does; I don't know about other XMS  *
*   drivers. If you haven't used it and should, or should use it and       *
*   haven't, the tester can detect this and will abort with a message      *
*   suggesting you do the opposite of whatever you did. For more detail on *
*   what this option actually does, see below. The tester produces output  *
*   on stdout. It performs 70+ tests, depending on the exact configuration *
*   of your system, and parts of it run quite fast. If you want to read    *
*   all the output, you should redirect the output to a file (but first    *
*   you should probably run it straight to get an idea of how long it      *
*   takes on your machine). If you just want to see whether the tests all  *
*   pass, just run it -- if a test fails, execution aborts immediately.    *
*                                                                          *
*   Certain types of failure may cause XMSTEST to not deallocate 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.                            *
*                                                                          *
*                                                                          *
*   The actions XMSTEST takes may be broken down into two parts: EMB tests *
*   and UMB tests. In the first part, it tests the XMM* functions which    *
*   operate on extended memory blocks (EMBs). In the second part, it tests *
*   the UMB* functions which operate on upper memory blocks (UMBs). Before *
*   doing any of this, it tries to initialize XMSIF, which checks for the  *
*   presence of an XMS driver, and aborts if there isn't one. Then it      *
*   starts the first part, performing various preliminary tests on EMBs.   *
*   If it determines that there isn't a large enough EMB free, it reports  *
*   this and skips the copying function tests (tests of _XMMcopy() and     *
*   _XMMicopy()), otherwise it performs the copying function tests. Then   *
*   it starts the second part by trying to determine whether your system   *
*   has UMBs. If it does not, it reports this fact and skips the rest of   *
*   the UMB tests. If it does, it performs the rest of the UMB tests. If   *
*   you see a message about "another broken system", that means that your  *
*   XMS driver's support of UMBs is somewhat flaky, and the workarounds    *
*   already built into XMSIF were unable to cover the problem. In this     *
*   case, it will perform the rest of the UMB tests but is unable to check *
*   that the functions actually did anything -- i.e. it can check the      *
*   return code from the function, but cannot independently verify via     *
*   other functions that the desired effect was actually achieved. See the *
*   XMSIF documentation for a description of problems seen in the UMB      *
*   support of various XMS drivers.                                        *
*                                                                          *
*   The "-q" option covers for some unusual behavior observed in QEMM      *
*   5.1x which may also occur in other XMS drivers which provide EMS       *
*   services as well. In QEMM's case, when extended memory is allocated,   *
*   the total amount of XMS still available is decreased by the allocation *
*   size rounded up to the nearest _16K_ (the EMS page size) instead of to *
*   the nearest 1K (the XMS minimum allocation unit). HIMEM.SYS does not   *
*   display this behavior. The "-q" option tells XMSTEST to expect a drop  *
*   of a multiple of 16K after an allocation instead of a multiple of 1K.  *
*   While XMSTEST can easily detect the difference, this option was added  *
*   so that XMSTEST would know which multiple was expected and could       *
*   detect incorrect drops more reliably.                                  *
*                                                                          *
*   The "-w" option covers for some different unusual behavior observed    *
*   in DOS windows running under MS Windows 3.1 (it is not known whether   *
*   Windows 3.0 does the same thing). In this case, when extended memory   *
*   is allocated, the total amount of XMS still available is decreased by  *
*   the allocation size rounded up to the nearest _4K_ (the 803/486 memory *
*   management page size). The "-w" option tells XMSTEST to expect a drop  *
*   of a multiple of 4K after an allocation instead of a multiple of 1K.   *
*   While XMSTEST can easily detect the difference, this option was added  *
*   so that XMSTEST would know which multiple was expected and could       *
*   detect incorrect drops more reliably.                                  *
*                                                                          *
*   The "-3" option indicates that XMSTEST is running on a 386 or better   *
*   and hence can test the XMMraw3() function safely (if the XMS version   *
*   is 3.0 or better).                                                     *
*                                                                          *
*                                                                          *
*   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 this file will be used instead.              *
*                                                                          *
***************************************************************************/

/*
** system includes <>
*/

#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <string.h>
#include <ctype.h>


/*
** custom includes ""
*/

#include "xmsif.h"
#include "xmstest.h"

#include "testutil.h"

/*
** local #defines
*/

/*
** misc: copyright strings, version macros, etc.
*/

/*
** typedefs
*/

/*
** global variables
*/

int testno = 1;                     /* number of test currently being done  */
char *gblmsg = "";                  /* msg to be printed in test header     */

int qflag = 0;                      /* running QEMM or not?                 */
int wflag = 0;                      /* running under Windows or not?        */
int on386 = 0;                      /* running on a 386+ or not?            */


/*
** static globals
*/

/*
** function prototypes
*/

/*
** functions
*/


/***************************************************************************
*   FUNCTION: MAIN                                                         *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       The master function.                                               *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       None.                                                              *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
main(int argc, char *argv[])
{
    int status;                     /* return status from library functions */
    unsigned int version;
    int i;

    for (i = 1; i < argc; i++)
    {
        if ((argv[i][0] == '-') || (argv[i][0] == '/'))
        {
            if (toupper(argv[i][1]) == 'Q')
            {
                qflag = 1;
            }
            else if (toupper(argv[i][1]) == 'W')
            {
                wflag = 1;
            }
            else if (argv[i][1] == '3')
            {
                on386 = 1;
            }
            else
            {
                printf("Usage: XMSTEST [-wq3]\n");
                exit(1);
            }
        }
        else
        {
            printf("Usage: XMSTEST [-wq3]\n");
            exit(1);
        }
    }

    /* set banner */
    gblmsg = "  PRELIMINARY EMB TESTS";

    /*
    ** check initialization test
    */
    TESTHEADER();
    printf("Making a call to XMMgetversion() before calling XMMlibinit().\n");
    printf("The call should fail.\n");
    XMMgetversion();
    nofailcheck("XMMgetversion()", (int) _XMMerror, (void far *) NULL, 0, 0);
    weirdcodechk("XMMgetversion()", XMM_NOINIT, (void far *) NULL, 0, 0);
    TESTTAILER();


    /*
    ** initialize XMSIF
    */
    TESTHEADER();
    printf("Calling XMMlibinit().\n");
    printf("Should succeed if Extended Memory Manager is present.\n");
    status = XMMlibinit();
    switch (status)
    {
        case XMMOOPS:
            printf("XMMlibinit() failed, code 0x%X.\n",
                                                    (unsigned int) _XMMerror);
            exit(3);
            break;

        case NOXMM:
            printf("XMMlibinit() did not find an Extended Memory Manager.\n");
            exit(3);
            break;

        case 0:
            printf("XMMlibinit() returned OK.\n");
            weirdcodechk("XMMlibinit()", 0, (void far *) NULL, 0, 0);
            break;

        default:
            printf("XMMlibinit() returned strange value %d.\n", status);
            exit(3);
            break;
    }
    TESTTAILER();


    /*
    ** test version call
    */
    TESTHEADER();
    printf("Testing XMMgetversion().\n");
    printf("Results should match value in _XMMversion.\n");
    version = XMMgetversion();
    weirdcodechk("XMMgetversion()", 0, (void far *) NULL, 0, 0);
    if (version != (int) _XMMversion)
    {
        printf("XMMgetversion() [0x%X] and _XMMversion [0x%X] differ.\n",
                                          version, (unsigned int) _XMMversion);
        exit(3);
    }
    printf("XMS version %d.%02d.\n", ((version >> 8) & 0xFF), (version & 0xFF));
    TESTTAILER();


    /*
    ** test allocation functions
    */
    if (do_alloc_tests(1L) == 0)
    {
        do_alloc_tests(16384L);
        do_alloc_tests(81111L);
        do_alloc_tests(0L);

        /*
        ** test rawcall interface
        */
        do_rawcall_tests();

        /*
        ** test copies
        */
        do_copy_tests();
    }

    /*
    ** test UMB functions (if possible)
    */
    do_UMB_tests();

    /* end and cleanup */
    printf(">>>END\n");
    printf("All tests succeeded.\n");

    exit(0);
} /* end of main() */


/***************************************************************************
*   FUNCTION: DO_ALLOC_TESTS                                               *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function tests XMSIF calls XMMcoreleft(), XMMallcoreleft(),   *
*       XMMalloc(), XMMrealloc(), and XMMfree().                           *                *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       bytes - number of bytes of XMS to try to allocate.                 *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
int do_alloc_tests(unsigned long bytes)
{
    unsigned long xmsfreeorig, xmsfreenow;
    unsigned long xmsallfreeorig, xmsallfreenow;
    unsigned long klen, mmupagelen, emspagelen;
    unsigned long expected;
    int handle;
    long diff = ((bytes > HALFLEN) ? -20000 : 20000);

    /* get bytes value rounded up to nearest K */
    klen = ((bytes + 1023L) & 0xFFFFFC00);
    /* get bytes value rounded up to nearest 4K */
    mmupagelen = ((bytes + 4095L) & 0xFFFFF000);
    /* get bytes value rounded up to nearest 16K */
    emspagelen = ((bytes + 16383L) & 0xFFFFC000);

    /* determine expected length */
    if (qflag != 0)
    {
        expected = emspagelen;
    }
    else if (wflag != 0)
    {
        expected = mmupagelen;
    }
    else
    {
        expected = klen;
    }

    /* test coreleft */
    TESTHEADER();
    printf("Testing XMMcoreleft().\n");
    printf("Result should be multiple of 1024.\n");
    xmsfreeorig = test_XMMcoreleft();
    printf("XMMcoreleft() returned OK, shows %lu bytes (%luK) free.\n",
           xmsfreeorig, (xmsfreeorig / 1024L));
    TESTTAILER();

    /* test allcoreleft */
    TESTHEADER();
    printf("Testing XMMallcoreleft().\n");
    printf("Result should be multiple of 1024 and >= XMMcoreleft().\n");
    xmsallfreeorig = test_XMMallcoreleft();
    if (xmsallfreeorig >= xmsfreeorig)
    {
        printf("XMMallcoreleft() returned OK, shows %lu bytes (%luK) free.\n",
               xmsallfreeorig, (xmsallfreeorig / 1024L));
    }
    else
    {
        printf("XMMallcoreleft() returned low value %lu.\n", xmsallfreeorig);
        exit(3);
    }

    /* make sure enough free */
    if (xmsallfreeorig < MINFREE)
    {
        printf("Insufficient free XMS to perform all tests. Aborting EMB tests.\n");
        return 1;
    }
    /* enough free, but is it unfragmented enough to be useable? */
    if (xmsfreeorig < MINFREE)
    {
        /* largest block isn't big enough -- can't finish tests */
        printf("XMS too fragmented to perform all tests. Aborting EMB tests.\n");
        return 1;
    }

    /* test allocation */
    TESTHEADER();
    printf("Testing XMMalloc(%lu).\n", bytes);
    printf("Should succeed. Free XMS should drop by %lu bytes (%luK).\n",
           expected, (expected / 1024L));
    handle = test_XMMalloc(bytes);
    printf("XMMalloc() returned OK.\n");
    xmsfreenow = test_XMMcoreleft();
    printf("XMMcoreleft() returned OK, shows %lu bytes (%luK) free.\n",
           xmsfreenow, (xmsfreenow / 1024L));
    xmsallfreenow = test_XMMallcoreleft();
    printf("XMMallcoreleft() returned OK, shows %lu bytes (%luK) free.\n",
           xmsallfreenow, (xmsallfreenow / 1024L));
    if ((xmsallfreeorig - xmsallfreenow) != expected)
    {
        printf("XMMalloc(%lu) caused total free to drop from %lu to %lu.\n",
               bytes, xmsallfreeorig, xmsallfreenow);
        if ((xmsallfreeorig - xmsallfreenow) == emspagelen)
        {
            if (emspagelen == mmupagelen)
            {
                printf("Try using the -q or -w switches.\n");
            }
            else
            {
                printf("Try using the -q switch.\n");
            }
        }
        else if ((xmsallfreeorig - xmsallfreenow) == mmupagelen)
        {
            printf("Try using the -w switch.\n");
        }
        else if ((xmsallfreeorig - xmsallfreenow) == klen)
        {
            printf("Try not using the -%c switch.\n",
                   ((qflag != 0) ? 'q' : 'w'));
        }
        XMMfree(handle);
        exit(3);
    }
    TESTTAILER();

    /* get realloc value rounded up to nearest K */
    klen = ((bytes + diff + 1023L) & 0xFFFFFC00);
    /* get realloc value rounded up to nearest 4K */
    mmupagelen = ((bytes + diff + 4095L) & 0xFFFFF000);
    /* get realloc value rounded up to nearest 16K */
    emspagelen = ((bytes + diff + 16383L) & 0xFFFFC000);

    /* determine expected value */
    if (qflag != 0)
    {
        expected = emspagelen;
    }
    else if (wflag != 0)
    {
        expected = mmupagelen;
    }
    else
    {
        expected = klen;
    }

    /* test reallocation */
    TESTHEADER();
    printf("Testing XMMrealloc(%lu).\n", bytes + diff);
    printf("Should succeed. Free XMS should now be %lu bytes (%luK) below original.\n",
           expected, (expected / 1024L));
    handle = test_XMMrealloc(handle, bytes + diff);
    printf("XMMrealloc() returned OK.\n");
    xmsfreenow = test_XMMcoreleft();
    printf("XMMcoreleft() returned OK, shows %lu bytes (%luK) free.\n",
           xmsfreenow, (xmsfreenow / 1024L));
    xmsallfreenow = test_XMMallcoreleft();
    printf("XMMallcoreleft() returned OK, shows %lu bytes (%luK) free.\n",
           xmsallfreenow, (xmsallfreenow / 1024L));
    if ((xmsallfreeorig - xmsallfreenow) != expected)
    {
        printf("XMMrealloc(%lu) caused total free to be %lu.\n",
               bytes, xmsallfreenow);
        if ((xmsallfreeorig - xmsallfreenow) == emspagelen)
        {
            if (emspagelen == mmupagelen)
            {
                printf("Try using the -q or -w switches.\n");
            }
            else
            {
                printf("Try using the -q switch.\n");
            }
        }
        else if ((xmsallfreeorig - xmsallfreenow) == mmupagelen)
        {
            printf("Try using the -w switch.\n");
        }
        else if ((xmsallfreeorig - xmsallfreenow) == klen)
        {
            printf("Try not using the -%c switch.\n",
                   ((qflag != 0) ? 'q' : 'w'));
        }
        XMMfree(handle);
        exit(3);
    }
    TESTTAILER();

    /* test free */
    TESTHEADER();
    printf("Testing XMMfree() on handle returned by XMMalloc().\n");
    printf("Should succeed. Free XMS should increase by %lu bytes (%luK).\n",
           expected, (expected / 1024L));
    test_XMMfree(handle);
    printf("XMMfree() returned OK.\n");
    xmsfreenow = test_XMMcoreleft();
    printf("XMMcoreleft() returned OK, shows %lu bytes (%luK) free.\n",
           xmsfreenow, (xmsfreenow / 1024L));
    xmsallfreenow = test_XMMallcoreleft();
    printf("XMMallcoreleft() returned OK, shows %lu bytes (%luK) free.\n",
           xmsallfreenow, (xmsallfreenow / 1024L));
    if (xmsallfreenow != xmsallfreeorig)
    {
        printf("Freeing handle returned by XMMalloc() did not restore\n");
        printf("   total free XMS count -- was %lu originally, now %lu.\n",
               xmsallfreeorig, xmsallfreenow);
        exit(3);
    }
    TESTTAILER();

    return 0;
} /* end of do_alloc_tests() */


/***************************************************************************
*   FUNCTION: DO_RAWCALL_TESTS                                             *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function tests the XMSIF calls XMMrawcall() and XMMraw3().    *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void do_rawcall_tests()
{
    unsigned long xmsfree, xmsallfree;
    struct XMMregs r;
    int status;

    /* first, get something to check against */
    xmsfree = test_XMMcoreleft();
    xmsallfree = test_XMMallcoreleft();
    printf("XMMcoreleft: %lu bytes (%luK)  XMMallcoreleft: %lu bytes (%luK)\n",
                 xmsfree, (xmsfree / 1024L), xmsallfree, (xmsallfree / 1024L));

    TESTHEADER();
    printf("Testing XMMrawcall() against XMMcoreleft()/XMMallcoreleft().\n");
    printf("Should succeed.\n");
    r.regAX = 0x0800;
    status = XMMrawcall(&r);
    TRIPLECHECK("XMMrawcall()", status, 0, (void far *) NULL, 0, 0);
    if ((r.regAX != (xmsfree / 1024L)) || (r.regDX != (xmsallfree / 1024L)))
    {
        printf("XMMrawcall() returned %uK and %uK.\n", r.regAX, r.regDX);
        exit(3);
    }
    TESTTAILER();

    if (on386 != 0)
    {
        struct XMMbigregs rr;

        TESTHEADER();
        printf("Testing XMMraw3() against XMMcoreleft()/XMMallcoreleft().\n");
        if (_XMMversion >= 0x0300)
        {
            printf("Should succeed.\n");
        }
        else
        {
            printf("Should fail.\n");
        }
        rr.regEAX = 0x00008800;
        XMMraw3(&rr);
        if (_XMMversion >= 0x0300)
        {
            weirdcodechk("XMMraw3()", 0, (void far *) NULL, 0, 0);
            if ((rr.regEAX < (xmsfree / 1024L)) ||
                (rr.regEDX < (xmsallfree / 1024L)))
            {
                printf("XMMraw3() returned %luK and %luK.\n", rr.regEAX,
                       rr.regEDX);
                exit(3);
            }
        }
        else if (_XMMerror != XMM_BADVERS)
        {
            printf("XMMraw3() returned unexpected code 0x%X.\n",
                   (unsigned int) _XMMerror);
            exit(3);
        }
        TESTTAILER();
    }

    return;
} /* end of do_rawcall_tests() */


/***************************************************************************
*   FUNCTION: DO_NCOPY1_TESTS                                              *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       Tests normal copy functions (_XMMcopy() and macros).               *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       None.                                                              *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void do_ncopy1_tests(void)
{
    int handle = 0;  /* dummy, to make some macros happy */
    unsigned char far *realbuf;
    unsigned char far *guard1, far *guard2, far *guard3;
    unsigned char far *testbuf;
    unsigned char far *testbuf2;
    int status;

    /* allocate a conventional memory buffer */
    realbuf = (unsigned char far *) LMALLOC((MINFREE/1024L) + 3);
    if (realbuf == (unsigned char far *) NULL)
    {
        printf("Can't allocate conventional buffer. Aborting.\n");
        exit(3);
    }

    /*
    ** Since we can't access EMBs directly, the only way we have of getting
    ** data there is via the copy functions, which we're trying to test. So
    ** the first round of tests will copy data from one half of the
    ** conventional memory to the other, something which we can verify
    ** directly.
    **
    ** Since _XMMcopy is symmetrical, we only need to give one direction
    ** a real workout.
    */
    guard1 = realbuf;
    FMEMSET(guard1, GUARDVAL, 1024);
    testbuf = (unsigned char far *) normptr(guard1 + 1024);
    guard2 = (unsigned char far *) normptr(testbuf + HALFLEN);
    FMEMSET(guard2, GUARDVAL, 1024);
    testbuf2 = (unsigned char far *) normptr(guard2 + 1024);
    guard3 = (unsigned char far *) normptr(testbuf2 + HALFLEN);
    FMEMSET(guard3, GUARDVAL, 1024);
    gblmsg = "  COPY TESTS CONVENTIONAL-CONVENTIONAL";

    /* fill first buffer with incrementing words */
    farincwordfill(testbuf, HALFLEN, 0);
    /* fill second buffer with zero */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try an even-length copy from offset 0 to offset 0 */
    TESTHEADER();
    printf("_XMMcopy(): even-length copy from offset 0 to offset 0.\n");
    printf("Should succeed.\n");
    status = _XMMcopy(50, 0, (unsigned long) testbuf, 0,
                                                     (unsigned long) testbuf2);
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    CPYWORDCHECK(testbuf2, 50);
    ZEROCHECK((testbuf2 + 50), (HALFLEN - 50));
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try an even-length copy from offset 0 to arbitrary offset */
    TESTHEADER();
    printf("_XMMcopy(): even-length copy from offset 0 to arbitrary offset.\n");
    printf("Should succeed.\n");
    status = _XMMcopy(50, 0, (unsigned long) testbuf, 0,
                                              (unsigned long)(testbuf2 + 477));
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    ZEROCHECK(testbuf2, 477);
    MEMCMP(testbuf, (testbuf2 + 477), 50);
    ZEROCHECK((testbuf2 + 477 + 50), ((HALFLEN - 477) - 50));
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try an even-length copy from offset 0 to just before the end */
    TESTHEADER();
    printf("_XMMcopy(): even-length copy from offset 0 to just before end.\n");
    printf("Should succeed.\n");
    status = _XMMcopy(50, 0, (unsigned long) testbuf, 0,
                                     (unsigned long)(testbuf2 + HALFLEN - 50));
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    ZEROCHECK(testbuf2, (HALFLEN - 50));
    CPYWORDCHECK((testbuf2 + HALFLEN - 50), 50);
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try an even-length copy from arbitrary offset to arbitrary offset */
    TESTHEADER();
    printf("_XMMcopy(): even-length copy from arbitrary offset to ");
    printf("arbitrary offset.\nShould succeed.");
    status = _XMMcopy(50, 0, (unsigned long)(testbuf + 384), 0,
                                           (unsigned long)(testbuf2 + 33333U));
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    ZEROCHECK(testbuf2, 33333U);
    MEMCMP((testbuf + 384), (testbuf2 + 33333U), 50);
    ZEROCHECK((testbuf2 + 33333U + 50), ((HALFLEN - 33333U) - 50));
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try an even-length copy from just before the end to arbitrary offset */
    TESTHEADER();
    printf("_XMMcopy(): even-length copy from just before end to arbitrary.\n");
    printf("Should succeed.\n");
    status = _XMMcopy(50, 0, (unsigned long)(testbuf + HALFLEN - 50), 0,
                                             (unsigned long)(testbuf2 + 7676));
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    ZEROCHECK(testbuf2, 7676);
    MEMCMP((testbuf + HALFLEN - 50), (testbuf2 + 7676), 50);
    ZEROCHECK((testbuf2 + 7676 + 50), ((HALFLEN - 7676) - 50));
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);


    /* try an odd-length copy from offset 0 to offset 0 */
    TESTHEADER();
    printf("_XMMcopy(): odd-length copy from offset 0 to offset 0.\n");
    printf("Should succeed.\n");
    status = _XMMcopy(111, 0, (unsigned long) testbuf, 0,
                                                     (unsigned long) testbuf2);
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    MEMCMP(testbuf, testbuf2, 111);
    ZEROCHECK((testbuf2 + 111), (HALFLEN - 111));
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try an odd-length copy from offset 0 to offset misc */
    TESTHEADER();
    printf("_XMMcopy(): odd-length copy from offset 0 to arbitrary offset.\n");
    printf("Should succeed.\n");
    status = _XMMcopy(111, 0, (unsigned long) testbuf, 0,
                                              (unsigned long)(testbuf2 + 477));
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    ZEROCHECK(testbuf2, 477);
    MEMCMP(testbuf, (testbuf2 + 477), 111);
    ZEROCHECK((testbuf2 + 477 + 111), ((HALFLEN - 477) - 111));
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try an odd-length copy from offset 0 to just before the end */
    TESTHEADER();
    printf("_XMMcopy(): odd-length copy from offset 0 to just before end.\n");
    printf("Should succeed.\n");
    status = _XMMcopy(111, 0, (unsigned long) testbuf, 0,
                                    (unsigned long)(testbuf2 + HALFLEN - 111));
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    ZEROCHECK(testbuf2, (HALFLEN - 111));
    MEMCMP(testbuf, (testbuf2 + HALFLEN - 111), 111);
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try an odd-length copy from arbitrary offset to arbitrary offset */
    TESTHEADER();
    printf("_XMMcopy(): odd-length copy from arbitrary offset to ");
    printf("arbitrary offset.\nShould succeed.\n");
    status = _XMMcopy(111, 0, (unsigned long)(testbuf + 384), 0,
                                           (unsigned long)(testbuf2 + 33333U));
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    ZEROCHECK(testbuf2, 33333U);
    MEMCMP((testbuf + 384), (testbuf2 + 33333U), 111);
    ZEROCHECK((testbuf2 + 33333U + 111), ((HALFLEN - 33333U) - 111));
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try an odd-length copy from just before the end to arbitrary offset */
    TESTHEADER();
    printf("_XMMcopy(): odd-length copy from just before end to arbitrary.\n");
    printf("Should succeed.\n");
    status = _XMMcopy(111, 0, (unsigned long)(testbuf + HALFLEN - 111), 0,
                                             (unsigned long)(testbuf2 + 7676));
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    ZEROCHECK(testbuf2, 7676);
    MEMCMP((testbuf + HALFLEN - 111), (testbuf2 + 7676), 111);
    ZEROCHECK((testbuf2 + 7676 + 111), ((HALFLEN - 7676) - 111));
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);


    /* try a one-byte copy from offset 0 to offset 0 */
    TESTHEADER();
    printf("_XMMcopy(): one-byte copy from offset 0 to offset 0.\n");
    printf("Should succeed.\n");
    status = _XMMcopy(1, 0, (unsigned long) testbuf, 0,
                                                     (unsigned long) testbuf2);
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    MEMCMP(testbuf, testbuf2, 1);
    ZEROCHECK((testbuf2 + 1), (HALFLEN - 1));
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try a one-byte copy from offset 0 to offset misc */
    TESTHEADER();
    printf("_XMMcopy(): one-byte copy from offset 0 to arbitrary offset.\n");
    printf("Should succeed.\n");
    status = _XMMcopy(1, 0, (unsigned long) testbuf, 0,
                                              (unsigned long)(testbuf2 + 477));
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    ZEROCHECK(testbuf2, 477);
    MEMCMP(testbuf, (testbuf2 + 477), 1);
    ZEROCHECK((testbuf2 + 477 + 1), ((HALFLEN - 477) - 1));
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try a one-byte copy from offset 0 to just before the end */
    TESTHEADER();
    printf("_XMMcopy(): one-byte copy from offset 0 to just before end.\n");
    printf("Should succeed.\n");
    status = _XMMcopy(1, 0, (unsigned long) testbuf, 0,
                                      (unsigned long)(testbuf2 + HALFLEN - 1));
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    ZEROCHECK(testbuf2, (HALFLEN - 1));
    MEMCMP(testbuf, (testbuf2 + HALFLEN - 1), 1);
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try a one-byte copy from arbitrary offset to arbitrary offset */
    TESTHEADER();
    printf("_XMMcopy(): one-byte copy from arbitrary offset to ");
    printf("arbitrary offset.\nShould succeed.");
    status = _XMMcopy(1, 0, (unsigned long)(testbuf + 384), 0,
                                           (unsigned long)(testbuf2 + 33333U));
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    ZEROCHECK(testbuf2, 33333U);
    MEMCMP((testbuf + 384), (testbuf2 + 33333U), 1);
    ZEROCHECK((testbuf2 + 33333U + 1), ((HALFLEN - 33333U) - 1));
    TESTTAILER();

    /* restore destination pattern */
    FMEMSET(testbuf2, 0, HALFLEN);

    /* try a one-byte copy from just before the end to arbitrary offset */
    TESTHEADER();
    printf("_XMMcopy(): one-byte copy from just before end to arbitrary.\n");
    printf("Should succeed.\n");
    status = _XMMcopy(1, 0, (unsigned long)(testbuf + HALFLEN - 1), 0,
                                             (unsigned long)(testbuf2 + 7676));
    TRIPLECHECK("_XMMcopy()", status, 0, (void far *) realbuf, 0, 0);
    GUARDCHECK(0);
    SRCWORDCHECK(testbuf, HALFLEN);
    ZEROCHECK(testbuf2, 7676);
    MEMCMP((testbuf + HALFLEN - 1), (testbuf2 + 7676), 1);
    ZEROCHECK((testbuf2 + 7676 + 1), ((HALFLEN - 7676) - 1));
    TESTTAILER();

    /*
    ** Clean up.
    */
    LFREE(realbuf);

    return;
} /* end of do_ncopy1_tests() */


/*
** The following group of functions {test_XMM*()} are wrapper functions
** that call the XMSIF 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_XMMCORELEFT                                             *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function calls XMSIF function XMMcoreleft() and checks the    *
*       return value to see if it is a multiple of 1024 (the minimum XMS   *
*       allocation unit).                                                  *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Returns the value returned by XMMcoreleft().                       *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
unsigned long test_XMMcoreleft(void)
{
    unsigned long xmsfree;

    /* call XMMcoreleft and check error code */
    xmsfree = XMMcoreleft();
    weirdcodechk("XMMcoreleft()", 0, (void far *) NULL, 0, 0);

    /* check if free byte count is multiple of 1024 */
    if ((xmsfree % 1024L) != 0)
    {
        printf("XMMcoreleft() returned strange number %lu.\n", xmsfree);
        exit(3);
    }

    return xmsfree;
} /* end of test_XMMcoreleft() */


/***************************************************************************
*   FUNCTION: TEST_XMMALLCORELEFT                                          *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function calls XMSIF function XMMallcoreleft() and checks     *
*       the return value to see if it is a multiple of 1024 (the minimum   *
*       XMS allocation unit).                                              *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Returns the value returned by XMMallcoreleft().                    *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
unsigned long test_XMMallcoreleft(void)
{
    unsigned long xmsfree;

    /* call XMMallcoreleft and check error code */
    xmsfree = XMMallcoreleft();
    weirdcodechk("XMMallcoreleft()", 0, (void far *) NULL, 0, 0);

    /* check if free byte count is multiple of 1024 */
    if ((xmsfree % 1024L) != 0)
    {
        printf("XMMallcoreleft() returned strange number %lu.\n", xmsfree);
        exit(3);
    }

    return xmsfree;
} /* end of test_XMMallcoreleft() */


/***************************************************************************
*   FUNCTION: TEST_XMMALLOC                                                *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function calls XMSIF function XMMalloc() and checks the       *
*       return codes.                                                      *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       bytes - bytes of XMS to allocate                                   *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Returns the handle returned by XMMalloc().                         *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
int test_XMMalloc(unsigned long bytes)
{
    int handle;

    /* call XMMalloc() and check the return */
    handle = XMMalloc(bytes);
    weirdcodechk("XMMalloc()", 0, (void far *) NULL, 0, 0);

    return handle;
} /* end of test_XMMalloc() */


/***************************************************************************
*   FUNCTION: TEST_XMMREALLOC                                              *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function calls XMSIF function XMMrealloc() and checks the     *
*       return codes.                                                      *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       handle - XMS handle to reallocate                                  *
*       bytes  - new size of XMS block                                     *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Returns the handle returned by XMMrealloc(), which is the same as  *
*       the argument handle.                                               *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
int test_XMMrealloc(int handle, unsigned long bytes)
{
    int newhandle;

    /* call XMMrealloc() and check the return */
    newhandle = XMMrealloc(handle, bytes);
    weirdcodechk("XMMrealloc()", 0, (void far *) NULL, 0, 0);
    if (newhandle != handle)
    {
        printf("Passed handle %d to XMMrealloc(), got handle %d back.\n",
               handle, newhandle);
        XMMfree(handle);
        exit(3);
    }

    return newhandle;
} /* end of test_XMMrealloc() */


/***************************************************************************
*   FUNCTION: TEST_XMMFREE                                                 *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function calls XMSIF function XMMfree() and checks the        *
*       return codes.                                                      *
*                                                                          *
*   ENTRY:                                                                 *
*                                                                          *
*       handle - XMS EMB handle to be freed                                *
*                                                                          *
*   EXIT:                                                                  *
*                                                                          *
*       Void.                                                              *
*                                                                          *
*   CONSTRAINTS/SIDE EFFECTS:                                              *
*                                                                          *
***************************************************************************/
void test_XMMfree(int handle)
{
    int status;

    /* call XMMfree() and check the return */
    status = XMMfree(handle);
    TRIPLECHECK("XMMfree()", status, 0, (void far *) NULL, 0, 0);

    return;
} /* end of test_XMMfree() */


/*
** 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 XMMOOPS, 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  - XMS handle to be freed on exit. Not freed if 0.         *
*       tofree2  - another XMS 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 far *tofree1, int tofree2,
                                                                   int tofree3)
{
    if ((status != XMMOOPS) && (status != 0))
    {
        printf("%s returned weird value %d, code 0x%X.\n", function, status,
                                                     (unsigned int) _XMMerror);
        if (tofree1 != (void far *) NULL)
        {
            LFREE(tofree1);
        }
        if (tofree2 != 0)
        {
            XMMfree(tofree2);
        }
        if (tofree3 != 0)
        {
            XMMfree(tofree3);
        }
        exit(3);
    }

    return;
} /* end of weirdretchk() */


/***************************************************************************
*   FUNCTION: WEIRDCODECHK                                                 *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function checks to see if the XMSIF 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 _XMMerror                             *
*       tofree1  - conventional memory block to be freed on exit. Not      *
*                  freed if NULL.                                          *
*       tofree2  - XMS handle to be freed on exit. Not freed if 0.         *
*       tofree2  - another XMS 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 far *tofree1, int tofree2,
                                                                   int tofree3)
{
    if ((int) _XMMerror != expected)
    {
        printf("%s returned unexpected code 0x%X.\n", function,
                                                     (unsigned int) _XMMerror);
        if (tofree1 != (void far *) NULL)
        {
            LFREE(tofree1);
        }
        if (tofree2 != 0)
        {
            XMMfree(tofree2);
        }
        if (tofree3 != 0)
        {
            XMMfree(tofree3);
        }
        exit(3);
    }

    return;
} /* end of weirdcodechk() */


/***************************************************************************
*   FUNCTION: FAILCHECK                                                    *
*                                                                          *
*   DESCRIPTION:                                                           *
*                                                                          *
*       This function checks to see if the status value passed to it is    *
*       XMMOOPS 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  - XMS handle to be freed on exit. Not freed if 0.         *
*       tofree2  - another XMS 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 far *tofree1, int tofree2,
                                                                  int tofree3)
{
    if (status == XMMOOPS)
    {
        printf("%s failed, code 0x%X.\n", function, (unsigned int) _XMMerror);
        if (tofree1 != (void far *) NULL)
        {
            LFREE(tofree1);
        }
        if (tofree2 != 0)
        {
            XMMfree(tofree2);
        }
        if (tofree3 != 0)
        {
            XMMfree(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  - XMS handle to be freed on exit. Not freed if 0.         *
*       tofree2  - another XMS 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 far *tofree1, int tofree2,
                                                                  int tofree3)
{
    if (status == 0)
    {
        printf("%s did not fail.\n", function);
        if (tofree1 != (void far *) NULL)
        {
            LFREE(tofree1);
        }
        if (tofree2 != 0)
        {
            XMMfree(tofree2);
        }
        if (tofree3 != 0)
        {
            XMMfree(tofree3);
        }
        exit(3);
    }

    return;
} /* end of nofailcheck() */

