/*----------------------------------------------------------------------------
** Name: stkcntxt.c  
**
** Title: Stack Context
**
** Purpose:
**   Calculates and maintains private variables for stack state
**
** Status: PRELIMINARY | CODED
**
** $Log:   S:/tbird/mt2_68k/stkservr/stkcntxt.c_v  $
** 
**    Rev 1.0   13 Feb 1997 09:04:58   gene
** Initial revision.
** 
**    Rev 1.1   01 Dec 1995 10:09:48   kevin
** joyce's modification
** 
**    Rev 1.0   07 Sep 1995 11:14:18   gene
** Initial revision.
** 
**    Rev 1.50   06 Apr 1994 17:08:18   nghia
** - Replaced AdrCreateAddress() with CpuGetPC, CpuGetSP() and 
** AdrDuplicateAddress() to make sure that address descriptor are compatible.
** - Forced the stack base to be in SPACE_SP - not the default SPACE_SD.
** 
**    Rev 1.49   28 Feb 1994 17:01:14   marilyn
** Moved maxOutput routines to the address server. Updated interfaces.
** 
**    Rev 1.48   27 Jul 1993 21:18:26   marilyn
** Fixed ppr 8644 where enabling the HWM was trying to free memory that
** it did not own.  Revised StkCalcHighWaterMark to be more cautious with
** its address descriptors.
** 
**    Rev 1.47   27 Jul 1993 20:17:02   nghia
** Fixed PPR 8319:
** - When stack area specified is out of range, adjust stack size to fit in.
** - Reset isStkFilled flag when set base, size.
** - Stack fill pattern will fill from the current SP if it's in the stack area,
**   else fill from stack base.
** - Revise error message to reflect the current functionality.
** 
**    Rev 1.46   26 Jul 1993 13:24:10   nghia
** Fixed bug: Use default stacksize when stack size is 0.
** 
**    Rev 1.45   20 Jul 1993 17:18:24   nghia
** Fixed bug:
** - STKWALK call StkDestroyStack() directly.
** - EnableAlarmLimit() will recalculate alarm physical address.
** - If failed to use the default session, create a new default.
** - Moved set SP to loader event processing, not in setBaseAndSize.
** 
**    Rev 1.44   19 Jul 1993 13:23:22   nghia
** Reset defaultSessionId and defaultContext global variables when destroy.
** 
**    Rev 1.43   19 Jul 1993 07:59:28   ernie
** Fixed off-by-1 error in HWM calculation.  This was caused by the algorithm
** change to search for the first unused location.  We must add 1 to this to
** get the last used location.
** 
**    Rev 1.42   16 Jul 1993 17:11:36   nghia
** Allowing initStackContext to complete.
** 
**    Rev 1.41   16 Jul 1993 12:10:24   nghia
** PIP for Stack:
** - Registered on loader STACK_TOP event to retrieve stack info.
** - Retrieved PWRVIEWS.INI stackInfo's data when initialize server.
** - Delay fill stack area if HWM is not enable.
** - Fixed bug: always recalculate alarm limit when base and size change.
** - Added StkGetStackInfo() to retrieve all context info in one call.
** - Added StkSetAlarmLimitByPercent() to set alarm by percent.
** - Fixed bug 1% off when calculate alarm percent - round value up.
** 
**    Rev 1.40   01 Jul 1993 17:17:26   doug
** Consolidate to existing error message.
** 
**    Rev 1.39   15 Jun 1993 11:01:48   mindy
** Added initialization of global variables used by stack walker.
** 
**    Rev 1.38   09 Jun 1993 08:53:14   ernie
** Changed stack high water mark calculation algorithm.  Rather than
** searching from the top of the defined stack for not(stackpattern),
** the alrgorithm now remembers the last high water mark and searches
** *toward* the top of the stack for the stackpattern.  The starting
** point for the search is one of (a) the stack bottom if the hwm has
** not already been calculated, (b) the stack pointer, or (c) the last
** high water mark, whichever is closer to the stack top.  This greatly
** accelerates HWM calculation.
** 
**    Rev 1.37   12 Jan 1993 10:31:56   marilyn
** There was a problem where old frame info was not released when the stack
** presenter wasn't up and this information was confusing the variable server.
** Now on a Mem ready event ReleasePreviousFrames is called to release old
** information. 
** 
**    Rev 1.36   06 Jan 1993 17:02:58   marilyn
** StackBaseAddr is now freed from StkDestroyStack.  
** 
**    Rev 1.35   15 Dec 1992 12:58:14   ernie
** Changed MemXXX to MemXXXSized with byte specified
** 
**    Rev 1.34   06 Nov 1992 04:30:02   brucea
** If call to SymMapAddr2Symbol returns ER_ADDRESS_NOT_FOUND (tested at end of
**    function), then set all descriptors to NULL_SYMBOL but return GOOD.
** 
**    Rev 1.33   22 Oct 1992 20:26:30   marilyn
** Alarm limits now set by percentage.
** 
**    Rev 1.32   22 Oct 1992 10:07:18   marilyn
** Recalculating the alarm state was based on an edit event and not the
** correct halt event.  Now based on PC_EDIT and MEM_HALT events.
** 
**    Rev 1.31   12 Oct 1992 10:53:20   brucea
** Changed: call to ProcReturnMaxAddress to ProcReturnMaxInputAddress
** 
**    Rev 1.30   14 Sep 1992 09:46:24   marilyn
** Halted and Changed events are no longer generated consecutively but 
** exclusively.
** 
**    Rev 1.29   08 Sep 1992 11:32:06   marilyn
** Modified for new events scheme.
** 
**    Rev 1.28   03 Sep 1992 13:50:08   marilyn
** Fixed storage leak bugs.
** 
**    Rev 1.27   27 Aug 1992 13:48:32   marilyn
** StkCalcSymbolicInfo now frees up the addr descriptors when it returns.
** 
**    Rev 1.26   20 Aug 1992 16:20:30   marilyn
** Bytes access only allowed.
**
**    Rev 1.25   19 Aug 1992 09:05:44   marilyn
** New interface for the FillStackToPattern routine.  
**
**    Rev 1.24   13 Aug 1992 17:06:22   marilyn
** Fixed problem of setting addresses past the max address.
** 
**    Rev 1.23   31 Jul 1992 15:19:34   marilyn
** Modified InitStkCntxt routine.
** 
**    Rev 1.21   28 May 1992 16:45:46   marilyn
** Modified StkGetStackDirection to allow sessionIds of 0.
**
**    Rev 1.20   30 Apr 1992 18:52:46   brucea
** Modified: StkCalcSymbolicInfo to only get symbolics when the currentPC is
**    different from a stored, previous address which is updated after getting
**    symbol information.  This removes the requirement for event notification
**    and frequent call to this function when it is not needed.
**    Now, any function that returns information about symbolic state of the
**    current PC calls StkCalcSymbolicInfo first to make sure the data is
**    current before returning it to the caller.
** Removed: calls to StkCalcSymbolicInfo in StkCallback.
** Added: reset of comparison address whenever symbols are deleted or loaded.
** 
**    Rev 1.19   24 Apr 1992 15:52:36   marilyn
** Added all new events for alarm changes and high water mark changes.
** 
**    Rev 1.18   17 Apr 1992 16:25:40   marilyn
** Fixed bug in fill to pattern routine, where endpoints were incorrectly
** computed.
**
**    Rev 1.17   15 Apr 1992 10:31:04   marilyn
** Fixed bug by replacing temp memfill code with the real MemFill call.
**
**    Rev 1.16   03 Apr 1992 17:41:52   marilyn
** Changed EVENT_CPU_REGISTER_CHANGED to EVENT_CPU_SP_CHANGED.
** 
**    Rev 1.15   26 Mar 1992 18:32:42   brucea
** Changed: StkCalcSymbolicInfo to EXPORTable
** Removed: call to StkCalcSymbolicInfo.  This is now done at low level bkpt
** 
**    Rev 1.14   16 Mar 1992 09:26:24   marilyn
** Fixed bug in FillToPattern when SP = 0.
** 
**    Rev 1.13   12 Mar 1992 17:24:30   marilyn
** Kludged out the MemFill call. Misc bug fixes.
** 
**    Rev 1.12   11 Mar 1992 10:34:22   marilyn
** Added StkGetStackDirection.
** 
**    Rev 1.11   28 Feb 1992 10:51:24   marilyn
** Included stkerrs.h
**
**    Rev 1.10   27 Feb 1992 15:37:38   marilyn
** Minor bug fixes.  Updated compatiblity to mem.dll and cpu.dll
**
**    Rev 1.9   19 Feb 1992 15:39:18   brucea
** Fixed bug in StkCreateStack
** 
**    Rev 1.8   19 Feb 1992 13:24:10   brucea
** Redefined MAX_STACK_BYTES to compute it from the sizeof(roundingMask)
** Added: inRange check in StkEnableAlarmLimit and StkSetStackBase
** Added: code that only checks alarmLimit entry when the alarm limit is enabled
** Changed: in StkSetStackBase, now round input address to an even stackUnitSize
** boundary (up or down depending on type of stack) instead of returning error
**
**    Rev 1.7   14 Feb 1992 17:42:08   brucea
** Added to context structure: BOOLEAN isValidAlarmLimit to indicate whether
**    the alarm limit address has been programmed with a valid value
** Changed calling method when getting addresses: if the function call returns
**    an error, the address descriptor is invalid and no AdrDestroyAddress call
**    should be made.  If no error, caller must ALWAYS destroy the descriptor
**    when finished with it.
** Added: isEnabled parameter to StkGetAlarmLimit
** 
**    Rev 1.6   14 Feb 1992 13:20:42   brucea
** Removed: isTopDown var; replaced with extern stackSex
** Changed all references to isTopDown to (stackSex == HI_TO_LOW)
**
**    Rev 1.5   14 Feb 1992 11:37:06   brucea
** Removed: event notification defines and put them in stkservr.h
** Added: PRIVATE parameters for linenum information
** Implemented: StkCalcSymbolicInfo and all the Get routines for symbol info
** Removed U32 pattern from parameters in StkSetStackToPattern
** 
**    Rev 1.4   13 Feb 1992 17:14:40   brucea
** Took out extern GetCurrentPC
** Fixed bugs
** 
**    Rev 1.3   13 Feb 1992 16:28:14   brucea
** StkCalcHighWaterMark and StkSetStackPattern implemented
** 
**    Rev 1.2   12 Feb 1992 09:02:10   brucea
** Implemented more of the context functions
** 
**    Rev 1.1   10 Feb 1992 15:25:22   brucea
** Initial implementation
** 
**    Rev 1.0   24 Jan 1992 12:08:46   brucea
** Initial revision.
**
** $Header:   S:/tbird/mt2_68k/stkservr/stkcntxt.c_v   1.0   13 Feb 1997 09:04:58   gene  $
**
** Copyright (C) 1991 Microtek International.  All rights reserved.
**
**--------------------------------------------------------------------------*/

		       /****************************
			*                          *
			*       INCLUDE FILES      *
			*                          *
			****************************/

