/*****************************************************************************
**
**    Name:    SWAT.C
** 
**    Description: 
**       PA and CA server
**
**    Status: PRELIMINARY
**
**    $Log:   S:/tbird/mt2_68k/trace/swat.c_v  $
** 
**    Rev 1.0   23 Mar 1998 10:36:18   Cathy
** Initial revision.
** 
**    Rev 1.0   13 Feb 1997 09:07:22   gene
** Initial revision.
** 
**    Rev 1.0   07 Sep 1995 11:22:22   gene
** Initial revision.
** 
**    Rev 1.14   30 Apr 1993 14:00:32   ernie
** Started debugging performance analysis.  Checking in file because
** I will be interrupting this project for a few months.
** 
**    Rev 1.13   10 Aug 1992 07:45:08   tom
** CLI registration changes.
** 
**    Rev 1.12   24 Jul 1992 14:44:40   doug
** using generic shared data
** 
**    Rev 1.11   10 Jul 1992 09:01:18   mindy
** added include file(s) needed to get rid of 3.1 warnings
** 
**    Rev 1.10   25 Jun 1992 12:32:18   mindy
** moved membername routines to trig.c
** 
**    Rev 1.9   14 May 1992 11:38:38   mindy
** removed GetErrorText routine and added ErrInitDLL call
** 
**    Rev 1.8   13 May 1992 08:12:06   mindy
** added get error text routine
** 
**    Rev 1.7   23 Apr 1992 11:16:16   mindy
** a) Ca and Pa opens now get their own event Id - no longer passed in
**    by the caller.
** b) changed cli commands to not require use to enter an id for each
**    command.
** 
**    Rev 1.6   27 Jan 1992 11:33:24   mindy
** SdRead/WriteMemberName is used by trace and trig.dlls
** 
**    Rev 1.5   27 Jan 1992 09:40:42   courtney
** Code is now integrated with the current version of the symbol server.
** 
**    Rev 1.4   17 Jan 1992 11:23:10   courtney
** Revised LibMain return type.
** 
**    Rev 1.3   07 Jan 1992 10:12:26   ernie
** use BOOLEAN not BOOL
** 
**    Rev 1.2   11 Dec 1991 15:01:24   mindy
** a) swat server in its own dll now.
** b) U48ToDouble was causing a floating point exception I think because
** it was trying to "return" a double (a 64bit) value.  There were no warnings
** given but it seems to work now that we return the results via a pointer.
** c)  Several portions of code are comments out because a floating point
** divide OF zero (not by zero) seems to cause floating point exceptions too!
** 
**    Rev 1.1   06 Dec 1991 10:34:58   mindy
** a) added to trace makefile
** b) added event programing
** c) added cli command support
** 
**    Rev 1.0   27 Nov 1991 12:33:46   ernie
** First pass at pa/ca/timestamp server.  Still to be done:
** 1. Add programming for pa, ca events when SwatSetActive is called.
** 2. This version is compatible with symblsvr.h version 1.32.1.1.  This
**    header will be changing as the languages group migrates toward
**    using the address server, necessitating further changes here.
** 3. "stubs.h" is included to provide the white-box test support for
**    the currently unimplemented symbol table routines.
**  
**    $Header:   S:/tbird/mt2_68k/trace/swat.c_v   1.0   23 Mar 1998 10:36:18   Cathy  $
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#include "stdlib.h"
#ifndef __STRING_H
#include <string.h>
#endif
#ifndef _BASEWIND_
#include "basewind.h"
#endif
#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif
#ifndef _SWAT_
#include "swat.h"
#endif
#ifndef _SSHARED_
#include "sshared.h"
#endif
#ifndef _SDPROBE_
#include "sdprobe.h"
#endif
#ifndef _SYMBLSVR_
#include "symblsvr.h"
#endif
#ifndef _SYMERROR_
#include "symerror.h"
#endif
#ifndef _CLISRV_
#include "clisrv.h"
#endif
#ifndef _EVENT_
#include "event.h"
#endif
#ifndef _LOCAL_
#include "local.h"
#endif
#ifndef _HEAP_
#include "heap.h"
#endif

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


#define ER_BAD_NUM_PARM     (E_SEVERE +MODULE_SWAT+ 0)
#define ER_NOT_CA_MODE      (E_SEVERE +MODULE_SWAT+ 1)
#define ER_NOT_PA_MODE      (E_SEVERE +MODULE_SWAT+ 2)
#define ER_NOT_TS_MODE      (E_SEVERE +MODULE_SWAT+ 3)
#define ER_NO_MODE          (E_SEVERE +MODULE_SWAT+ 4)
#define ER_CLOCK_ZERO       (E_SEVERE +MODULE_SWAT+ 5)
#define ER_NOT_IN_CA_REGION (E_WARNING+MODULE_SWAT+ 6)
#define ER_NOT_ACTIVE_DESC  (E_SEVERE +MODULE_SWAT+ 7)

#define TMOD_40MHZ 2              /* min compatibility to have 2 vram banks */

#define MAX_SYMBOL 64

#define REGION_SIZE (0x80000L)     /*   size of one coverage region in bits */
#define REGION_GRAN 4              /* bytes of memory covered by one ca bit */
#define REGION_GRAN_SHIFT 2 
#define REGION_COVERAGE ((U32)(REGION_SIZE * REGION_GRAN))  /* bytes/region */

typedef struct {
   U32 lsd;
   U16 msw;
} U48;

typedef struct {
   SWAT_MODE mode;
   U32 regionBase[NUM_CA_RANGES];
   S8 qualifyEvent[MAX_SYMBOL];
   CA_REGION currentRegion;
   HWND hWndModule;
   HWND hWndFunction;
   HWND hWndStatement;
   DESCRIPTOR eventId;
   HANDLE hcaData[NUM_CA_RANGES];
   U8 *caData[NUM_CA_RANGES];
   DESCRIPTOR semaphoreDesc;
} CA_DESCRIPTOR;

typedef struct {
   SWAT_MODE mode;
   SWAT_UNIT currentUnit;
   S8 startEvent[NUM_PA_RANGES][MAX_SYMBOL];
   S8 stopEvent[NUM_PA_RANGES][MAX_SYMBOL];
   PA_BIN currentBin;
   DESCRIPTOR eventId;
} PA_DESCRIPTOR;

typedef struct {
   SWAT_MODE mode;
   SWAT_UNIT currentUnit;
} TS_DESCRIPTOR;

typedef union {
   CA_DESCRIPTOR ca;
   PA_DESCRIPTOR pa;
   TS_DESCRIPTOR ts;
} SWAT_DESCRIPTOR;

typedef U32 CA_RESULT;
#define MAX_CA_RESULTS (SIZE_CA_RESULTS / sizeof(CA_RESULT))

                        /****************************
                         *                          *
                         *     LOCAL STORAGE        *
                         *                          *
                         ****************************/

static HANDLE cliServerHandle = 0;  /* return address for CLI results */
static SWAT_DESCRIPTOR *activeDesc;
static U32 framesPerSubbuffer;
static U32 clockRate;  /* in hertz */

                       /****************************
                        *                          *
                        *     LOCAL PROTOTYPES     *
                        *                          *
                        ****************************/
static RETCODE UpdateCoverage(SWAT_DESCRIPTOR FAR *swatDesc);
static RETCODE InCaRegion(SWAT_DESCRIPTOR *swatDesc,
   OFFSET_ADDR_RANGE_TYPE *modRange, BOOLEAN *inRegion, CA_REGION *region);
static U16 GetCaProportion (SWAT_DESCRIPTOR *swatDesc,
                            OFFSET_ADDR_RANGE_TYPE *range, CA_REGION region);
static void AddStringToListbox(HWND hWndBox, char *string);
static RETCODE InitFunctionBox(SWAT_DESCRIPTOR *swatDesc);
static RETCODE InitStatementBox(SWAT_DESCRIPTOR *swatDesc);
static RETCODE SdCmdResp(MEMBER_INDEX cmd, MEMBER_INDEX resp, VOID *data);
static void U48ToDouble(U48 u48, double FAR *result);
static void lmemset(U8 *mem, U16 value, U32 length);
RETCODE PRIVATE MakeRange(DESCRIPTOR Addr, OFFSET_ADDR_RANGE_TYPE *Range);
RETCODE PRIVATE PrintSwatStatus(VOID);


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

