/*
	File:		MemoryMgr24Patches.c

	Contains:	Process Mgr heap routines that manipulate data blocks according to the
				24-bit memory manager structures.  MemoryMgr24Patches.c and
				MemoryMgr32Patches.c are virtually identical in source code.  The header
				files each uses, though, determines the how the block header access
				macros are expanded.  This (hopefully) makes it easier to maintain given
				the 24/32 duality imposed on us.

	Written by:	Phil Goldman

	Copyright:	© 1986-1992 by Apple Computer, Inc., all rights reserved.

	Change History (most recent first):

		<11>	 6/22/92	DTY		#1033275 <csd>: Remove last change.  The quad alignment is now
									done inside NewHiProcessManagerHandle or whatever it’s called
									inside MemoryMgrPatches.c.
		<10>	 6/12/92	pvh		#1032359: Added quad word alignment code in ChipFromFreeBlock
									function so that we are properly aligned with the rest of the
									Memory manager on 68040 Macs.
		 <9>	10/28/91	SAM/KSM	Rolled in Regatta change.
	
	Regatta Change History:

	 	 <1>	  8/8/91	SAM		(RMP) Fixed MoveHeap bug where slop wasn't taken into account when
		 							calculating zcbFree.
	7.0 History:

		 <8>	 5/23/91	dba		do a style of coercion that MPW 3.2 C likes
		 <7>	  4/9/91	DFH		VL, #83672 : Added GetProcessMgrHiMaxBlock.
		 <6>	 3/15/91	DFH		sad,WS#DFH-910315a: Fixed GetProcessMgrLoFreeBytes to not
									dereference sawInterestingBlock parameter if handleOfInterest is
									nil.
		 <5>	  3/4/91	DFH		JWM,WS#DFH-910226b: Fixed GetSystemHiFreeBytes loop termination.
									Was counting the zone trailer.
		 <4>	 2/21/91	DFH		dba,#82504, #82681, #83168, #83182, #83207: Process Mgr heap
									allocation now makes sure there is sufficient memory in the
									system heap even when ProcessMgr zone doesn't need to grow.
		 <2>	11/21/90	DFH		Fixed GZ parameter adjustments to deal correctly with boundary
									conditions. Also, use ROUTINE_ADDR when setting gzProc, to
									bypass jumptable.
		 <0>	 x/xx/86	PYG		New Today.

*/

#include <types.h>
#include <memory.h>
#include <menus.h>
#include <files.h>
#include <quickdraw.h>
#include <windows.h>
#include <osutils.h>
#include <devices.h>
#include <retrace.h>
#include <errors.h>

#include "Glue.h"
#include "Lomem.h"
#include "Data.h"
#include "SysMisc.h"
#include "Zone.h"
#include "Zone24.h"

#pragma segment zone24_tools

/* ZoneMPBlockPtr24.  Locate the master pointer block for the given zone */
Ptr
ZoneMPBlockPtr24(THz pZone)
	{
	return((Ptr)pZone + SIZEOF_ZONE_HEADER + SIZEOF_BLOCK_HEADER);
	}

/* MakeNonRelocatable24.  Given a pointer to a block's data, convert the block type
 * to "nonrelocatable".
 * Assumes block is from THEZONE.
 */
void
MakeNonRelocatable24(Ptr pBlock)
	{
	pBlock -= SIZEOF_BLOCK_HEADER;
	BLOCK_HANDLE((HeapBlockPtr)pBlock) = (Handle)THEZONE;
	TAGBYTE((HeapBlockPtr)pBlock) = nRelTagVal;
	}

/* GetProcessMgrLoFreeBytes24.  Find out how many free or purgeable bytes there are below
 * the lowest immovable block in the Process Mgr heap.  Size returned is physical byte
 * count.  As a side benefit, this routine also tells the caller whether the specified
 * handle was encountered in this low range.  Finding the block implies that freeing or
 * shrinking it would donate free space area of the Process Mgr heap into which the
 * system heap can grow.
 */