#ifndef _STKCNTXT_
#include "stkcntxt.h"
#endif

#ifndef _STKERRS_
#include "stkerrs.h"
#endif

#ifndef _STKUTIL_
#include "stkutil.h"
#endif

#ifndef _EVENTS_
#include "events.h"
#endif

#ifndef _LDRSVR_
#include "ldrsvr.h"
#endif;

#ifndef __STRING_H
#include <string.h>
#endif

#include <math.h>

#ifdef __cplusplus

extern "C" {
#endif


		       /****************************
			*                          *
			*        EXTERNALS         *
			*                          *
			****************************/

extern U8 stackUnitSize;
extern STACK_TYPE stackSex;
extern HANDLE hLib;

		       /****************************
			*                          *
			*        DEFINITIONS       *
			*                          *
			****************************/

/* maximum number of stacks supported; must fit in a U8 size */
#define MAX_NUMBER_OF_STACKS 50

/* max number of bytes making up a stack work */
#define NULL_CONTEXT 0L

#define STACK_PATTERN_LENGTH 2

		       /****************************
			*                          *
			*     LOCAL PROTOTYPES     *
			*                          *
			****************************/
			   
DESCRIPTOR StkGetDefaultSessionId(VOID);
RETCODE PRIVATE StkGetStackInfoFromLoader(VOID);
RETCODE EXPORT StkCallback(U32 eventNum);
RETCODE PRIVATE StkValidateStackArea(U32 stackStart, U32 *stackSizeParam);

		       /***************************
			*                         *
			*    PRIVATE VARIABLES    *
			*                         *
			***************************/

PRIVATE BOOLEAN initStkAlready = FALSE;

/* handle to TMalloc'ed data; 0 if no stacks yet allocated */
PRIVATE STK_CONTEXT *firstStkContext;

/* handle to the default stack */
STK_CONTEXT *defaultContext = NULL_CONTEXT;
DESCRIPTOR defaultSessionId = NULL;

/* maximum address allowed for stack */
U32 stkMaxAddress;
/* processor type - needed for processing frame types */
PROC_CPU cpu;
/* default stack size - minimum stack area a variable can take up */
U32 defaultStackSize;

/* symbolic context of present Program Counter */
DESCRIPTOR     stkCurrentPCAddrDesc;  /* address desc */
PRIVATE SYM_DESCRIPTOR stkModuleDesc;
PRIVATE SYM_DESCRIPTOR stkFunctionDesc;
PRIVATE SYM_DESCRIPTOR stkLowestLevelDesc;
PRIVATE U32 stkSymbolOffset;  /* distance from currentPC to found symbol */

/* current line number information; values = 0 if none found */
PRIVATE LINENUM_TYPE stkCurrentLinenum;
PRIVATE COLUMN_TYPE  stkCurrentColumn;
PRIVATE LINENUM_DESCRIPTOR stkLinenumIndex;

PRIVATE const U16 stackSearchPattern = 0x55AA;

PRIVATE const U32 stackFillPattern   = 0x55AA55AAL;

/* contains rounding masks for the stack address */
PRIVATE U32 roundingMask[] = {0x00000000L,
			      0x00000000L,
			      0x00000000L,  /* should never occur */
			      0x00000000L};

/* the constant "4" below must be modified if roundingMask type size (U32)
   changes */
#define MAX_STACK_BYTES (sizeof(roundingMask) / 4)

/* Global variables to hold PWRVIEWS.INI StackInfo */
U32 defaultStackSize;    
DESCRIPTOR iniStkBaseAddr;                                                   
U32 iniStkSize;                                                              
U32 iniStkPercentAlarm = 95;
BOOLEAN iniStkEnableAlarm = FALSE;
BOOLEAN iniStkEnableHWM = FALSE;

			/**************************** 
			 *                          *
			 *     EXECUTABLE CODE      *
			 *                          *
			 ****************************/

/*------------------------------------------------------------------------
** StkInitStackContext
**
** Purpose:
**    Initialize all the PRIVATE variables used in this module; only called
**    once when DLL loaded.  Also registers to receive event notification
**
** Input parameters: none
**
** Output parameters: none
*/
/*------------------------------------------------------------------------*/
RETCODE StkInitStackContext()  {
   DESCRIPTOR descEvt;     /* unused return param from EnlRegister */
   FARPROC myCallback;
   RETCODE err;

   if (!initStkAlready) {
      firstStkContext  = NULL_CONTEXT;
      defaultContext   = NULL_CONTEXT;
      defaultSessionId = NULL;

      /* create an address and init to all ones; used to compare with
	 current PC to determine whether to calculate symbolic info
	 for new PC */
      if ((err = CpuGetPC(&stkCurrentPCAddrDesc)) != GOOD)
	 return err;
      
      if ((err = AdrGetMaxInputAddrOffset(stkCurrentPCAddrDesc,
	    &stkMaxAddress)) != GOOD)
	 return err;
      if ((err = AdrSetAddrOffset(stkCurrentPCAddrDesc, stkMaxAddress))
	 != GOOD) return err;

      if((err=ProcReturnCpu(&cpu))!=GOOD) return(err);

      if( (err=ProcDefaultStackSize(&defaultStackSize))!= GOOD )
	 return(err);

      /* initialize the rounding mask values based on the
      ** maximum address allowed for the stack
      */
      roundingMask[0] = stkMaxAddress;
      roundingMask[1] = stkMaxAddress - 1;
      roundingMask[2] = stkMaxAddress - 1;
      roundingMask[3] = stkMaxAddress - 3;

      stkModuleDesc = NULL_SYMBOL;
      stkFunctionDesc = NULL_SYMBOL;
      stkLowestLevelDesc = NULL_SYMBOL;

      myCallback = MakeProcInstance((FARPROC)StkCallback,hLib);
      /* register to receive events from ... */
      if ((err = EnlRegister(EVENT_CPU_SP_EDIT,
			     (EVCALLBACK) myCallback,
			     &descEvt))!=GOOD)
	 return err;
      if ((err = EnlRegister(EVENT_CPU_FP_EDIT,
			     (EVCALLBACK) myCallback,
			     &descEvt))!=GOOD)
	 return err;
      if ((err = EnlRegister(EVENT_CPU_PC_EDIT,
			     (EVCALLBACK) myCallback,
			     &descEvt))!=GOOD)
	 return err;
      if ((err = EnlRegister(EVENT_MEM_HALTED,                    
			     (EVCALLBACK) myCallback,
			     &descEvt))!=GOOD)
	 return err;
      if ((err = EnlRegister(EVENT_SYMBOL_INIT_LOAD,
			     (EVCALLBACK) myCallback,
			     &descEvt))!=GOOD)
	 return err;
      if ((err = EnlRegister(EVENT_SYMBOL_DELETED,
			     (EVCALLBACK) myCallback,
			     &descEvt))!=GOOD)
	 return err;
      if ((err = EnlRegister(EVENT_LDR_STACKTOP,
			     (EVCALLBACK) myCallback,
			     &descEvt))!=GOOD)
	 return err;
      /* Restore the PWRVIEWS.INI StackInfo values */
      err = StkRestoreIniValues();
      initStkAlready = TRUE;
   }
   return GOOD;
}  /* end of StkInitStackContext */

/*------------------------------------------------------------------------
** StkCreateStack
**
** Purpose:
**   Allocates memory for a stack context, initializes it, then links it with
**   the other context linked list.
*/
/*------------------------------------------------------------------------*/
RETCODE StkCreateStack(DESCRIPTOR *stkSessionId)  {
   STK_CONTEXT *contextPtr;
   RETCODE     err;

   /* If there is a default session use it, else create a new one  */
   if ((defaultContext != NULL_CONTEXT) &&
       (defaultSessionId == (DESCRIPTOR) defaultContext)) {
      *stkSessionId = defaultSessionId;
      return(GOOD);
   }
   /* Allocate a new context and add to list */
   if (!(contextPtr = (STK_CONTEXT *)TMalloc(sizeof(STK_CONTEXT))))
      return ER_OUT_OF_MEMORY;
   defaultContext = contextPtr;
   defaultSessionId = (DESCRIPTOR) contextPtr;
   /* return the descriptor in output param */
   *stkSessionId = (DESCRIPTOR)contextPtr;

   /* NOTES: init all bytes to 0, then fill in non-zero values 
   ** all BOOLEANs set to FALSE,
   ** all descriptors set to 0 (NULL_SYMBOL) for SYM_DESCRIPTORs,
   ** sets stack name to null
   */
   memset(contextPtr, 0, sizeof(STK_CONTEXT));
   /* insert addr of context memory into context for later validity check */
   contextPtr->sessionId = *stkSessionId;
   contextPtr->isHighWaterMarkEnabled = FALSE;
   contextPtr->isStkFilled = FALSE;
   /* add to front of linked list */
   if (firstStkContext == NULL_CONTEXT)  {
      firstStkContext = contextPtr;
      /* nextContext and prevContext have already been initialized to 0 */
   } else {
      /* link in new context at front of list. Set new context next pointer
      ** to what the first context pointer presently points to
      */
      (contextPtr->nextContext) = firstStkContext;
      /* set existing context previous pointer to the new context */
      firstStkContext->prevContext = contextPtr;
      /* set firstStkContext pointer to the new context */
      firstStkContext = contextPtr;
      /* notice that the new context previous pointer remains zero (null) */
   }
   /* create an alarm limit address so code always duplicates it to
   ** return it
   */
   if (GOOD != (err = AdrDuplicateAddress(iniStkBaseAddr,
					  &(contextPtr->alarmLimitAddr))))
      return err;
   if (GOOD != (err = AdrDuplicateAddress(iniStkBaseAddr,
					  &(contextPtr->highWaterMarkAddr))))
      return err;
   /* Set the new stack context with the PWRVIEWS.INI values */
   if ((err = StkInitWithIniValues(*stkSessionId)) != GOOD)
      return(err);
   return GOOD;
}  /* end of StkCreateStack */

/*------------------------------------------------------------------------
** StkDestroyStack
**
** Purpose:
**   Removes stack context from linked list (and relinks list), then frees the
**   memory and save the stack context data to global INI varaibles.
**   NOTES: DO NOT CALL THIS FUNCTION DIRECTLY - ONLY ONE STACK SESSION IS
**   AVAILABLE.
*/
/*------------------------------------------------------------------------*/
RETCODE StkDestroyStack(DESCRIPTOR stkSessionId)  {
   RETCODE err;
   STK_CONTEXT *contextPtr;
   U32 offset;
   
   contextPtr = (STK_CONTEXT *)stkSessionId;
   if (stkSessionId == defaultSessionId) {
      defaultSessionId = NULL; /* destroy the default session */
      defaultContext = NULL_CONTEXT;
   }
   if ((contextPtr->sessionId) != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;

   /* Save the content of the stack context to INI values */
   if (contextPtr->stackBaseAddr && 
       (((err = AdrGetAddrOffset(contextPtr->stackBaseAddr, &offset)) != GOOD)
	 || ((err = AdrSetAddrOffset(iniStkBaseAddr, offset)) != GOOD)))
      return err;
   iniStkSize = contextPtr->stackSize;
   iniStkPercentAlarm = contextPtr->alarmLimitPercent;
   iniStkEnableAlarm = contextPtr->isAlarmLimitEnabled;
   iniStkEnableHWM = contextPtr->isHighWaterMarkEnabled;
   /* Destroy address descriptors in the context */
   if ((err = AdrDestroyAddress(contextPtr->alarmLimitAddr)) != GOOD)
      return err;
   if ((err = AdrDestroyAddress(contextPtr->highWaterMarkAddr)) != GOOD)
      return err;
   if (contextPtr->stackBaseAddr != NULL) {
      if ((err = AdrDestroyAddress(contextPtr->stackBaseAddr)) != GOOD)
	 return err;
   }
   /* remove context from doubly linked list before freeing the memory */
   if (firstStkContext == contextPtr) {
      /* context is first in list */
      firstStkContext = (contextPtr->nextContext);
      if (firstStkContext != NULL_CONTEXT)
	 firstStkContext->prevContext = NULL_CONTEXT;
   } else {
      /* reattach previous pointer of next neighbor context if it exists */
      if (contextPtr->nextContext != NULL_CONTEXT) {
	 /* there is a next neighbor */
	 ((STK_CONTEXT *)(contextPtr->nextContext))->prevContext =
	 contextPtr->prevContext;
      }
      /* reattach previous neighbor's next context to next neighbor */
      ((STK_CONTEXT *)(contextPtr->prevContext))->nextContext =
      contextPtr->nextContext;
   }
   if ((err = TFree((LPSTR)contextPtr)) != GOOD) return err;
   return GOOD;
}  /* end of StkDestroyStack */

/*--------------------------------------------------------------------------
** StkCalcAlarmState
**
** Purpose:
**    calculate and fill in alarmLimitReached variable
**
** Input parameters:
**  stkSessionId: which stack to process
**
** Output parameters:
**    PRIVATE var alarmLimitReached: sets to new value
** 
** Pseudocode:
**    Check that the alarm limit is inside the user-specified stack range
**    Search target stack area for <pattern>
**    May want to make search easier by using start and end addresses that
**    are long word even
*/
/*------------------------------------------------------------------------*/
RETCODE StkCalcAlarmState(DESCRIPTOR stkSessionId)  {

   RETCODE     err = GOOD;
   STK_CONTEXT *contextPtr;
   DESCRIPTOR  currentStackAddrDesc;
   U32         currentStackAddr, alarmLimit;
   BOOLEAN     inRange;

   contextPtr = (STK_CONTEXT *)stkSessionId;

   /* check for valid session id */
   if (contextPtr->sessionId != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;

   /* don't calculate if disabled; set alarm state to off */
   if (!(contextPtr->isAlarmLimitEnabled))  {
      contextPtr->alarmLimitReached = FALSE;
      return GOOD;
   }

   /* check for alarm limit reached or not */

   /* get stack pointer */
   if (GOOD != (err = CpuGetSP(&currentStackAddrDesc)))
      return err;                                                  

   /* if current stack pointer not in stack range, return with no error */
   if (GOOD != (err = StkCheckAddrInStackRange(stkSessionId,
					       currentStackAddrDesc,
					       &inRange)))
      goto CLEANUP;
   if (!inRange) {
      err = GOOD;
      goto CLEANUP;
   }

   /* get the address offsets of stack pointer and alarm limit to compare */
   if (GOOD !=
      (err = AdrGetAddrOffset(currentStackAddrDesc, &currentStackAddr)))
      goto CLEANUP;
   if (GOOD !=
      (err = AdrGetAddrOffset(contextPtr->alarmLimitAddr,&alarmLimit)))
      goto CLEANUP;

   contextPtr->alarmLimitReached = FALSE;
   if (stackSex == HI_TO_LOW) {
      if (currentStackAddr <= alarmLimit) {
	 contextPtr->alarmLimitReached = TRUE;
      }
   } else {
      if (currentStackAddr >= alarmLimit) {
	 contextPtr->alarmLimitReached = TRUE;
      }
   }
CLEANUP:
   if (!err) {
      if (GOOD != (err = AdrDestroyAddress(currentStackAddrDesc)))
	 return err;
   }
   else
      AdrDestroyAddress(currentStackAddrDesc);

   return err;
}  /* end of StkCalcAlarmState */

/*------------------------------------------------------------------------
** StkCalcHighWaterMark
**
** Purpose:
**    fill in highWaterMark variable
**
** Input parameters:
**  stkSessionId: which stack to process
**
** Output parameters:
**    PRIVATE var highWaterMark: sets to new value
**
** Error:
**
** Pseudocode:
**    Search target stack area for <pattern>
**    May want to make search easier by using start and end addresses that
**    are long word even
*/
/*------------------------------------------------------------------------*/
RETCODE StkCalcHighWaterMark(DESCRIPTOR stkSessionId)  {

   RETCODE err = GOOD;
   STK_CONTEXT *contextPtr;
   DESCRIPTOR startDesc = NULL,      /* destroy these descriptors */
	      endDesc = NULL,
	      stackPointer = NULL;
   DESCRIPTOR tempDesc = NULL;      /* inherited desc, do not destroy */
   BOOLEAN searchFound;
   ADDR_SPACE space;
   
#define IN_RANGE searchFound   /* alias to reuse variable */

   contextPtr = (STK_CONTEXT *)stkSessionId;

   /* check for valid session id */
   if (contextPtr->sessionId != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;

   if (contextPtr->isHighWaterMarkEnabled)  {  /* return if not enabled */

      /* find the high water mark */

      /*
      **  Stack search algorithm:
      **
      **  If HWM not already calculated, search for our pattern starting at
      **  the stack base searching toward the stack top.  If already
      **  calculated, start the search at either the previous HWM or the
      **  stack pointer, whichever is closer to the stack top.
      */

      /* clone the stack base */
      if ((err = AdrDuplicateAddress(contextPtr->stackBaseAddr,&endDesc))
	 != GOOD) return(err);
      if ((err = CpuGetSP(&stackPointer)) != GOOD) {
	 AdrDestroyAddress(endDesc);
	 return(err);
      }

      if (stackSex == HI_TO_LOW)  {
	 if ((err = AdrSubtractFromAddress(endDesc,contextPtr->stackSize))
	    != GOOD) goto CLEANUP;  /* stack top */
	 if (contextPtr->isHighWaterMarkCalculated) {
	    ADDR_COMPARE addrCompare;
	    if ((err=AdrCompareAddresses(stackPointer,
	       contextPtr->highWaterMarkAddr, &addrCompare)) != GOOD)
	       goto CLEANUP;
	    if (addrCompare == SECOND_ADDR_GREATER) 
	       tempDesc = stackPointer;
	    else
	       tempDesc = contextPtr->highWaterMarkAddr;
	 } else {  /* Not calculated yet...start search at stack base */
	    tempDesc = contextPtr->stackBaseAddr;
	 }
      } else {
	 if ((err = AdrAddToAddress(endDesc, (contextPtr->stackSize-1)))
	    != GOOD) goto CLEANUP;
	 if (contextPtr->isHighWaterMarkCalculated) {
	    ADDR_COMPARE addrCompare;
	    if ((err=AdrCompareAddresses(stackPointer,
	       contextPtr->highWaterMarkAddr, &addrCompare)) != GOOD)
	       goto CLEANUP;
	    if (addrCompare == FIRST_ADDR_GREATER) 
	       tempDesc = stackPointer;
	    else
	       tempDesc = contextPtr->highWaterMarkAddr;
	 } else {  /* Not calculated yet...start search at stack base */
	    tempDesc = contextPtr->stackBaseAddr;
	 }
      }
      if ((err = AdrDuplicateAddress(tempDesc,&startDesc)) != GOOD)
	  goto CLEANUP;
      if (((err = StkGetAddrSpace(&space)) != GOOD) ||
	  ((err = AdrSetAddrSpace(startDesc, space)) != GOOD) ||
	  ((err = AdrSetAddrSpace(endDesc, space)) != GOOD))
	 goto CLEANUP;
      if (GOOD != (err =
	 MemSearchSized(startDesc,            /* start address */
			endDesc,              /* length */
			FALSE,                /* means looking for pattern */
			(LPU8)&stackSearchPattern, /* not NULL terminated */
			sizeof(stackSearchPattern),
			&searchFound, WORD_SIZE)))
	 goto CLEANUP;
      if (!searchFound)  {    /* Stack is full */
	 if ((err = AdrCopyAddress(endDesc,startDesc)) != GOOD)
	    goto CLEANUP;  
      }
      /* We have found the first *unused* location.  The last *used* location
      ** is the HWM.  Add 1 to get this. */
      if (stackSex == HI_TO_LOW)  {
	 if ((err = AdrAddToAddress(endDesc, 1)) != GOOD) goto CLEANUP;
      } else {
	 if ((err = AdrSubtractFromAddress(endDesc, 1)) != GOOD) goto CLEANUP;
      }

      /* check for valid return value; should never be bad because search
	 is done on the stack boundary limits */
      if (GOOD != (err = StkCheckAddrInStackRange(stkSessionId,
						  startDesc,
						  &IN_RANGE)))
	 goto CLEANUP;
      if (!IN_RANGE) {
	 err = ER_STK_INVALID_HWM;
	 goto CLEANUP;
      }

      /* copy new hwm address into context */
      if ((err = AdrCopyAddress(startDesc,contextPtr->highWaterMarkAddr))
	 != GOOD) goto CLEANUP;

      contextPtr->isHighWaterMarkCalculated = TRUE;

CLEANUP:
      if (!err) {
	 if (endDesc != NULL)
	    if ((err = AdrDestroyAddress(endDesc)) != GOOD) return err;
	 if (startDesc != NULL)      
	    if ((err = AdrDestroyAddress(startDesc)) != GOOD) return err;
	 if (stackPointer != NULL)   
	    if ((err = AdrDestroyAddress(stackPointer)) != GOOD) return err;
      }
      else {
	 if (endDesc != NULL)
	    AdrDestroyAddress(endDesc);
	 if (startDesc != NULL)
	    AdrDestroyAddress(startDesc);
	 if (stackPointer != NULL)
	    AdrDestroyAddress(stackPointer);
      }
   }
   return err;
}  /* end of StkCalcHighWaterMark */


/*------------------------------------------------------------------------
** StkCalcSymbolicInfo
**
** Purpose:
**    maps current PC to symbols and saves symbol descriptors for
**    any caller to access.
**    CHANGE: uses private variable holding last-calculated address to
**    compare against.  If current PC is same, function returns.  If
**    different, symbolics are calculated for new address.
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkCalcSymbolicInfo()  {

   DESCRIPTOR     currentPCDesc;
   RETCODE        err = GOOD;
   RETCODE        firstErr = GOOD;
   MEM_ADDR_CLASS memoryClass;
   SYM_TYPE_TYPE  symbolType;
   DESCRIPTOR     dummyAddrDesc;
   ADDR_COMPARE   addrCompareResult;

   if (GOOD != (err = CpuGetPC(&currentPCDesc)))
      goto CLEANUP;

   if (GOOD != (err = AdrCompareAddresses(currentPCDesc,
					  stkCurrentPCAddrDesc,
					  &addrCompareResult))) {
      AdrDestroyAddress(currentPCDesc);
      goto CLEANUP;
   }
   if (EQUAL_ADDRS == addrCompareResult)  {  /* don't look up if already
						done */
      if ((err = AdrDestroyAddress(currentPCDesc)) != GOOD)
	 goto CLEANUP;
      return GOOD;
   }
   /* addresses not same; save new address for next comparison */
   if (GOOD != (err = AdrDestroyAddress(stkCurrentPCAddrDesc)))
      goto CLEANUP;
   stkCurrentPCAddrDesc = currentPCDesc;  /* save returned addr from CPU */

   err = SymMapAddr2Symbol(currentPCDesc,
			   &memoryClass,
			   &symbolType,
			   &stkSymbolOffset,
			   &stkLowestLevelDesc,
			   &stkFunctionDesc,
			   &stkModuleDesc);
   if (GOOD != err) {
      goto CLEANUP;
   }
   if ((stkLowestLevelDesc == NULL_SYMBOL) ||
       (stkModuleDesc      == NULL_SYMBOL)) {
      goto CLEANUP;
   }
   /* look up linenum stuff */
   if (GOOD != (err = AdrDuplicateAddress(currentPCDesc, &dummyAddrDesc)))
      goto CLEANUP;
   firstErr = SymMapAddr2Linenum(currentPCDesc,
				 stkModuleDesc,
				 &stkCurrentLinenum,
				 &stkCurrentColumn,
				 dummyAddrDesc,
				 &stkLinenumIndex);
   err = AdrDestroyAddress(dummyAddrDesc);

CLEANUP:
   if (GOOD == firstErr)
      firstErr = err;
   if (GOOD != firstErr) {  /* clear out vars if error occurred */
      stkModuleDesc = NULL_SYMBOL;
      stkFunctionDesc = NULL_SYMBOL;
      stkLowestLevelDesc = NULL_SYMBOL;
      stkSymbolOffset = 0L;
      stkCurrentLinenum = 0;
      stkCurrentColumn  = 0;
      stkLinenumIndex   = 0;
   }
   if (ER_ADDRESS_NOT_FOUND == firstErr) {
      return GOOD;   /* don't return error if address not found in symbol
			table */
   }
   return firstErr;
}  /* end of StkCalcSymbolicInfo */

/*------------------------------------------------------------------------
**  StkCallback
**
**  Purpose: is called when event that the stack server has registered on
**           occurs
**
-------------------------------------------------------------------------*/
RETCODE EXPORT StkCallback(U32 eventNum) {

   RETCODE err;
   BOOLEAN reached, oredBoolean;
   STK_CONTEXT *sessionId;

   switch (eventNum) {
      /* CPU registers modified (caused by break or user modifying regs) */
      case EVENT_CPU_SP_EDIT:
      case EVENT_MEM_HALTED:

	 /* calculate alarm state; this needs to be ahead of the stack
	    changed event notify in case any notifyees request their
	    alarm state */
	 for (sessionId = firstStkContext;
	      sessionId != NULL_CONTEXT;
	      sessionId = (STK_CONTEXT *)sessionId->nextContext) {
	    StkCalcAlarmState((DESCRIPTOR)sessionId); /* any errors ignored */
	 }
	 /* no break here let this event fall through to complete its
	 ** purpose.
	 */
      case EVENT_CPU_FP_EDIT:
      case EVENT_CPU_PC_EDIT:
	 if (eventNum == EVENT_MEM_HALTED) {
	    if ((err = ReleasePreviousFrames()) != GOOD)
	       return err;
	    if ((err = EnlEventNotify(EVENT_STK_HALTED)) != GOOD)
	       return err;
	 }
	 else {
	    /* notify interested parties that the stack has changed */
	    if ((err = EnlEventNotify(EVENT_STK_STACK_CHANGED)) != GOOD)
	       return err;
	 }
	 /* check if any alarm limit has fired */
	 oredBoolean = FALSE;
	 for (sessionId = firstStkContext;
	      sessionId != NULL_CONTEXT;
	      sessionId = (STK_CONTEXT *)sessionId->nextContext) {
	    StkGetAlarmState((DESCRIPTOR)sessionId, &reached);
	    oredBoolean |= reached;
	 }
	 if (oredBoolean)  {
	    if ((err = EnlEventNotify(EVENT_STK_ALARM_OVER_LIMIT)) != GOOD)
	       return err;
	 }
	 break;

      case EVENT_SYMBOL_INIT_LOAD:
      case EVENT_SYMBOL_DELETED:
	 /* reset comparison address for symbolic context info to force
	    new calculation when symbolic info requested */
	 if ((err = AdrSetAddrOffset(stkCurrentPCAddrDesc, stkMaxAddress))
	    != GOOD) return err;

	 /* notify interested parties that the stack display should be
	    updated */
	 if ((err = EnlEventNotify(EVENT_STK_STACK_CHANGED)) != GOOD)
	    return err;
	 break;

      case EVENT_LDR_STACKTOP:
	 /* Extract stack base and size from the loader and set the default
	 ** session with these value. - If there is no default session, create
	 ** one.
	 */
	 if ((err = StkGetStackInfoFromLoader()) != GOOD)
	    return(err);
	 /* notify interested parties that the stack base and size should be
	    updated */
	 if ((err = EnlEventNotify(EVENT_STK_BASE_SIZE_AVAIL)) != GOOD)
	    return err;
	 /* Drop through */
   /* default is to return GOOD */
   }
   return GOOD;
}

/*------------------------------------------------------------------------
** StkCheckAddrInStackRange
**
** Purpose:
**    Checks the input address (offset value) to see if it is within
**    the range of the stack as defined in the context variables.
**    Various error codes are returned depending on the result of the test.
**    Returns GOOD if address inside stack range.
**
** Input parameters:
**    stkSessionId: which stack context
**    addr: standard address descriptor of address offset to compare
*/
/*------------------------------------------------------------------------*/
RETCODE StkCheckAddrInStackRange(DESCRIPTOR stkSessionId,
				 DESCRIPTOR addr,
				 BOOLEAN    *inRange)  {
   RETCODE     err;
   U32         requestedLimit, stackBase;
   STK_CONTEXT *contextPtr;

   contextPtr = (STK_CONTEXT *) stkSessionId;
   if (!contextPtr->isValidStackBase)
      return ER_STK_STK_BASE_INVALID;
   /* check that the new address is inside the defined stack range */
   if (GOOD !=
      (err = AdrGetAddrOffset(addr, &requestedLimit)))
      return err;
   if (GOOD !=
      (err = AdrGetAddrOffset(contextPtr->stackBaseAddr, &stackBase)))
      return err;

   *inRange = TRUE;   /* initialize */
   if (stackSex == HI_TO_LOW) {
      if ((requestedLimit > stackBase) ||
	  (requestedLimit < (stackBase - contextPtr->stackSize)))
	  *inRange = FALSE;
   } else {
      if ((requestedLimit < stackBase) ||
	  (requestedLimit > (stackBase + contextPtr->stackSize)))
	  *inRange = FALSE;
   }
   return GOOD;
}

/*------------------------------------------------------------------------
** StkDisableAlarmLimit
**
** Purpose: Turns off the check for stack alarm limit
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkDisableAlarmLimit(DESCRIPTOR stkSessionId)  {
   STK_CONTEXT *contextPtr;

   /* check for valid session id */
   contextPtr = (STK_CONTEXT *)stkSessionId;
   if (contextPtr->sessionId != stkSessionId)
      return(ER_STK_INVALID_DESCRIPTOR);
   contextPtr->isAlarmLimitEnabled = FALSE;
   return(GOOD);
}  /* end of StkDisableAlarmLimit */

/*------------------------------------------------------------------------
** StkDisableHighWaterMark
**
** Purpose: Turns off the measurement of the stack high water mark
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkDisableHighWaterMark(DESCRIPTOR stkSessionId)  {
   STK_CONTEXT *contextPtr;

   /* check for valid session id */
   contextPtr = (STK_CONTEXT *)stkSessionId;
   if (contextPtr->sessionId != stkSessionId)
      return(ER_STK_INVALID_DESCRIPTOR);

   contextPtr->isHighWaterMarkEnabled = FALSE;
   contextPtr->isHighWaterMarkCalculated = FALSE;
   return(GOOD);
}  /* end of StkDisableHighWaterMark */                             