#pragma argsused
int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg, WORD cbHeapSize,
    LPSTR lpszCmdLine) {

   if (cbHeapSize != 0)
	   UnlockData(0);	/* allows heap to grow */

   {
      U8 tmodCompatibility;
      if(SdnReadMember(SDN_TRACE_0_COMPATIBILITY,&tmodCompatibility) == GOOD )
         framesPerSubbuffer = (tmodCompatibility >= TMOD_40MHZ ) ? 256L : 128L;
      else
         framesPerSubbuffer = 128L;
   }

#if 0
   {
      BOOLEAN true = TRUE;
      SdCmdResp(SDN_MEASURE_CLOCK,SDN_MEASURE_CLOCK,&true);
      SdnReadMember(SDN_CLOCK_RATE,(U8*)&clockRate);
   }
#endif

#if 1
   clockRate = 16000000L;       /* !!! until swat works */
#endif

   activeDesc = NULL;
   
   ErrInitDLL(MODULE_SWAT,"swat.dll");

   /* initialize successfully */
    return(TRUE);
}

/**************************************************************************
**
** InitCServer
**
***************************************************************************/
RETCODE EXPORT InitCServer(HANDLE cliHandle, HANDLE dllHandle) {
   CSERVER_NEW_REGISTRATION FAR * msgBufPtr;

   cliServerHandle = cliHandle;
   msgBufPtr =
      (CSERVER_NEW_REGISTRATION FAR *)TMalloc(sizeof(CSERVER_VARIABLE_VALUE));
   if (msgBufPtr == NULL) {
      return(ER_OUT_OF_MEMORY);
   }

   msgBufPtr->stringResourceHandle = dllHandle;

   msgBufPtr->serverNameIndex = 30;
   msgBufPtr->dllNameIndex = 31;
   msgBufPtr->numberOfCommandsIndex = 32;
   msgBufPtr->commandStartIndex = 33;
   SendMessage(cliHandle, CLI_NEW_SVR_REGISTRATION, CLI_NEW_SVR_REGISTRATION,
      (DWORD)msgBufPtr);
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatOpenCa
** 
*****************************************************************************/
RETCODE EXPORT SwatOpenCa(DESCRIPTOR FAR *desc, LPSTR templateName) {
   LOOP_VAR i;
   SWAT_DESCRIPTOR *swatDesc;
   RETCODE err;
   if ((swatDesc = (SWAT_DESCRIPTOR FAR *) TMalloc(sizeof(*swatDesc))) == NULL)
      return(ER_OUT_OF_MEMORY);
   swatDesc->ca.mode = SWAT_CA;
   *(swatDesc->ca.qualifyEvent) = NULL;
   swatDesc->ca.currentRegion = CA_REGION0;
   if((err=EvtOpenEvent(&swatDesc->ca.eventId,templateName)) != GOOD)
       return(err);
   for (i=0; i<NUM_CA_RANGES; i++)
      swatDesc->ca.regionBase[i] = i*REGION_COVERAGE;
   swatDesc->ca.hWndModule = NULL;
   swatDesc->ca.hWndFunction = NULL;
   swatDesc->ca.hWndStatement = NULL;
   *desc = (DESCRIPTOR) swatDesc;
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatOpenPa
** 
*****************************************************************************/
RETCODE EXPORT SwatOpenPa(DESCRIPTOR FAR *desc, LPSTR templateName) {
   LOOP_VAR i;
   SWAT_DESCRIPTOR *swatDesc;
   RETCODE err;
   if ((swatDesc = (SWAT_DESCRIPTOR *) TMalloc(sizeof(*swatDesc))) == NULL)
      return(ER_OUT_OF_MEMORY);
   swatDesc->pa.mode = SWAT_PA;
   if((err=EvtOpenEvent(&swatDesc->pa.eventId,templateName)) != GOOD)
       return(err);
   for (i=0; i<NUM_PA_RANGES; i++) {
      *(swatDesc->pa.startEvent[i]) = NULL;
      *(swatDesc->pa.stopEvent[i]) = NULL;
   }
   swatDesc->pa.currentBin = PA_BIN0;
   swatDesc->pa.currentUnit = SWAT_TIME;
   *desc = (DESCRIPTOR) swatDesc;
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatOpenTs
** 
*****************************************************************************/
RETCODE EXPORT SwatOpenTs(DESCRIPTOR FAR *desc) {
   SWAT_DESCRIPTOR *swatDesc;

   if ((swatDesc = (SWAT_DESCRIPTOR *) TMalloc(sizeof(*swatDesc))) == NULL)
      return(ER_OUT_OF_MEMORY);
   swatDesc->ts.mode = SWAT_TIMESTAMP;
   swatDesc->ts.currentUnit = SWAT_TIME;
   *desc = (DESCRIPTOR)swatDesc;
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatClose
** 
*****************************************************************************/
RETCODE EXPORT SwatClose(DESCRIPTOR desc) {
   RETCODE err=GOOD,err2;
   SWAT_DESCRIPTOR *swatDesc = (SWAT_DESCRIPTOR*)desc;
   if (!swatDesc) return(ER_NO_MODE);
   switch (swatDesc->ca.mode) {
      case SWAT_CA:
      case SWAT_PA:
      case SWAT_TIMESTAMP:
         if (swatDesc == activeDesc) err = SwatSetActive(NULL);
         err2 = TFree((LPSTR)desc);
         return(err?err:err2);
      default:
         return(ER_NO_MODE);
   }
}
      
/*****************************************************************************
** 
**    SwatSetActive
** 
*****************************************************************************/
RETCODE EXPORT SwatSetActive(DESCRIPTOR desc) {
   BOOLEAN False=FALSE;
   BOOLEAN true=TRUE;
   SWAT_DESCRIPTOR *swatDesc = (SWAT_DESCRIPTOR*)desc;
   LOOP_VAR group, event;
   EVENT_GROUP_INFO sdEvent[NUM_EVENTS];
   RETCODE err;
   LOOP_VAR i;
   
   if (activeDesc == (SWAT_DESCRIPTOR *) desc) return(GOOD);
/* Stop collection in current mode */
   if (activeDesc!=NULL) {
      switch (activeDesc->ca.mode) {
         case SWAT_CA:
            if ((err = SdnWriteMember(SDN_CA_RUN,&False,GOOD)) != GOOD)
               return(err);
            if ((err = SdUnRegisterSemaphore(activeDesc->ca.semaphoreDesc))
               != GOOD) return(err);
            for (i=0; i<NUM_CA_RANGES; i++) {
               GlobalUnlock(activeDesc->ca.hcaData[i]);
               GlobalFree(activeDesc->ca.hcaData[i]);
            } 
            break;
         case SWAT_PA:
            if ((err = SdnWriteMember(SDN_PA_RUN,&False,GOOD)) != GOOD)
               return(err);
            break;
         case SWAT_TIMESTAMP:
            /* No need to stop -- this is a monitor-only configuration */
            break;
      }
   }
/* Start collection in new mode */
   if (desc == NULL) {
      activeDesc=(SWAT_DESCRIPTOR *)desc;
      return(GOOD);
   }
   if ((err = SdnWriteMember(SDN_SWAT_MODE,(U8*)&(swatDesc->ca.mode),GOOD))
      != GOOD) return(err);
   switch (swatDesc->ca.mode) {
      case SWAT_CA: {
         if ((err = SdnWriteMember(SDN_CA_RUN,&False,GOOD)) != GOOD)
            return(err);
         if ((err = SdRegisterSemaphore(SD_CA_READ_SEMAPHORE,NULLPTR,
            &swatDesc->ca.semaphoreDesc)) != GOOD) return(err);
         for (i=0; i<NUM_CA_RANGES; i++) {
            swatDesc->ca.hcaData[i] = GlobalAlloc(GMEM_MOVEABLE,REGION_SIZE/8);
            if (swatDesc->ca.hcaData == NULL) return(ER_OUT_OF_MEMORY);
            swatDesc->ca.caData[i] = (U8*)GlobalLock(swatDesc->ca.hcaData[i]);
            if (swatDesc->ca.caData == NULL) return(ER_WINDOWS_MEMLOCK);
            lmemset(swatDesc->ca.caData[i], 0, REGION_SIZE/8);
            if ((err = SdnWriteMember(SDN_CA_BASE+i,
               (U8*)&(swatDesc->ca.regionBase), GOOD)) != GOOD) return(err);
         }
         /* Program event recognizers */
         for (group=0; group<NUM_GROUPS; group++) {
            BOOLEAN exists; 

            /* Program qualify event into event0 */
            if((err=EvtDoesEventExist(swatDesc->ca.qualifyEvent,&exists))
               !=GOOD) return(err);
            if( exists ) {
               if((err=EvtSetActiveEvent(swatDesc->ca.eventId,
                  swatDesc->ca.qualifyEvent)) != GOOD) return(err);
               if((err=EvtGetEventGroupInfo(swatDesc->ca.eventId, group,
                  &sdEvent[0])) != GOOD ) return(err);
            } else {
               sdEvent[0].hi=sdEvent[0].lo=sdEvent[0].mask=0;
            }
            for (event=1; event<NUM_EVENTS; event++) {
               sdEvent[event].hi=sdEvent[event].lo=sdEvent[event].mask=0;
            }
            if((err=SdnWriteMember(SDN_EVTREC+group,(U8*)&sdEvent,GOOD))
               !=GOOD) return(err);
         }
         if ((err = SdCmdResp(SDN_CA_CLEAR,SDN_CA_CLEAR,&true)) != GOOD)
            return(err);
         break;
      }
      case SWAT_PA: {
         if ((err = SdnWriteMember(SDN_PA_RUN,&False,GOOD)) != GOOD)
            return(err);
         /* Program event recognizers */
         for (group=0; group<NUM_GROUPS; group++) {
            BOOLEAN exists; 

            for(event=0; event<NUM_EVENTS; event++) {
               LPSTR eventName;
               /* Program start, stop events for each bin */
               if( (event % 2) == 0 )  /* start event */
                  eventName = swatDesc->pa.startEvent[event/2];
               else
                  eventName = swatDesc->pa.stopEvent[event/2];
               if((err=EvtDoesEventExist(eventName,&exists))!=GOOD) 
                  return(err);
               if( exists ) {
                  if((err=EvtSetActiveEvent(swatDesc->pa.eventId,eventName))
                     != GOOD) return(err);
                  if((err=EvtGetEventGroupInfo(swatDesc->pa.eventId, group,
                     &sdEvent[0])) != GOOD ) return(err);
               }
               else {
                  sdEvent[event].hi=sdEvent[event].lo=sdEvent[event].mask=0;
               }
            }
            if((err=SdnWriteMember(SDN_EVTREC+group,(U8*)&sdEvent,GOOD))
               !=GOOD) return(err);
         }
         if ((err = SdCmdResp(SDN_PA_CLEAR,SDN_PA_CLEAR,&true)) != GOOD)
            return(err);
         break;
      }
      case SWAT_TIMESTAMP:
         /* No additional setup required */
         break;
      default:
         return(ER_NO_MODE);
   }
   activeDesc = (SWAT_DESCRIPTOR *)desc;
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatGetActive
** 
*****************************************************************************/
RETCODE EXPORT SwatGetActive(DESCRIPTOR FAR *desc) {
   *desc = (DESCRIPTOR) activeDesc;
   return(GOOD);
}

/*****************************************************************************
** 
**    SwatSetRun
** 
*****************************************************************************/
RETCODE EXPORT SwatSetRun(DESCRIPTOR desc, BOOLEAN run) {
   RETCODE err;
   SWAT_DESCRIPTOR *swatDesc = (SWAT_DESCRIPTOR*)desc;
   if (!swatDesc) return(ER_NO_MODE);
   if (swatDesc != activeDesc) return(ER_NOT_ACTIVE_DESC);
   switch (activeDesc->ca.mode) {
      case SWAT_CA:
         if ((err = SdnWriteMember(SDN_CA_RUN,(U8*)&run,GOOD)) != GOOD)
            return(err);
         break;
      case SWAT_PA:
         if ((err = SdnWriteMember(SDN_PA_RUN,(U8*)&run,GOOD)) != GOOD)
            return(err);
         break;
      case SWAT_TIMESTAMP:
         /* No need to start/stop -- this is a monitor-only configuration */
         break;
   }
   return(GOOD);
}

/*****************************************************************************
** 
**    SwatGetRun
** 
*****************************************************************************/
#pragma argsused
RETCODE EXPORT SwatGetRun(DESCRIPTOR desc, BOOLEAN *run) {
   RETCODE err;
   if (!activeDesc) *run=FALSE;
   else switch (activeDesc->ca.mode) {
      case SWAT_CA:
         if ((err = SdnReadMember(SDN_CA_RUN,(U8*)run)) != GOOD)
            return(err);
         break;
      case SWAT_PA:
         if ((err = SdnReadMember(SDN_PA_RUN,(U8*)run)) != GOOD)
            return(err);
         break;
      case SWAT_TIMESTAMP:
         *run = TRUE;
         break;
   }
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatSetCaRegionBase
** 
*****************************************************************************/
RETCODE EXPORT SwatSetCaRegionBase(DESCRIPTOR desc, U32 base) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc->ca.mode != SWAT_CA) return(ER_NOT_CA_MODE);
   swatDesc->ca.regionBase[swatDesc->ca.currentRegion] = base;
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatGetCaRegionBase
** 
*****************************************************************************/
RETCODE EXPORT SwatGetCaRegionBase(DESCRIPTOR desc, U32 FAR *base) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc->ca.mode != SWAT_CA) return(ER_NOT_CA_MODE);
   *base = swatDesc->ca.regionBase[swatDesc->ca.currentRegion];
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatSetCaQualifyEvent
** 
*****************************************************************************/
RETCODE EXPORT SwatSetCaQualifyEvent(DESCRIPTOR desc, LPSTR event) {
   SWAT_DESCRIPTOR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc->ca.mode != SWAT_CA) return(ER_NOT_CA_MODE);
   strncpy((LPSTR)(swatDesc->ca.qualifyEvent), event,
      sizeof(swatDesc->ca.qualifyEvent));
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatGetCaQualifyEvent
** 
*****************************************************************************/
RETCODE EXPORT SwatGetCaQualifyEvent(DESCRIPTOR desc, LPSTR event, U16 size) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc->ca.mode != SWAT_CA) return(ER_NOT_CA_MODE);
   strncpy(event, (LPSTR)(swatDesc->ca.qualifyEvent), size);
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatSetCaCurrentRegion
** 
*****************************************************************************/
RETCODE EXPORT SwatSetCaCurrentRegion(DESCRIPTOR desc, CA_REGION region) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc->ca.mode != SWAT_CA) return(ER_NOT_CA_MODE);
   swatDesc->ca.currentRegion = region;
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatGetCaCurrentRegion
** 
*****************************************************************************/
RETCODE EXPORT SwatGetCaCurrentRegion(DESCRIPTOR desc,CA_REGION FAR *region){
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc->ca.mode != SWAT_CA) return(ER_NOT_CA_MODE);
   *region = swatDesc->ca.currentRegion;
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatCaResultInit
** 
*****************************************************************************/
RETCODE EXPORT SwatCaResultInit(DESCRIPTOR desc, HWND hWndModule,
                                HWND hWndFunction, HWND hWndStatement) {
   char temp[MAX_SYMNAME_LENGTH];
   RETCODE err,err2;
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;

   if(swatDesc != activeDesc) return(ER_NOT_ACTIVE_DESC);
   if(swatDesc->ca.mode != SWAT_CA) return(ER_NOT_CA_MODE);
/*!!! Until SWAT works
   if ((err = UpdateCoverage(swatDesc)) != GOOD) return(err); !!!*/
   swatDesc->ca.hWndModule = hWndModule;
   swatDesc->ca.hWndFunction = hWndFunction;
   swatDesc->ca.hWndStatement = hWndStatement;
   {
      SYM_DESCRIPTOR module;
      SYM_TYPE_TYPE symType;
      char moduleName[MAX_SYMNAME_LENGTH];
      DESCRIPTOR moduleAddr;
      OFFSET_ADDR_RANGE_TYPE moduleRange;
      BOOLEAN moduleInRange=FALSE;
      
      SendMessage(hWndModule,LB_RESETCONTENT,0,0);

      if ((err = SymGetModuleListHead(&module)) != GOOD) return(err);
      if ((err = AdrCreateAddress(&moduleAddr)) != GOOD) return(err);

      while (module != NULL) {
         CA_REGION region;
         BOOLEAN inRegion;
         U16 caProportion;
         if ((err = AdrSetAddrType(moduleAddr,ADDR_LOGICAL)) != GOOD) break;
         if ((err = SymGetModule(module, moduleName, moduleAddr)) != GOOD)
            break;
         if ((err = MakeRange(moduleAddr, &moduleRange)) != GOOD) break;
         if ((err = InCaRegion(swatDesc,&moduleRange,&inRegion,&region))
            != GOOD) break;
         if (inRegion) {
            caProportion = GetCaProportion(swatDesc,&moduleRange,region);
            wsprintf(temp,"%3d%% %s",caProportion,moduleName);
            AddStringToListbox(hWndModule,temp);
            moduleInRange=TRUE;
         }
         SymGetSymbolSibling(module, &symType, &module);
      }
      err2 = AdrDestroyAddress(moduleAddr);
      if (!err) err=err2;
      if ((!err) && moduleInRange) {
         SendMessage(hWndModule,LB_SETCURSEL,0,0);   /* Select first module */
         err = InitFunctionBox(swatDesc);
      }
   }
   return(err);
}
      
/*****************************************************************************
** 
**    SwatSetCaModule
** 
*****************************************************************************/
RETCODE EXPORT SwatSetCaModule(DESCRIPTOR desc) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc != activeDesc) return(ER_NOT_ACTIVE_DESC);
   if(swatDesc->ca.mode != SWAT_CA) return(ER_NOT_CA_MODE);
   return(InitFunctionBox(swatDesc));
}
      