u_size
GetProcessMgrLoFreeBytes24(Handle handleOfInterest, Boolean *sawInterestingBlock)
	{
	HeapBlockPtr	heapBlockPtr, blockOfInterest;
	u_size			blockSize, loFreeBytes;
	unsigned int	blockTags;
	unsigned int	blockFlags;

	/* Convert Handle to physical block address.  This works fine even if the handle
	 * has been purged (physical address will not be found in the loop), but of course
	 * we are never called with a purged handle, anyway.
	 */
	if (handleOfInterest != nil)
		{
		blockOfInterest = (HeapBlockPtr) ((((u_long) (*handleOfInterest)) & LO3BYTES) - SIZEOF_BLOCK_HEADER);
		*sawInterestingBlock = false;
		}
	else
		blockOfInterest = nil;

	/* Skip dummy block */
	heapBlockPtr = &ProcessMgrZone->heapData;
	(Ptr)heapBlockPtr += BLOCK_SIZE(heapBlockPtr);
	loFreeBytes = 0;

	/* Walk the heap. Use infinite for loop instead of while loop, since we are guaranteed
	 * to find at least one non-relocatable block (e.g. a master pointer block).
	 */
	for (;;)
		{
		blockTags = (unsigned int) TAGBYTE(heapBlockPtr);
		blockSize = BLOCK_SIZE(heapBlockPtr);

		/* Are we looking at the interesting block? */
		if (heapBlockPtr == blockOfInterest)
			*sawInterestingBlock = true;

		/* Count this block in if it is free or purgeable.  Stop when we reach
		 * an immovable block (assume unrecognized block is immovable).
		 */
		if (blockTags == freeTagVal)
			loFreeBytes += blockSize;
		else if (blockTags == relTagVal)
			{
			blockFlags = (unsigned int) MP_TAGBYTE(heapBlockPtr,ProcessMgrZone);
			if ((blockFlags & lockedMPTagFlag) != 0)
				break;
			else if ((blockFlags & purgeableMPTagFlag) != 0)
				loFreeBytes += blockSize;
			}
		else
			break;

		(Ptr)heapBlockPtr += blockSize;
		}

	return(loFreeBytes);
	}

/* GetSystemHiFreeBytes24.  Find out how many free or purgeable bytes there are above
 * the highest immovable block in the system heap.  Size returned is physical byte count.
 */
u_size
GetSystemHiFreeBytes24(void)
	{
	HeapBlockPtr	heapBlockPtr;
	u_size			blockSize, hiFreeBytes;
	unsigned int	blockTags;
	unsigned int	blockFlags;

	hiFreeBytes = 0;

	/* Begin at the beginning */
	heapBlockPtr = &SYSZONE->heapData;

	/* Walk the heap */
	while (heapBlockPtr < SYSZONE->bkLim)
		{
		blockTags = (unsigned int) TAGBYTE(heapBlockPtr);
		blockSize = BLOCK_SIZE(heapBlockPtr);

		/* Count this block in if it is free or purgeable.  Reset the counter if the
		 * if the block is non-relocatable, relocatable but locked, or of some unknown
		 * new type, since we require memory above all such blocks.
		 */
		if (blockTags == freeTagVal)
			hiFreeBytes += blockSize;
		else if (blockTags == relTagVal)
			{
			blockFlags = (unsigned int) MP_TAGBYTE(heapBlockPtr,SYSZONE);
			if ((blockFlags & lockedMPTagFlag) != 0)
				hiFreeBytes = 0;
			else if ((blockFlags & purgeableMPTagFlag) != 0)
				hiFreeBytes += blockSize;
			}
		else
			hiFreeBytes = 0;

		(Ptr)heapBlockPtr += blockSize;
		}

	return(hiFreeBytes);
	}

/* ExtendZone24.  Extend the given zone upwards cbNeeded bytes by changing the
 * trailer block into a free block and creating a new block right after it.  This
 * parallels procedure ZoneAdjustEnd() in ROM.
 * NOTE:  This must be extended in blocks of at least MIN_PHYSICAL_SIZE, as we need
 * to create a new free block underneath the current trailer block.
 * A5 world needed:  None
 */
void
ExtendZone24(THz theZone, unsigned long cbNeeded)
	{
	HeapBlockPtr	oldBkLim,newBkLim;

	assert(cbNeeded >= MIN_PHYSICAL_SIZE);

	/* Get current zone trailer block and calc address of the new one */
	(Ptr)oldBkLim = theZone->bkLim;
	(Ptr)newBkLim = (Ptr)oldBkLim + cbNeeded;

	/* Enlarge the zone trailer to the amount of the extension */
	CLEAR_BLOCK_ATTRS(oldBkLim);
	BLOCK_SIZE(oldBkLim) = cbNeeded;
	TAGBYTE(oldBkLim) = freeTagVal;

	/* Init the block header of the new zone trailer */
	CLEAR_BLOCK_ATTRS(newBkLim);
	BLOCK_SIZE(newBkLim) = MIN_PHYSICAL_SIZE;
	TAGBYTE(newBkLim) = freeTagVal;

	/* Adjust zone header to reflect the added free bytes */
	theZone->zcbFree += cbNeeded;
	theZone->bkLim = (Ptr)newBkLim;
	}

