/*----------------------------------------------------------------------------
**
** Name: stkwalk.c  
**
** Title: Stack Walker
**
** Purpose:
**   Walks the stack to obtain dynamic function call list
**
** Status: PRELIMINARY |
**
** $Log:   S:/tbird/mt2_68k/stkservr/stkwalk.c_v  $
** 
**    Rev 1.0   13 Feb 1997 09:05:04   gene
** Initial revision.
** 
**    Rev 1.3   09 Sep 1996 09:43:04   kevin
** hera's modification
** 
**    Rev 1.2   01 Dec 1995 10:10:22   kevin
** joyce's modification
** 
**    Rev 1.1   10 Nov 1995 14:08:34   kevin
** checked 68000 type
** 
**    Rev 1.0   07 Sep 1995 11:14:26   gene
** Initial revision.
** 
**    Rev 1.51   06 Apr 1994 17:02:38   nghia
** - Replaced the AdrCreateAddress() with AdrDuplicateAddress() to preserve
** the original address information for each stack frame.
** 
**    Rev 1.50   30 Mar 1994 11:34:36   john
** Added changes for 360
** 
**    Rev 1.49   26 Oct 1993 17:46:46   nghia
** Revised to display only one stack frame if the top most stack frame is
** an assembly call.
** 
**    Rev 1.48   11 Oct 1993 11:15:06   nghia
** Revised to handle assembly stack frame better.
** CPU32 use SYM_MODULE and CPU16 use SYM_PUBLIC_LABEL.
** 
**    Rev 1.47   07 Oct 1993 17:14:58   nghia
** Fixed PPR 8848, 8850, 8851 - HC16 stack display
** Revised the SearchForReturnPC() and FindReturnPC to adjust the return PC by
** -2.  The stacked return PC is 2 more than the actual value.  This changes
** make the symbol lookup work better.
** Revised GetFuncInfo() to fill in NO_FRAME_TYPE for assembly routine and
** fill the public label for the routine name.
** Cleanup Address descriptor destroying to avoid destroying them twice.
** 
**    Rev 1.46   01 Sep 1993 21:41:30   mindy
** added HiWare stack frame support
** 
**    Rev 1.45   10 Aug 1993 17:43:28   nghia
** Fixed bug:
** When stack window display a NULL frame, close window will cause the frame to 
** release. check for valid address descriptors before destroying them.
** 
**    Rev 1.44   06 Aug 1993 16:54:18   mindy
** Code fixes for Cosmic
** 
**    Rev 1.43   04 Aug 1993 11:26:56   marilyn
** Made changes to StkVarIsActive to return the first encountered frame where 
** the variable is active.  Fixes ppr 8691.
** 
**    Rev 1.42   03 Aug 1993 14:47:08   marilyn
** Fixed pprs 8360, blank lines in cli commands, and ppr 8624, displaystack
** locals was no longer working.
** 
**    Rev 1.41   29 Jul 1993 19:46:02   marilyn
** Fixed memory leak from the memRead in GetAddressAtLocation routine.
** PPR 8706 for PV 2.0.
** 
**    Rev 1.40   29 Jul 1993 11:41:38   nghia
** Fixed PPR 8705:
** - Destroy bufptr when error occurred.
** 
**    Rev 1.39   27 Jul 1993 17:14:52   marilyn
** Incorporated Nghia's changes previously marked at 1.1.  Fixed bug:
** Never destroy default stack session unless server terminated.
** 
**    Rev 1.38   27 Jul 1993 17:05:16   marilyn
** Changed U16 abortFromEsc to BOOLEAN.
** 
**    Rev 1.37   28 Jun 1993 08:57:36   paul
** Change CHECK_ABORT to TskCheckAbort
** 
**    Rev 1.36   15 Jun 1993 11:09:40   mindy
** Major restructing to get things into place to add support for Introl
** and Cosmic's (HC16) tool chains.  
** 
**    Rev 1.35   25 May 1993 11:47:04   ernie
** 1. Changed to conform to new MemReadSized() interface.
** 2. Added call to cache top-most block of the stack memory.  This
**    accelerates reads later.
** 
**    Rev 1.34   15 Dec 1992 12:57:58   ernie
** Changed MemXXX to MemXXXSized with byte specified
** 
**    Rev 1.33   14 Dec 1992 17:09:08   marilyn
** Fixed bug where main still disappeared.
** 
**    Rev 1.32   07 Dec 1992 16:44:14   marilyn
** Fixed problem where main was not always detected on the stack walk.
** 
**    Rev 1.31   02 Dec 1992 15:51:28   doug
** free memory if MemRead errors
** 
**    Rev 1.30   02 Dec 1992 13:43:34   marilyn
** Added check abort.
** 
**    Rev 1.29   12 Nov 1992 15:06:32   marilyn
** Initialize parameters in SearchForReturn... to prevent deallocation
** of same descriptor twice.
** 
**    Rev 1.28   11 Nov 1992 10:40:36   marilyn
** Just one more descriptor leak...
** 
**    Rev 1.27   10 Nov 1992 10:00:48   marilyn
** Fixed memory leak in StkOpenStack, ppr7509.
** 
**    Rev 1.26   06 Nov 1992 16:22:04   marilyn
** Fixed memory leakage in StkVarIsActive.
** 
**    Rev 1.25   03 Nov 1992 10:39:42   marilyn
** In StkVarIsActive initializing myDescriptor to NULL so that doing a
** DestroyStack doesn't use bogus descriptor.
** 
**    Rev 1.24   24 Oct 1992 10:46:00   marilyn
** Fixed ppr5284
** 
**    Rev 1.23   22 Oct 1992 20:28:46   marilyn
** If GetStkBase fails it does not return a descriptor.  Fixed cleanup.
** 
**    Rev 1.22   21 Oct 1992 17:10:28   marilyn
** Made some additions to the frame information stored to fix recursion
** bugs for locked reg types.
** 
**    Rev 1.21   03 Sep 1992 13:50:34   marilyn
** Fixed storage leak bugs.
** 
**    Rev 1.20   20 Aug 1992 16:20:50   marilyn
** Bytes access only allowed.
** 
**    Rev 1.19   19 Aug 1992 09:06:44   marilyn
** Fixed bugs in SearchForReturnPC routine.
** 
**    Rev 1.18   13 Aug 1992 17:06:54   marilyn
** Modified algorithm to work for functions without frames.
** 
**    Rev 1.17   02 Jun 1992 16:15:48   marilyn
** Completed inplementation of ReadFrameAndNLocals and ReadFrameHex.
**
**    Rev 1.16   28 May 1992 16:42:20   marilyn
** Added routine StkVarIsActive.
** 
**    Rev 1.15   08 May 1992 16:46:08   marilyn
** Fixed bug where funcDesc was NULL in OpenStack.
**
**    Rev 1.14   15 Apr 1992 10:28:10   marilyn
** Fixed ReadFrameHex to return not implemented instead of good.
** 
**    Rev 1.13   20 Mar 1992 08:45:34   marilyn
** Expanded interface for StkGetFrameSymbol.
** 
**    Rev 1.12   12 Mar 1992 17:26:00   marilyn
** No change.
** 
**    Rev 1.11   11 Mar 1992 10:38:52   marilyn
** Added StkGetFrameCount and StkGetFrameSymbol.
** 
**    Rev 1.10   02 Mar 1992 16:23:02   marilyn
** Added includes for prototypes.
**
**    Rev 1.9   02 Mar 1992 11:10:00   marilyn
** Update the MemRead interface to use LPU8 *.
**
**    Rev 1.8   28 Feb 1992 10:52:18   marilyn
** Included stkerrs.h
**
**    Rev 1.7   27 Feb 1992 15:38:46   marilyn
** Minor bug fixes.  Updated compatibility to mem.dll and cpu.dll
** 
**    Rev 1.6   14 Feb 1992 11:38:08   marilyn
** Removed processor stack dependencies to stkcli.c.
** 
**    Rev 1.5   13 Feb 1992 16:56:52   marilyn
** Made stackUnitSize a word boundary, made GetCurrentPC public.
**
**    Rev 1.4   11 Feb 1992 17:21:46   marilyn
** Made stackUnitSize externable.
**
**    Rev 1.3   11 Feb 1992 15:57:44   marilyn
** No change.
**
**    Rev 1.2   11 Feb 1992 12:09:34   marilyn
** Fixed bogus error code.
** 
**    Rev 1.1   10 Feb 1992 13:56:06   marilyn
** Added all stack walking functionality.
**
**    Rev 1.0   24 Jan 1992 12:07:56   brucea
** Initial revision.
**
** $Header:   S:/tbird/mt2_68k/stkservr/stkwalk.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 _STKWALK_
#include "stkwalk.h"
#endif

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

#ifndef _STKCLI_
#include "stkcli.h"
#endif

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

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

#ifndef _CPU_
#include "cpu.h"
#endif

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

#ifndef _HEAP_
#include "heap.h"
#endif

#ifndef _PROC_
#include "proc.h"
#endif

#ifndef __MEM_H_
#include <mem.h>
#endif

#ifndef __STDLIB_H_
#include <stdlib.h>
#endif

#ifndef _STRING_H_
#include <string.h>
#endif
                       /****************************
                        *                          *
                        *        DEFINITIONS       *
                        *                          *
                        ****************************/