/*****************************************************************************
** 
**    SwatSetCaFunction
** 
*****************************************************************************/
RETCODE EXPORT SwatSetCaFunction(DESCRIPTOR desc) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc != activeDesc) return(ER_NOT_ACTIVE_DESC);
   if(swatDesc->ca.mode != SWAT_CA) return(ER_NOT_CA_MODE);
   return(InitStatementBox(swatDesc));
}
      
/***************** LOCAL CA SUPPORT FUNCTIONS *******************/

/*****************************************************************************
** 
**    UpdateCoverage
** 
** description:
**    Renews local copy of coverage information from the hardware.
**
*****************************************************************************/
#define SETCOVERAGE(_m,addr)\
   ((_m)[(U16)((addr)>>(REGION_GRAN_SHIFT+3))]\
   |= (1<<(((addr)>>REGION_GRAN_SHIFT)&7)))
#define CLRCOVERAGE(_m,addr)\
   ((_m)[(U16)((addr)>>(REGION_GRAN_SHIFT+3))]\
   &=~(1<<(((addr)>>REGION_GRAN_SHIFT)&7)))


static RETCODE UpdateCoverage(SWAT_DESCRIPTOR FAR *swatDesc) {
   RETCODE err;
   U16 i;
   U32 addr,caLength;
   CA_RESULT caResult;
   U16 caIndex;

   for (i=0; i<NUM_CA_RANGES; i++) {
      BOOLEAN covered;
      if ((err=SdnWriteMember(SDN_CA_READ_SEGMENT,
         (U8*)&(swatDesc->ca.currentRegion), GOOD)) != GOOD) return(err);
      addr=0;
      if ((err = SdnWriteMember(SDN_CA_READ_ADDRESS,(U8*)&addr,GOOD)) != GOOD)
         return(err);
      for (caLength=0,caIndex=0,covered=FALSE; addr<REGION_COVERAGE; ) {
         if (caIndex>=caLength) {
            caLength=MAX_CA_RESULTS;
            if ((err = SdCmdResp(SDN_CA_READ_LENGTH,SDN_CA_READ_LENGTH,
               &caLength)) != GOOD) return(err);
            if ((err = SdnReadMember(SDN_CA_READ_LENGTH,(U8*)&caLength))
               != GOOD) return(err);  /* length actually read */
            caIndex=0;
         }
         if ((err = SdnReadPartialMember(SDN_CA_RESULTS,caIndex,
            sizeof(caResult),(U8*)&caResult)) != GOOD) return(err);
         caResult = min(caResult,REGION_COVERAGE);       /* truncate at end */
         /*
         **    Now set copy of coverage memory to either all 1's or all 0's
         ** from address in last record to address in this record -1.
         **    This is done by setting individual bits up to the next byte
         ** boundary, then using memset to set all bits up to the end, then
         ** finishing up by setting bits in the last byte until addr reaches
         ** caResult-1.   This method is MUCH faster than setting individual
         ** bits for the whole area.
         */
         if (caResult-addr > 64) {    /* Only bother with this if large area*/
            for (; addr%(REGION_GRAN*8); addr++)
               if (covered) SETCOVERAGE(swatDesc->ca.caData[i],addr);
               else CLRCOVERAGE(swatDesc->ca.caData[i],addr);
            lmemset(&(swatDesc->ca.caData[i]
                  [(U16)(addr>>(REGION_GRAN_SHIFT+3))]),
               (covered) ? 0xffff : 0,
               (caResult-addr)>>(REGION_GRAN_SHIFT+3));
            addr = caResult&~((1<<(REGION_GRAN_SHIFT+3))-1);
         }
         for ( ; addr < caResult; addr++)            /* Fill by bits to end */
            if (covered) SETCOVERAGE(swatDesc->ca.caData[i],addr);
            else CLRCOVERAGE(swatDesc->ca.caData[i],addr);
         covered ^= 1;
         caIndex++;
      }
   }
   return(GOOD);
}

