Category : C++ Source Code
Archive   : VMVCPP.ZIP
Filename : VMPHYS.C

 
Output of file : VMPHYS.C contained in archive : VMVCPP.ZIP
/***
* vmphys.c -
*
* Copyright (c) 1989-1992, Microsoft Corporation. All rights reserved.
*
*Purpose:
*
* PUBLIC Functions:
*
* VpAllocatePage:
*
* VpReleasePage:
*
* PVmLoadVp:
*
*******************************************************************************/

#pragma title("Virtual Memory Manager - Physical Memory Management")

#include
#include
#include
#include
#include
#include

#if !defined(DOS)
#error DOS must be defined
#endif

#include
#include

#include
#include
#include

static unsigned _near _cVmUmb = 0; /* Number of UMBs allocated */
static _segment _near rgsegVmUmb[cVmUmbMax-1]; /* Segments of allocated UMBs */
static unsigned _near rgcPageVmUmb[cVmUmbMax-1]; /* Count of pages in each UMB */

unsigned _near _cPageDos; /* Count of DOS pages (EMS/UMB not included) */
unsigned _near _cVmPage; /* Count of non-EMS pages allocated */
_segment _near _segVmDos; /* Segment of DOS heap allocation */

HPGD _near _rghpgdHash[ipgdHashMax];

#pragma page()

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* CPageVmAllocateUmbs
*
* This function allocates XMS upper memory blocks (UMBs) for use
* as physical pages for paging.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Entry Conditions:
*
* DOS only.
*
* Exit Conditions:
*
* The global variables _cVmUmb, rgsegVmUmb, and rgcPageVmUmb are
* initialized. The function returns the total number of pages that
* can be created from the UMBs allocated. There is a compile time
* limit (cVmUmbMax) on the number of UMBs that can be allocated. The
* size of these UMBs and therefore the number of pages that can be
* allocated is unlimited.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


unsigned LOCAL __CPageVmAllocateUmbs(void)
{
unsigned cPageUmb;
ERR err;

cPageUmb = 0; /* No UMB pages allocated */

if (__FXmsCheckInstalled())
while (_cVmUmb < cVmUmbMax - 1)
{
_segment segDummy;
unsigned cPara;

/* To allocate a UMB, first request a block that is too large. */
/* Though this will fail, it returns the size of the largest. */
/* block available. This is used to request a reasonable sized */
/* block that will be broken into one or more physical pages */

cPara = 0xffff;
err = __ErrXmsRequestUpperMemoryBlock(&segDummy, (unsigned short _far *) &cPara);
if ((err != errXmsSmallerUMBIsAvailable) || (cPara < cbVmPage / 16))
break;

/* Use the cPara field returned and round to a page boundary. */

rgcPageVmUmb[_cVmUmb] = cPara / cParaPerPage;
cPara = rgcPageVmUmb[_cVmUmb] * cParaPerPage;
err = __ErrXmsRequestUpperMemoryBlock(&rgsegVmUmb[_cVmUmb], (unsigned short _far *) &cPara);
Assert(err == errNoError);
if (err != errNoError) /* Just to be safe */
break;

cPageUmb += rgcPageVmUmb[_cVmUmb];
_cVmUmb++; /* UMB successfully allocated */
}

return(cPageUmb);
}

#pragma page()

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* VmFreeUmbs
*
* This function releases the XMS upper memory blocks (UMBs)
* allocated in CPageVmAllocateUmbs.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Entry Conditions:
*
* DOS only.
*
* Exit Conditions:
*
* The global variable _cVmUmb is reset to zero.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


void LOCAL __VmFreeUmbs(void)
{
ERR err;

while (_cVmUmb)
{
err = __ErrXmsReleaseUpperMemoryBlock(rgsegVmUmb[--_cVmUmb]);
Assert(err == errNoError);
}
}