#define DEFAULT_FRAMES 4
#define HEX_DISPLAY_BYTES 16
#define HEX_RADIX 16
#define STK_MAX_STRING 256
// Use in adjusting return PC of stack frame for CPU16
#define CPU16_RETURN_PC_OFFSET 0x2  

                       /****************************
                        *                          *
                        *     EXTERN VARIABLES     *
                        *                          *
                        ****************************/

extern STACK_TYPE stackSex;
extern U8 stackUnitSize;
extern U32 defaultStackSize;
extern U32 retPCOffset;
extern U32 stkMaxAddress;
extern PROC_CPU cpu;

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

/*
**  #define DEBUG_MEM_LEAKS
*/
#ifdef DEBUG_MEM_LEAKS
static U32 allocAddrs;
static U32 destroyedAddrs;
static U32 memAlloc;
static U32 memFreed;
#endif

/*******************************************************************/


PRIVATE STK_FRAME_LIST_TYPE stkFrameList = NULL;


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

RETCODE PRIVATE AddFrameToList(STK_FRAME_NODE *frame);


RETCODE PRIVATE GetFrameBackLink(DESCRIPTOR frameAddrDesc,
                                 U8 frameType,
                                 U32 frameOffset,
                                 DESCRIPTOR *backframeAddrDesc);

RETCODE PRIVATE FindFrameReturnPC(DESCRIPTOR frameDesc,
                                  U32 pcOffset,
                                  DESCRIPTOR *retPCLocationDesc,
                                  DESCRIPTOR *retPCDesc);

RETCODE PRIVATE FreeFrameNode(STK_FRAME_NODE *node);

RETCODE PRIVATE GetAddressAtLocation(DESCRIPTOR inputDesc,
                                     DESCRIPTOR addrDesc);

BOOLEAN PRIVATE MoreFrames(DESCRIPTOR currFrameAddrDesc,
                           DESCRIPTOR currStackDesc,
                           DESCRIPTOR baseAddrDesc);

RETCODE PRIVATE StoreSymFuncInfo(STK_FRAME_NODE *frame,
                                 FUNC_INFO_TYPE *frameInfo,
                                 DESCRIPTOR frameAddrDesc,
                                 DESCRIPTOR PCAddrDesc,
                                 U8 frameNum);

RETCODE PRIVATE GetFuncInfo(FUNC_INFO_TYPE *funcInfo,
                                 SYM_DESCRIPTOR symDesc,
                                 SYM_TYPE_TYPE  symType,
                                 SYM_DESCRIPTOR funcDesc,
                                 SYM_DESCRIPTOR modDesc);

VOID FlipBuffer(LPU8 buffer, U32 numBytes);

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

/***********************************************************************
**
**   AddFrameToList
**
**   Add a frame into the linked list.
**
*************************************************************************/
RETCODE PRIVATE AddFrameToList(STK_FRAME_NODE *frame) {

   STK_FRAME_NODE *ptrFrame;

   if (stkFrameList == NULL)  /* empty list, add first one */
      stkFrameList = frame;
   else {
      ptrFrame = stkFrameList;
      while (ptrFrame->next != NULL)
         ptrFrame = ptrFrame->next;
      ptrFrame->next = frame;
      frame->previous = ptrFrame;
   }
   return GOOD;
}

/***********************************************************************
**
**   FindFrame
**
**   Find a frame in the linked list.
**
*************************************************************************/
RETCODE FindFrame(U8 frameNum,
                  STK_FRAME_NODE **frame) {

   STK_FRAME_NODE *ptrFrame;
   U8 count = 0;

   if (stkFrameList == NULL) {
      *frame = NULL;
      return ER_STK_FRAME_INVALID;
   }
   else {
      ptrFrame = stkFrameList;
      while ((ptrFrame->next != NULL) && (frameNum != count)) {
         count++;
         ptrFrame = ptrFrame->next;
      }
      if (frameNum == count)
          *frame = ptrFrame;
      else
         return ER_STK_FRAME_NOT_OPENED;
   }
   return GOOD;
}

/*************************************************************************
**
**   GetFrameBackLink
**
**   Get the frame pointer back link.
**
***************************************************************************/
RETCODE PRIVATE GetFrameBackLink(DESCRIPTOR frameAddrDesc,
                                 U8 frameType,
                                 U32 frameOffset,
                                 DESCRIPTOR *backFrameAddrDesc) {
   BOOLEAN fpOnly = FALSE;
   DESCRIPTOR copyFramePtrDesc;
   RETCODE error;
   LPU8 memBuffer;
   U32 memSize, offset;

   *backFrameAddrDesc = NULL;
   if( ((cpu==PROC_CPU_CPU16)&&(frameType<=COSMIC_1ST_PARM_IN_DE_REG))
       || ((cpu==PROC_CPU_CPU16)&&(frameType==HICROSS_FRAME_TYPE)) )
      fpOnly = TRUE;
   
   /*
   **  Frame ptr and stack ptr are stack addresses
   **  and therefor logical addresses. Get the absolute address
   **  of the frame ptr.
   **
   **  steps of the UNLK instruction in CPU32 world
   **  A6 => SP  unlink (deallocate) locals from stack space
   **  (SP) => A6 restore old frame pointer
   */
   if( (error = AdrDuplicateAddress(frameAddrDesc,&copyFramePtrDesc))
       != GOOD ) return(error);
   if( (error = PopStack(copyFramePtrDesc,frameOffset)) != GOOD ) {
      AdrDestroyAddress(copyFramePtrDesc);
      return(error);
   }
   memSize = defaultStackSize;
   if( !fpOnly ) memSize *= 2;
   if ((error = StkSetAddrSpace(copyFramePtrDesc)) != GOOD) {
      AdrDestroyAddress(copyFramePtrDesc);
      return error;
   }
   if ((error = MemReadSized(copyFramePtrDesc,memSize,&memBuffer,
         BYTE_SIZE,CACHE_USE)) != GOOD) {
      TFree((LPSTR)memBuffer);
      AdrDestroyAddress(copyFramePtrDesc);
      return error;
   }
   AdrDestroyAddress(copyFramePtrDesc);
   if( !fpOnly ) {
      U32 *memBufferLongValue;
      FlipBuffer(memBuffer,4);
      memBufferLongValue = (U32 *)memBuffer;
      offset = *memBufferLongValue;
      if( (cpu==PROC_CPU_CPU16) && (frameType==INTROL_FRAME_TYPE) ) 
         offset &= 0xfffffL;     // E:Z need to AND off upper bits 
   }
   else {
      // Cosmic and HiCross only pushes the lower 16 bits of it frame pointer
      // need to get current upper nibble from incoming frame pointer.
      U16 *memBufferWordValue;
      U32 frameNibble;  
      if((error = AdrGetAddrOffset(frameAddrDesc,&frameNibble)) != GOOD)
          return error;
      FlipBuffer(memBuffer,2);
      memBufferWordValue = (U16 *)memBuffer;
      offset = (U32)((frameNibble&0xf0000L) | (U32)*memBufferWordValue);
   }      
   if (offset != 0) {
      if ((error = AdrDuplicateAddress(frameAddrDesc, backFrameAddrDesc))
          != GOOD) return error;
      if ((error = AdrSetAddrOffset(*backFrameAddrDesc,offset))
          != GOOD) {
         // NOTES: Nghia - 10/07/93
         // Do not destroy the *backFrameAddrDesc - Caller will destroy
         return error;
      }
   }
   if ((error = TFree((LPSTR)memBuffer)) != GOOD) return error;
   return GOOD;
}