/*****************************************************************************
** 
**    InCaRegion
** 
** description:
**    Determines whether a symbol is in an active CA region.
**    Returns TRUE if range is completely covered by any CA region.
**
** inputs:
**    range:    address range of symbol
** outputs:
**    (retval)  whether or not being covered
**    region:   region covering symbol (if being covered)
**
*****************************************************************************/
static RETCODE InCaRegion(SWAT_DESCRIPTOR *swatDesc,
     OFFSET_ADDR_RANGE_TYPE *modRange,BOOLEAN *inRegion, CA_REGION *region) {
   LOOP_VAR i;

   *inRegion = FALSE;
   if(swatDesc != activeDesc) return(ER_NOT_ACTIVE_DESC);
   if (swatDesc==NULL) return(ER_NO_MODE);
   if (swatDesc->ca.mode != SWAT_CA) return(ER_NOT_CA_MODE);
   for (i=0; i<NUM_CA_RANGES; i++) {
      if (modRange->offsetStartAddr < swatDesc->ca.regionBase[i]) continue;
      if (modRange->offsetEndAddr >=
         swatDesc->ca.regionBase[i]+REGION_COVERAGE) continue;
      *region = (CA_REGION)i;
      *inRegion=TRUE;
      break;
   }
   return(GOOD);
}

/*****************************************************************************
** 
**    GetCaProportion
** 
** description:
**    Determines amount of given address range that has been covered.
**    Result is returned as a percentage integer from 0 to 100.
**
** inputs:
**    swatDesc: descriptor
**    range:    physical address range of symbol
**    region:   CA region containing the address range.
** outputs:
**    (retval)  percentage of region covered.
**
*****************************************************************************/
#define COVERED(_a,_m)\
   ((_m)[(U16)(((_a) & (REGION_COVERAGE-1)) >> (REGION_GRAN_SHIFT+3))] &\
   (1<<((((_a) & (REGION_COVERAGE-1)) >> REGION_GRAN_SHIFT) & 7)))

static U16 GetCaProportion(SWAT_DESCRIPTOR *swatDesc,
                           OFFSET_ADDR_RANGE_TYPE *modRange, CA_REGION region){
   U32 addr,covered;
   for (covered=0,addr = modRange->offsetStartAddr;
         addr <= modRange->offsetEndAddr; addr++) {
      if (COVERED(addr,swatDesc->ca.caData[region])) covered++;
   }
   return((100L * covered)/
      (modRange->offsetEndAddr+1-modRange->offsetStartAddr));
}

/*****************************************************************************
** 
**    AddStringToListbox
** 
** description:
**    Inserts specified string into specified listbox.
**    
** inputs:
**    hWndBox:  Window handle of listbox
**    string:   String to insert
** outputs:
**    none
**
*****************************************************************************/
static void AddStringToListbox(HWND hWndBox, char *string) {
   SendMessage(hWndBox,LB_INSERTSTRING,-1,(LONG)string);
}

