/*----------------------------------------------------------------------------
** Name: stkutil.c  
**
** Title: Stack Utility
**
** Purpose: Implements stack read and write
**
** Status: PRELIMINARY |
**
** $Log:   S:/tbird/mt2_68k/stkservr/stkutil.c_v  $
** 
**    Rev 1.0   13 Feb 1997 09:05:04   gene
** Initial revision.
** 
**    Rev 1.2   01 Dec 1995 10:09:14   kevin
** joyce's modification
** 
**    Rev 1.1   10 Nov 1995 14:08:02   kevin
** checked 68000 type
** 
**    Rev 1.0   07 Sep 1995 11:14:24   gene
** Initial revision.
** 
**    Rev 1.30   30 Mar 1994 11:34:28   john
** Added changes for 360
** 
**    Rev 1.29   28 Feb 1994 17:02:32   marilyn
** Moved maxOutput routines to the address server. Updated interfaces.
** 
**    Rev 1.28   06 Aug 1993 15:52:56   mindy
** Made changes to fix Cosmic
** 
**    Rev 1.27   03 Aug 1993 14:49:14   marilyn
** Fixed ppr 8360, extra blank line in displaystack locals command, and ppr
** 8624, displaystack locals not showing the locals. 
** 
**    Rev 1.26   29 Jul 1993 11:39:34   nghia
** Fixed PPR 8705:
** - Check for NULL returnPC before convert it to text (when step into library
** function).
** 
**    Rev 1.25   20 Jul 1993 18:09:06   nghia
** Fixed bug: load from button window without the SHELL script run will UAE.
** - Remove the bogus client ID for StkCreateStack().
** 
**    Rev 1.24   16 Jul 1993 12:05:52   nghia
** PIP for Stack:
** - Added global iniStkxxx to hold PWRVIEWS.INI stackInfo's data.
** - Added routine to save and restore stackInfo's data.
** - Made StkEndSession to check for defaultSessionId before destroy session.
** - Created routine to initialize stack session with PWRVIEWS stackInfo's data.
** - Only destroy and write iniStkxxx value when TerminateServer() get called.
** 
**    Rev 1.23   28 Jun 1993 08:56:58   paul
** Change CHECK_ABORT to TskCheckAbort
** 
**    Rev 1.22   15 Jun 1993 11:10:28   mindy
** StkRead and StkWrite have been changed to use the new information that
** is now contained in the STK_FRAME_NODE list of functions.
** 
**    Rev 1.21   25 May 1993 11:48:40   ernie
** Changed to conform to new MemReadSized() prototype
** 
**    Rev 1.20   23 Mar 1993 09:40:06   doug
** merged John's generic 340 change
** 
**    Rev 1.19.1.1   19 Mar 1993 07:26:36   john
** use dash instead of X
** 
**    Rev 1.19.1.0   04 Mar 1993 17:48:20   john
** The actor code expects to see frames returned that have an 
** [address, address, symbol] format.  I changed the code to fill in the
** first "address" with maxOutputAddrDigits of place holder char's if
** the address is not valid.
** 
**    Rev 1.19   15 Dec 1992 12:58:08   ernie
** Changed MemXXX to MemXXXSized with byte specified
** 
**    Rev 1.18   07 Dec 1992 16:44:54   marilyn
** Customized StkRead a little more.
** 
**    Rev 1.17   02 Dec 1992 15:51:22   doug
** free memory if MemRead errors
** 
**    Rev 1.16   02 Dec 1992 13:42:46   marilyn
** Added check abort.
** 
**    Rev 1.15   21 Oct 1992 17:15:26   marilyn
** Updates to stkRead and stkWrite.
** 
**    Rev 1.14   09 Oct 1992 12:22:34   marilyn
** Fixed memory leak in StkWrite.
** 
**    Rev 1.13   20 Aug 1992 16:21:10   marilyn
** Bytes access only allowed.
** 
**    Rev 1.12   02 Jun 1992 16:18:26   marilyn
** Added FreeStkVarBuf
** 
**    Rev 1.11   29 May 1992 09:13:54   marilyn
** Updated StkWrite routine to use frameNum.
** 
**    Rev 1.10   28 May 1992 16:43:44   marilyn
** Modified StkRead and StkWrite.  Added new parameter frameNum.
** 
**    Rev 1.7   24 Apr 1992 15:49:02   marilyn
** Changed all params for StkCalcStkOffset to be S32.
** 
**    Rev 1.6   17 Apr 1992 16:26:34   marilyn
** Added StkCalcStkOffset.
** 
**    Rev 1.5   12 Mar 1992 17:27:22   marilyn
** No change.
** 
**    Rev 1.4   11 Mar 1992 10:43:34   marilyn
** Included stkerrs.h
** 
**    Rev 1.3   02 Mar 1992 15:30:48   marilyn
** Updated MemRead interface to use LPU8 *.
** 
**    Rev 1.2   27 Feb 1992 15:41:14   marilyn
** Updated to new cpu and memory interfaces.
** 
**    Rev 1.1   11 Feb 1992 15:58:26   marilyn
** integrated dll version.
**
**    Rev 1.0   24 Jan 1992 12:07:28   brucea
** Initial revision.
**
** $Header:   S:/tbird/mt2_68k/stkservr/stkutil.c_v   1.0   13 Feb 1997 09:05:04   gene  $
**
** Copyright (C) 1991 Microtek International.  All rights reserved.
**
**--------------------------------------------------------------------------*/

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