/************************************************************************
**
**  FindFrameReturnPC
**
**  Creates address descriptors for return PC and return PC stack location.
**  Calcalates the PC stack location by adding the pcOffset to the frameDesc.
**  Then calls the get address at location function to fill in the return
**  PC address descriptor's offset.
**  NOTES: Caller responsible for destroy descriptors (if they're not NULL)
**
*************************************************************************/
RETCODE PRIVATE FindFrameReturnPC(DESCRIPTOR frameDesc,
                                  U32 pcOffset,
                                  DESCRIPTOR *retPCLocationDesc,
                                  DESCRIPTOR *retPCDesc) {
   RETCODE error;
   /*
   **  determine the absolute address for the return PC for the frame
   */
   *retPCLocationDesc = NULL;
   *retPCDesc = NULL;
   
   if ((error = AdrDuplicateAddress(frameDesc,retPCLocationDesc)) != GOOD)
      return error;
   if( (error = PopStack(*retPCLocationDesc,pcOffset)) != GOOD ) 
      return error;
   if ((error = AdrDuplicateAddress(frameDesc,retPCDesc)) != GOOD) 
      return error;
 
   if( (error=GetAddressAtLocation(*retPCLocationDesc,*retPCDesc)) != GOOD ) 
      return(error);

   // NOTES: Nghia - 10/06/93
   // In CPU16, the return PC is subtract $02 to get the actual return
   // PC value -  Adjust returnPC value for stack frame and symbol lookup.
   if (cpu == PROC_CPU_CPU16) {
      if ((error = AdrSubtractFromAddress(*retPCDesc,
                                           CPU16_RETURN_PC_OFFSET)) != GOOD) {
         return(error);
      }
   }

   return GOOD;
}

/*************************************************************************
**
** GetAddressAtLocation
**
**************************************************************************/
RETCODE PRIVATE GetAddressAtLocation(DESCRIPTOR locationDesc,
                                     DESCRIPTOR addrDesc) {
   LPU8 memBuffer;
   U32 *memBufferLongValue;
   RETCODE err;
   if ((err = StkSetAddrSpace(locationDesc)) != GOOD) {
      AdrDestroyAddress(addrDesc);
      return err;
   }
   if((err=MemReadSized(locationDesc,stackUnitSize*2,&memBuffer,BYTE_SIZE,
                        CACHE_USE)) != GOOD) {
      TFree((LPSTR)memBuffer);
      AdrDestroyAddress(addrDesc);
      return err;
   }
   FlipBuffer(memBuffer,4);
   memBufferLongValue = (U32 *)memBuffer;
   // AND off upper E or PC extension nibbles in HC16 world
   // shouldn't hurt anything in 68K world.  
   *memBufferLongValue &= stkMaxAddress;
   if ((err = AdrSetAddrOffset(addrDesc,*memBufferLongValue)) != GOOD) {
      AdrDestroyAddress(addrDesc);
      TFree((LPSTR)memBuffer);
      return err;
   }
   if ((err = TFree((LPSTR)memBuffer)) != GOOD) return err;
   return GOOD;
}

/************************************************************************
**
** SearchForReturnPC
**
** NOTES: Caller responsible for clean up if error return.
**
***************************************************************************/
RETCODE SearchForReturnPC(DESCRIPTOR stackDesc,
                          DESCRIPTOR stackBaseDesc,
                          DESCRIPTOR *retPCDesc,
                          DESCRIPTOR *retAddrStackPosDesc,
                          BOOLEAN *isValid) {
   MEM_ADDR_CLASS memClass;
   SYM_TYPE_TYPE symType;
   U32 offset;
   SYM_DESCRIPTOR symDesc,funcDesc,modDesc;
   U32 stackPos;
   BOOLEAN found = FALSE, done = FALSE, noSymbols;
   RETCODE error;
   BOOLEAN abortFromEsc;

   *isValid = FALSE;
   *retPCDesc = NULL;
   *retAddrStackPosDesc = NULL;
   
   // If no symbols loaded no reason to continue still we use symbols
   // to pick our way through the stack.
   if((error=SymCheckForNoSymbols(&noSymbols))!=GOOD) return(error);
   if( noSymbols ) return(GOOD);
   
   if ((error = AdrGetAddrOffset(stackDesc,&stackPos)) != GOOD) return error;
   if (stackPos == 0) return GOOD;
   if((error = AdrDuplicateAddress(stackDesc,retPCDesc))!=GOOD) return error;

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

      if((error=GetAddressAtLocation(stackDesc,*retPCDesc))!=GOOD)
          return error;

      // NOTES: Nghia - 10/06/93
      // In CPU16, the return PC is subtract $02 to get the actual return
      // PC value -  Adjust returnPC value for stack frame and symbol lookup.
      if (cpu == PROC_CPU_CPU16) {
         if ((error = AdrSubtractFromAddress(*retPCDesc,
                                           CPU16_RETURN_PC_OFFSET)) != GOOD)
            return(error);
      }

      if ((error = SymMapAddr2Symbol(*retPCDesc,&memClass,&symType,&offset,
               &symDesc,&funcDesc,&modDesc)) == GOOD) {
         // Any code address a good address
         if (memClass == CODE_ADDR)  {
            if ((error = AdrDuplicateAddress(stackDesc,
                                             retAddrStackPosDesc)) != GOOD)
               return error;
            found = TRUE;
         }
      }
      if( (error=PopStack(stackDesc,defaultStackSize)) != GOOD )
         return(error);
      if (!MoreFrames(stackDesc,stackDesc,stackBaseDesc)) done = TRUE;

   }
   if (found) *isValid = TRUE;
   return GOOD;
}
/***********************************************************************
**
**   FreeStkVarBuf
**
*************************************************************************/
RETCODE FreeStkVarBuf(VS_DISPLAY_PTR displayBufPtr) {
   LPWORD bufptr;
   RETCODE error;
   bufptr = (LPWORD)displayBufPtr;
   if (*bufptr == VS_TMALLOC) {
      if ((error = TFree((LPSTR)displayBufPtr)) != GOOD)
         return error;
   }
   else
      GlobalFree(*bufptr);

   return GOOD;
}

/***************************************************************************
**
**  GetStkBuf :
**
**  This routine allocates a buffer of the requested size.  It uses
**  TMalloc first and if that fails then trys GlobalAlloc.  The
**  allocation method is stored in the first word of the buffer.
**  Note:  This routine does not unlock global allocated memory.
**
**  Inputs:
**      size : size requested in bytes
**
**  Outputs:
**      bufptr : pointer to the allocated buffer
**
***************************************************************************/
RETCODE GetStkBuf(U32 size,
                  LPWORD *bufptr)   {
   HANDLE bufHandle;

   if ((*bufptr = (LPWORD)TMalloc(size)) == NULL)   {
      if ((bufHandle = GlobalAlloc(STACK_WFLAGS,size)) == NULL)
         return ER_OUT_OF_MEMORY;
      if ((*bufptr = (LPWORD)GlobalLock(bufHandle)) == NULL) 
         return ER_WINDOWS_MEMLOCK;
      *(*bufptr) = bufHandle;
   }
   else
      *(*bufptr) = STK_TMALLOC;
   return GOOD;
}