/*****************************************************************************
** 
**    InitFunctionBox
** 
** description:
**    Fills in coverage function listbox with all the functions in the
**    currently selected module.
**    
** inputs:
**    swatDesc: ca descriptor (contains listbox window handles)
** outputs:
**    none
**
*****************************************************************************/
static RETCODE InitFunctionBox(SWAT_DESCRIPTOR *swatDesc) {
   RETCODE err,err2;
   SYM_DESCRIPTOR functionSym;
   char temp[MAX_SYMNAME_LENGTH];
   SYM_TYPE_TYPE symType;
   OFFSET_ADDR_RANGE_TYPE functionRange;
   CA_REGION region;
   U16 caProportion;
   SYM_DESCRIPTOR moduleSym;
   U32 curIndex;
   S8 moduleName[MAX_SYMNAME_LENGTH];
   S8 functionName[MAX_SYMNAME_LENGTH];
   DESCRIPTOR functionAddr;
   BOOLEAN functionInRange=FALSE;

   SendMessage(swatDesc->ca.hWndFunction,LB_RESETCONTENT,0,0);
   curIndex = SendMessage(swatDesc->ca.hWndModule,LB_GETCURSEL,0,0);
   SendMessage(swatDesc->ca.hWndModule,LB_GETTEXT,(U16)curIndex,
      (LONG)moduleName);
   if ((err = SymGetModuleDescNth(moduleName+5,(U8)1,&moduleSym)) != GOOD)
      return(err);
   if ((err = SymGetSymbolChild(moduleSym, &symType, &functionSym)) != GOOD)
      return(err);

   if ((err = AdrCreateAddress((DESCRIPTOR FAR *)&functionAddr)) != GOOD)
      return (err);
   while (functionSym != NULL) {
      FUNC_CLASS class;
      BOOLEAN inRegion;
      U32 stack;
      if ((err = AdrSetAddrType(functionAddr,ADDR_LOGICAL)) != GOOD) break;
      if ((err = SymGetFunc(functionSym,functionName,&class,&stack,
         functionAddr)) != GOOD) break;
      if ((err = MakeRange(functionAddr, &functionRange)) != GOOD) break;
      if ((err = InCaRegion(swatDesc,&functionRange,&inRegion,&region))
         != GOOD) break;
      if (inRegion) {
         caProportion = GetCaProportion(swatDesc,&functionRange,region);
         wsprintf(temp,"%3d%% %s",caProportion,functionName);
         AddStringToListbox(swatDesc->ca.hWndFunction,temp);
         functionInRange=TRUE;
      }
      SymGetSymbolSibling(functionSym, &symType, &functionSym);
   }
   err2 = AdrDestroyAddress(functionAddr);
   if (!err) err = err2;
   if ((!err) && functionInRange) {
      SendMessage(swatDesc->ca.hWndFunction,LB_SETCURSEL,0,0);
      err = InitStatementBox(swatDesc);
   }
   return(err);
}

/*****************************************************************************
** 
**    InitStatementBox
** 
** description:
**    Fills in coverage statement listbox with all the statements in the
**    currently selected module and function.
**    
** inputs:
**    swatDesc: ca descriptor (contains listbox window handles)
** outputs:
**    none
**
*****************************************************************************/
static RETCODE InitStatementBox(SWAT_DESCRIPTOR *swatDesc) {
   RETCODE err,err2;
   DESCRIPTOR functionAddr, statementAddr;
   OFFSET_ADDR_RANGE_TYPE functionRange, statementRange;
   LINENUM_TYPE linenum;
   LINENUM_DESCRIPTOR statementSym;
   SYM_DESCRIPTOR moduleSym,functionSym;
   FUNC_CLASS class;
   U32 stack;
   U32 curIndex;
   S8 moduleName[MAX_SYMNAME_LENGTH];
   S8 functionName[MAX_SYMNAME_LENGTH];
   COLUMN_RANGE_TYPE columnRange;

   SendMessage(swatDesc->ca.hWndStatement,LB_RESETCONTENT,0,0);

/* get currently selected function */
   curIndex = SendMessage(swatDesc->ca.hWndModule,LB_GETCURSEL,0,0);
   SendMessage(swatDesc->ca.hWndModule,LB_GETTEXT,(U16)curIndex,
      (LONG)moduleName);
   if ((err=SymGetModuleDescNth(moduleName+5,(U8)1,&moduleSym)) != GOOD)
      return(err);
   curIndex = SendMessage(swatDesc->ca.hWndFunction,LB_GETCURSEL,0,0);
   SendMessage(swatDesc->ca.hWndFunction,LB_GETTEXT,(U16)curIndex,
      (LONG)functionName);
   if ((err=SymGetFuncByName(moduleSym,functionName+5,&functionSym)) != GOOD)
      return(err);
   if ((err = AdrCreateAddress((DESCRIPTOR FAR *)&functionAddr)) != GOOD)
      return (err);
   if ((err = AdrCreateAddress((DESCRIPTOR FAR *)&statementAddr)) != GOOD) {
      AdrDestroyAddress(functionAddr);
      return (err);
   }
   err = AdrSetAddrType(functionAddr,ADDR_LOGICAL);
   if (!err) err = AdrSetAddrType(statementAddr,ADDR_LOGICAL);

   if (!err)
       err = SymGetFunc(functionSym,functionName+5,&class,&stack,functionAddr);
   if (!err) err = SymMapAddr2LinenumStmt(functionAddr,moduleSym,&linenum,
      &columnRange,statementAddr,&statementSym);
   if (!err) err = MakeRange(functionAddr, &functionRange);

   if (!err) while (1) {
      char temp[MAX_SYMNAME_LENGTH];
      CA_REGION region;
      BOOLEAN inRegion;
      U16 caProportion;

      if ((err = MakeRange(statementAddr, &statementRange)) != GOOD) break;
      if (statementRange.offsetStartAddr > functionRange.offsetEndAddr) break;
      if ((err = InCaRegion(swatDesc,&statementRange,&inRegion,&region))
         != GOOD) break;
      if (inRegion) {
         caProportion = GetCaProportion(swatDesc,&statementRange,region);
         wsprintf(temp,"%3d%% line%d,col%d-%d",caProportion,linenum,
            columnRange.columnStart,columnRange.columnEnd);
         AddStringToListbox(swatDesc->ca.hWndStatement,temp);
      }
      if ((err = AdrSetAddrType(statementAddr,ADDR_LOGICAL)) != GOOD) break;
      if ((err=SymGetLinenumStmtByIndex(moduleSym,statementSym,statementAddr,
            &linenum,&columnRange,&statementSym)) != GOOD) {
         if (err == ER_LINENUM_INDEX_TOO_LARGE) err = GOOD;  /* end of list */
         break;
      }
   }
   err2 = AdrDestroyAddress(functionAddr);
   if (!err) err = err2;
   err2 = AdrDestroyAddress(statementAddr);
   if (!err) err = err2;
   return(err);
}

/*****************************************************************************
** 
**    SwatCaClear
** 
*****************************************************************************/
RETCODE EXPORT SwatCaClear(DESCRIPTOR desc) {
   RETCODE err;
   BOOLEAN true=TRUE;
   U16 i;
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc != activeDesc) return(ER_NOT_ACTIVE_DESC);
   if(swatDesc->ca.mode != SWAT_CA) return(ER_NOT_CA_MODE);
   if ((err = SdCmdResp(SDN_CA_CLEAR,SDN_CA_CLEAR,&true)) != GOOD) return(err);
   for (i=0; i<NUM_CA_RANGES; i++) {
      lmemset(swatDesc->ca.caData[i], 0, REGION_SIZE/8);
   }
   return(GOOD);
}

/**************** PA FUNCTIONS *******************/


/*****************************************************************************
** 
**    SwatSetPaCurrentBin
** 
*****************************************************************************/
RETCODE EXPORT SwatSetPaCurrentBin(DESCRIPTOR desc, PA_BIN bin) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *)desc;
   if(swatDesc->ca.mode != SWAT_PA) return(ER_NOT_PA_MODE);
   swatDesc->pa.currentBin = bin;
   return(GOOD);
}

/*****************************************************************************
** 
**    SwatGetPaCurrentBin
** 
*****************************************************************************/
RETCODE EXPORT SwatGetPaCurrentBin(DESCRIPTOR desc, PA_BIN FAR *bin) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *)desc;
   if(swatDesc->ca.mode != SWAT_PA) return(ER_NOT_PA_MODE);
   *bin = swatDesc->pa.currentBin;
   return(GOOD);
}