/*------------------------------------------------------------------------
** StkEnableAlarmLimit
**
** Purpose: Turns on the check for stack alarm limit
**
** Design:
**  If alarm limit address is outside defined stack for this session,
**  then do not allow alarm limit to be enabled.
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkEnableAlarmLimit(DESCRIPTOR stkSessionId)  {
   RETCODE err;
   BOOLEAN inRange;
   STK_CONTEXT *contextPtr;

   /* check for valid session id */
   contextPtr = (STK_CONTEXT *)stkSessionId;
   if (contextPtr->sessionId != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;
   if ((err = RecalcAlarmLimit(stkSessionId,
	   contextPtr->alarmLimitPercent)) != GOOD) return(err);
   
   if (GOOD != (err = StkCheckAddrInStackRange(stkSessionId,
					       contextPtr->alarmLimitAddr,
					       &inRange)))
      return err;
   if (!inRange)
      return ER_STK_INVALID_ALARM_LIMIT;
   contextPtr->isAlarmLimitEnabled = TRUE;
   return GOOD;
}  /* end of StkEnableAlarmLimit */

/*------------------------------------------------------------------------
** StkEnableHighWaterMark
**
** Purpose: Turns on the measurement of the stack high water mark
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkEnableHighWaterMark(DESCRIPTOR stkSessionId) {
   STK_CONTEXT *contextPtr;
   RETCODE err;
   DESCRIPTOR baseAddr;
   S16 queryStatus;
   
   /* check for valid session id */
   contextPtr = (STK_CONTEXT *)stkSessionId;
   if (contextPtr->sessionId != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;
   /* Check if the current stack area already filled, else filled it */
   if (!contextPtr->isStkFilled) {
      err = ER_STK_FILL_STACK_AREA; 
      /* ErrText takes care the display mode: Shell/Window */
      if ((err = ErrDisplayErrorEx(err, CHECK_MODE, MB_OKCANCEL |
		     MB_ICONQUESTION, (S16 FAR *)&queryStatus)) != GOOD)
	 return(err);
      /* Do nothing if cancel */
      if (queryStatus == IDCANCEL)
	 return(ER_STK_FILL_STACK_AREA); /* User abort so it's OK to return */
      if ((err = AdrDuplicateAddress(contextPtr->stackBaseAddr,&baseAddr))
	    != GOOD) return(err);
      if ((err = StkSetStackToPattern(baseAddr,contextPtr->stackSize)) != GOOD)
	 return(err); /* do not destroy baseAddr */
      contextPtr->isStkFilled = TRUE;       
   }
   contextPtr->isHighWaterMarkEnabled = TRUE;
   return GOOD;
}  /* end of StkEnableHighWaterMark */