/* MoveZone24.  Move the Process Mgr zone from here to there.
 * Assumes A5 == PROCESSMGRGLOBALS
 */
void
MoveZone24(THz oldZone, THz newZone)
	{
	HeapBlockPtr	pBlock, pMaxBlock;
	unsigned long	cbDelta, oldDummyBlockSize;
	Boolean			zoneWentUp;

	/* Update appropriate locations to reflect new location */
	if (THEZONE == oldZone)
		THEZONE = newZone;
	if (APPLZONE == oldZone)
		APPLZONE = newZone;
	pNullProcess->p_zone = ProcessMgrZone = newZone;

	/* remember size of current dummy ptr (big if shrinking Process Mgr zone) */
	pBlock = &oldZone->heapData;
	oldDummyBlockSize = BLOCK_SIZE(pBlock);

	/* Invalidate the rover and purgeptr */
	oldZone->purgePtr = nil;
	oldZone->allocPtr = nil;

	/* Move the zone header */
	BlockMove(oldZone, newZone, SIZEOF_ZONE_HEADER);

	/* Set up new dummy nonrelocatable block */
	pBlock = &newZone->heapData;
	CLEAR_BLOCK_ATTRS(pBlock);
	BLOCK_SIZE(pBlock) = MIN_PHYSICAL_SIZE;
	BLOCK_HANDLE(pBlock) = (Handle) newZone;
	TAGBYTE(pBlock) = nRelTagVal;

	/* Need to know sign and magnitude of move */
	zoneWentUp = (newZone > oldZone);
	cbDelta = (zoneWentUp) ? (unsigned long)newZone - (unsigned long)oldZone : (unsigned long)oldZone - (unsigned long)newZone;

	/* If the zone was lowered, there is room to make a free block above
	 * the dummy block.  Have to add on extra slop if old size > MIN_PHYSICAL_SIZE.
	 * Also, keep the zone free count in sync with this addition.
	 */
	if (zoneWentUp == false)
		{
		(Ptr)pBlock += MIN_PHYSICAL_SIZE;
		CLEAR_BLOCK_ATTRS(pBlock);
		BLOCK_SIZE(pBlock) = cbDelta + (oldDummyBlockSize - MIN_PHYSICAL_SIZE);
		BLOCK_HANDLE(pBlock) = (Handle) nil;
		TAGBYTE(pBlock) = freeTagVal;
		newZone->zcbFree += BLOCK_SIZE(pBlock);							/* <8> was += cbDelta; */
		}

	/* Traverse zone to adjust handle field in the block headers */
	(Ptr)pBlock += BLOCK_SIZE(pBlock);
	pMaxBlock = newZone->bkLim;
	while (pBlock < pMaxBlock)
		{
		if (IS_RELOC_BLOCK(pBlock))
			{
			if (zoneWentUp)
				(Ptr)(BLOCK_HANDLE(pBlock)) -= cbDelta;
			else
				(Ptr)(BLOCK_HANDLE(pBlock)) += cbDelta;
			}
		else
			if (IS_NRELOC_BLOCK(pBlock))
				BLOCK_HANDLE(pBlock) = newZone;

		/* go to next block */
		(Ptr)pBlock += BLOCK_SIZE(pBlock);
		}
	}

/* ShrinkProcessMgrZone24.  Try to shrink the Process Mgr heap by cbNeeded bytes.  Works
 * by growing the dummy block up (with the GZ proc disabled!), then moving the zone
 * header up to the end of it.
 * NOTE: Does not adjust the system zone!  When we return to caller, there is a
 * vacuum between the two zones that should caller should fix with ExtendZone.
 *
 * Returns cbActual (where cbActual >= cbNeeded) on success, zero on failure.
 * A5 world needed:  PROCESSMGRGLOBALS.
 */