/*****************************************************************************
** 
**    SwatSetPaStartEvent
** 
*****************************************************************************/
RETCODE EXPORT SwatSetPaStartEvent(DESCRIPTOR desc, LPSTR event) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *)desc;
   if(swatDesc->ca.mode != SWAT_PA) return(ER_NOT_PA_MODE);
   strncpy((LPSTR)(swatDesc->pa.startEvent[swatDesc->pa.currentBin]), event,
      sizeof(swatDesc->pa.startEvent[swatDesc->pa.currentBin]));
   return(GOOD);
}

/*****************************************************************************
** 
**    SwatGetPaStartEvent
** 
*****************************************************************************/
RETCODE EXPORT SwatGetPaStartEvent(DESCRIPTOR desc, LPSTR event, U16 size) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc->ca.mode != SWAT_PA) return(ER_NOT_PA_MODE);
   strncpy(event, (LPSTR)(swatDesc->pa.startEvent[swatDesc->pa.currentBin]),
      size);
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatSetPaStopEvent
** 
*****************************************************************************/
RETCODE EXPORT SwatSetPaStopEvent(DESCRIPTOR desc, LPSTR event) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc->ca.mode != SWAT_PA) return(ER_NOT_PA_MODE);
   strncpy((LPSTR)(swatDesc->pa.stopEvent[swatDesc->pa.currentBin]), event,
      sizeof(swatDesc->pa.stopEvent[swatDesc->pa.currentBin]));
   return(GOOD);
}

/*****************************************************************************
** 
**    SwatGetPaStopEvent
** 
*****************************************************************************/
RETCODE EXPORT SwatGetPaStopEvent(DESCRIPTOR desc, LPSTR event, U16 size) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *)desc;
   if(swatDesc->ca.mode != SWAT_PA) return(ER_NOT_PA_MODE);
   strncpy(event,(LPSTR)(swatDesc->pa.stopEvent[swatDesc->pa.currentBin]),
      size);
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatSetPaUnit
** 
*****************************************************************************/
RETCODE EXPORT SwatSetPaUnit(DESCRIPTOR desc, SWAT_UNIT unit) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc->ca.mode != SWAT_PA) return(ER_NOT_PA_MODE);
   swatDesc->pa.currentUnit = unit;
   return(GOOD);
}

/*****************************************************************************
** 
**    SwatGetPaUnit
** 
*****************************************************************************/
RETCODE EXPORT SwatGetPaUnit(DESCRIPTOR desc, SWAT_UNIT FAR *unit) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc->ca.mode != SWAT_PA) return(ER_NOT_PA_MODE);
   *unit = swatDesc->pa.currentUnit;
   return(GOOD);
}

/*****************************************************************************
** 
**    SwatGetPaResult
** 
*****************************************************************************/
RETCODE EXPORT SwatGetPaResult(DESCRIPTOR desc, PA_RESULT FAR *result) {
   RETCODE err;
   BOOLEAN True=TRUE;
   double unitDivider;
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   U48 paCount, paMinTime, paMaxTime, paTotalTime, paCollectionTime;
   double count, minTime, maxTime, totalTime, collectionTime;

   if(swatDesc != activeDesc) return(ER_NOT_ACTIVE_DESC);
   if(swatDesc->ca.mode != SWAT_PA) return(ER_NOT_PA_MODE);

   if ((err = SdCmdResp(SDN_PA_GET_RESULTS,SDN_PA_GET_RESULTS,
      &True)) != GOOD)  return(err);
   if ((err = SdnReadMember(SDN_PA_INVOCATION_COUNT+swatDesc->pa.currentBin,
      (U8*)&paCount)) != GOOD)  return(err);
   if ((err = SdnReadMember(SDN_PA_TOTAL_TIME+swatDesc->pa.currentBin,
      (U8*)&paTotalTime)) != GOOD) return(err);
   if ((err = SdnReadMember(SDN_PA_MAX_TIME+swatDesc->pa.currentBin,
      (U8*)&paMaxTime)) != GOOD) return(err);
   if ((err = SdnReadMember(SDN_PA_MIN_TIME+swatDesc->pa.currentBin,
      (U8*)&paMinTime)) != GOOD) return(err);
   if ((err = SdnReadMember(SDN_PA_COLLECTION_TIME,(U8*)&paCollectionTime)) 
      != GOOD) return(err);

   if (swatDesc->pa.currentUnit == SWAT_CLOCKS) unitDivider = 1.0;
   else unitDivider = (double)clockRate;

   if (unitDivider == 0) return(ER_CLOCK_ZERO);
      
   U48ToDouble(paCount,&count);
   U48ToDouble(paMinTime,&minTime);
   U48ToDouble(paMaxTime,&maxTime);
   U48ToDouble(paTotalTime,&totalTime);
   U48ToDouble(paCollectionTime,&collectionTime);
   if (count == 0) {
      result->count = 0;
      result->minDuration = 0;
      result->maxDuration = 0;
      result->aveDuration = 0;
   } else {
      result->count = paCount.lsd;
      result->minDuration = minTime / unitDivider;
      result->maxDuration = maxTime / unitDivider;
      result->aveDuration = (totalTime / count) / unitDivider;
   }
   if (collectionTime == 0)
      result->proportion = 0;
   else 
      result->proportion = (U32)((totalTime * 65536.0) / collectionTime);

#if 0
   result->count = 0x1234;
   result->minDuration = 20.0 + (double)(swatDesc->pa.currentBin*15);
   result->maxDuration = 78.0 + (double)(swatDesc->pa.currentBin*15);
   result->aveDuration = 53.0 + (double)(swatDesc->pa.currentBin*15);
   result->proportion = 0x4300 + ((swatDesc->pa.currentBin*15)<<16);
#endif
      
   return(GOOD);
}
      
/*****************************************************************************
** 
**    SwatPaClear
** 
*****************************************************************************/
RETCODE EXPORT SwatPaClear(DESCRIPTOR desc) {
   BOOLEAN true=TRUE;
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) desc;
   if(swatDesc != activeDesc) return(ER_NOT_ACTIVE_DESC);
   if(swatDesc->ca.mode != SWAT_PA) return(ER_NOT_PA_MODE);
   return(SdCmdResp(SDN_PA_CLEAR,SDN_PA_CLEAR,&true));
}

/*****************************************************************************
** 
**    SwatSetTsUnit
** 
*****************************************************************************/
RETCODE EXPORT SwatSetTsUnit(DESCRIPTOR desc, SWAT_UNIT unit) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *)desc;
   if(swatDesc->ca.mode != SWAT_TIMESTAMP) return(ER_NOT_TS_MODE);
   swatDesc->ts.currentUnit = unit;
   return(GOOD);
}

/*****************************************************************************
** 
**    SwatGetTsUnit
** 
*****************************************************************************/
RETCODE EXPORT SwatGetTsUnit(DESCRIPTOR desc, SWAT_UNIT FAR *unit) {
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *)desc;
   if(swatDesc->ca.mode != SWAT_TIMESTAMP) return(ER_NOT_TS_MODE);
   *unit = swatDesc->ts.currentUnit;
   return(GOOD);
}

/*****************************************************************************
** 
**    SwatGetTimestamp
** 
*****************************************************************************/
#define  CLOCKS_PRINT_FORMAT "%12.0f"
#define  TIME_PRINT_FORMAT "%.0fd %2.0f:%2.0f:%2.9f"

RETCODE EXPORT SwatGetTimestamp(DESCRIPTOR desc, U32 frame, LPSTR result,
                                U16 size) {
   char temp[256];
   RETCODE err;
   U48 timestamp;
   U32 subbuffer;
   double time, timestampVal;
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *)desc;

   if(swatDesc != activeDesc) return(ER_NOT_ACTIVE_DESC);
   if(swatDesc->ca.mode != SWAT_TIMESTAMP) return(ER_NOT_TS_MODE);
   
   subbuffer = frame / framesPerSubbuffer;

   if ((err = SdnReadPartialMember(SDN_TIMESTAMPS,subbuffer*sizeof(timestamp),
         sizeof(timestamp), (U8*)&timestamp)) != GOOD) {
      return(err);
   }
   U48ToDouble(timestamp,&timestampVal);
   if (swatDesc->ts.currentUnit == SWAT_CLOCKS) {
      time = timestampVal+(double)(frame & (framesPerSubbuffer-1));
      sprintf(temp,CLOCKS_PRINT_FORMAT,time);
   } else {
      double days, hours, minutes, seconds;
#if 1
      days = 0.0;
      hours = 1.0;
      minutes = 2.0;
      seconds = (double)frame;
#else
      if (clockRate == 0) return(ER_CLOCK_ZERO);
      time = (timestampVal+(double)(frame & (framesPerSubbuffer-1)))
         / (double)clockRate;
      days = floor(time / (60.0 * 60.0 * 24.0));
      hours = floor((time-days) / (60.0 * 60.0));
      minutes = floor((time-days-hours) / 60.0);
      seconds = time-days-hours-minutes;
#endif
      sprintf(temp,TIME_PRINT_FORMAT,days,hours,minutes,seconds);
   }
   strncpy(result, (LPSTR)temp, size);
   return(GOOD);
}