/*------------------------------------------------------------------------
** StkGetAlarmLimit
**
** Purpose:
**    Get the address from which a warning is generated if, when the emulator
**    is halted, the stack usage has gone beyond
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkGetAlarmLimit(DESCRIPTOR stkSessionId,
				DESCRIPTOR *alarmLimit,
				U32        *percent,
				BOOLEAN    *isValid,
				BOOLEAN    *isEnabled)  {
   RETCODE err;
   STK_CONTEXT *contextPtr;

   /* check for valid DESCRIPTOR */
   contextPtr = (STK_CONTEXT *)stkSessionId;
   if (contextPtr->sessionId != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;
   /* NOTES: Alway return alarm limit even if it is not valid */
   if ((err = AdrDuplicateAddress(contextPtr->alarmLimitAddr, alarmLimit))
       != GOOD) return err;

   *percent = contextPtr->alarmLimitPercent;
   *isValid = contextPtr->isValidAlarmLimit;
   *isEnabled = contextPtr->isAlarmLimitEnabled;
   return GOOD;
}  /* end of StkGetAlarmLimit */

/*------------------------------------------------------------------------
** StkGetAlarmState
**
** Purpose:
**    Returns the current status of the alarm limit; StkCalcAlarmState
**    does the actual calculation     
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkGetAlarmState(DESCRIPTOR stkSessionId,
				BOOLEAN *localAlarmLimitReached)  {

   STK_CONTEXT *contextPtr;

   contextPtr = (STK_CONTEXT *)stkSessionId;

   /* check for valid session id */
   if (contextPtr->sessionId != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;
   *localAlarmLimitReached = contextPtr->alarmLimitReached;
   return GOOD;
}  /* end of StkGetAlarmState */