unsigned long
ShrinkProcessMgrZone24(unsigned long cbNeeded)
	{
	register THz	origZone;
	HeapBlockPtr	dummyHdrPtr;
	Ptr				dummyPtr;
	unsigned long	sizeToGrow;

	/* check param */
	assert((cbNeeded != 0) && (WORD_ALIGNED(cbNeeded)));

	/* Calculate size that dummy block should grow.  Allow for overflow. */
	if ((sizeToGrow = cbNeeded + SIZEOF_SMALLEST_PTR_DATUM) < cbNeeded)
		return(0);

	/* Try to grow the dummy block to desired size, making sure that
	 * the ProcessMgrZone GZ proc is temporarily disabled.
	 */
	origZone = ProcessMgrZone;
	dummyHdrPtr = (HeapBlockPtr)(&origZone->heapData);
	dummyPtr = (Ptr)dummyHdrPtr + SIZEOF_BLOCK_HEADER;
	origZone->gzProc = nil;
	SetPtrSize(dummyPtr, sizeToGrow);
	origZone->gzProc = ROUTINE_ADDR(ProcessMgr_GZProc_Glue);

	/* Not enough memory ... oh well! */
	if (MEMERROR != noErr)
		return(0);

	/* Find out how much we actually were given (could be more if filling a large
	 * free block), then subtract overhead, to determine the true offset of the new
	 * zone header.  Save result in cbNeeded.
	 */
	cbNeeded = BLOCK_SIZE(dummyHdrPtr) - (SIZEOF_BLOCK_HEADER + SIZEOF_SMALLEST_PTR_DATUM);
	assert((cbNeeded != 0) && (WORD_ALIGNED(cbNeeded)));

	/* Calculate new start of zone, and move it there */
	MoveZone(origZone, (Ptr) origZone + cbNeeded);

	/* tell caller how much we got */
	return(cbNeeded);
	}

/* GetLockPtr24.  Get a non-relocatable block as close to (but below) ceilingPtr
 * as possible, and stamp it with the signature 'LOCK'.
 * A5 world needed:  PROCESSMGRGLOBALS
 */
Ptr
GetLockPtr24(Ptr ceilingPtr)
	{
	register THz	localProcessMgrZone;
	HeapBlockPtr	dummyBlockPtr;
	Ptr				dummyPtr,lockPtr;
	u_size			requestSize;

	assert(THEZONE == ProcessMgrZone);

	/* Get current value of Process Mgr zone, and determine address of the
	 * minimal dummy block at the low end of the zone.
	 */
	localProcessMgrZone = ProcessMgrZone;
	dummyBlockPtr = (HeapBlockPtr)&localProcessMgrZone->heapData;
	dummyPtr = BLOCK_DATA(dummyBlockPtr);


	/* Try to grow the dummy block big enough so its high end matches
	 * ceilingPtr, but without letting the heap move.
	 */
	assert(ceilingPtr > dummyPtr);
	requestSize = ceilingPtr - dummyPtr;
	localProcessMgrZone->gzProc = nil;
	SetPtrSize(dummyPtr, requestSize);

	/* Request to grow up failed, so try for free block immediately
	 * following.  Assumption is that memory manager always moves
	 * handles up high, coelescing free space low.  Failure is fatal.
	 */
	if (MEMERROR != noErr)
		{
		dummyBlockPtr = (Ptr)dummyBlockPtr + BLOCK_SIZE(dummyBlockPtr);
		if ( IS_NOT_FREE_BLOCK(dummyBlockPtr)
			|| (BLOCK_SIZE(dummyBlockPtr) < MIN_PHYSICAL_SIZE) )
				return(nil);

		requestSize = BLOCK_SIZE(dummyBlockPtr) - MIN_PHYSICAL_SIZE;
		SetPtrSize(dummyPtr, requestSize);
		if (MEMERROR != noErr)
			return(nil);
		}

	/* Dummy block is now up as close to ceilingPtr as possible.  I.e. The
	 * low free space was eaten, so a new allocation will be at (we hope)
	 * or above ceilingPtr.  Allocate the lock block as a minimal sized
	 * pointer, then shrink the dummy back to leave room for the real
	 * allocation later.
	 */
	if ((lockPtr = NewPtr(sizeof(long))) != nil)
		*((long *)lockPtr) = 'LOCK';
	SetPtrSize(dummyPtr, SIZEOF_SMALLEST_PTR_DATUM);
	assert(MEMERROR == noErr);

	/* Reallow growth */
	localProcessMgrZone->gzProc = ROUTINE_ADDR(ProcessMgr_GZProc_Glue);

	return(lockPtr);
	}