/*****************************************************************************
** 
**    SwatGetTimestampDisplayLength
** 
*****************************************************************************/
RETCODE EXPORT SwatGetTimestampDisplayLength(DESCRIPTOR id, U8 FAR *length) {
   char temp[256];
   SWAT_DESCRIPTOR FAR *swatDesc = (SWAT_DESCRIPTOR *) id;
   if(swatDesc->ca.mode != SWAT_TIMESTAMP) return(ER_NOT_TS_MODE);
   if (swatDesc->ts.currentUnit == SWAT_CLOCKS) {
      sprintf(temp,CLOCKS_PRINT_FORMAT,0);
   } else {
      sprintf(temp,TIME_PRINT_FORMAT,0,0,0,0);
   }
   *length = strlen(temp);
   return(GOOD);
}

/*************************** CLI FUNCTIONS ******************************/
static DESCRIPTOR cliId=0;
#pragma argsused
RETCODE EXPORT SwatCliOpenCa(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err;
   if( cliId ) {
      if((err = SwatClose(cliId))!=GOOD) return(err);
      cliId = 0;
   }
   if( (err = SwatOpenCa(&cliId,"default")) != GOOD ) return(err);
   return(SendCliResults("ca opened",cliServerHandle));
}

#pragma argsused
RETCODE EXPORT SwatCliOpenPa(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err;
   if( cliId ) {
      if((err = SwatClose(cliId))!=GOOD) return(err);
      cliId = 0;
   }
   if( (err = SwatOpenPa(&cliId,"default")) != GOOD ) return(err);
   return(SendCliResults("pa opened",cliServerHandle));
}

#pragma argsused
RETCODE EXPORT SwatCliOpenTs(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err;
   if(argc != 1) return(ER_BAD_NUM_PARM);
   if( cliId ) {
      if((err = SwatClose(cliId))!=GOOD) return(err);
      cliId = 0;
   }
   if( (err = SwatOpenTs(&cliId)) != GOOD ) return(err);
   return(SendCliResults("ts opened",cliServerHandle));
}

#pragma argsused
RETCODE EXPORT SwatCliClose(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err=GOOD;
   if( cliId ) {
      if ((err = SwatClose(cliId)) != GOOD) return(err);
      cliId = 0;
   }
   return(SendCliResults("swat closed",cliServerHandle));
}

#pragma argsused
RETCODE EXPORT SwatCliRun(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err;
   if (argc >= 2) {
      if (cliId==0) return(ER_NO_MODE);
      if (stricmp(&cmdString[(U16)(argv[1])],"on") == 0) {
         if((err=SwatSetRun(cliId, TRUE)) != GOOD) return(err);
      } else if (stricmp(&cmdString[(U16)(argv[1])],"off") == 0) {
         if((err=SwatSetRun(cliId, FALSE)) != GOOD) return(err);
      }
   }
   return(PrintSwatStatus());
}

RETCODE PRIVATE PrintSwatStatus(VOID) {
   RETCODE err;
   S8 resultBuff[128];
   BOOLEAN running;
   DESCRIPTOR desc;
   if( (err=SwatGetActive(&desc)) != GOOD ) return(err);
   strcpy(resultBuff,"swat is ");
   if (desc) {
      strcat(resultBuff,"loaded with ");
      if (desc == cliId) strcat(resultBuff,"the shell's ");
      else strcat(resultBuff,"the presenter's ");
      switch (((SWAT_DESCRIPTOR*)desc)->ca.mode) {
         case SWAT_CA: strcat(resultBuff,"CA configuration."); break;
         case SWAT_PA: strcat(resultBuff,"PA configuration."); break;
         case SWAT_TIMESTAMP: strcat(resultBuff,"TS configuration."); break;
      }
   } else {
      strcat(resultBuff,"not loaded with any configuration.");
   }
   if ((err = SendCliResults(resultBuff,cliServerHandle)) != GOOD) return(err);
   if( (err=SwatGetRun(cliId, &running)) != GOOD ) return(err);
   if (running) 
      strcpy(resultBuff,"swat is running.");
   else
      strcpy(resultBuff,"swat is not running.");
   return(SendCliResults(resultBuff,cliServerHandle));
}

#pragma argsused
RETCODE EXPORT SwatCliActive(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err;
   if( (err=SwatSetActive(cliId)) != GOOD ) return(err);
   return(PrintSwatStatus());
}

RETCODE EXPORT SwatCliCaRegionBase(LPSTR cmdString, U32 argc, U32 argv[]) {
   U32 base;
   S8 resultBuff[128];
   RETCODE err;
   if (cliId==0) return(ER_NO_MODE);
   if( argc == 2 ) {
      if((err=SwatSetCaRegionBase(cliId,atoi(&cmdString[(U16)argv[1]])))
         != GOOD) return(err);
   }
   if( (err=SwatGetCaRegionBase(cliId, &base)) != GOOD ) return(err);
   wsprintf(resultBuff,"current ca base address = 0x%lX",base);
   return(SendCliResults(resultBuff,cliServerHandle));
}

RETCODE EXPORT SwatCliCaQualifyEvent(LPSTR cmdString, U32 argc, U32 argv[]) {
   S8 resultBuff[128];
   RETCODE err;
   if (cliId==0) return(ER_NO_MODE);
   if( argc == 2 ) {
      if( (err=SwatSetCaQualifyEvent(cliId, &cmdString[(U16)argv[2]]))!=GOOD ) 
         return(err);
   }
   if((err=SwatGetCaQualifyEvent(cliId,resultBuff,128)) != GOOD) return(err);
   return(SendCliResults(resultBuff,cliServerHandle));
}

RETCODE EXPORT SwatCliCaCurrentRegion(LPSTR cmdString, U32 argc, U32 argv[]) {
   CA_REGION region;
   S8 resultBuff[128];
   RETCODE err;
   if (cliId==0) return(ER_NO_MODE);
   if( argc == 2 ) {
      if( (err=SwatSetCaCurrentRegion(cliId, atoi(&cmdString[(U16)argv[2]]))) 
         != GOOD ) return(err);
   }
   if((err=SwatGetCaCurrentRegion(cliId,&region))!=GOOD) return(err);
   wsprintf(resultBuff,"current ca region = %d",region);
   return(SendCliResults(resultBuff,cliServerHandle));
}

RETCODE EXPORT SwatCliCaResults(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err,err2;
   OFFSET_ADDR_RANGE_TYPE range;
   CA_REGION region;
   BOOLEAN inRegion;
   S8 resultBuff[128];
   U16 caProportion;
   if (cliId==0) return(ER_NO_MODE);
   if ((argc < 2) || (argc > 4)) return(ER_BAD_NUM_PARM);
   {
      DESCRIPTOR addrDesc;
      if ((err = AdrCreateAddress(&addrDesc)) != GOOD) return(err);
      err = AdrConvTextToAddress(addrDesc,&cmdString[(U16)argv[1]]);
      if (!err) err = AdrGetAddrOffset(addrDesc, &range.offsetStartAddr);
      err2 = AdrDestroyAddress(addrDesc);
      if (!err) err = err2;
   }
   if (err) return(err);
   if (argc >= 3) {
      DESCRIPTOR addrDesc;
      if ((err = AdrCreateAddress(&addrDesc)) != GOOD) return(err);
      err = AdrConvTextToAddress(addrDesc,&cmdString[(U16)argv[2]]);
      if (!err) err = AdrGetAddrOffset(addrDesc, &range.offsetEndAddr);
      err2 = AdrDestroyAddress(addrDesc);
      if (!err) err = err2;
   }
   if (err) return(err);
   if ((err = InCaRegion((SWAT_DESCRIPTOR*)cliId,&range,&inRegion,&region))
      != GOOD) return(err);
   if (inRegion) {
      caProportion = GetCaProportion((SWAT_DESCRIPTOR*)cliId,&range,region);
      wsprintf(resultBuff,"%3d%% coverage.",caProportion);
      err = SendCliResults(resultBuff,cliServerHandle);
   } else {
      err = ER_NOT_IN_CA_REGION;
   }
   return(err);
}

