/*
 * IBM/Microsoft Confidential
 *
 * Copyright (c) IBM Corporation 1987, 1991
 * Copyright (c) Microsoft Corporation 1987, 1991
 *
 * All Rights Reserved
 */

/*
 * cdoshlp.c - doshlp's and parts of doshlp's written in C
 *
 * Change history:
 * ---------------
 * 69458  LRN  06/15/93 Added Int15E8Mem for Compaq machines.
 * 104879 ChgTeam  03/09/95  Removed code which discarded the value returned
 *                           by Int 15-E8 if > 16MB. Today, this would
 *                           effectively keep OS/2 from reporting > 64Meg RAM.
 *                           All sanity checks are now done in DOSHLP.ASM at
 *                           the time of the Int calls.
 *
 */

#include <os2def16.h>
#include <sysinvar.h>
#include <newexe.h>
typedef unsigned long DWORD;
typedef unsigned short WORD;
#define FOR_EXEHDR      1
#include <exe386.h>
#include <dos.h>
#include <dbprt.h>
#include <osconst.h>
#include <ostypes.h>
#include <ordinals.h>
#include <pgexport.h>
#include <vmowner.h>
#include <filetab.h>
#define FOR_MVDMEXP     1
#include <mvdmexp.h>
#include <mi.h>
#include <eisa.h>
#include <doshlp.h>
#include "loader.h"

extern void dprintf(char *,...);



/* ------------ start of special declarations for cgetdhphysmem ------------ */

/* the following are declared and filled in by DOSHLP.ASM */
extern unsigned LowMem;
extern unsigned HighMem;
extern unsigned long ExtendMem;
extern unsigned Int12Mem;               // amount of real memory 0 to 640KB-1
extern unsigned Int1588Mem;             // amount of protect mem 1MB to 64MB-1
extern unsigned Int15E8Mem;             // 69458  amount 16M to 64M on
                                        // 69458    some ISA machines
extern unsigned I15E8AX;                //104879 Values returned by Int 15-E801
extern unsigned I15E8BX;                //104879
extern unsigned I15E8CY;                //104879

extern unsigned char DHMachModel;
extern unsigned char DHMachSubModel;
extern unsigned char NvramMem[16];      // info directly from NVRAM
extern unsigned char AoxMem[17];        // info on memory on Aox MicroMaster
extern unsigned char AoxTrans[12];      // info on translation on MicroMaster
extern unsigned char AoxID;
extern unsigned char AoxQty;
extern unsigned char WD_found;          // MCA Watchdog support found/not found  113247
extern void QueryNvram(void);

extern struct Int15c7Table Int15c7Mem;

/* GLOBAL VARIABLES -- filled in by cgetdhphysmem */
/* meant to remain available throughout the time OS/2 is running */
PHYSMEMBLOCK PhysMemArray[PHYSMEMARRAYLIMIT];
unsigned PhysMemArrayQty;               // number of entries in PhysMemArray
unsigned long WhichCPUUsable;           // which CPU's can we control/run on
unsigned long WhichCPUAreWe;            // which CPU is this code running on
unsigned long WhichCPUXlateMem;         // which CPU does address translation

/* ------------- end of special declarations for cgetdhphysmem ------------- */



/***     cgetdhphysmem
 *
 * FUNCTION:
 *      Locates and then creates an array describing where all physical memory
 *      is located throughout the computer system.
 *
 * INPUT:
 *      Miscellaneous pieces of data passed to this C-based section of this
 *      dos-help from its assembly-based counterpart in DOSHLP.ASM.
 *
 * NOTE:
 *      This is "part 2" of the getdhphysmem doshlp in DOSHLP.ASM and is
 *      normally called by that routine after it makes a series of BIOS
 *      interrupt calls and checks for some of the possible hardware-specific
 *      situations affecting the layout of physical memory.  This code is
 *      NEVER to be called by anything other than getdhphysmem.
 */