/* GetProcessMgrHiMaxBlock24.  Do like MaxBlock() on the Process Mgr zone, but ignore
 * the straddle area part.
 */
u_size
GetProcessMgrHiMaxBlock24(void)
	{
	HeapBlockPtr	heapBlockPtr;
	u_size			blockSize, hiMaxBlock, thisRangeFree;
	unsigned int	blockTags;
	unsigned int	blockFlags;
	Boolean			aboveStraddle;

	aboveStraddle = false;
	thisRangeFree = 0;
	hiMaxBlock = 0;

	/* Skip dummy block */
	heapBlockPtr = &ProcessMgrZone->heapData;
	(Ptr)heapBlockPtr += BLOCK_SIZE(heapBlockPtr);

	/* Walk the heap */
	while (heapBlockPtr < ProcessMgrZone->bkLim)
		{
		blockTags = (unsigned int) TAGBYTE(heapBlockPtr);
		blockSize = BLOCK_SIZE(heapBlockPtr);

		/* Count this block in if it is free or purgeable.  Reset the counter if the
		 * if the block is non-relocatable, relocatable but locked, or of some unknown
		 * new type, since we're looking for memory *between* such blocks.
		 */
		if (blockTags == freeTagVal)
			thisRangeFree += blockSize;
		else if (blockTags == relTagVal)
			{
			blockFlags = (unsigned int) MP_TAGBYTE(heapBlockPtr,ProcessMgrZone);
			if ((blockFlags & lockedMPTagFlag) != 0)
				{
				if ((thisRangeFree > hiMaxBlock) && (aboveStraddle))
					hiMaxBlock = thisRangeFree;
				aboveStraddle = true;
				thisRangeFree = 0;
				}
			else if ((blockFlags & purgeableMPTagFlag) != 0)
				thisRangeFree += blockSize;
			}
		else
			{
			if ((thisRangeFree > hiMaxBlock) && (aboveStraddle))
				hiMaxBlock = thisRangeFree;
			aboveStraddle = true;
			thisRangeFree = 0;
			}

		(Ptr)heapBlockPtr += blockSize;
		}

	/* Allow for block header we'd need */
	if (hiMaxBlock != 0)
		PhysicalToLogicalSize(hiMaxBlock);
	return(hiMaxBlock);
	}

/* GetHighestLargeFreeBlock24.  This function returns a pointer to a block that
 * is >= size, or nil if one couldn't be located.  It will not pass any
 * non-relocatable blocks during it's search.
 */
FreeBlockAddr
GetHighestLargeFreeBlock24(u_size size, Ptr lockPtr)
	{
	HeapBlockPtr	blockPtr, highestLargeFreeBlock, trailerBlockPtr;
	u_size			blockSize;

	assert(lockPtr == nil || *((long *)lockPtr) == 'LOCK');

	/* Looking for physical blocks now */
	size += SIZEOF_BLOCK_HEADER;

	/* Start at bottom of heap, and go up to trailerBlock */
	blockPtr = (HeapBlockPtr)&THEZONE->heapData;
	trailerBlockPtr = (HeapBlockPtr)THEZONE->bkLim;
	highestLargeFreeBlock = nil;
	while (blockPtr < trailerBlockPtr)
		{
		blockSize = BLOCK_SIZE(blockPtr);

		/* Check whether this is a valid block for our purposes */
		if ((IS_FREE_BLOCK(blockPtr) && (blockSize >= size)))
			highestLargeFreeBlock = blockPtr;

		/* Advance to next block */
		(Ptr)blockPtr += blockSize;

		/* Don't go past ceiling ptr for Excel.  Note that this check is not made
		 * against the first block in the zone.  This is OK because a) the lock
		 * block does no good in that case, and b) the first block is our dummy block.
		 */
		if (BLOCK_DATA(blockPtr) == lockPtr)
			break;
		}

	return((FreeBlockAddr) highestLargeFreeBlock);
	}

/* GetTopBlockSize24.  Extract the size of the specified block.  Return 0 if address
 * is nil or the block is not the absolute highest in the zone.
 */