/***********************************************************************
**
**  MoreFrames
**
**  Determine if there are frames in the stack before stack base
**  is reached.
**
***********************************************************************/
BOOLEAN PRIVATE MoreFrames(DESCRIPTOR currFrameAddrDesc,
                           DESCRIPTOR currStackDesc,
                           DESCRIPTOR baseAddrDesc) {
   ADDR_COMPARE results;
   RETCODE error;
   U32 offset=0L;
   /*
   ** is the current frame valid? maybe there are no frames yet!
   */
   if( (currFrameAddrDesc!=NULL)
       && (error = AdrGetAddrOffset(currFrameAddrDesc,&offset)) != GOOD)
      return error;
   if (offset == 0L) {
      /*
      ** don't have a current frame ptr but there could still be
      ** functions on the stack, since not every function may create
      ** its own frame.  So check the SP just to make sure it isnt
      ** equal to the base of the stack.
      **
      **  determine if there are more frames on the stack before
      **  the stack base.  Can not search past stack base.
      **  Stack base depends on stack growth direction too.
      */
      if (AdrCompareAddresses(baseAddrDesc,currStackDesc,&results) != GOOD)
         return FALSE;
   }
   else {
      /*
      **  determine if there are more frames on the stack before
      **  the stack base.  Can not search past stack base.
      **  Stack base depends on stack growth direction too.
      */
      if (AdrCompareAddresses(baseAddrDesc,currFrameAddrDesc,
                              &results) != GOOD)
         return FALSE;
   }
   if( ((stackSex == LOW_TO_HI) && (results == SECOND_ADDR_GREATER))
       || ((stackSex == HI_TO_LOW) && (results == FIRST_ADDR_GREATER)) )
      return(TRUE);
   return FALSE;
}

/*************************************************************************
**
**  FreeFrameNode
**
**************************************************************************/
RETCODE PRIVATE FreeFrameNode(STK_FRAME_NODE *node) {
   RETCODE error;

   if (node != NULL) {
      if (node->frameDesc != NULL) {
         if ((error = AdrDestroyAddress(node->frameDesc)) != GOOD)
            return error;
      }
      if (node->returnPCDesc != NULL) {   
         if ((error = AdrDestroyAddress(node->returnPCDesc)) != GOOD)
            return error;
      }
      if (node->funcInfo.funcAddrRangeDesc != NULL) {   
         if ((error = AdrDestroyAddress(node->funcInfo.funcAddrRangeDesc))
            != GOOD)
            return error;
      }
      if ((error = TFree((LPSTR)node)) != GOOD)
            return error;
      node = NULL;
   }
   return GOOD;
}

/***********************************************************************
**
**   ReleasePreviousFrames
**
**   Delete all frames from the linked list.
**
*************************************************************************/
RETCODE ReleasePreviousFrames() {

   STK_FRAME_NODE *p;
   RETCODE error;

   while (stkFrameList != NULL) {
      p = stkFrameList;
      stkFrameList = p->next;
      if ((error = FreeFrameNode(p)) != GOOD)
         return error;
   }
   return GOOD;
}
/***************************************************************************
**
** StkAnyFrames
**
**************************************************************************/
RETCODE EXPORT StkAnyFrames(DESCRIPTOR stkSessionId, BOOLEAN FAR *anyFrames) {
   DESCRIPTOR stackDesc, stackBaseDesc;
   RETCODE error;
   BOOLEAN isValid;

#ifdef DEBUG_MEM_LEAKS
   if ((error = AdrGetAllocCounts(&allocAddrs,&destroyedAddrs)) != GOOD)
      return error;
#endif
   // Frame pointer any be bonus but not NULL so just check SP.
   *anyFrames = FALSE;
   if ((error = StkGetStackBase(stkSessionId,&stackBaseDesc,
                                &isValid)) != GOOD) goto CLEANUP0;

   if (!isValid) {
      error = ER_STK_STK_BASE_INVALID;
      goto CLEANUP0;
   }

   if ((error = CpuGetSP(&stackDesc)) != GOOD) goto CLEANUP1;

   *anyFrames = MoreFrames(NULL,stackDesc,stackBaseDesc);

CLEANUP2:
   if ((error = AdrDestroyAddress(stackDesc)) != GOOD) return error;

CLEANUP1:
   if ((error = AdrDestroyAddress(stackBaseDesc)) != GOOD) return error;

CLEANUP0:
#ifdef DEBUG_MEM_LEAKS
   if ((error = AdrGetAllocCounts(&allocAddrs,&destroyedAddrs)) != GOOD)
      return error;
#endif

   return error;
}