void cgetdhphysmem(void)
{

    struct _FINDERBLOCK
    {
        unsigned long BlockStartAddr;   // flat start address of memory block
        unsigned long BlockEndAddr;     // flat end address of memory block
        unsigned long WhichCPULocal;    // flags -- block is local to which CPU
        unsigned long WhichCPUCached;   // flags -- block cached by which CPU
        unsigned long WhichCPUBusMas;   // flags -- busmastered for which CPU
    };
    typedef struct _FINDERBLOCK FINDERBLOCK;
    #define FINDERARRAYLIMIT 64
    FINDERBLOCK FinderArray[FINDERARRAYLIMIT];

    struct _SORTERBLOCK
    {
        unsigned long AddrData;         // flat physical address
        unsigned char DataFlag;         // flag -- data is start or end address
    };
    typedef struct _SORTERBLOCK SORTERBLOCK;
    #define SORTERARRAYLIMIT 128
    SORTERBLOCK SorterArray[SORTERARRAYLIMIT];

    unsigned i, j;                      // miscellaneous index
    unsigned fcount;                    // # of entries in FinderArray
    unsigned long flength;              // byte length of block in FinderArray
    unsigned long fstart;               // start address of block in F-Array
    unsigned scount;                    // # of entries in SorterArray
    unsigned snewcount;                 // new # of entries in SorterArray
    unsigned long stempdata;            // swap variable
    unsigned char stempflag;            // ditto
    unsigned bcount;                    // # of "builder" entries PhysMemArray
    unsigned char btempflag;            // temp var used to control "builder"
    unsigned ccount;                    // # of "condenser" entries P-M-Array
    unsigned long ulTemp;

#ifdef PMDEBUG

    DBCHECK();
    DBPRT(1+16,("CGetDHPhysMem\n"));
    DBPRT(1+16,("I12 %u\n",Int12Mem));
    DBPRT(1+16,("I1588 %u\n",Int1588Mem));
    DBPRT(1+16,("I15E8(AX) %u\n",I15E8AX));//104879 Vals returned by Int15-E801
    DBPRT(1+16,("I15E8(BX) %u\n",I15E8BX));//104879
    DBPRT(1+16,("I15E8(CY) %u\n",I15E8CY));//104879
    DBPRT(1+16,("I15E8 %u\n",Int15E8Mem));
    DBPRT(1+16,("I15C7Bytes %u\n",Int15c7Mem.ByteLength));
    DBPRT(1+16,("SysHi %u\n",Int15c7Mem.SysHiMem));
    DBPRT(1+16,("SysEx %u\n",Int15c7Mem.SysExMem));
    DBPRT(1+16,("LocHi %u\n",Int15c7Mem.LocHiMem));
    DBPRT(1+16,("LocEx %u\n",Int15c7Mem.LocExMem));
    DBPRT(1+16,("CacHi %u\n",Int15c7Mem.CacHiMem));
    DBPRT(1+16,("CacEx %u\n",Int15c7Mem.CacExMem));
    DBPRT(1+16,("PadHi %u\n",Int15c7Mem.PadHiMem));
    DBPRT(1+16,("PadEx %u\n",Int15c7Mem.PadExMem));
    DBPRT(1+16,("MCA Watchdog supported?: NO=0;YES=non-zero. Rslt=%x\n",WD_found));  // 113247
    DBPRT(1+16,("NVRam "));
    i=0;
    while (i<16)
    {
        DBPRT(1+16,(" %u",(unsigned)NvramMem[i]));
        i++;
    }

    DBPRT(1+16,("\nAoxID %u\n",(unsigned)AoxID));
    DBPRT(1+16,("AoxQty %u\n",(unsigned)AoxQty));
    DBPRT(1+16,("AoxMem "));
    i=0;
    while (i<17)
    {
        DBPRT(1+16,(" %u",(unsigned)AoxMem[i]));
        i++;
    }

    DBPRT(1+16,("\nAoxTrans "));
    i=0;
    while (i<12)
    {
        DBPRT(1+16,(" %u",(unsigned)AoxTrans[i]));
        i++;
    }
    DBPRT(1+16,("\n"));

#endif

    // for the time being, assume that we are running on the planar
    WhichCPUUsable = 1;                 // note planar CPU as usable
    WhichCPUAreWe = 1;                  // starting with planar CPU
    WhichCPUXlateMem = 0;               // have not yet found any translating
    PhysMemArrayQty = 0;                // no entries in PhysMemArray yet

    /* note caching of the 1st 16MB of extended memory in 25MHz 70 and 80 */
    if
    (
        ( DHMachModel==0xf8 && ( DHMachSubModel==0x0d ||  // 25MHz M70
                                 DHMachSubModel==0x80 ) ) // 25MHz M80
        &&
        ( Int15c7Mem.SysExMem && Int15c7Mem.CacHiMem )
    )
    {
        DBPRT(1+16,("16-32M cached\n"));
        if (Int15c7Mem.SysExMem > _16MEGB)
        {                                                  // cached memory
            Int15c7Mem.CacExMem = (16*1024);               // amount cannot be
        }                                                  // larger than 16MB
        else                                               // with the cache
        {                                                  // controller in
            Int15c7Mem.CacExMem = Int15c7Mem.SysExMem;     // these PS/2's
        }
    }


    /* sanity checks for systems where int 15-c7 is questionably implemented */
    if (Int15c7Mem.LocHiMem > Int15c7Mem.SysHiMem)    // local memory amount
    {                                                 // can't be larger than
        Int15c7Mem.LocHiMem = 0;                      // system memory amount
    }
    if (Int15c7Mem.LocExMem > Int15c7Mem.SysExMem)    // if have this problem
    {                                                 // then set local memory
        Int15c7Mem.LocExMem = 0;                      // amount to 0
    }
    if (Int15c7Mem.CacHiMem > Int15c7Mem.SysHiMem)    // cached memory amount
    {                                                 // can't be larger than
        Int15c7Mem.CacHiMem = Int15c7Mem.SysHiMem;    // system memory amount
    }
    if (Int15c7Mem.CacExMem > Int15c7Mem.SysExMem)    // if have this problem
    {                                                 // then set cached memory
        Int15c7Mem.CacExMem = Int15c7Mem.SysExMem;    // amount equal to system
    }                                                 // amount

    /* find first 0 to 640KB-1 -- presumed to be local to planar CPU */
    /* we presume there will be some memory here since we are in real mode */
    // Note for AOX - the local CPU might be the AOX CPU - there might be
    // no other memory switched on.
    fcount = 0;
    FinderArray[fcount].BlockStartAddr = 0;
    flength = (1024 * ((long)Int12Mem));
    FinderArray[fcount].BlockEndAddr = flength - 1;
    FinderArray[fcount].WhichCPULocal = 1;
    FinderArray[fcount].WhichCPUCached = 0;
    DBPRT(17,("Int12 st %lx end %lx\n",FinderArray[fcount].BlockStartAddr,
                                       FinderArray[fcount].BlockEndAddr));

    /* Find memory between addresses 1MB to 64MB-1. */
    /* For Compaq ISA machines, this is from 1MB to 16MB and...69458 */
    /* Int15E8Mem contains the value from 16MB to 64MB-1 */
    if (Int1588Mem)
    {
        fcount++;
        FinderArray[fcount].BlockStartAddr = _1MEGB;
        flength = (1024 * ((long)Int1588Mem));
//104879  /* Do a sanity check on the length...if it is > 16MB, then ignore */
//104879  /* the value in Int15E8Mem.                                 69458 */
//104879  if (flength > _16MEGB)   /* 15-88 returned value > 16 MB.   69458 */
//104879    Int15E8Mem = 0;        /* so ignore 15-E8 value.          69458 */
        /* We now do our validity check in DOSHLP.ASM at the time of  104879*/
        /* the Int 15 E8h call.                                       104879*/
        FinderArray[fcount].BlockEndAddr = flength + _1MEGB - 1;
        FinderArray[fcount].WhichCPULocal = 0;
        FinderArray[fcount].WhichCPUCached = 0;
        DBPRT(17,("Int1588 st %lx end %lx\n",FinderArray[fcount].BlockStartAddr,
                                             FinderArray[fcount].BlockEndAddr));
    }

    /* find memory between addresses 16MB to 64MB-1 on Compaq's....69458 */
    /* this entire block of code was added for 69458 */
    /* Keep in mind that Int15E8Mem is in 64K increments */
    if (Int15E8Mem)
    {
        fcount++;
        FinderArray[fcount].BlockStartAddr = _16MEGB;
        flength = (64L * 1024L * ((long)Int15E8Mem));
        FinderArray[fcount].BlockEndAddr = flength + _16MEGB - 1;
        FinderArray[fcount].WhichCPULocal = 0;
        FinderArray[fcount].WhichCPUCached = 0;
        DBPRT(17,("Int15E8 st %lx end %lx\n",FinderArray[fcount].BlockStartAddr,
                                             FinderArray[fcount].BlockEndAddr));
    }

    /* find memory between addresses 1MB to 16MB-1 */
    if (Int15c7Mem.SysHiMem)
    {
        fcount++;
        FinderArray[fcount].BlockStartAddr = _1MEGB;
        flength = (1024 * ((long)Int15c7Mem.SysHiMem));
        FinderArray[fcount].BlockEndAddr = flength + _1MEGB - 1;
        FinderArray[fcount].WhichCPULocal = 0;
        FinderArray[fcount].WhichCPUCached = 0;
        DBPRT(1+16,("SysHi %lx %lx\n",FinderArray[fcount].BlockStartAddr,
                                      FinderArray[fcount].BlockEndAddr));
    }


    /* find cached memory between addresses 1MB to 16MB-1 */
    if (Int15c7Mem.CacHiMem)
    {
        fcount++;
        FinderArray[fcount].BlockStartAddr = _1MEGB;
        flength = (1024 * ((long)Int15c7Mem.CacHiMem));
        FinderArray[fcount].BlockEndAddr = flength + _1MEGB - 1;
        FinderArray[fcount].WhichCPULocal = 0;
        FinderArray[fcount].WhichCPUCached = 1;
        DBPRT(1+16,("CacHi %lx %lx\n",FinderArray[fcount].BlockStartAddr,
                                      FinderArray[fcount].BlockEndAddr));
    }

    /* find local memory between addresses 1MB to 16MB-1 */
    if (Int15c7Mem.LocHiMem)
    {
        fcount++;
        FinderArray[fcount].BlockStartAddr = _1MEGB;
        flength = (1024 * ((long)Int15c7Mem.LocHiMem));
        FinderArray[fcount].BlockEndAddr = flength + _1MEGB - 1;
        FinderArray[fcount].WhichCPULocal = 1;
        FinderArray[fcount].WhichCPUCached = 0;
        DBPRT(1+16,("LocHi %lx %lx\n",FinderArray[fcount].BlockStartAddr,
                                      FinderArray[fcount].BlockEndAddr));
    }

    /* find memory between addresses 16MB to 4GB-1 */
    if (Int15c7Mem.SysExMem)
    {
        fcount++;
        FinderArray[fcount].BlockStartAddr = _16MEGB;
        flength = (1024 * ((long)Int15c7Mem.SysExMem));
        FinderArray[fcount].BlockEndAddr = flength + _16MEGB - 1;
        FinderArray[fcount].WhichCPULocal = 0;
        FinderArray[fcount].WhichCPUCached = 0;
        DBPRT(1+16,("SysEx %lx %lx\n",FinderArray[fcount].BlockStartAddr,
                                      FinderArray[fcount].BlockEndAddr));
    }

    /* find cached memory between addresses 16MB to 4GB-1 */
    if (Int15c7Mem.CacExMem)
    {
        fcount++;
        FinderArray[fcount].BlockStartAddr = _16MEGB;
        flength = (1024 * ((long)Int15c7Mem.CacExMem));
        FinderArray[fcount].BlockEndAddr = flength + _16MEGB - 1;
        FinderArray[fcount].WhichCPULocal = 0;
        FinderArray[fcount].WhichCPUCached = 1;
        DBPRT(1+16,("CacEx %lx %lx\n",FinderArray[fcount].BlockStartAddr,
                                      FinderArray[fcount].BlockEndAddr));
    }

    /* find local memory between addresses 16MB to 4GB-1 */
    if (Int15c7Mem.LocExMem)
    {
        fcount++;
        FinderArray[fcount].BlockStartAddr = _16MEGB;
        flength = (1024 * ((long)Int15c7Mem.LocExMem));
        FinderArray[fcount].BlockEndAddr = flength + _16MEGB - 1;
        FinderArray[fcount].WhichCPULocal = 1;
        FinderArray[fcount].WhichCPUCached = 0;
        DBPRT(1+16,("LocEx %lx %lx\n",FinderArray[fcount].BlockStartAddr,
                                      FinderArray[fcount].BlockEndAddr));
    }

    // Find memory on an AOX MicroMaster adapter.
    // NOTE - AOX must run with translation off.
    if (AoxMem[0]==0x55 && AoxMem[1]==0xaa)
    {
        if ((1<AoxQty)&&(AoxQty<9)) // if more than one, there's co-processing
        {
            WhichCPUAreWe = 1;
            i=0;
            while (i<AoxID)
            {
                WhichCPUAreWe = WhichCPUAreWe * 2;
                i++;
            }
            WhichCPUUsable = ( (WhichCPUAreWe) *2) -1;
        }
        else
        {
            WhichCPUAreWe = 2;     // have only one, and we're running on it
            WhichCPUUsable = 3;
        }
        fstart = (((long)AoxMem[11]) +
                   (256*(long)AoxMem[12]) +
                   (65536*(long)AoxMem[13]) +
                   (16777216*(long)AoxMem[14]));
        flength = (((long)AoxMem[7]) +
                   (256*(long)AoxMem[8]) +
                   (65536*(long)AoxMem[9]) +
                   (16777216*(long)AoxMem[10]));

        if (flength)  // do not fill out a block with no memory on AOX card
        {
            fcount++;
            FinderArray[fcount].BlockStartAddr = fstart;
            FinderArray[fcount].BlockEndAddr = fstart + flength - 1;
            FinderArray[fcount].WhichCPULocal = WhichCPUAreWe;
            DBPRT(1+16,("Aox %lx %lx\n",FinderArray[fcount].BlockStartAddr,
                                        FinderArray[fcount].BlockEndAddr));
            if (AoxMem[15] + AoxMem[16])
            {
                FinderArray[fcount].WhichCPUCached = WhichCPUAreWe;
            }
            else
            {
                FinderArray[fcount].WhichCPUCached = 0;
            }
            /* find if memory on the AOX MicroMaster is at translated addresses */
            if (AoxTrans[0]==0x55 && AoxTrans[1]==0xaa)
            {
                if (!(AoxTrans[4]==0 &&
                      AoxTrans[5]==0 &&
                      AoxTrans[6]==0 &&
                      AoxTrans[7]==0 &&
                      AoxTrans[8]==0 &&
                      AoxTrans[9]==0 &&
                      AoxTrans[10]==0 &&
                      AoxTrans[11]==0x10))
                {
                    WhichCPUXlateMem = WhichCPUXlateMem | WhichCPUAreWe;
                }
            }

        }  // end of if memory on the AOX card
    }


    if (WhichCPUAreWe & WhichCPUXlateMem)
    {
        fcount = 1;
    }

#ifdef PMDEBUG
    i=0;
    DBPRT(1+16,("Finder:\n"));      // debug printing of FinderArray
    while (i<=fcount)
    {
        DBPRT(1+16,("st %lx end %lx loc %lx cac %lx\n",
                     FinderArray[i].BlockStartAddr,
                     FinderArray[i].BlockEndAddr,
                     FinderArray[i].WhichCPULocal,
                     FinderArray[i].WhichCPUCached ) );
        i++;
    }
#endif


/*     Sort the blocks of memory and eliminate duplications while            */
/*     sorting -- multiple occurences of starting and ending addresses are   */
/*     condensed down into single occurences after all starting and ending   */
/*     addresses are placed into SorterArray                                 */
/*                                                                           */
/*     the number or stored addresses is reduced by compressing SorterArray  */
/*     from a size specified by scount to a new size specified by snewcount  */
/*                                                                           */
/*     NOTE:  it is assumed that none of the blocks in FinderArray are less  */
/*            than two bytes in size, i.e., no block should have an ending   */
/*            address that is less-than or equal-to its starting address     */


/* load FinderArray information into SorterArray                             */
/* SorterArray will be filled with all memory block start and end addresses  */
/* each entry in FinderArray should provide both a start and end address     */

    i=0;                                // index for FinderArray
    scount=0;                           // index and count for SorterArray
    while (i<=fcount)
    {
        SorterArray[scount].AddrData = FinderArray[i].BlockStartAddr;
        SorterArray[scount].DataFlag = 's';           // 's' for start address
        ++scount;
        SorterArray[scount].AddrData = FinderArray[i].BlockEndAddr;
        SorterArray[scount].DataFlag = 'e';           // 'e' for end address
        ++scount;
        ++i;
    }
    --scount;


    snewcount=0;
    for (i=0; i<scount; i++)
    {
        for (j=i+1; j<=scount; j++)
        {
            if (SorterArray[i].AddrData > SorterArray[j].AddrData)
            {
                stempdata=SorterArray[i].AddrData;
                stempflag=SorterArray[i].DataFlag;
                SorterArray[i].AddrData=SorterArray[j].AddrData;
                SorterArray[i].DataFlag=SorterArray[j].DataFlag;
                SorterArray[j].AddrData=stempdata;
                SorterArray[j].DataFlag=stempflag;
            }
        }
        if (i>0)
        {
            if (SorterArray[i].AddrData > SorterArray[snewcount].AddrData)
            {
                ++snewcount;
                SorterArray[snewcount].AddrData=SorterArray[i].AddrData;
                SorterArray[snewcount].DataFlag=SorterArray[i].DataFlag;
            }
        }
    }
    if (SorterArray[scount].AddrData > SorterArray[snewcount].AddrData)
    {
        ++snewcount;
        SorterArray[snewcount].AddrData=SorterArray[scount].AddrData;
        SorterArray[snewcount].DataFlag=SorterArray[scount].DataFlag;
    }
    scount=snewcount;


/*     Build up PhysMemArray                                                 */
/*     data from SorterArray sets the start and end addresses of each block  */
/*     data from FinderArray sets the flags of each block                    */
/*     NOTE:  BlockByteLength is used, at first, to hold end addresses which */
/*            through later calculations are converted into byte lengths     */


    bcount=0;
    PhysMemArray[bcount].BlockStartAddr = SorterArray[0].AddrData;
    btempflag = SorterArray[0].DataFlag;

    for (i=1; i<=snewcount; i++)
    {
        if (btempflag == 's')           // if previous data was a start address
        {
            if (SorterArray[i].DataFlag == 's')
            {
                PhysMemArray[bcount].BlockByteLength =
                    (SorterArray[i].AddrData-1);
                ++bcount;
                PhysMemArray[bcount].BlockStartAddr =
                    SorterArray[i].AddrData;
            }
            else
            {
                PhysMemArray[bcount].BlockByteLength =
                    SorterArray[i].AddrData;
                btempflag = 'e';
            }
        }
        else                            // if previous data was an end address
        {
            if (SorterArray[i].DataFlag == 's')
            {
                ++bcount;
                PhysMemArray[bcount].BlockStartAddr =
                    SorterArray[i].AddrData;
                btempflag = 's';
            }
            else
            {
                ++bcount;
                PhysMemArray[bcount].BlockStartAddr =
                    (PhysMemArray[(bcount-1)].BlockByteLength + 1);
                PhysMemArray[bcount].BlockByteLength =
                    SorterArray[i].AddrData;
            }
        }
    }


/*     Fill in flags from values contained within FinderArray                */
/*     done by matching start address of each valid entry of FinderArray to  */
/*     a start address within an entry in PhysMemArray and then or-ing the   */
/*     flags from the FinderArray entry into one or more consequtive entries */
/*     in PhysMemArray until a matching end address is encountered           */
/*     NOTE:  remember that BlockByteLength in PhysMemArray is actually the  */
/*            end address and isn't yet converted into a byte length         */

    i=0;
    while (i <= fcount)
    {
        j=0;
        while (FinderArray[i].BlockStartAddr > PhysMemArray[j].BlockStartAddr)
        {
            j++;
        }
        while ((FinderArray[i].BlockEndAddr >= PhysMemArray[j].BlockByteLength)
                & (j <= bcount))
        {
            PhysMemArray[j].WhichCPULocal =
              (FinderArray[i].WhichCPULocal | PhysMemArray[j].WhichCPULocal);
            PhysMemArray[j].WhichCPUCached =
              (FinderArray[i].WhichCPUCached | PhysMemArray[j].WhichCPUCached);
            j++;
        }
        i++;
    }

/*     Searches through PhysMemArray for adjacent blocks of memory           */
/*     with identical sets of flags and condenses the array so that such     */
/*     adjacent entries are replaced by only one.  If such a situation is    */
/*     found, the array entries after the one(s) with the duplicate flags    */
/*     are "moved up" as by having the data of later entries copied into     */
/*     the spaces of earlier entries starting with the duplicate one(s).     */

    i=1;
    ccount=0;
    while (i <= bcount)
    {                                   // if adjacent and flags match
        if (  ((PhysMemArray[i].BlockStartAddr -
                         PhysMemArray[ccount].BlockByteLength) == 1) &&
              (PhysMemArray[ccount].WhichCPULocal ==
                         PhysMemArray[i].WhichCPULocal) &&
              (PhysMemArray[ccount].WhichCPUCached ==
                         PhysMemArray[i].WhichCPUCached)  )
        {                               // then copy only the end address
            PhysMemArray[ccount].BlockByteLength =
                PhysMemArray[i].BlockByteLength;
            i++;
        }
        else
        {                               // else copy over the entire entry
                                        // but if indices match, then entries
                                        // already the same, so don't copy
            ccount++;
            if (ccount != i)            // copy only if indices don't match
            {
                PhysMemArray[ccount].BlockStartAddr =
                    PhysMemArray[i].BlockStartAddr;
                PhysMemArray[ccount].BlockByteLength =
                    PhysMemArray[i].BlockByteLength;
                PhysMemArray[ccount].WhichCPULocal =
                    PhysMemArray[i].WhichCPULocal;
                PhysMemArray[ccount].WhichCPUCached =
                    PhysMemArray[i].WhichCPUCached;
            }
            i++;
        }
    }


    /* convert value in PhysMemArray.BlockByteLength from an end address */
    /* to a value describing the length of the block in bytes */
    i=0;
    while (i <= ccount)
    {
        PhysMemArray[i].BlockByteLength =
            ((PhysMemArray[i].BlockByteLength -
                PhysMemArray[i].BlockStartAddr) + 1);
        i++;
    }


    i=0;
    ulTemp = 0L;
    while ((PhysMemArray[i].BlockStartAddr < _640KB) && (i<=ccount))
    {
        if ( (PhysMemArray[i].WhichCPULocal == 0) ||
             (PhysMemArray[i].WhichCPULocal == 1) ||
             ((PhysMemArray[i].WhichCPULocal & WhichCPUAreWe)==WhichCPUAreWe) )
        {
            ulTemp += PhysMemArray[i].BlockByteLength;
        }
        i++;
    }
    LowMem =  (unsigned)(ulTemp / _1KB);

    ulTemp = ExtendMem = 0L;
    while ((PhysMemArray[i].BlockStartAddr < _16MEGB) && (i<=ccount))
    {
        if ( (PhysMemArray[i].WhichCPULocal == 0) ||
             (PhysMemArray[i].WhichCPULocal == 1) ||
             ((PhysMemArray[i].WhichCPULocal & WhichCPUAreWe)==WhichCPUAreWe) )
        {
            if ( (PhysMemArray[i].BlockStartAddr +
                 PhysMemArray[i].BlockByteLength) < _16MEGB )
            {
                ulTemp += PhysMemArray[i].BlockByteLength;
            }
            else
            {
                ulTemp += _16MEGB - PhysMemArray[i].BlockStartAddr;
                ExtendMem = ((PhysMemArray[i].BlockStartAddr +
                    PhysMemArray[i].BlockByteLength - _16MEGB) / _1KB);
            }
        }
        i++;
    }
    HighMem = (unsigned)(ulTemp / _1KB);


    ulTemp = 0L;
    while (i <= ccount)
    {
        if ( (PhysMemArray[i].WhichCPULocal == 0) ||
             (PhysMemArray[i].WhichCPULocal == 1) ||
             ((PhysMemArray[i].WhichCPULocal & WhichCPUAreWe)==WhichCPUAreWe) )
        {
            ulTemp += PhysMemArray[i].BlockByteLength;
        }
        i++;
    }
    ExtendMem += ulTemp / _1KB;

    PhysMemArrayQty = (ccount + 1);

#ifdef PMDEBUG
    DBPRT(1+16,("CPUUsable = %lx\n",WhichCPUUsable));
    DBPRT(1+16,("CPUWeAre = %lx\n",WhichCPUAreWe));
    DBPRT(1+16,("CPUXlateMem = %lx\n",WhichCPUXlateMem));
    DBPRT(1+16,("PhysMem:\n"));
    for(i = 0; i <= ccount; i++)
    {
        DBPRT(1+16,("Base %lx Sz %lx Loc %lx Cac %lx\n",
                         PhysMemArray[i].BlockStartAddr,
                         PhysMemArray[i].BlockByteLength,
                         PhysMemArray[i].WhichCPULocal,
                         PhysMemArray[i].WhichCPUCached));

    }
#endif

}