unsigned long
GetTopBlockSize24(FreeBlockAddr pTopBlock)
	{
	u_size	blockSize;

	if ((pTopBlock == nil)
		|| ((Ptr)pTopBlock + (blockSize = BLOCK_SIZE((HeapBlockPtr) pTopBlock)) != THEZONE->bkLim) )
			return(0);

	return(blockSize);
	}

/* FreeTop24.  Shrink the size of the given free block to the specified value, and
 * create a zone trailer block after the result.  Returns address of trailer block.
 */
FreeBlockAddr
FreeTop24(FreeBlockAddr pBlockHdr, u_size newSize)
	{
	/* Resize the free block: only the size field needs to change */
	BLOCK_SIZE((HeapBlockPtr)pBlockHdr) = newSize;

	/* Advance pBlockHdr to header of block to follow, and init the header */
	*(Ptr*)&pBlockHdr += newSize;
	CLEAR_BLOCK_ATTRS(pBlockHdr);
	TAGBYTE((HeapBlockPtr)pBlockHdr) = freeTagVal;
	BLOCK_SIZE((HeapBlockPtr)pBlockHdr) = MIN_PHYSICAL_SIZE;
	return(pBlockHdr);
	}


/* ChipOffRelocFromFree24.  Allocate a relocatable block at the high end of the
 * given free block.
 * Assumes that the free block length is at least sizeNeeded.
 * NOTE: The size correction will be incorrect if the free block is bigger
 * than sizeNeeded, but not big enough to be cleaved.  The resulting logical
 * size will be slightly larger than requested.
 */
void
ChipOffRelocFromFree24(FreeBlockAddr pBlock, Handle theHandle, u_size sizeNeeded, short sizeCorrection)
	{
	HeapBlockPtr	hbPtr = (HeapBlockPtr) pBlock;
	u_size			unusedBytes;

	sizeNeeded += SIZEOF_BLOCK_HEADER;

	if ((unusedBytes = BLOCK_SIZE(hbPtr) - sizeNeeded) >= MIN_PHYSICAL_SIZE)
		{
		SIZE_CORR(hbPtr) = 0;
		BLOCK_SIZE(hbPtr) = unusedBytes;
		(Ptr)hbPtr += unusedBytes;
		BLOCK_SIZE(hbPtr) = sizeNeeded;
		}
	else
		{
		sizeNeeded = BLOCK_SIZE(hbPtr);
		sizeCorrection += unusedBytes;
		assert(sizeCorrection <= 15);
		}

	/* Convert block to relocatable, and set master pointer and size correction */
	CLEAR_BLOCK_ATTRS(hbPtr);
	TAGBYTE(hbPtr) = relTagVal;
	BLOCK_HANDLE(hbPtr) = (Ptr)(theHandle) - (long)THEZONE;
	SIZE_CORR(hbPtr) = sizeCorrection;
	*theHandle = BLOCK_DATA(hbPtr);

	/* Adjust heap for the loss */
	HEAP_FREE_SPACE(THEZONE) -= sizeNeeded;
	}

/* InitVectorTable24.  Stuff the function vectors with our values. */
void
InitVectorTable24(void)
	{
	vrGetProcessMgrLoFreeBytes = (Ptr) ROUTINE_ADDR(GetProcessMgrLoFreeBytes24);
	vrExtendZone = (Ptr) ROUTINE_ADDR(ExtendZone24);
	vrMoveZone = (Ptr) ROUTINE_ADDR(MoveZone24);
	vrShrinkProcessMgrZone = (Ptr) ROUTINE_ADDR(ShrinkProcessMgrZone24);
	vrGetLockPtr = (Ptr) ROUTINE_ADDR(GetLockPtr24);
	vrGetHighestLargeFreeBlock = (Ptr) ROUTINE_ADDR(GetHighestLargeFreeBlock24);
	vrGetTopBlockSize = (Ptr) ROUTINE_ADDR(GetTopBlockSize24);
	vrFreeTop = (Ptr) ROUTINE_ADDR(FreeTop24);
	vrChipOffRelocFromFree = (Ptr) ROUTINE_ADDR(ChipOffRelocFromFree24);
	vrZoneMPBlockPtr = (Ptr) ROUTINE_ADDR(ZoneMPBlockPtr24);
	vrGetSystemHiFreeBytes = (Ptr) ROUTINE_ADDR(GetSystemHiFreeBytes24);
	vrGetProcessMgrHiMaxBlock = (Ptr) ROUTINE_ADDR(GetProcessMgrHiMaxBlock24);
	}