#pragma page()

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* FVmInitializePhys
*
* This function initializes the physical memory manager.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Entry Conditions:
*
* DOS only.
*
* The parameter cVmParaMin specifies the minumum number of paras
* of physical memory to be allowed. If this number of pages can not
* be allocated, the initialization fails.
*
* The parameter cParaVmDosMax specifies the maximum number of
* paragraphs to be allocated from conventional DOS memory. If the
* value is 0, the default of 0xffff is used.
*
* The physical memory manager must not have been previously
* initialized without an intermediate call to VmTerminatePhys.
*
* Exit Conditions:
*
* The function returns TRUE if the initialization is successful.
* Otherwise it returns FALSE. Initialization can fail if there is
* not enough available DOS memory to allocate the miniumum number of
* pages or if there is not enough memory in the near heap to allocate
* the page descriptors for the minimum number of pages.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


int PRIVATE __FVmInitializePhys(unsigned cVmParaMin, unsigned cParaVmDosMax)
{
unsigned cPageUmb;
unsigned cParaDos;
HPGD hpgd;
_segment segPage;
unsigned iPage;
unsigned iUmb;
unsigned ipgd;

VmTracePrintf(("FVmInitializePage\n"));

Assert(_osmode == DOS_MODE);

if (cParaVmDosMax == 0) /* Zero indicates no limit */
cParaVmDosMax = 0xffff;

/* safety belts */
if (cVmParaMin > cParaVmDosMax)
return FALSE;

/* Allocate upper memory blocks (UMBs) from an XMS driver. This */
/* memory is within the 1M address space and is suitable for */
/* paging */

cPageUmb = __CPageVmAllocateUmbs();

/* Now allocate memory from the DOS heap to use for paging and for */
/* maintaining the page tables. This memory is allocated as a */
/* single block so if the heap contains several big blocks, this */
/* allocation strategy may not be ideal. I don't expect this to */
/* be a problem. */

/* The size of the block allocated is subject to several constraints. */
/* The obvious constraint in the availability of memory in the DOS */
/* heap. In addition, the application may impose an upper limit on */
/* the consumption of DOS memory as well as a lower limit on the */
/* number of pages needed in the paging area. */

cParaDos = min(__CParaVmDosAvail(), cParaVmDosMax);

/* The block of DOS memory allocated is divided into pages and page */
/* descriptors. Each page descriptor requires one paragraph. Each */
/* page requires (cbVmPage/16) paragraphs. The gives a formula for */
/* memory required as */
/* cParaDos = _cPageDos * (cbVmPage/16+1) + cPageUmb + _cVmPageEms;

Assert(sizeof(PGD) == cbVmPara); /* One PGD == 1 Paragraph */

// How many whole pages of DOS and UMB
_cVmPage = cPageUmb + (cParaDos / cParaPerPage);

// subtract space needed for whole pages of PDs
_cVmPage -= _cVmPage/cParaPerPage;

// DOS allocated in paragraphs not page units
// if enough extra paras, use them for PDs, if not get another page
_cVmPage -= (cParaDos % cParaPerPage) < (_cVmPage % cParaPerPage) ? 1 : 0;

// ensure the amount of space available for swap area is enough
if (_cVmPage * cParaPerPage < cVmParaMin)
{
__VmFreeUmbs();
return FALSE;
}

_cPageDos = _cVmPage - cPageUmb;

// Cannot work with only one page of DOS swap area
if (_cPageDos <= 1)
{
__VmFreeUmbs();
return FALSE;
}

// Grab only as needed for DOS pages and PD pages
cParaDos = (_cPageDos * cParaPerPage) + _cVmPage;

_segVmDos = __SegVmDosAllocate(cParaDos);
if (_segVmDos == _NULLSEG)
{
Assert(FALSE); /* This should not be possible */
__VmFreeUmbs(); /* Free already allocated UMBs */
return(FALSE); /* Return failure indication */
}

/* Initialize the PGD structures */
(unsigned)hpgd = HpgdOfIPage0;

{
size_t cw;
short _far *pw = _segVmDos:>((short _based(void) *) 0);

for (cw = 8 * _cVmPage; cw != 0; cw--)
*pw++ = 0;
}
segPage = (_segment) ((unsigned) _segVmDos + _cVmPage);

for (iPage = 0; iPage < _cPageDos; iPage++)
{
PpgdOfHpgd(hpgd)->segPage = segPage;
PpgdOfHpgd(hpgd)->Flags = (unsigned char) (iPage ? 0U : fDiscontinousPgd);

hpgd = HpgdAdd(hpgd, 1);

segPage = (_segment) ((unsigned) segPage) + (cbVmPage / 16);
}

for (iUmb = 0; iUmb < _cVmUmb; iUmb++)
{
segPage = rgsegVmUmb[iUmb]; /* Segment base of this UMB */
cPageUmb = rgcPageVmUmb[iUmb]; /* Number of pages in this UMB */

for (iPage = 0; iPage < cPageUmb; iPage++)
{
PpgdOfHpgd(hpgd)->segPage = segPage;
PpgdOfHpgd(hpgd)->Flags = (unsigned char) (fUmbPgd | (iPage ? 0U : fDiscontinousPgd));

hpgd = HpgdAdd(hpgd, 1);

segPage = (_segment) ((unsigned) segPage) + (cbVmPage / 16);
}
}

/* Initialize hash table */

for (ipgd = 0; ipgd < ipgdHashMax; ipgd++)
_rghpgdHash[ipgd] = hpgdNil;

return(TRUE);
}