/***************************************************************************
**
**  StkGetFrameSymbol
**
**  Purpose:
**      Given the frame number (0 to N-1 for N frames) return the
**      symbol descriptor for the function and module associated with the
**      frame.  Also provide an address descriptor for the address range
**      of the function.  This address descriptor is created by the
**      stack server and should be destroyed by the caller.
**
**  Input:
**      stkSessionId: stack selector
**      frameNum: the frame number
**
**  Output:
**      funcDesc: the symbol descriptor for the function associated
**         with the requested frame.
**      modDesc: the symbol descriptor for the module associated with the
**         requested frame.
**      funcAddrRangeDesc: the address descriptor for the address range of
**         the function.
**
**  Error:  ER_STK_FRAME_INVALID
**          ER_STK_INVALID_DESCRIPTOR
**
***************************************************************************/
RETCODE EXPORT StkGetFrameSymbol(DESCRIPTOR stkSessionId,
                                 U8 frameNum,
                                 SYM_DESCRIPTOR *funcDesc,
                                 SYM_DESCRIPTOR *modDesc,
                                 DESCRIPTOR *funcAddrRangeDesc) {
   STK_FRAME_NODE  *frame;
   RETCODE error;

   /*
   **   Should use the stkSessionId to get the correct
   **   frame list but for now just use the static one stkFrameList
   */
   if (!stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;
   if ((error = FindFrame(frameNum,&frame)) != GOOD)
      return error;
   *funcDesc = frame->funcInfo.functionDesc;
   *modDesc = frame->funcInfo.moduleDesc;
   if ((error = AdrDuplicateAddress(frame->funcInfo.funcAddrRangeDesc,
         funcAddrRangeDesc)) != GOOD)
      return error;
   return GOOD;
}

/***********************************************************************
** StkOpenStack
**
**   Purpose:
**      To open the stack or portions of the stack for perusal.  This
**      places the associated frame functions into a frame table for
**      future reference.
**
**  !!!! Please note that for expediancy the entire stack is opened
**       for the beta release.
**
**   Input parameters:
**      frameCount : suggested number of frames to open starting at
**         stack top which is frame 0.  If frameCount is 0, the stack
**         server will default to 4 frames.
**      stkSessionId : stack identifier
**
**   Output parameters:
**      frameCount : the total number of displayable frames found
**
**   Error:
**      Returns ER_STK_STK_BASE_UNKNOWN if no stack base has
**      been determined.
**      Returns ER_STK_FRAME_RELEASE_FAILED if there is some problem
**      releasing the previous stack frames (deallocation problem).
**
*************************************************************************/
RETCODE EXPORT StkOpenStack(U8 *frameCount,
                            DESCRIPTOR stkSessionId) {
   RETCODE err;
   MEM_ADDR_CLASS memClass;
   SYM_TYPE_TYPE symType;
   U32 offset;
   SYM_DESCRIPTOR symDesc,funcDesc,modDesc;
   STK_FRAME_NODE *frame;
   DESCRIPTOR retPCDesc=NULL, retPCLocationDesc=NULL, backLinkFrameDesc=NULL,
              stackDesc=NULL, baseAddrDesc=NULL, lastFoundFramePtr=NULL,
              frameAddrDesc=NULL, PCAddrDesc=NULL;
   U8 frameNumCount = 0;
   ADDR_COMPARE results;
   BOOLEAN isValid;
   FUNC_INFO_TYPE funcInfo;
   BOOLEAN abortFromEsc;

#ifdef DEBUG_MEM_LEAKS
   if ((err = AdrGetAllocCounts(&allocAddrs,&destroyedAddrs)) != GOOD)
      return err;
   if ((err = HeapDump(&memAlloc,&memFreed)) != GOOD)
      return err;
#endif

   *frameCount = 0;
   if ((err = StkGetStackBase(stkSessionId,&baseAddrDesc,
         &isValid)) != GOOD) goto CLEANUP0;
   if (!isValid) {
      err = ER_STK_STK_BASE_INVALID;
      goto CLEANUP0;
   }
   if ((err = CpuGetSP(&stackDesc)) != GOOD) goto CLEANUP1;
   if ((err = AdrCompareAddresses(baseAddrDesc,stackDesc,&results))
         != GOOD) goto CLEANUP2;
   if (results == EQUAL_ADDRS){
      err = GOOD;
      goto CLEANUP2;
   }
   /*
   ** Add bytes near top of stack to memory server cache to accelerate reading
   ** them later.
   */
   {
      U32 spOffset, baseOffset, length;
      ADDR_SPACE addrSpace;
      if ((err = AdrGetAddrOffset(stackDesc,&spOffset)) != GOOD)
         goto CLEANUP2;
      if ((err = AdrGetAddrOffset(baseAddrDesc,&baseOffset)) != GOOD)
         goto CLEANUP2;
      if ((err=StkGetAddrSpace(&addrSpace)) != GOOD)
         goto CLEANUP2;
      switch (stackSex) {
         case HI_TO_LOW:
            length = min(baseOffset - spOffset, SIZE_DUMP);

            if ((err = MemReadQuick(spOffset,addrSpace,length,NULL,
               SIZE_WORD,CACHE_USE)) != GOOD) goto CLEANUP2;
            break;
         case LOW_TO_HI:
            length = min(spOffset - baseOffset, SIZE_DUMP);
            if ((err = MemReadQuick(spOffset-length,addrSpace,length,NULL,
               SIZE_WORD,CACHE_USE)) != GOOD) goto CLEANUP2;
            break;
      }
   }

   if (stkFrameList != NULL) {
      if ((err = ReleasePreviousFrames()) != GOOD) {
         stkFrameList = NULL;
         err = ER_STK_FRAME_RELEASE_FAILED;
         goto CLEANUP2;
      }
   }
   if ((err = CpuGetPC(&PCAddrDesc)) != GOOD)
      goto CLEANUP2;
   if ((err = AdrGetAddrOffset(PCAddrDesc,&offset)) != GOOD)
      goto CLEANUP3;
   if (offset == 0) {
      err = GOOD;         /* no PC so no frames */
      goto CLEANUP3;
   }
   
   if ((err = CpuGetFramePtr(&lastFoundFramePtr)) != GOOD)
      goto CLEANUP3;
   
   if ((err = SymMapAddr2Symbol(PCAddrDesc,&memClass,&symType,&offset,
                                &symDesc,&funcDesc,&modDesc)) != GOOD) {
      // Returns an err based on address discrepancies
      // not lack of symbol information. 
      goto CLEANUP4;
   }

   if ((funcDesc == NULL_SYMBOL) && (memClass != CODE_ADDR))  {
      err = GOOD;
      goto CLEANUP4;
   }

   
   if ((err=GetFuncInfo(&funcInfo, symDesc, symType, funcDesc,
			modDesc)) != GOOD) goto CLEANUP5;
      
   /*
   **  While there's still stuff on stack between processing point
   **  (stackDesc) and base then continue.
   */
   while (MoreFrames(NULL,stackDesc,baseAddrDesc)) {
      U32 frameOffset=0, pcOffset = 0;

      err = TskCheckAbort(&abortFromEsc);
      if(err!=GOOD) {
         goto CLEANUP5;
      }
      if (abortFromEsc!=0) {
         err = ER_ABORT_FROM_ESC;
         goto CLEANUP5;
      }

      if( ((cpu == PROC_CPU_CPU32) && (funcInfo.frameType==CPU32_FRAME_TYPE))
       || ((cpu == PROC_CPU_68000) && (funcInfo.frameType==CPU32_FRAME_TYPE))
       || ((cpu==PROC_CPU_CPU32P)&&(funcInfo.frameType==CPU32_NO_FRAME_TYPE))
       || ((cpu==PROC_CPU_CPU16)&&(funcInfo.frameType!=CPU16_NO_FRAME_TYPE))){
         pcOffset = retPCOffset;
         if( cpu == PROC_CPU_CPU16 ) {
            frameOffset = funcInfo.stackSize;
            pcOffset += frameOffset; 
            if( funcInfo.frameType <= COSMIC_1ST_PARM_IN_DE_REG ) {
               pcOffset += funcInfo.frameType * 2; // size of 1st parm.
               pcOffset -= 2;  /* Cosmic only pushes X register for fp */
            }
            else if( funcInfo.frameType == HICROSS_FRAME_TYPE ) {
               frameOffset = 0;
               pcOffset = funcInfo.stackSize + 2; /*hicross only pushes Z*/
            }
         }
         // else it's all setup for CPU32 types
         if( offset != 0 ) {
            if( lastFoundFramePtr == NULL ) goto EXIT_LOOP;
            frameAddrDesc = lastFoundFramePtr;
            lastFoundFramePtr = NULL;
            if((err=GetFrameBackLink(frameAddrDesc,funcInfo.frameType,
                                     frameOffset,&backLinkFrameDesc)) != GOOD)
               goto CLEANUP6; 
               
            if( backLinkFrameDesc != NULL ) {
               // lastFoundFramePtr = (framePtr) alias backLinkFrameDesc.
               if((err=AdrDuplicateAddress(backLinkFrameDesc,
                                           &lastFoundFramePtr)) != GOOD) 
                  goto CLEANUP6; 
            }
            // get returnPC in retPCDesc.
            if( (err=FindFrameReturnPC(frameAddrDesc,pcOffset,
                                       &retPCLocationDesc,&retPCDesc))
                !=GOOD ) goto CLEANUP6;
         }
      }
      if( ((cpu==PROC_CPU_CPU32) && (funcInfo.frameType==CPU32_NO_FRAME_TYPE))
        || ((cpu==PROC_CPU_68000) &&(funcInfo.frameType==CPU32_NO_FRAME_TYPE))
        || ((cpu==PROC_CPU_CPU32P)&&(funcInfo.frameType==CPU32_NO_FRAME_TYPE))
        ||((cpu==PROC_CPU_CPU16) && (funcInfo.frameType==CPU16_NO_FRAME_TYPE))
        || (offset == 0) ) {
         // else Treat frame'd routine that we haven't entered
         // yet (i.e.: offset == 0) as a no frame'd routine
         // if offset == 0 and this is the top frame (i.e.: framenum==0)
         // then there is no valid stack frame available regardless
         // of the stack frame type until we get past the "link"
         // instruction(s).

         // No reason to check at least this many locations for
         // return PC as long as we are "inside" the routine
         if( offset &&
             ((err=PopStack(stackDesc,funcInfo.stackSize)) != GOOD) )
            goto CLEANUP6;
      
         // !! also check pushReg mask to pop off these locations???
            
         // finds return PC and framePtr.
         // frame pointer for no frame routine is the location
         // where return PC is.
         if((err=SearchForReturnPC(stackDesc,baseAddrDesc,&retPCDesc,
                                   &frameAddrDesc,&isValid))!=GOOD) 
            goto CLEANUP6;

         if (isValid) {
            if((err=AdrDuplicateAddress(frameAddrDesc,&retPCLocationDesc))
               != GOOD) goto CLEANUP6;
         }
      }
      frameNumCount++;
      if ((frame = (STK_FRAME_NODE *)TMalloc(sizeof(*frame))) == NULL) {
         err = ER_OUT_OF_MEMORY;
         goto CLEANUP6;
      }
         
      if ((err = StoreSymFuncInfo(frame,&funcInfo,frameAddrDesc,
                                  retPCDesc,frameNumCount-1)) != GOOD)
         goto CLEANUP6;

      // address desc copied in StoreSymFuncInfo
      frameAddrDesc = retPCDesc = NULL;
      if ((err = AddFrameToList(frame)) != GOOD) goto CLEANUP6;

      if ((err = SymMapAddr2Symbol(frame->returnPCDesc,&memClass,&symType,
                                   &offset,&symDesc,&funcDesc,&modDesc))
           != GOOD) break;

      // NOTES: Nghia - 10/08/93
      // As long as there are stack frame (i.e. SP within stack area)
      // we continue to process the stack.
      // retPCDesc needs to map to a function type so we can process
      // next routine.
      // ??? 10/26/93 - limit the stack walk to just the first stack frame
      // ??? if it is assembly routine/public label.

      //**Hera 08/19/96
      if ((symType != SYM_FUNCTION) && (symType != SYM_BLOCK) && (symType != SYM_MODULE))
         break;
      if ((funcDesc == NULL_SYMBOL) && (memClass != CODE_ADDR)) {
         err = GOOD;
         break;
      }
      //**Hera

      if ((err = GetFuncInfo(&funcInfo, symDesc, symType,
                             funcDesc, modDesc)) !=GOOD) break;
      
      // Process NO_FRAME_TYPE stack frames
      if (((cpu==PROC_CPU_CPU32)&&(funcInfo.frameType==CPU32_NO_FRAME_TYPE))
        || ((cpu==PROC_CPU_68000)&&(funcInfo.frameType==CPU32_NO_FRAME_TYPE))
        || ((cpu==PROC_CPU_CPU32P)&&(funcInfo.frameType==CPU32_NO_FRAME_TYPE))
        ||((cpu==PROC_CPU_CPU16)&&(funcInfo.frameType==CPU16_NO_FRAME_TYPE))){
         // if a frameless type then the stack processing position
         // is just pass where the returnPC was found.
         if (stackDesc != NULL) AdrDestroyAddress(stackDesc);
         stackDesc = retPCLocationDesc;
         if ((err = PopStack(stackDesc,retPCOffset))!=GOOD) break;
         retPCLocationDesc = NULL;
      }
      else {
         // Else if we have a frame - the backLink points to previous
         // routine's frame pointer.
         // if no backlink then just continue to use current stackDesc
         if (backLinkFrameDesc != NULL) {
            if( stackDesc != NULL ) AdrDestroyAddress(stackDesc);
            stackDesc = backLinkFrameDesc;
         }
         if (retPCLocationDesc) {
            AdrDestroyAddress(retPCLocationDesc);
            retPCLocationDesc = NULL;
         }
         backLinkFrameDesc = NULL;
      }
   }  /* end while */
EXIT_LOOP:
   *frameCount = frameNumCount;

CLEANUP6:
   if( frameAddrDesc != NULL ) AdrDestroyAddress(frameAddrDesc);
   if( backLinkFrameDesc != NULL ) AdrDestroyAddress(backLinkFrameDesc);
   if( retPCDesc != NULL ) AdrDestroyAddress(retPCDesc);
   if( retPCLocationDesc != NULL ) AdrDestroyAddress(retPCLocationDesc);
   
CLEANUP5:
   if( funcInfo.funcAddrRangeDesc != NULL )
      AdrDestroyAddress(funcInfo.funcAddrRangeDesc);
CLEANUP4:
   if( lastFoundFramePtr != NULL )
      AdrDestroyAddress(lastFoundFramePtr);
CLEANUP3:
   AdrDestroyAddress(PCAddrDesc);

CLEANUP2:
   AdrDestroyAddress(stackDesc);

CLEANUP1:
   AdrDestroyAddress(baseAddrDesc);

CLEANUP0:
#ifdef DEBUG_MEM_LEAKS
   if ((err = AdrGetAllocCounts(&allocAddrs,&destroyedAddrs)) != GOOD)
      return err;
   if ((err = HeapDump(&memAlloc,&memFreed)) != GOOD)
      return err;
#endif
   // Guess we really don't want to report any errors.  Errors only
   // tell stack walker it's time to stop.
   return GOOD;
}


/***************************************************************************
**   StkReadFrameLocals
**
**   Purpose:
**      Given the frame number (0 to n-1), return a pointer to a list of
**      descriptors for the locals and pararmeters of a frame.
**
**   Input parameters:
**      stkSessionId : stack identifier
**      frameNum : the frame number to read locals for
**
**   Output parameters:
**      numOfParams:
**         number of function parameters.  The presenter can use this value
**         to only display the parameters (ignoring the locals) if the user
**         has selected this display option.  The assumption is that the
**         parameters are always at the beginning of list of variables.
**
**      firstLocalPtr :
**         pointer to the head of the local variable list.  To access the
**         remaining variables on the list, use the SymGetSymbolSibling until
**         NULL_SYMBOL is returned.
**
**   Error:
**      Returns ER_STK_FRAME_NOT_OPENED if the frame number is invalid
**      NOTE:  Returns FirstLocalPtr = NULL_SYMBOL when error is indicated.
**
***************************************************************************/
RETCODE EXPORT StkReadFrameLocals(DESCRIPTOR stkSessionId,
                                  U8 frameNum,
                                  U8 *numOfParams,
                                  SYM_DESCRIPTOR *firstLocalPtr) {
   STK_FRAME_NODE *p;

   /*
   **  !!!!! Should use the stkSessionId to get the correct
   **        frame list but for now just use the static one stkFrameList
   */
   if (!stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;
   *firstLocalPtr = NULL_SYMBOL;
   if (stkFrameList == NULL)
      return ER_STK_NO_FRAMES;
   p = stkFrameList;
   while ((p != NULL) && (p->frameNum != frameNum))
      p = p->next;
   if (p == NULL)
      return ER_STK_FRAME_NOT_OPENED;
   *numOfParams = p->funcInfo.numParams;
   *firstLocalPtr = p->funcInfo.firstLocal;

   return GOOD;
}

/*************************************************************************
**   StkReadFrameN
**
**   Purpose:
**      This routine is intended for use after the StkOpenStack routine.
**      Given the frame number (0 to n-1 for n frames), return a display
**      buffer containing the formatted frame information.  This routine
**      is used to read any frame, or multiple frames.  A maximum of
**      twenty frames can be read at one time.  Frames are numbered 0 to
**      n-1, the most recent to the oldest.
**
**   Input parameters:
**      stkSessionId : stack identifier
**      frameStart : the starting frame to read
**      frameEnd : the last frame to read
**      interActvie : indicates caller mode, interactive is TRUE and
**         batch is FALSE
**      displayBufPtr : ptr to be set to the new display buffer
**
**   Output parameters:
**      displayBufPtr: pointer to the buffer containing formatted display
**      information of the requested frame or frames.  The display
**      buffer is allocated by the stack server and deallocated by the
**      calling routine (see STK_DISPLAY_TYPE).
**
**   Error:
**      Returns ER_STK_FRAMENUM_INVALID if the frame number is invalid
**      NOTE:  Returns displayBufPtr set to NULL when error is indicated.
**
***************************************************************************/
RETCODE EXPORT StkReadFrameN(DESCRIPTOR stkSessionId,
                             U8 frameStart,
                             U8 frameEnd,
                             BOOLEAN interActive,
                             STK_DISPLAY_PTR *dispPtr) {
   RETCODE error;
   STK_FRAME_NODE *p,*q;
   STRING textptr;
   LPWORD bufptr;
   U16 numLines,textSize;

   /*
   **  !!!! should use the stkSessionId to determine the correct
   **       frame list but for now just use the static one stkFrameList
   */
   
#ifdef DEBUG_MEM_LEAKS
   if ((error = HeapDump(&memAlloc,&memFreed)) != GOOD)
      return error;
#endif
   if (!stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;
   if (stkFrameList == NULL)
      return ER_STK_NO_FRAMES;
   p = stkFrameList;
   q = stkFrameList;
   while ((p != NULL) && (p->frameNum != frameStart))
      p = p->next;
   if (p == NULL)
      return ER_STK_FRAME_NOT_OPENED;
   while ((q != NULL) && (q->frameNum != frameEnd))
      q = q->next;
   if (q == NULL)
      return ER_STK_FRAME_NOT_OPENED;
   /*
   ** calculate number of lines required to display number of frames
   */
   if (frameStart < frameEnd)
      numLines = frameEnd - frameStart + 1;
   else
      numLines = frameStart - frameEnd + 1;
   if (numLines > STK_MAX_LINES_PER_BUF)
       numLines = STK_MAX_LINES_PER_BUF;
   textSize = STK_MAX_CHARS_PER_LINE * numLines;
   if ((error = GetStkBuf(textSize+STK_DISPLAY_BUF_OVERHEAD,&bufptr))
          != GOOD)
      return error;
   *dispPtr = (STK_DISPLAY_TYPE *)bufptr;
   (*dispPtr)->numOfFrames = numLines;
   textptr = (*dispPtr)->text;
   memset(textptr,'\0',textSize);

   if (frameStart < frameEnd)
      error = FormatFrames(p,q,interActive,&textptr);
   else
      error = FormatFrames(q,p,interActive,&textptr);
   
   /* Destroy the allocated buffer if error occurred */
   if (error != GOOD) TFree((LPSTR)bufptr);
   else (*dispPtr)->textLength = textptr - (*dispPtr)->text;
#ifdef DEBUG_MEM_LEAKS
   if ((error = HeapDump(&memAlloc,&memFreed)) != GOOD)
      return error;
#endif
   return(error);
}

/**************************************************************************
**
**  StkReadFrameNAndLocals
**
***************************************************************************/
RETCODE StkReadFrameNAndLocals(DESCRIPTOR stkSessionId,
                               U8 frameStart,
                               U8 frameEnd,
                               BOOLEAN interActive,
                               STK_DISPLAY_PTR *dispPtr) {

   RETCODE error;
   STK_FRAME_NODE *p,*q,*temp;
   STRING textptr;
   LPWORD bufptr;
   U16 numLines,textSize;
   U8 frameCount;
   BOOLEAN abortFromEsc;
   

   /*
   **  !!!! should use the stkSessionId to determine the correct
   **       frame list but for now just use the static one stkFrameList
   */
   if (!stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;
   if (stkFrameList == NULL)
      return ER_STK_NO_FRAMES;
   p = stkFrameList;
   q = stkFrameList;
   while ((p != NULL) && (p->frameNum != frameStart))
      p = p->next;
   if (p == NULL)
      return ER_STK_FRAME_NOT_OPENED;
   while ((q != NULL) && (q->frameNum != frameEnd))
      q = q->next;
   if (q == NULL)
      return ER_STK_FRAME_NOT_OPENED;
   /*
   ** calculate number of lines required to display number of frames
   */
   numLines = 0;
   temp = p;               /* pointer to the starting frame */
   if (frameStart < frameEnd)
      frameCount = frameEnd - frameStart + 1;
   else
      frameCount = frameStart - frameEnd + 1;
   for (;frameCount != 0; frameCount--) {
      if (temp->funcInfo.localListCount)
         numLines += (temp->funcInfo.localListCount + 1);
      else
         numLines += 1;
      if (frameStart < frameEnd)
         temp = temp->next;
      else
         temp = temp->previous;
   } /* end for */
   textSize = STK_MAX_CHARS_PER_LINE * numLines;
   if ((error = GetStkBuf(textSize+STK_DISPLAY_BUF_OVERHEAD,&bufptr))
          != GOOD)
      return error;
   *dispPtr = (STK_DISPLAY_TYPE *)bufptr;
   (*dispPtr)->numOfFrames = numLines;
   textptr = (*dispPtr)->text;
   memset(textptr,'\0',textSize);

   while (p != q) {
      if ((error = TskCheckAbort(&abortFromEsc)) != GOOD) return error;
      if (abortFromEsc!=0) return ER_ABORT_FROM_ESC;
      if ((error = GetSingleFrame(p,&textptr,interActive)) != GOOD)
         return error;
      GetEndOfLine(&textptr);
      if (p->funcInfo.localListCount) {
         if ((error = GetParamsForFrame(p,&textptr,interActive)) != GOOD)
            return error;
         GetEndOfLine(&textptr);
      }
      if (p->frameNum < q->frameNum)
         p = p->next;
      else
         p = p->previous;
   }
   if (p == q) {  /* get last one or only one */
      if ((error = GetSingleFrame(p,&textptr,interActive)) != GOOD)
         return error;
      GetEndOfLine(&textptr);
      if ((error = GetParamsForFrame(p,&textptr,interActive)) != GOOD)
         return error;
      GetEndOfBuffer(&textptr);
   }
   (*dispPtr)->textLength = textptr - (*dispPtr)->text;
   return error;
}

/************************************************************************
**
**  StkReadFrameHex
**
**************************************************************************/
RETCODE StkReadFrameHex(DESCRIPTOR stkSessionId,
                        U8 frameStart,
                        U8 frameEnd,
                        STK_DISPLAY_PTR *dispPtr) {

   STRING textPtr;
   LPWORD bufptr;
   U16 numLines,textSize;
   U32 count,readBytes;
   U32 stackSize,stackBaseOffset,spOffset;
   LPU8 memBuffer,aByte;
   DESCRIPTOR spDesc,stackBaseDesc;
   STK_FRAME_NODE *p,*q;
   BOOLEAN isValid;
   RETCODE error;
   BOOLEAN abortFromEsc;


   /*
   **  !!!! should use the stkSessionId to determine the correct
   **       frame list but for now just use the static one stkFrameList
   */
   if (!stkSessionId)
      return ER_STK_INVALID_DESCRIPTOR;
   if (stkFrameList == NULL)
      return ER_STK_NO_FRAMES;

   /*
   ** check to make sure that starting and ending frames
   ** are ok.
   */
   p = stkFrameList;
   q = stkFrameList;
   while ((p != NULL) && (p->frameNum != frameStart))
      p = p->next;
   if (p == NULL)
      return ER_STK_FRAME_NOT_OPENED;
   while ((q != NULL) && (q->frameNum != frameEnd))
      q = q->next;
   if (q == NULL)
      return ER_STK_FRAME_NOT_OPENED;
   if ((error = StkGetStackBase(stkSessionId,&stackBaseDesc,
         &isValid)) != GOOD)
      return error;
   if ((error = AdrGetAddrOffset(stackBaseDesc,&stackBaseOffset)) != GOOD)
      return error;
   if ((error = CpuGetSP(&spDesc)) != GOOD)
      return error;
   if ((error = AdrGetAddrOffset(spDesc,&spOffset)) != GOOD)
      return error;

   switch (stackSex) {
      case HI_TO_LOW:
         stackSize = stackBaseOffset - spOffset;
         break;
      case LOW_TO_HI:
         stackSize = spOffset - stackBaseOffset;
         if((error=AdrSubtractFromAddress(spDesc,HEX_DISPLAY_BYTES)) != GOOD)
            return error;
         break;
   }
   /*
   **  determine the number of lines required to display
   */
   numLines = (stackSize/HEX_DISPLAY_BYTES) + 1;
   textSize = STK_MAX_CHARS_PER_LINE * numLines;
   if ((error = GetStkBuf(textSize+STK_DISPLAY_BUF_OVERHEAD,&bufptr))
          != GOOD)
      return error;
   *dispPtr = (STK_DISPLAY_TYPE *)bufptr;
   (*dispPtr)->numOfFrames = numLines;
   textPtr = (*dispPtr)->text;
   memset(textPtr,'\0',textSize);

   while (stackSize > 0) {
      if ((error = TskCheckAbort(&abortFromEsc)) != GOOD) return(error);
      if (abortFromEsc !=0) return ER_ABORT_FROM_ESC;
      readBytes = (stackSize < HEX_DISPLAY_BYTES) ?
             stackSize:HEX_DISPLAY_BYTES;
      if ((error = AdrConvAddressToText(spDesc,(LPSTR)textPtr)) != GOOD)
         return error;
      GetBlankSpace(&textPtr);
      if ((error = StkSetAddrSpace(spDesc)) != GOOD)
         return error;
      if ((error = MemReadSized(spDesc,readBytes,&memBuffer,BYTE_SIZE,
                                CACHE_USE)) != GOOD) {
         TFree((LPSTR)memBuffer);
         return error;
      }
      /*
      ** convert the buffer to ascii bytes and copy to the buffer
      */
      aByte = memBuffer;
      count = readBytes;
      while (count--) {
         if (*aByte < HEX_RADIX) {
            strcat((LPSTR)textPtr,"0");
            textPtr++;
         }
         itoa(*aByte,(LPSTR)textPtr,HEX_RADIX);
         GetBlankSpace(&textPtr);
         textPtr += lstrlen((LPSTR)textPtr);
         aByte++;
      }
      if (stackSize - readBytes > 0)
         GetEndOfLine(&textPtr);
      else
         GetEndOfBuffer(&textPtr);
      if ((error = TFree((LPSTR)memBuffer)) != GOOD) return error;
      if( (error=PopStack(spDesc,HEX_DISPLAY_BYTES)) != GOOD ) return(error);
      stackSize -= readBytes;
   }
   (*dispPtr)->textLength = textPtr - (*dispPtr)->text;
   return GOOD;
}

/***************************************************************************
**
**  StoreSymFuncInfo
**
**  Store the relevant frame information into the frame struct node.
**
**************************************************************************/
RETCODE PRIVATE StoreSymFuncInfo(STK_FRAME_NODE *frame,
                                 FUNC_INFO_TYPE *funcInfo,
                                 DESCRIPTOR frameAddrDesc,
                                 DESCRIPTOR PCAddrDesc,
                                 U8 frameNum) {
   frame->frameNum = frameNum;
   frame->frameDesc = frameAddrDesc;
   frame->returnPCDesc = PCAddrDesc;
   frame->next = NULL;
   frame->previous = NULL;
   memmove(&frame->funcInfo,funcInfo,sizeof(*funcInfo));
   frame->funcInfo.funcNamePtr = (LPSTR)frame->funcInfo.funcName;
   funcInfo->funcAddrRangeDesc = NULL;   // desc has been copied to frame
   return(GOOD);
}

/************************************************************************
**
** StkVarIsActive
**
** Purpose:
**    Returns a boolean indicating if the given variable context
**    exists within the call stack.
**
** Input parameters:
**    varModule : variable's module descriptor
**    varFunc : variable's function descriptor
**    varLow : variable's lowest level descriptor
**
** Output parameters:
**    Returns TRUE if a match occurs else returns FALSE.
**    varFrame : returns the frame at which the first occurance of the variable
**               is active.
**
** Error:
**
*************************************************************************/
RETCODE EXPORT StkVarIsActive(SYM_DESCRIPTOR varModule,
                              SYM_DESCRIPTOR varFunc,
                              SYM_DESCRIPTOR varLow,
                              U8 *varFrame) {

   STK_FRAME_NODE *ptrFrame;
   DESCRIPTOR defaultStack = NULL;
   U8 frameCount;
   BOOLEAN retVal = FALSE;

   *varFrame = 0;
   if (stkFrameList == NULL) {
      /* Use the default stack session */
      if (StkCreateStack(&defaultStack) != GOOD)
         return FALSE;
      if (StkOpenStack(&frameCount,defaultStack) != GOOD) {
         retVal = FALSE;
         goto CLEANUP;
      }
   }
   if (stkFrameList == NULL) {
      retVal = FALSE;
      goto CLEANUP;
   }
   else {
      ptrFrame = stkFrameList;
      while (ptrFrame != NULL) {
         if ((varModule == ptrFrame->funcInfo.moduleDesc) &&
              (varFunc == ptrFrame->funcInfo.functionDesc)) {
            if (varLow == varFunc) {
               retVal = TRUE;
               *varFrame = ptrFrame->frameNum;
               goto CLEANUP;
            }
            else {
               retVal = FALSE;
               goto CLEANUP;
            }
         }
         else
            ptrFrame = ptrFrame->next;
      }

   }
CLEANUP:
  /* NOTES: Nghia - 07/20/93 - Do not destroy the Stack session */
  return retVal;
}

/****************************************************************************
**
** GetFuncInfo
**
*****************************************************************************/
RETCODE PRIVATE GetFuncInfo(FUNC_INFO_TYPE *funcInfo,
                            SYM_DESCRIPTOR symDesc,
                            SYM_TYPE_TYPE  symType,
                            SYM_DESCRIPTOR funcDesc,
                            SYM_DESCRIPTOR modDesc) {
   U8 level;
   U32 attribute;
   TYPE_HEADER_TYPE temphdr;
   U8 tempbuf[MAX_SYMNAME_LENGTH];
   RETCODE error;

   temphdr.typeName = (LPSTR)tempbuf;
   funcInfo->funcNamePtr = (LPSTR)funcInfo->funcName;
   *(funcInfo->funcNamePtr) = '\0';
   funcInfo->functionDesc = funcDesc;
   funcInfo->moduleDesc = modDesc;
   funcInfo->frameType = NULL_FRAME_TYPE;
   funcInfo->numParams = 0;
   funcInfo->firstLocal = 0;
   funcInfo->localListCount = 0;
   if ((error = AdrCreateAddress(&funcInfo->funcAddrRangeDesc)) != GOOD)
      return error;

   if (funcDesc != NULL_SYMBOL) {
      error = SymGetFunc(funcDesc,funcInfo->funcNamePtr,
         &funcInfo->funcClass,&funcInfo->stackSize,
         funcInfo->funcAddrRangeDesc);
      if (!error) {
         error = SymGetFuncType(funcDesc,&attribute,&funcInfo->frameType,
                                &funcInfo->pushMask, &temphdr,
                                &funcInfo->numParams, &level,
                                (LPSTR)tempbuf);
      }
      if (!error)
	 error = SymGetFuncVarHeadList(funcDesc,&funcInfo->firstLocal,
	       &funcInfo->localListCount);
      if (error) {
         AdrDestroyAddress(funcInfo->funcAddrRangeDesc);
         funcInfo->funcAddrRangeDesc = NULL;
         return error;
      }
   }

   else if ((symDesc != NULL_SYMBOL) &&
	      ((symType == SYM_PUBLIC_LABEL)|| (symType == SYM_MODULE)) )
   {
      // NOTES: Nghia - 10/07/93
      // Although the current frame is not a function block if it's a label
      // use the label name.
      DESCRIPTOR symbolName = NULL;
      // Reset all funcInfo
      funcInfo->stackSize = 0;
      funcInfo->numParams = 0;
      funcInfo->pushMask  = 0;

      //**Hera 08/26/96
      if(modDesc != NULL_SYMBOL && symType == SYM_MODULE)
	 SymGetSymbolHeadList(modDesc, SYM_LOCAL_VAR, &funcInfo->firstLocal, &funcInfo->localListCount);
      //**Hera

      if (SymGetSymbolName(symDesc, (DESCRIPTOR FAR *)&symbolName) == GOOD) 
         lstrcpy((LPSTR)(funcInfo->funcName), (LPSTR)symbolName);
      // Always destroy the symbolName
      TFree((LPSTR)symbolName);
     
      // Fill in the frameType for each CPU family
      if (cpu == PROC_CPU_CPU16)
         funcInfo->frameType = CPU16_NO_FRAME_TYPE;
      else
         funcInfo->frameType = CPU32_NO_FRAME_TYPE;
      // Fill in the address range.
      SymGetSymbolAddress(symDesc, (DESCRIPTOR) funcInfo->funcAddrRangeDesc);
   }
      
   return(GOOD);
}

RETCODE PopStack(DESCRIPTOR stackDesc, U32 offset) {
   RETCODE err;
   if( !offset ) return(GOOD);
   switch (stackSex) {
      case HI_TO_LOW :
         err = AdrAddToAddress(stackDesc,offset);
         break;
      case LOW_TO_HI :
         err = AdrSubtractFromAddress(stackDesc,offset);
         break;
   }
   return(err);
}
   
VOID FlipBuffer(LPU8 buffer, U32 numBytes) {
   //  This routine can just be bypassed if target or host byte order
   //   happens to change.
   U8 temp, highest;
   LOOP_VAR i, end;
   if( numBytes % 2 ) return;  //  just get out if numBytes not even
   end = (LOOP_VAR)(numBytes / 2);  // speed up for compare
   highest = numBytes - 1;
   for(i=0; i < end; i++) {
      temp = buffer[i];
      buffer[i] = buffer[highest-i];
      buffer[highest-i] = temp;
   }
}

/******************************** E O F ***********************************/