/*------------------------------------------------------------------------
** StkGetCurrentFunction
**
** Purpose:
**    Returns the SYM_DESCRIPTOR of the function that contains the current PC
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkGetCurrentFunction(SYM_DESCRIPTOR *currentFunction)  {

   RETCODE err;

   err = StkCalcSymbolicInfo();
   *currentFunction = stkFunctionDesc;
   return err;
}  /* end of StkGetCurrentFunction */


/*------------------------------------------------------------------------
** StkGetCurrentLinenum
**
** Purpose:
**    Returns the SYM_DESCRIPTOR of the lowest level in the symbol table
**    hierarchy that contains the current PC.  This will be a function, block,
**    or label.
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT
StkGetCurrentLinenum(LINENUM_TYPE       *currentLinenum,
		     COLUMN_TYPE        *currentColumn,
		     LINENUM_DESCRIPTOR *linenumIndex)  {
   RETCODE err;

   err = StkCalcSymbolicInfo();
   *currentLinenum = stkCurrentLinenum;
   *currentColumn  = stkCurrentColumn;
   *linenumIndex   = stkLinenumIndex;
   return err;
}  /* end of StkGetCurrentLinenum */

/*------------------------------------------------------------------------
** StkGetCurrentLowestlevel
**
** Purpose:
**    Returns the SYM_DESCRIPTOR of the lowest level in the symbol table
**    hierarchy that contains the current PC.  This will be a function, block,
**    or label.
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT
StkGetCurrentLowestlevel(SYM_DESCRIPTOR *currentLowestLevel,
			 U32            *currentSymbolOffset)  {
   RETCODE err;

   err = StkCalcSymbolicInfo();
   *currentLowestLevel  = stkLowestLevelDesc;
   *currentSymbolOffset = stkSymbolOffset;
   return err;
}  /* end of StkGetCurrentLowestLevel */


/*------------------------------------------------------------------------
** StkGetCurrentModule
**
** Purpose:
**    Returns the SYM_DESCRIPTOR of the module that contains the current PC
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkGetCurrentModule(SYM_DESCRIPTOR *currentModule)  {

   RETCODE err;

   err = StkCalcSymbolicInfo();
   *currentModule = stkModuleDesc;
   return err;
}  /* end of StkGetCurrentModule */

/*------------------------------------------------------------------------
** StkGetDefaultSessionId
**
** Purpose :
**    Return the default context in the form of a descriptor.
**
** Input parameters:
**    none
**
** Output parameters:
**    Returns a descriptor.
**
**-----------------------------------------------------------------------*/
DESCRIPTOR StkGetDefaultSessionId(VOID) {

   DESCRIPTOR sessionId;

   sessionId = (DESCRIPTOR)defaultContext;
   return(sessionId);
}