#pragma page()

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* VmTerminatePhys
*
* This function releases physical memory resources allocated in
* FVmInitializePhys.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Entry Conditions:
*
* DOS only.
*
* Exit Conditions:
*
* None.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


void PRIVATE __VmTerminatePhys(void)
{
__VmDosFreeSeg(_segVmDos); /* Free DOS heap block */
__VmFreeUmbs(); /* Free already allocated UMBs */
}

#pragma page()

#ifdef VMDEBUG


void LOCAL __AssertValidHashTable(void)
{
HPGD hpgd;
unsigned ipgd;
unsigned cPageAllocated;
unsigned cPageHashed;
unsigned cPageDiscardable;

(unsigned)hpgd = HpgdOfIPage0;
cPageAllocated = 0;
cPageDiscardable = 0;

for (ipgd = 0; ipgd < _cVmPage; ipgd++)
{
/* An unallocated page has timeRef == 0 and cLock == 0 and */
/* is not marked as dirty. */

Assert((PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd) ||
((PpgdOfHpgd(hpgd)->timeRef == 0) &&
(PpgdOfHpgd(hpgd)->cLock == 0) &&
!(PpgdOfHpgd(hpgd)->Flags & fDirtyPgd)));

if (PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd)
cPageAllocated++;

hpgd = HpgdAdd(hpgd, 1);
}

cPageHashed = 0;
for (ipgd = 0; ipgd < ipgdHashMax; ipgd++)
{
hpgd = _rghpgdHash[ipgd];
while (hpgd != hpgdNil)
{
Assert(ipgd == IpgdHashOfVp(PpgdOfHpgd(hpgd)->vp));
Assert(PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd);
hpgd = PpgdOfHpgd(hpgd)->hpgdHashNext;
cPageHashed++;
Assert(cPageHashed <= cPageAllocated);
}
}

Assert(cPageHashed == cPageAllocated);
}

#endif /* VMDEBUG */


#pragma page()

void PRIVATE __VmWriteDirtyPgd(HPGD hpgd)
{
VmTracePrintf(("VmWriteDirtyPgd: hpgd = %04X, vp = %08lX\n",
(unsigned) hpgd, PpgdOfHpgd(hpgd)->vp));

Assert(PpgdOfHpgd(hpgd)->Flags & fDirtyPgd);

Assert(PpgdOfHpgd(hpgd)->pte & (fEmsPte | fXmsPte | fDiskPte));

if (PpgdOfHpgd(hpgd)->pte & fXmsPte)
__FVmSwapOutXmsPage(PpgdOfHpgd(hpgd)->pte, hpgd);

else if (PpgdOfHpgd(hpgd)->pte & fEmsPte)
__FVmSwapOutEmsPage(PpgdOfHpgd(hpgd)->pte, hpgd);

else
__FVmSwapOutDiskPage(PpgdOfHpgd(hpgd)->pte, hpgd);

PpgdOfHpgd(hpgd)->Flags &= ~fDirtyPgd;
}

#pragma page()