#pragma argsused
RETCODE EXPORT SwatCliSetCaModule(LPSTR cmdString, U32 argc, U32 argv[]) {
   if (cliId==0) return(ER_NO_MODE);
   return(SwatSetCaModule(cliId));
}

#pragma argsused
RETCODE EXPORT SwatCliSetCaFunction(LPSTR cmdString, U32 argc, U32 argv[]) {
   if (cliId==0) return(ER_NO_MODE);
   return(SwatSetCaFunction(cliId));
}

RETCODE EXPORT SwatCliPaCurrentBin(LPSTR cmdString, U32 argc, U32 argv[]) {
   PA_BIN bin;
   S8 resultBuff[128];
   RETCODE err;
   if (cliId==0) return(ER_NO_MODE);
   if(argc == 2) {
      if( (err=SwatSetPaCurrentBin(cliId,atoi(&cmdString[(U16)argv[2]])))
         != GOOD ) return(err);
   }
   if((err=SwatGetPaCurrentBin(cliId,&bin))!=GOOD) return(err);
   wsprintf(resultBuff,"current pa bin = %d",bin);
   return(SendCliResults(resultBuff,cliServerHandle));
}

RETCODE EXPORT SwatCliPaStartEvent(LPSTR cmdString, U32 argc, U32 argv[]) {
   S8 resultBuff[128];
   RETCODE err;
   if (cliId==0) return(ER_NO_MODE);
   if( argc == 2 ) {
      if( (err=SwatSetPaStartEvent(cliId, &cmdString[(U16)argv[2]])) != GOOD ) 
         return(err);
   }
   if((err=SwatGetPaStartEvent(cliId,resultBuff,128)) != GOOD) return(err);
   return(SendCliResults(resultBuff,cliServerHandle));
}

RETCODE EXPORT SwatCliPaStopEvent(LPSTR cmdString, U32 argc, U32 argv[]) {
   S8 resultBuff[128];
   RETCODE err;
   if (cliId==0) return(ER_NO_MODE);
   if( argc == 2 ) {
      if( (err=SwatSetPaStopEvent(cliId, &cmdString[(U16)argv[2]])) != GOOD ) 
         return(err);
   }
   if((err=SwatGetPaStopEvent(cliId,resultBuff,128)) != GOOD) return(err);
   return(SendCliResults(resultBuff,cliServerHandle));
}

RETCODE EXPORT SwatCliPaUnit(LPSTR cmdString, U32 argc, U32 argv[]) {
   SWAT_UNIT unit;
   S8 resultBuff[128];
   RETCODE err;
   if (cliId==0) return(ER_NO_MODE);
   if( argc == 2 ) {
      LPSTR ptr = &cmdString[(U16)argv[1]];
      if(strncmpi(ptr, "seconds", strlen(ptr))==0) unit = SWAT_TIME;
      else if(strncmpi(ptr, "clocks",  strlen(ptr))==0) unit = SWAT_CLOCKS;
      if( (err=SwatSetPaUnit(cliId, unit)) != GOOD ) return(err);
   }
   if((err=SwatGetPaUnit(cliId,&unit))!=GOOD) return(err);
   strcpy(resultBuff,"current pa unit = ");
   if( unit == SWAT_CLOCKS ) strcat(resultBuff,"clocks");
   else strcat(resultBuff,"seconds");
   return(SendCliResults(resultBuff,cliServerHandle));
}

#pragma argsused
RETCODE EXPORT SwatCliPaClear(LPSTR cmdString, U32 argc, U32 argv[]) {
   if (cliId==0) return(ER_NO_MODE);
   return(SwatPaClear(cliId));
}

#pragma argsused
RETCODE EXPORT SwatCliGetPaResult(LPSTR cmdString, U32 argc, U32 argv[]) {
   PA_RESULT results;
   S8 resultBuff[128];
   SWAT_UNIT unit;
   RETCODE err;
   if (cliId==0) return(ER_NO_MODE);
   if((err=SwatGetPaResult(cliId,&results)) != GOOD ) return(err);

   if((err=SwatGetPaUnit(cliId,&unit))!=GOOD) return(err);
   if (unit == SWAT_CLOCKS) {
      sprintf(resultBuff,
         "count: %lx\r\nmin: %.0f\r\nmax: %.0f\r\navg: %.0f\r\n%.2f\%",
         results.count, results.minDuration, results.maxDuration, 
         results.aveDuration, ((double)results.proportion/65536.0));
   } else {
      sprintf(resultBuff,
         "count: %lx\r\nmin: %.9f\r\nmax: %.9f\r\navg: %.9f\r\n%.2f\%",
         results.count, results.minDuration, results.maxDuration, 
         results.aveDuration, ((double)results.proportion/65536.0));
   }
   return(SendCliResults(resultBuff,cliServerHandle));
}

RETCODE EXPORT SwatCliTsUnit(LPSTR cmdString, U32 argc, U32 argv[]) {
   SWAT_UNIT unit;
   S8 resultBuff[128];
   RETCODE err;
   if (cliId==0) return(ER_NO_MODE);
   if( argc == 2 ) {
      LPSTR ptr = &cmdString[(U16)argv[1]];
      if(strncmpi(ptr, "seconds", strlen(ptr))==0) unit = SWAT_TIME;
      else if(strncmpi(ptr, "clocks",  strlen(ptr))==0) unit = SWAT_CLOCKS;
      if( (err=SwatSetTsUnit(cliId, unit)) != GOOD ) return(err);
   }
   if((err=SwatGetTsUnit(cliId,&unit))!=GOOD) return(err);
   strcpy(resultBuff,"current timestamp unit = ");
   if( unit == SWAT_CLOCKS ) strcat(resultBuff,"clocks");
   else strcat(resultBuff,"seconds");
   return(SendCliResults(resultBuff,cliServerHandle));
}

RETCODE EXPORT SwatCliGetTimestamp(LPSTR cmdString, U32 argc, U32 argv[]) {
   S8 resultBuff[128];
   RETCODE err;
   if (cliId==0) return(ER_NO_MODE);
   if( argc != 2 ) return(ER_BAD_NUM_PARM);
   if((err=SwatGetTimestamp(cliId,atoi(&cmdString[(U16)argv[1]]),resultBuff,
      sizeof(resultBuff))) != GOOD) return(err);
   return(SendCliResults(resultBuff,cliServerHandle));
}

#pragma argsused
static RETCODE SdCmdResp(MEMBER_INDEX cmd, MEMBER_INDEX resp, VOID *data) {
   BOOLEAN timedOut;
   return(SdnWriteCmdReadResponse(cmd,data,GOOD,resp,0,&timedOut));
}

static void U48ToDouble(U48 u48, double FAR *result) {
   *result = ((double)(u48.lsd)) + (((double)(u48.msw)) * 65536.0 * 65536.0);
}

static void lmemset(U8 *mem, U16 value, U32 length) {
   size_t size;
   do {
      size = (size_t)min(0x8000L, length);
      memset(mem,value,size);
      mem+=size;
      length-=size;
   } while (length>0);
}

/*****************************************************************************
** CvtLogicalPhysical
**   convert logical (opaque address descriptor) to physical address,
**   stuff fields from result into symbol-table-type range.
*****************************************************************************/
RETCODE PRIVATE MakeRange(DESCRIPTOR addr, OFFSET_ADDR_RANGE_TYPE *range) {
   RETCODE err;
   if ((err = AdrSetAddrType(addr, ADDR_PHYSICAL)) != GOOD) return(err);
   if ((err = AdrGetAddrOffset(addr, &range->offsetStartAddr)) != GOOD)
      return(err);
   if ((err = AdrGetEndAddrOffset(addr, &range->offsetEndAddr)) != GOOD)
      return(err);
   return(GOOD);
}

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