/*------------------------------------------------------------------------
** StkGetDescFromName
**
** Purpose:
**    Looks up the passed stack name and returns the descriptor assigned to it
*/
/*------------------------------------------------------------------------*/
RETCODE StkGetDescFromName(LPSTR stackName, DESCRIPTOR *stkSessionId)  {

   STK_CONTEXT *contextPtr;
   BOOLEAN found = FALSE;
   
   for (contextPtr = firstStkContext;
	contextPtr != NULL_CONTEXT;
	contextPtr = contextPtr->nextContext) {
      if (strcmp(stackName, (LPSTR)contextPtr->name) == 0)  {
	 *stkSessionId = (DESCRIPTOR)contextPtr;
	 found = TRUE;
	 break;
      }
   }
   if (!found)
      return(ER_STK_SESSION_NAME_NOT_FOUND);
   
   return GOOD;
}  /* end of StkGetDescFromName */

/*------------------------------------------------------------------------
** StkGetHighWaterMark
**
** Purpose:
**    returns the current stack high water mark or FALSE for <isValid>.
**    Does not calculate if the measurement is disabled otherwise this
**    call generates a calculation of the high water mark.
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkGetHighWaterMark(DESCRIPTOR stkSessionId,
				   DESCRIPTOR *highWaterMark,
				   BOOLEAN    *isEnabled)  {

   STK_CONTEXT *contextPtr;
   RETCODE err;

   contextPtr = (STK_CONTEXT *)stkSessionId;
   /* check for valid session id */
   if (contextPtr->sessionId != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;
   if (GOOD != (err = StkCalcHighWaterMark(stkSessionId)))
      return err;

   /* create an address, copy static baseAddr into created addr, then */
   /* return to caller */
   if ((err = AdrDuplicateAddress(contextPtr->highWaterMarkAddr,
				 highWaterMark)) != GOOD)
      return err;

   *isEnabled = contextPtr->isHighWaterMarkCalculated; /* ??? - Why */
   return GOOD;
}  /* end of StkGetHighWaterMark */


/*------------------------------------------------------------------------
** StkGetStackBase
**
** Purpose:
**    Returns value of the stack base address and its validity.
**    The initial value is unknown.
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkGetStackBase(DESCRIPTOR stkSessionId,
			       DESCRIPTOR *baseAddr,
			       BOOLEAN    *isValid)  {
   RETCODE err;
   STK_CONTEXT *contextPtr;

   contextPtr = (STK_CONTEXT *)stkSessionId;

   /* check for valid session id */
   if (contextPtr->sessionId != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;

   /* want assignment here, then test for TRUE */
   if ((*isValid = contextPtr->isValidStackBase) == TRUE) {
      /* create an address, copy static baseAddr into created addr, then */
      /* return to caller */
      if (GOOD !=
	 (err = AdrDuplicateAddress(contextPtr->stackBaseAddr, baseAddr)))
	 return err;
   }
   return GOOD;
}  /* end of StkGetStackBase */

/*------------------------------------------------------------------------
** StkGetStackDirection
**
** Purpose:
**    Returns the growth direction of the stack.
**
** Input parameters:
**    stkSessionId: stack selector
**
** Output parameters:
**    direction: a boolean indicating direction, FALSE = high to low memory,
**       TRUE = low to high memory.
**
** Error:
**    none
**
**-----------------------------------------------------------------------*/
RETCODE EXPORT StkGetStackDirection(DESCRIPTOR stkSessionId,
				    BOOLEAN *direction) {
   STK_CONTEXT *contextPtr;

   contextPtr = (STK_CONTEXT *)stkSessionId;

   /* check for valid session id */

   if ((contextPtr != NULL) && (contextPtr->sessionId != stkSessionId))
      return ER_STK_INVALID_DESCRIPTOR;

   *direction = stackSex;
   return GOOD;
}

/*------------------------------------------------------------------------
** StkGetStackSize
**
** Purpose: Returns size stack.  Initial value = 0;
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkGetStackSize(DESCRIPTOR stkSessionId,
			       U32 *stackSizeParam)  {
   STK_CONTEXT *contextPtr;

   contextPtr = (STK_CONTEXT *)stkSessionId;

   /* check for valid session id */
   if (contextPtr->sessionId != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;

   *stackSizeParam = ((STK_CONTEXT *)stkSessionId)->stackSize;
   return GOOD;

}  /* end of StkGetStackSize */

/*------------------------------------------------------------------------
** StkSetAlarmLimitPercent
**
** Purpose:  Set the alarm limit percent
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkSetAlarmLimitPercent(DESCRIPTOR stkSessionId,
				U32 alarmLimitPercent)  {
   /* Recalculate alarm limit */
   return(RecalcAlarmLimit(stkSessionId, alarmLimitPercent));
}  /* end of StkSetAlarmLimitPercent */

/*------------------------------------------------------------------------
** StkSetAlarmLimit
**
** Purpose:  Set the alarm limit address
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkSetAlarmLimit(DESCRIPTOR stkSessionId,
				DESCRIPTOR alarmLimitAddrParam)  {

   RETCODE err;
   STK_CONTEXT *contextPtr;
   U32 stackBase,limit;
   BOOLEAN inRange;


   contextPtr = (STK_CONTEXT *)stkSessionId;

   /* check for valid session id */
   if (contextPtr->sessionId != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;

   if (contextPtr->isAlarmLimitEnabled)  {
      /* check for valid address before modifying the alarm limit addr,
	 but only if alarm already enabled */
      if (GOOD != (err = StkCheckAddrInStackRange(stkSessionId,
						  alarmLimitAddrParam,
						  &inRange)))
	 return err;
      if (!inRange)
	 return ER_STK_INVALID_ALARM_LIMIT;
   }

   /* free existing alarm limit address and replace with passed-in
      descriptor */
   if (GOOD != (err = AdrDestroyAddress(contextPtr->alarmLimitAddr)))
      return err;
   contextPtr->alarmLimitAddr = alarmLimitAddrParam;
   contextPtr->isValidAlarmLimit = TRUE;

   /* Calculate the percentage and store this information */
   if ((contextPtr->isValidStackBase) && (contextPtr->stackSize > 0)) {
      if ((err = AdrGetAddrOffset(contextPtr->stackBaseAddr,
	    &stackBase)) != GOOD)
	 return err;
      if ((err = AdrGetAddrOffset(contextPtr->alarmLimitAddr,&limit)) != GOOD)
	 return err;
      /* Round up the percentage */
      if (stackSex == HI_TO_LOW) {
	 contextPtr->alarmLimitPercent = (U32)
	       ceil(((stackBase - limit)*100)/contextPtr->stackSize);
      }
      else {
	 contextPtr->alarmLimitPercent  = (U32)
	       ceil(((limit - stackBase)*100)/contextPtr->stackSize);
      }
   } /* !!! should return error if size = 0 or invalid baseAddr */
   return(GOOD);
   /* NOTE: the alarm limit is enabled with StkEnableAlarmLimit call, not here */
}  /* end of StkSetAlarmLimit */


/*------------------------------------------------------------------------
** StkSetStackBase
**
** Purpose: Set the stack base address
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkSetStackBase(DESCRIPTOR stkSessionId,
			       DESCRIPTOR baseAddr)  {

   RETCODE err;
   STK_CONTEXT *contextPtr;
   U32     percent, offset;
   DESCRIPTOR stkAddr;
   BOOLEAN isValid, isEnabled;

   contextPtr = (STK_CONTEXT *)stkSessionId;

   /* check for valid session id */
   if (contextPtr->sessionId != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;

   /* do not accept address if not on an even boundary based on the minimum
      writable stack size */
   if ((err = AdrGetAddrOffset(baseAddr, &offset)) != GOOD) return err;

   if (stackSex == HI_TO_LOW) {
      /* round down */
      offset &= roundingMask[(min(stackUnitSize, MAX_STACK_BYTES)-1)];
   } else {
      /* round up if any of the lower significant address values are 1 */
      offset = (offset + (stackUnitSize - 1)) &
	       roundingMask[(min(stackUnitSize, MAX_STACK_BYTES)-1)];
   }
   /* Validate if the new stack area is valid - adjust the size if necessary */
   if ((err = StkValidateStackArea(offset, &(contextPtr->stackSize))) != GOOD)
      return(err);
   
   /* Save the stack base to INI value */
   if (((err = AdrSetAddrOffset(baseAddr,offset)) != GOOD) ||
       ((err = StkSetAddrSpace(baseAddr)) != GOOD))
      return err; 
   if ((err = AdrSetAddrOffset(iniStkBaseAddr, offset)) != GOOD)
      return err;

   /* free existing stack address and replace with passed in descriptor */
   if (contextPtr->isValidStackBase)  {
      if ((err = AdrDestroyAddress(contextPtr->stackBaseAddr)) != GOOD)
	 return err;
   }
   contextPtr->stackBaseAddr = baseAddr;
   contextPtr->isValidStackBase = TRUE;
   contextPtr->isHighWaterMarkCalculated = FALSE;
   contextPtr->isStkFilled = FALSE;
   /* It might be changed thru validation, so better save it */
   iniStkSize = contextPtr->stackSize;
   
   /* If alarm limit is enabled the recalculate the alarm physical address */
   if (contextPtr->isAlarmLimitEnabled) {
      if ((err = StkGetAlarmLimit((DESCRIPTOR)contextPtr, &stkAddr, &percent,
	    &isValid,&isEnabled)) != GOOD)
	  return(err);
       if ((err = AdrDestroyAddress(stkAddr)) != GOOD)
	  return err;
       if ((err = RecalcAlarmLimit((DESCRIPTOR)contextPtr, percent)) != GOOD)
	  return err;
   }
   return GOOD;
}  /* end of StkSetStackBase */