void PRIVATE __VmRemovePgdFromCache(HPGD hpgd)
{
unsigned ipgd;
HPGD hpgdNext;
HPGD hpgdPrev;

VmTracePrintf(("VmRemovePgdFromCache: hpgd = %04X, vp = %08lX\n",
(unsigned) hpgd, PpgdOfHpgd(hpgd)->vp));

Assert(PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd);

#ifdef VMDEBUG
__AssertValidHashTable();
#endif

hpgdNext = _rghpgdHash[ipgd = IpgdHashOfVp(PpgdOfHpgd(hpgd)->vp)];
if (hpgdNext == hpgd)
_rghpgdHash[ipgd] = PpgdOfHpgd(hpgd)->hpgdHashNext;

else {
while (hpgdNext != hpgd) {
Assert(hpgdNext != hpgdNil);

hpgdPrev = hpgdNext;
hpgdNext = PpgdOfHpgd(hpgdNext)->hpgdHashNext;
}

PpgdOfHpgd(hpgdPrev)->hpgdHashNext = PpgdOfHpgd(hpgd)->hpgdHashNext;
}
}


#pragma page()

HPGD PRIVATE __HpgdVmAllocate(unsigned cPage)
{
unsigned iPage;
HPGD hpgd;
HPGD hpgdLru;
unsigned timeLru;

VmTracePrintf(("HpgdVmAllocate\n"));

#ifdef VMDEBUG
__AssertValidHashTable();
#endif

/* Make one pass over the allocated pages to find a free page or the */
/* least recently used allocated page */


(unsigned)hpgd = HpgdOfIPage0; /* HPGD of first PGD */
hpgdLru = hpgdNil; /* No LRU page yet */
timeLru = UINT_MAX; /* Latest possible time */

for (iPage = 0; iPage < _cVmPage; iPage++)
{
/* An unallocated page has timeRef == 0 and cLock == 0 and */
/* is not marked as dirty. */

Assert((PpgdOfHpgd(hpgd)->Flags & fAllocatedPgd) ||
((PpgdOfHpgd(hpgd)->timeRef == 0) &&
(PpgdOfHpgd(hpgd)->cLock == 0) &&
!(PpgdOfHpgd(hpgd)->Flags & fDirtyPgd)));

if ((PpgdOfHpgd(hpgd)->timeRef <= timeLru) &&
(PpgdOfHpgd(hpgd)->cLock == 0))
{
hpgdLru = hpgd;
timeLru = PpgdOfHpgd(hpgd)->timeRef;
}

hpgd = HpgdAdd(hpgd, 1);
}

if (hpgdLru != hpgdNil)
{
if (PpgdOfHpgd(hpgdLru)->Flags & fAllocatedPgd)
__VmRemovePgdFromCache(hpgdLru);

else
PpgdOfHpgd(hpgdLru)->Flags |= fAllocatedPgd;

if (PpgdOfHpgd(hpgdLru)->Flags & fDirtyPgd)
__VmWriteDirtyPgd(hpgdLru);
}

VmTracePrintf(("HpgdVmAllocate: hpgd = %04X\n", (unsigned) hpgdLru));

return(hpgdLru);
}


#pragma page()

#if 0

/* Recoded in MASM for speed. See vmutil.asm. */

HPGD PRIVATE HpgdSearchCache(VPVOID vp)
{
unsigned ipgd;
HPGD hpgd;

VmTracePrintf(("HpgdSearchCache: vp = %08lX.\n", vp));

#ifdef VMDEBUG
AssertValidHashTable();
#endif

Assert((vp >= cbVmPage) && (vp < vpMax));
vp = VpPageOfVp(vp); /* Clear page offset */

/* Hash vp and quickly check if already resident */

hpgd = _rghpgdHash[ipgd = IpgdHashOfVp(vp)];
for (; hpgd != hpgdNil; hpgd = PpgdOfHpgd(hpgd)->hpgdHashNext)
if (PpgdOfHpgd(hpgd)->vp == vp)
break;

VmTracePrintf(("HpgdSearchCache: hpgd = %04X.\n", (unsigned) hpgd));

return(hpgd);
}

#endif /* 0 */

#pragma page()

void PRIVATE __VmUpdateTimestamps(void)
{
_timeCur = 1; /* Reset counter. */
}


  3 Responses to “Category : C++ Source Code
Archive   : VMVCPP.ZIP
Filename : VMPHYS.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/