#ifndef _PVTASK_
#include "pvtask.h"
#endif

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

#ifndef _STKWALK_
#include "stkwalk.h"
#endif

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

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

#ifndef _ANOTEXT_
#include "anotext.h"
#endif

#ifndef _TBIRDMEM_
#include "tbirdmem.h"
#endif

#ifndef _VARSERVR_
#include "varservr.h"
#endif

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

#ifndef __STRING_H
#include "string.h"
#endif

#ifndef _ERRCODEC_
#include "errcodec.h"
#endif

#ifndef _INIUTIL_
#include "iniutil.h"
#endif

#include <math.h>
#ifdef __cplusplus

extern "C" {
#endif


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

#define STK_MAX_ADDR_BUF 20

typedef U8 STK_ADDR_BUFFER_TYPE[STK_MAX_ADDR_BUF];

                       /****************************
                        *                          *
                        *    EXTERNAL VARIABLES    *
                        *                          *
                        ****************************/

extern DESCRIPTOR stkCurrentPCAddrDesc;
extern DESCRIPTOR defaultSessionId;
extern STACK_TYPE stackSex;
extern U32 defaultStackSize;	 
extern DESCRIPTOR iniStkBaseAddr;
extern U32 iniStkSize;			 
extern U32 iniStkPercentAlarm; 
extern BOOLEAN iniStkEnableAlarm;
extern BOOLEAN iniStkEnableHWM;

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

const char eolnString[] = "\r\n";
const char endOfBuffer[] = "\0";
const char blankChar[] = " ";
const char indentStr[] = "        ";           /* 8 spaces for indent */


PRIVATE U8 specialChar = AT_SPECIAL_MARK;

PRIVATE STK_ADDR_BUFFER_TYPE addrBuf;

extern PROC_CPU cpu;


                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/

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

/*************************************************************************
**
**   FormatFrames
**
**************************************************************************/
RETCODE FormatFrames(STK_FRAME_NODE *startPtr,
                     STK_FRAME_NODE *endPtr,
                     BOOLEAN interActive,
                     STRING *textPtr) {

   RETCODE error;
   BOOLEAN abortFromEsc;

   while (startPtr != endPtr) {
      if ((error = TskCheckAbort(&abortFromEsc)) != GOOD)
			return(error);
      if (abortFromEsc!=0)
         return ER_ABORT_FROM_ESC;

      if ((error = GetSingleFrame(startPtr,textPtr,interActive)) != GOOD)
         return error;
      GetEndOfLine(textPtr);
      if (startPtr->frameNum < endPtr->frameNum)
         startPtr = startPtr->next;
      else
         startPtr = startPtr->previous;
   }
   if (startPtr == endPtr) {  /* get last one or only one */
      if ((error = GetSingleFrame(startPtr,textPtr,interActive)) != GOOD)
         return error;
      GetEndOfBuffer(textPtr);
   }
   return GOOD;
}

/***********************************************************************
**
**  GetBlankSpace :
**
*********************************************************************/
VOID GetBlankSpace(STRING *textPtr)  {

   lstrcat((LPSTR)*textPtr,(LPSTR)blankChar);
   *textPtr += lstrlen((LPSTR)*textPtr);
}

/***********************************************************************
**
**  GetEndOfBuffer :
**
*********************************************************************/
VOID GetEndOfBuffer(STRING *textPtr)  {

   lstrcat((LPSTR)*textPtr,(LPSTR)endOfBuffer);
   *textPtr += lstrlen((LPSTR)*textPtr);
}


/***********************************************************************
**
**  GetEndOfLine :
**
*********************************************************************/
VOID GetEndOfLine(STRING *textPtr)  {

   lstrcat((LPSTR)*textPtr,(LPSTR)eolnString);
   *textPtr += lstrlen((LPSTR)*textPtr);
}

/***********************************************************************
**
**  GetParamsForFrame :
**
***********************************************************************/
RETCODE GetParamsForFrame(STK_FRAME_NODE *frame,
                          STRING *textPtr,
                          BOOLEAN interActive)  {
   DESCRIPTOR varId;
   VS_DISPLAY_PTR varBuf;
   SYM_DESCRIPTOR symbolDesc;
   SYM_TYPE_TYPE symType;
   U8 paramCount;
   LPU8 ptr;
   RETCODE error;

   symbolDesc = frame->funcInfo.firstLocal;
   paramCount = frame->funcInfo.localListCount;
   while (paramCount != 0) {
      if ((error = VSOpenVar(VS_ANONYMOUS,symbolDesc,interActive,&varId))
             != GOOD)
         return error;
      if ((error = VSReadStackVar(VS_ANONYMOUS,varId,0,frame->frameNum,
            &varBuf)) != GOOD)
         return error;
      /*
      **  copy the first line of the parameter to the textPtr buffer
      */
      lstrcat((LPSTR)*textPtr,(LPSTR)indentStr);
      ptr = (LPU8)strpbrk((LPSTR)varBuf->text,"\n");
      if (ptr != NULL)
         *ptr = '\0';
      lstrcat((LPSTR)*textPtr,(LPSTR)varBuf->text);
      /*
      **  free the variable display buffer
      */
      if ((error = FreeStkVarBuf(varBuf)) != GOOD)
         return error;
      if ((error = VSCloseVar(VS_ANONYMOUS,varId)) != GOOD)
         return error;
      if ((error = SymGetSymbolSibling(symbolDesc,&symType,&symbolDesc))
            != GOOD)
         return error;
      paramCount--;
      if (paramCount > 0)
         GetEndOfLine(textPtr);
   }

   return GOOD;
}

/************************************************************************
**
**   GetSingleFrame
**
**   Format a single frame to a text buffer.  If interActive == TRUE then
**   format for anotated text.
**
*************************************************************************/
RETCODE GetSingleFrame(STK_FRAME_NODE *frame,
                       STRING *textPtr,
                       BOOLEAN interActive) {

   RETCODE error;
   LPSTR bufPtr;
   U16 outputAddrDigits;

   if ((error = AdrGetMaxOutputAddrDigits(stkCurrentPCAddrDesc,
         &outputAddrDigits)) != GOOD)
      return(error);
   /* Prepare addrBuf for address formatting */
   memset(&addrBuf[0],'\0',sizeof(addrBuf));
   bufPtr = (LPSTR)&addrBuf[0];
   if (frame->frameDesc != NULL) {
      if ((error = AdrConvAddressToTextWithParams(frame->frameDesc,
            FALSE, TRUE, bufPtr)) != GOOD)
         return error;
      lstrcat((LPSTR)*textPtr,bufPtr);
   }
   else /* place holder digits.  should match addr digits */
      memset((LPSTR)*textPtr,'-',outputAddrDigits);
   GetBlankSpace(textPtr);
   memset(&addrBuf[0],'\0',sizeof(addrBuf));
   if (frame->returnPCDesc != NULL) {
      if ((error = AdrConvAddressToTextWithParams(frame->returnPCDesc,
         FALSE, TRUE, bufPtr)) != GOOD)
         return error;
      lstrcat((LPSTR)*textPtr,bufPtr);
   } else 
      /* place holder digits.  should match addr digits */
      memset((LPSTR)*textPtr,'-',outputAddrDigits);
   GetBlankSpace(textPtr);
   if (interActive) {
      **textPtr = specialChar|AT_BEGIN_TAG|AT_FUNC_NAME_FIELD;
      (*textPtr)++;
   }
   lstrcat((LPSTR)*textPtr,frame->funcInfo.funcNamePtr);
   *textPtr += lstrlen((LPSTR)*textPtr);
   if (interActive) {
      **textPtr = specialChar|AT_END_TAG|AT_FUNC_NAME_FIELD;
      (*textPtr)++;
   }
   return GOOD;
}

/**************************************************************************
**
**  InitServer
**
****************************************************************************/
RETCODE EXPORT InitServer() {
   return (StkInitStackContext());
}

/**************************************************************************
** StkCalcStkOffset
**
** Purpose:
**    Given a current stack offset and an additional byte offset
**    calculate the resultant offset based on stack direction.  The
**    resultant offset is returned in stack offset.
**
** Input parameter:
**    stackoffset : original stack offset
**    byteOffset : additional byte offset
**
** Output parameter:
**    Recalculated stack offset returned.
**
***************************************************************************/
RETCODE EXPORT StkCalcStkOffset(S32 *stackOffset,
                                S32 byteOffset) {

   if (stackSex == HI_TO_LOW)
      *stackOffset += byteOffset;
   else
      *stackOffset -= byteOffset;
   return GOOD;
}


/***************************************************************************
**   StkEndSession
**
**   Purpose:
**      To shut down a client's access to the stack server.
**
**   Input parameters:
**      stkSessionId:  client id
**
**   Output parameters:  none
**
**   Error:
**      Returns ER_STK_CLIENT_DISCREP if the client is not registered
**      (client discrepancy).
**
***************************************************************************/
RETCODE EXPORT StkEndSession(DESCRIPTOR stkSessionId) {
   RETCODE error;

   /* currently only one stack is supported */
   if (stkSessionId != defaultSessionId) {
      if ((error = StkDestroyStack(stkSessionId)) != GOOD)
         return error;
   }
   /* Always release stack frames */
   return(ReleasePreviousFrames());
}


/*************************************************************************
**   StkRead
**
**   Purpose:
**      To read variable and parameter values from the stack based on
**      a stack offset in the frame and the current context.
**      NOTE: stack reads are always in the direction of stack growth,
**      that is high to low or low to high depending on processor.
**
**   Input parameters:
**      stkOffset : the offset from stack frame for the value on the stack.
**      length : the length of the read in bytes.
**
**   Output parameters:
**      buffer : the value(s) read are stored in buffer.  The buffer is
**         allocated by the memory server based on the size required for
**         the requested read.
**
**   Error:
**      ER_STK_STK_READ_FAILED if the operation failed for any reason.
**
**************************************************************************/
RETCODE EXPORT StkRead(S32 stkOffset,
                       U32 length,
                       U8 frameNum,
                       U8 frameType,
                       LPU8 *buffer) {
   RETCODE error, err;
   DESCRIPTOR stackDesc;
   STK_FRAME_NODE *framePtr;
   /* get the correct frame */
   if( (FindFrame(frameNum,&framePtr)==GOOD) && (framePtr->frameDesc!=NULL) ) {
      if ((error = AdrDuplicateAddress(framePtr->frameDesc,&stackDesc))
          != GOOD)
         return error;
   }  
   else {
      if( ((cpu==PROC_CPU_CPU32)&&(frameType==CPU32_NO_FRAME_TYPE))
       || ((cpu==PROC_CPU_68000)&&(frameType==CPU32_NO_FRAME_TYPE))
       || ((cpu==PROC_CPU_CPU32P)&&(frameType==CPU32_NO_FRAME_TYPE))
       || ((cpu==PROC_CPU_CPU16)&&(frameType==CPU16_NO_FRAME_TYPE)) ) {
         DESCRIPTOR retPCDesc, frameAddrDesc, baseAddrDesc;
         BOOLEAN isValid;
         /* must be relative to current SP */
         if ((error = CpuGetSP(&stackDesc)) != GOOD) return error;
         if((error=AdrDuplicateAddress(stackDesc,&baseAddrDesc))!=GOOD)
            goto CLEANUP;
         // Set base to no more than 50 bytes !!! good guessimate??
         if((error = PopStack(baseAddrDesc,50)) != GOOD ) {
            AdrDestroyAddress(baseAddrDesc);
            goto CLEANUP;
         }
         frameAddrDesc = stackDesc;
         if((error=SearchForReturnPC(frameAddrDesc,baseAddrDesc,&retPCDesc,
                                   &stackDesc,&isValid))!=GOOD) {
            AdrDestroyAddress(frameAddrDesc);
            AdrDestroyAddress(stackDesc);
            AdrDestroyAddress(baseAddrDesc);
            AdrDestroyAddress(retPCDesc);
            return error;
         }
         AdrDestroyAddress(frameAddrDesc);
         AdrDestroyAddress(baseAddrDesc);
         AdrDestroyAddress(retPCDesc);
         if( !isValid ) {
            AdrDestroyAddress(stackDesc);
            return(ER_STK_STK_BASE_INVALID);  // !!! need a good error
         }
// !!! why???         stkOffset = -stkOffset;
      }
      else {  // All other types are "frame'd" frames
         if ((error = CpuGetFramePtr(&stackDesc)) != GOOD)
            return error;
      }
   }      
   if (stkOffset < 0) {  /* negative offset */
      if ((error = AdrSubtractFromAddress(stackDesc,(~stkOffset + 1)))
          != GOOD)
         goto CLEANUP;
   }
   else {
      if ((error = AdrAddToAddress(stackDesc,stkOffset)) != GOOD)
      goto CLEANUP;
   }
   if ((error = StkSetAddrSpace(stackDesc)) != GOOD)
      goto CLEANUP;

   if ((error = MemReadSized(stackDesc,length,buffer,SIZE_BYTE,CACHE_USE))
        != GOOD) {
      TFree((LPSTR)buffer);
      goto CLEANUP;
   }
CLEANUP:
   if ((err = AdrDestroyAddress(stackDesc)) != GOOD)
      return err;
   return error;
}

/***************************************************************************
**   StkStartSession
**
**   Purpose:
**      To register a calling client with a unique id for access to the
**      stack server.
**
**    Input parameters:
**      winHandle:  the calling window's id
**
**   Output parameters:
**      stkSessionId:  client id
**
**   Error:
**      Returns ER_OUT_OF_MEMORY
**
****************************************************************************/
#pragma argsused
RETCODE EXPORT StkStartSession(HWND  winHandle,
                               DESCRIPTOR *stkSessionId) {
   /*
   **  implemented currently for only one stack
   */
   return(StkCreateStack(stkSessionId));
}

/*************************************************************************
**   StkWrite
**
**   Purpose:
**      To write variable and parameter values to the stack based on
**      stack offset from frame pointer and current context.
**      NOTE: stack writes are always in the direction of stack growth,
**      that is high to low or low to high depending on processor.
**
**   Input parameters:
**      stkOffset : the offset for the value on the stack.
**      length : the length of the write in bytes.
**      buffer : the value(s) to write are read from buffer
**         The buffer is allocated by the caller based on the size required
**         to contain the requested write.
**
**   Output parameters:
**      none
**
**   Error:
**      ER_STK_STK_WRITE_FAILED if the operation fails for any reason.
**
**************************************************************************/
RETCODE EXPORT StkWrite(S32 stkOffset,
                       U32 length,
                       U8 frameNum, 
                       U8 frameType,
                       LPU8 buffer) {
   RETCODE error;
   DESCRIPTOR stackDesc;
   STK_FRAME_NODE *framePtr;
   /* get the correct frame */
   if( (FindFrame(frameNum,&framePtr)==GOOD) && (framePtr->frameDesc!=NULL) ) {
      if ((error = AdrDuplicateAddress(framePtr->frameDesc,&stackDesc))
          != GOOD)
         return error;
   }  
   else {
      if( ((cpu==PROC_CPU_CPU32)&&(frameType==CPU32_NO_FRAME_TYPE))
       || ((cpu==PROC_CPU_68000)&&(frameType==CPU32_NO_FRAME_TYPE))
       || ((cpu==PROC_CPU_CPU32P)&&(frameType==CPU32_NO_FRAME_TYPE))
       || ((cpu==PROC_CPU_CPU16)&&(frameType==CPU16_NO_FRAME_TYPE)) ) {
         DESCRIPTOR retPCDesc, frameAddrDesc, baseAddrDesc;
         BOOLEAN isValid;
         /* must be relative to current SP */
         if ((error = CpuGetSP(&stackDesc)) != GOOD) return error;
         if((error=AdrDuplicateAddress(stackDesc,&baseAddrDesc))!=GOOD) {
            AdrDestroyAddress(stackDesc);
            return error;
         }
         // Set base to no more than 50 bytes !!! good guessimate??
         if((error = PopStack(baseAddrDesc,50)) != GOOD ) {
            AdrDestroyAddress(stackDesc);
            AdrDestroyAddress(baseAddrDesc);
            return error;
         }
         frameAddrDesc = stackDesc;
         if((error=SearchForReturnPC(frameAddrDesc,baseAddrDesc,&retPCDesc,
                                   &stackDesc,&isValid))!=GOOD) {
            AdrDestroyAddress(frameAddrDesc);
            AdrDestroyAddress(stackDesc);
            AdrDestroyAddress(baseAddrDesc);
            AdrDestroyAddress(retPCDesc);
            return error;
         }
         AdrDestroyAddress(frameAddrDesc);
         AdrDestroyAddress(baseAddrDesc);
         AdrDestroyAddress(retPCDesc);
         if( !isValid ) {
            AdrDestroyAddress(stackDesc);
            return(ER_STK_STK_BASE_INVALID);  // !!! need a good error
         }
      // !!! why???         stkOffset = -stkOffset;
      }
      else {
         if ((error = CpuGetFramePtr(&stackDesc)) != GOOD)
            return error;
      }
   }
   if (stkOffset < 0) {  /* negative offset */
      if ((error = AdrSubtractFromAddress(stackDesc,(~stkOffset + 1)))
            != GOOD)  {
         AdrDestroyAddress(stackDesc);
         return error;
      }
   }
   else {
      if ((error = AdrAddToAddress(stackDesc,stkOffset)) != GOOD) {
         AdrDestroyAddress(stackDesc);
         return error;
      }
   }
   if ((error = StkSetAddrSpace(stackDesc)) != GOOD) {
      AdrDestroyAddress(stackDesc);
      return error;
   }
   /*
   **  Remember that MemWrite will free the buffer
   */
   if ((error = MemWriteSized(stackDesc,length,buffer,BYTE_SIZE)) != GOOD) {
      AdrDestroyAddress(stackDesc);
      return error;
   }
   if ((error = AdrDestroyAddress(stackDesc)) != GOOD)
      return error;
   return GOOD;
}

/******************************************************************************
**
**   TerminateServer
**
*****************************************************************************/
RETCODE EXPORT TerminateServer() {
   RETCODE err1, err = GOOD;

   /* Destroy the default context and its data */
   if (defaultSessionId != NULL) {
      /* StkDestroyStack will destroy the default session */
      err = StkDestroyStack(defaultSessionId);
   }
   err1 = StkSaveIniValues();
   return(err != GOOD ? err : err1);
}

/*****************************************************************************
**
**  RecalcAlarmLimit
**
** Purpose:
**    Recalculate the physical address of the alarm limit according to
**    the input percent for the specified stack session.
**
** Input parameter:
**    sessionId (in) : Stack session Id.
**    percent (in)   : percent of alarm limit setting.
**
** Output parameter:
**    none
**
**************************************************************************/
RETCODE RecalcAlarmLimit(DESCRIPTOR sessionId, U32 percent) {
   DESCRIPTOR tmpAddr;
   STK_CONTEXT *contextPtr;
   U32 stackSize, alarmStackSize;
   BOOLEAN inRange, isValid;
   RETCODE error = GOOD;

   contextPtr = (STK_CONTEXT *)sessionId;

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

   if ((error = StkGetStackBase(sessionId,&tmpAddr,&isValid)) != GOOD)
       return error;
   if (!isValid)
      return  ER_STK_STK_BASE_INVALID;
    if ((error = AdrSetAddrSymbolUsage(tmpAddr,FALSE)) != GOOD)
      return error;
   if ((error = StkGetStackSize(sessionId,&stackSize)) != GOOD)
      return error;
	/* Compute the physical address of alarm limit */
   alarmStackSize = ceil((stackSize * percent)/100);
   
   if (stackSex == HI_TO_LOW) {
      if ((error = AdrSubtractFromAddress(tmpAddr,alarmStackSize)) != GOOD)
         return error;
   }
   else {
      if ((error = AdrAddToAddress(tmpAddr,alarmStackSize)) != GOOD)
         return error;
   }
   /* Set the physical alarm address */
      if (contextPtr->isAlarmLimitEnabled)  {
      /* check for valid address before modifying the alarm limit addr,
         but only if alarm already enabled */
      if (((error = StkCheckAddrInStackRange(sessionId,
             tmpAddr, &inRange)) != GOOD) || (!inRange)) {
         return(error != GOOD ? error : ER_STK_INVALID_ALARM_LIMIT);
      }
   }

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

/**************************************************************************
** StkRestoreIniValues
**
** Purpose:
**    Restore the curent stack information that saved in the PWRVIEWS.INI
**    to be used with stack session.  Information store in the globals
**    iniXXXX items (See EXTERNAL VARIABLES section).
**
** Input parameter:
**    none
**
** Output parameter:
**    none
**
***************************************************************************/
RETCODE StkRestoreIniValues(VOID) {
   RETCODE err, errReport = GOOD;
   U32 iniValue;
   
   /* Find the current directory of PWRVIEWS.INI file first */
   if ((err = IniGetPwrViewsIniPath()) != GOOD)
      return(err);
   /* Retrieve all INI values of the StackInfo section */
   if ((err = IniGetAddress((LPSTR) STK_APPNAME, (LPSTR) STK_BASE_ITEM,
        &iniStkBaseAddr)) != GOOD) {
     /* Set default iniStkbaseAddr = current SP */
     if ((err != ER_CLIULIB_UNABLE_TO_READ_PFILE) ||
            ((err = CpuGetSP(&iniStkBaseAddr)) != GOOD))
        return(err);
   } 
   if ((err = IniGetNum((LPSTR) STK_APPNAME, (LPSTR) STK_SIZE_ITEM,
           (U32 FAR *)&iniStkSize)) != GOOD) {
      errReport = err;   /* save error to report */
      iniStkSize = 1024; /* default to 1K */
   }

   if ((err = IniGetNum((LPSTR) STK_APPNAME, (LPSTR) STK_PERCENT_ALARM,
           (U32 FAR *)&iniStkPercentAlarm)) != GOOD) {
      errReport = (errReport != GOOD ? errReport : err);
      iniStkPercentAlarm = 95; /* default to 95% */
   }
   
   if ((err = IniGetNum((LPSTR) STK_APPNAME, (LPSTR) STK_ENABLE_ALARM,
           (U32 FAR *)&iniValue)) != GOOD) {
      errReport = (errReport != GOOD ? errReport : err);
      iniValue = FALSE;
   }
   iniStkEnableAlarm = (BOOLEAN) iniValue;
   
   if ((err = IniGetNum((LPSTR) STK_APPNAME, (LPSTR) STK_ENABLE_HWM,
      (U32 FAR *)&iniValue)) != GOOD) {
      errReport = (errReport != GOOD ? errReport : err);
      iniValue = FALSE;
   }
   iniStkEnableHWM = (BOOLEAN) iniValue;
  
   return(errReport);
}

/**************************************************************************
** StkSaveIniValues
**
** Purpose:
**    Save the curent stack information in the iniXXX items to the PWRVIEWS.INI
**    (See EXTERNAL VARIABLES section).
**
** Input parameter:
**    none
**
** Output parameter:
**    none
**
***************************************************************************/
RETCODE StkSaveIniValues(VOID) {
   RETCODE err;

   /* Find the current directory of PWRVIEWS.INI file first */
   if ((err = IniGetPwrViewsIniPath()) != GOOD)
      return(err);  
   /* Save all INI values of the StackInfo section */
   if ((err = IniSetAddress((LPSTR) STK_APPNAME, (LPSTR) STK_BASE_ITEM,
         iniStkBaseAddr)) != GOOD)
      return(err);
   if ((err = IniSetNum((LPSTR) STK_APPNAME, (LPSTR) STK_SIZE_ITEM,
         iniStkSize)) != GOOD)
      return(err);
   if ((err = IniSetNum((LPSTR) STK_APPNAME, (LPSTR) STK_PERCENT_ALARM,
         iniStkPercentAlarm)) != GOOD)
      return(err);
   if ((err = IniSetNum((LPSTR) STK_APPNAME, (LPSTR) STK_ENABLE_ALARM,
         (U32)iniStkEnableAlarm)) != GOOD)
      return(err);
   if ((err = IniSetNum((LPSTR) STK_APPNAME, (LPSTR) STK_ENABLE_HWM,
         (U32)iniStkEnableHWM)) != GOOD)
      return(err);
   return(GOOD);
}

/**************************************************************************
** StkInitWithIniValues
**
** Purpose:
**    Set the input stack context with the iniXXX item value.
**    (See EXTERNAL VARIABLES section).
**
** Input parameter:
**    stkSessionId (in) : Stack session to be initialized with INI values
**
** Output parameter:
**    none
**
***************************************************************************/
RETCODE StkInitWithIniValues(DESCRIPTOR stkSessionId) {
   RETCODE err = GOOD;
   DESCRIPTOR tmpBaseDesc;
   STK_CONTEXT *contextPtr;

   contextPtr = (STK_CONTEXT *)stkSessionId;
   /* Set HWM state first - so the Stack fill can carry out */
   contextPtr->isHighWaterMarkEnabled = iniStkEnableHWM;
   /* Set Alarm limit by percentage */
   contextPtr->alarmLimitPercent = iniStkPercentAlarm;
   /* Set the alarm limit state */
   contextPtr->isAlarmLimitEnabled = iniStkEnableAlarm;
   /* Set Stack base and size */
   if ((err = AdrDuplicateAddress(iniStkBaseAddr, &tmpBaseDesc)) != GOOD)
		return(err);
   /* StkSetBaseAndSize() will recalculate alarm limit if it's enabled */
   err = StkSetStackBaseAndSize(stkSessionId, tmpBaseDesc, iniStkSize);
   /* don't need to destroy baseDesc even if error occurrs */
   return(err);
}

/**********************************************************************
**
** StkSetAddrSpace
**
*************************************************************************/
RETCODE StkSetAddrSpace(DESCRIPTOR addr) {
RETCODE err;
ADDR_SPACE addrSpace;

   if ((err=StkGetAddrSpace(&addrSpace)) != GOOD)
      return err;
   return AdrSetAddrSpace(addr, addrSpace);
}

/**********************************************************************
**
** StkGetAddrSpace
**
*************************************************************************/
RETCODE StkGetAddrSpace(ADDR_SPACE *addrSpace) {
SPACE_MODE spaceMode;
RETCODE err;

   if ((err=CpuGetSpaceMode(&spaceMode)) != GOOD)
      return err;
   if (LdrSpaceMode() != spaceMode)
      return ER_ADR_SPACE_INVALID;
   *addrSpace = (spaceMode == SUPERVISOR_MODE) ? SPACE_SD : SPACE_UD;
   return GOOD;
}

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