/*------------------------------------------------------------------------
** StkSetStackSize
**
** Purpose:
**    Set up the stack size in bytes
**
** Error:
**    Returns errors if any arithmetic combination of
**       <baseAddr> and <stackSize> causes an overflow; i.e.
**       if STK_BOTTOM_UP and stackSize + baseAddr > 0xFFFFFFFF
**    or if STK_TOP_DOWN and stackSize - baseAddr < 0
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkSetStackSize(DESCRIPTOR stkSessionId, U32 stackSizeParam) {
   U32 percent, stackStart;
   RETCODE err;
   STK_CONTEXT *contextPtr;
   DESCRIPTOR stkAddr;
   BOOLEAN isValid, isEnabled;
   
   contextPtr = (STK_CONTEXT *)stkSessionId;

   /* check for valid session id */
   if (contextPtr->sessionId != stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;

   if (!(contextPtr->isValidStackBase))
      /* don't save the stack size if the base is no good */
      return ER_STK_STK_BASE_INVALID;

   if ((err = AdrGetAddrOffset(contextPtr->stackBaseAddr, &stackStart)) !=
      GOOD)  return err;

   if (!stackSizeParam) {
      stackSizeParam = iniStkSize; /* Use default stack size continue */
      ErrDisplayError(ER_STK_INVALID_STK_SIZE, CHECK_MODE);
   }
   
   /* Validate if the specified stack area is within physical memory range */
   if ((err = StkValidateStackArea(stackStart, &stackSizeParam)) != GOOD)
      return(err);
   
   /* round size to make stack range even boundary for the processor */
   stackSizeParam &= roundingMask[(min(stackUnitSize, MAX_STACK_BYTES)-1)];
   contextPtr->stackSize = stackSizeParam;
   /* It might be changed thru validation, so better save it */
   iniStkSize = stackSizeParam;
   contextPtr->isHighWaterMarkCalculated = FALSE;
   contextPtr->isStkFilled = FALSE;
   
   /* If alarm limit is enabled the recalculate the physical address */
   if (contextPtr->isAlarmLimitEnabled) {
      if ((err = StkGetAlarmLimit((DESCRIPTOR)contextPtr, &stkAddr, &percent,
	    &isValid,&isEnabled)) != GOOD) return(err);
      if ((err = AdrDestroyAddress(stkAddr)) != GOOD) return err;
      if ((err = RecalcAlarmLimit((DESCRIPTOR)contextPtr, percent)) != GOOD)
	 return err;
   }
   return GOOD;
}  /* end of StkSetStackSize */

/*------------------------------------------------------------------------
** StkSetStackToPattern
**
** Purpose:
**    Sets the target memory from the current stack pointer to the size
**    of the stack with a pattern that can be later searched for.  The actual
**    start address should be rounded up (or down depending on the type of
**    stack) to an even processor stack access boundary.
**    The stack size must also be rounded.
**    Initially, the stack pointer will equal the stack base, thus the
**    entire stack will be initialized.
**    if (sp in not in range)
**       fill from base to top
**    else
**       fill from sp to top
**    NOTE: this function does not modify the state of measuring the
**    high water mark; it stays enabled or disabled.
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkSetStackToPattern(DESCRIPTOR stackBaseAddrDesc,
				    U32 stackSize)  {
   RETCODE err1, err = GOOD;
   DESCRIPTOR stkAreaRangeDesc, currentStackAddrDesc;
   U32 stackEnd, stackBase, currentStack, stackFillArea, startPoint;
   DESCRIPTOR startDesc;
   BOOLEAN inRange = FALSE;

   if (stackSize == 0)
      return GOOD;
   /* Create a stack area address range */
   if ((err = AdrDuplicateAddress(stackBaseAddrDesc, &stkAreaRangeDesc))
	!= GOOD) {
      AdrDestroyAddress(stackBaseAddrDesc);
      return(err);
   }
   /* Make the range by subtract the size to get the start offset and set
   ** its length to stack size */
   if (((err = AdrSubtractFromAddress(stkAreaRangeDesc, stackSize)) != GOOD) ||
       ((err = AdrSetAddrRangeLength(stkAreaRangeDesc, stackSize)) != GOOD)) {
      AdrDestroyAddress(stkAreaRangeDesc);
      AdrDestroyAddress(stackBaseAddrDesc);
      return(err);
   }
   
   /* Get the current stack pointer and start from that point */
   if ((err = CpuGetSP(&currentStackAddrDesc)) != GOOD) {
      AdrDestroyAddress(stkAreaRangeDesc);
      AdrDestroyAddress(stackBaseAddrDesc);
      return err;
   }
   /* Verify that the current SP is within the stack area - inRange == TRUE */
   if ((err = AdrIsAddrInRange(currentStackAddrDesc, stkAreaRangeDesc,
			       &inRange)) != GOOD) {
      goto CLEANUP1;
   }
   /* if it within range - get the current stack address */
   if ((inRange) && ((err = AdrGetAddrOffset(currentStackAddrDesc,
					     &currentStack)) != GOOD)) {
      goto CLEANUP1;
   }
   /* get the stack base address */
   if ((err = AdrGetAddrOffset(stackBaseAddrDesc, &stackBase)) != GOOD)
      goto CLEANUP1;

   /*
   ** calculate the stack end address, starting point of fill,
   ** and stack length
   ** Remember that the fill is not inclusive of the stackbase
   ** where ever it is.
   ** adjust the stack pointer to skip over the data it currently
   ** is pointing to.
   */
   if (stackSex == HI_TO_LOW) {
      stackEnd = stackBase - stackSize;
      startPoint = stackEnd;
      /* Only use the SP when it's in range */
      if (inRange) {
	 stackFillArea = currentStack - stackEnd;
      }
      else
	 stackFillArea = stackSize;
   } else {
      stackEnd = stackBase + stackSize;
      /* Only use the SP when it's in range */
      if (inRange) {
	 stackFillArea = stackEnd - currentStack;
	 startPoint = currentStack;
      }
      else {
	 stackFillArea = stackSize;
	 startPoint = stackBase;
      }
   }
   if (stackFillArea > stackSize) {
      err = ER_STK_INIT_STK_FAIL;
      goto CLEANUP1;
   }
   /* put start address into form that MemFill requires */
   if ((err = AdrDuplicateAddress(stkAreaRangeDesc, &startDesc)) != GOOD)
      goto CLEANUP1; 
   if ((err = AdrSetAddrOffset(startDesc, startPoint)) != GOOD) goto CLEANUP;
 
   if ((err = StkSetAddrSpace(startDesc)) != GOOD)  goto CLEANUP;

   /* Fill stack using DWORD AccessSize for maximum performance */
   err = MemFillSized(startDesc,
		      stackFillArea,
		      (LPU8)&stackFillPattern,
		      sizeof(stackFillPattern), DWORD_SIZE);

CLEANUP:
   err1 = AdrDestroyAddress(startDesc);
CLEANUP1:
   err1 = AdrDestroyAddress(currentStackAddrDesc); 
   err1 = AdrDestroyAddress(stackBaseAddrDesc); 
   err1 = AdrDestroyAddress(stkAreaRangeDesc); 
   return((err != GOOD) ? err : err1);
}  /* end of StkSetStackToPattern */

/*------------------------------------------------------------------------
** StkGetStackInfoFromLoader
**
** Purpose:
**    Retrieve stack information (stack top and size) from the loader and
**    set the default stack session with these info.  If there is no default
**    stack session avaliable, then create one.  Upon receing stack info, check
**    for the current setting of the stack HWM, if it's enabled then fill the
**    stack else set flag for isStkFilled for later stack filling.
**    Initially, the stack pointer will equal the stack base, thus the
**    entire stack will be initialized.
**    if (sp is not in range)
**       fill from base to top
**    else
**       fill from sp to top
**    NOTES: if stack base or size is invalid, use the ini values.
*/
/*------------------------------------------------------------------------*/
RETCODE PRIVATE StkGetStackInfoFromLoader(VOID)  {
   RETCODE err = GOOD;
   DESCRIPTOR  baseAddr, spAddr;
   DESCRIPTOR stkSessionId;
   U32 stkSize;
   
   if ((err = InitSessionForCli(&stkSessionId)) != GOOD)
      return(err);
   /* Create address for stack base */
   if ((err = CpuGetSP(&baseAddr)) != GOOD) return(err);
   /* Call the loader the get stack info */
   if ((err = LdrGetStack((DESCRIPTOR FAR *)&baseAddr, &stkSize)) != GOOD) {
      AdrDestroyAddress(baseAddr);
      return(err);
   }
   /* Set the current stack pointer to the stack base.
   ** CpuSetSP() consumes spAddr
   */
   if (((err = AdrDuplicateAddress(baseAddr, &spAddr))
	 != GOOD) || ((err = CpuSetSP(spAddr)) != GOOD))
      return(err);

   /* StkSetStackBaseAndSize() consumes the baseAddr and save to INI values */
   return(StkSetStackBaseAndSize(stkSessionId, baseAddr, stkSize));
} /* end of StkGetStackInfoFromLoader*/

/*----------------------------------------------------------------------------
** StkSetStackBaseAndSize
**
** Purpose:
**    Set the current stack session with base and size. Check
**    for the current setting of the stack HWM, if it's enabled then fill the
**    stack else set flag for isStkFilled for later stack filling.
**    Initially, the stack pointer will equal the stack base, thus the
**    entire stack will be initialized.  Also, it will recalculate the Alarm
**    limit of the new Stack base and size.
**    if (sp == 0)
**       fill from base to top
**    else
**       fill from sp to top
**    NOTES: baseAddr will be used or destroyed by this function even if error.
**
-----------------------------------------------------------------------------*/
RETCODE EXPORT StkSetStackBaseAndSize(DESCRIPTOR stkSessionId,
				      DESCRIPTOR baseAddr, U32 stkSize)  {
   RETCODE err = GOOD;
   STK_CONTEXT *contextPtr;
   U32 percent, offset;
   DESCRIPTOR stkAddr;
   BOOLEAN isValid,isEnabled;
   
   contextPtr = (STK_CONTEXT *)stkSessionId;
   /* check for valid session id */
   if (contextPtr->sessionId != stkSessionId) {
      err = ER_STK_INVALID_DESCRIPTOR;
	goto CLEANUP;
   }

   /*
   ** Set Stack base and size do not accept address if not on an
   ** even boundary based on the minimum writable stack size
   */
   if ((err = AdrGetAddrOffset(baseAddr, &offset)) != GOOD)
      goto CLEANUP;

   if (!stkSize) {
      ErrDisplayError(ER_STK_INVALID_STK_SIZE, CHECK_MODE);
      stkSize = iniStkSize;  /* Use default value to contine */
   }
   /* Round  base address */
   if (stackSex == HI_TO_LOW) {
      /* round down */
      offset &= roundingMask[(min(stackUnitSize, MAX_STACK_BYTES)-1)];
   } else {
      /* round up if any of the lower significant address values are 1 */
      offset = (offset + (stackUnitSize - 1)) &
	       roundingMask[(min(stackUnitSize, MAX_STACK_BYTES)-1)];
   }
   /* Check the specified stack area is within the physical memory range */
   if ((err = StkValidateStackArea(offset, &stkSize)) != GOOD)
      goto CLEANUP;
   
   /* Save the stack base to INI value */
   if (((err = AdrSetAddrOffset(baseAddr,offset)) != GOOD) ||
       ((err = StkSetAddrSpace(baseAddr)) != GOOD))
      goto CLEANUP;
   
   /* free existing stack address and replace with passed in descriptor */
   if ((contextPtr->isValidStackBase) &&
      ((err = AdrDestroyAddress(contextPtr->stackBaseAddr)) != GOOD))
      goto CLEANUP;

   /* round size to make stack range even boundary for the processor */
   stkSize &= roundingMask[(min(stackUnitSize, MAX_STACK_BYTES)-1)];
   contextPtr->stackBaseAddr = baseAddr;  
   contextPtr->isValidStackBase = TRUE;
   contextPtr->stackSize = stkSize;
   contextPtr->isStkFilled = FALSE; 
   contextPtr->isHighWaterMarkCalculated = FALSE;
   /* Save the new stack base and size to INI values */
   if ((err = AdrSetAddrOffset(iniStkBaseAddr, offset)) != GOOD) 
      return(err);
   iniStkSize = stkSize;
	       
   /* If alarm limit is enabled the recalculate the alarm physical address */
   if (contextPtr->isAlarmLimitEnabled) {
      if ((err = StkGetAlarmLimit((DESCRIPTOR) contextPtr,&stkAddr,&percent,
	    &isValid,&isEnabled)) == GOOD) {
	 if ((err = AdrDestroyAddress(stkAddr)) != GOOD) return(err);
	 if ((err = RecalcAlarmLimit((DESCRIPTOR) contextPtr,percent))
		 != GOOD) {
	    /* Report error - disable alarm limit and continue */
	    ErrDisplayError(err, CHECK_MODE);
	    contextPtr->isAlarmLimitEnabled = FALSE;
	 }
      } else {
	 /* Report error - disable alarmlimit - continue */
	 ErrDisplayError(err, CHECK_MODE);
	 contextPtr->isAlarmLimitEnabled = FALSE;
      }
   }

   /* Fill Stack area with pattern if HWM is enabled */
   if (contextPtr->isHighWaterMarkEnabled) {
      if (((err = AdrDuplicateAddress(contextPtr->stackBaseAddr,&baseAddr))
	 != GOOD) || ((err = StkSetStackToPattern(baseAddr,stkSize))
								!= GOOD)) {
	 /* StkSetStackToPattern() consumes the baseAddr 
	 ** Report error, disableHWM and return GOOD,
	 ** since HWM will be filled later.
	 */
	 contextPtr->isHighWaterMarkEnabled = FALSE;
	 ErrDisplayError(err, CHECK_MODE); /* do not destroy baseAddr */
      }
      else
	 contextPtr->isStkFilled = TRUE;  /* Only fill it once */
   }
   return(GOOD);

CLEANUP:
   /* Only destroy the address base descriptor if error occurred */
   AdrDestroyAddress(baseAddr);
   return err;
} /* end of StkSetStackBaseAndSize */

/*------------------------------------------------------------------------
** StkGetStackInfo
**
** Purpose:
**    Returns all values of the current stack session information.
**    The addresses are created locally, and their descriptors are returned
**    in parameters. The caller is responsible for de-allocating the memory.
**    If error occurrs, there is no memory allocate.
**    IMPORTANT NOTE: BaseAddr is invalid if isBaseValid = FALSE.
**    In fact, it is not allocated (i.e. do not call AdrDestroyAddress
**    if this is the case)
**
** Parameters: NOTES: 11 args total.
**    stkSessionId (in):    Stack selector
**    baseAddr (out):       Base address of the stack.
**    isBaseValid (out):    Flag indicates that the value in *baseAddr is
**                            good (TRUE) or not good (FALSE)
**    stackSize (out):      Size of the stack in bytes.
**    alarmLimitAddr (out): Alarm address where, if stack usage goes beyond,
**                          a warning is issued
**    alarmPercent (out):   Percentage equivalent to the alarm limit
**                          physical address
**    isALarmValid (out):   TRUE | FALSE = validity state of alarm value.
**    isAlarmEnabled (out): TRUE | FALSE = Alarm enabled | disabled
**    highWaterMarkAddr (out): The address of the highest (or lowest) part of
**                             the dynamic stack that has been accessed.
**    isHWMEnabled (out):   TRUE | FALSE = HWM enabled | disabled
**    stkDirection (out):   FALSE | TRUE = high to low | low to high memory.
**
** Error: none
*/
/*------------------------------------------------------------------------*/
RETCODE EXPORT StkGetStackInfo(DESCRIPTOR stkSessionId,
			       DESCRIPTOR *baseAddr,
			       BOOLEAN    *isBaseValid,
			       U32        *stackSize,
			       DESCRIPTOR *alarmLimitAddr,
			       U32        *alarmPercent,
			       BOOLEAN    *isAlarmValid,
			       BOOLEAN    *isAlarmEnabled,
			       DESCRIPTOR *highWaterMarkAddr,
			       BOOLEAN    *isHWMEnabled,
			       BOOLEAN    *stkDirection) {
   RETCODE err = GOOD;
   STK_CONTEXT *contextPtr;

   contextPtr = (STK_CONTEXT *)stkSessionId;
   /* check for valid session id */
   if (contextPtr->sessionId != stkSessionId) {
      return(ER_STK_INVALID_DESCRIPTOR);
   }
   if ((err = StkGetStackBase(stkSessionId, baseAddr, isBaseValid)) != GOOD)
      return(err);
   *stackSize = contextPtr->stackSize;
   if ((err = StkGetAlarmLimit(stkSessionId, alarmLimitAddr, alarmPercent,
      isAlarmValid, isAlarmEnabled)) != GOOD) {
      goto CLEANUP;
   }
   if ((err = StkGetHighWaterMark(stkSessionId, highWaterMarkAddr,
				  isHWMEnabled)) != GOOD) {
      AdrDestroyAddress(*alarmLimitAddr);
      goto CLEANUP;
   }
   *stkDirection = stackSex;
   return(GOOD);
   
CLEANUP:
   AdrDestroyAddress(*baseAddr);
   return(err);
}

/*------------------------------------------------------------------------
** stkValidateStackArea
**
** Purpose:
**    Validate if the specified stack area is within the boundary of physical
** memory range.
** NOTES: stackSizeParam will be adjusted if the specified area is invalid.
**
** Error:
**    Report a warning if any arithmetic combination of
**       <baseAddr> and <stackSize> causes an overflow; i.e.
**       if STK_BOTTOM_UP and stackSize + baseAddr > 0xFFFFFFFF
**    or if STK_TOP_DOWN and stackSize - baseAddr < 0
**    Should alway return GOOD.
*/
/*------------------------------------------------------------------------*/
RETCODE PRIVATE StkValidateStackArea(U32 stackStart,
				     U32 *stackSizeParam) {
   RETCODE err = GOOD;
   U32 tmpEnd, oldStkSize = 0L;
   S32 rangeOffset = 0;
   S16 dummyArg;
   CHAR tmpStr1[32], tmpStr2[32];
   
   oldStkSize = *stackSizeParam;
   if (stackSex == HI_TO_LOW) {
      tmpEnd = rangeOffset = stackStart - *stackSizeParam;
      if (tmpEnd > stackStart) {
	 /* bottom wrap-around occurred - RangeOffset is negative */
	 if (rangeOffset < 0)
	   *stackSizeParam += rangeOffset;
	 /* Adjust stack size to fix within the physical memory */
	 err = ER_STK_RANGE_TOO_LARGE;  
      }
   } else {
      tmpEnd = stackStart + *stackSizeParam;
      if (tmpEnd < stackStart) {
	 /* top wrap-around occurred */
	 *stackSizeParam = stkMaxAddress - stackStart;
	 err = ER_STK_RANGE_TOO_SMALL;
      } 
   }
   if (err != GOOD) {
      /* Format the value to report */
      wsprintf((LPSTR)tmpStr1, "%lu", oldStkSize);
      wsprintf((LPSTR)tmpStr2, "%lu", *stackSizeParam);
      /* err should be reset to GOOD after report the warning */
      err = ErrDisplayFormattedError(err, CHECK_MODE,
				 (LPSTR)tmpStr1,
				 (LPSTR)tmpStr2,
				 (LPSTR)NULL,
				 MB_OK,
				 &dummyArg);
   }
   return(err);
}


#ifdef __cplusplus
}
#endif
/******************************** E O F ***********************************/
