/****************************************************************************
**
**  Name:  evttmplt.c
**
**  Description:
**     Routines to manage the trigger and trace event templates.
**
**  Status:  PRELIMINARY
**
**  $Log:   S:/tbird/mt2_68k/trace/evttmplt.c_v  $
** 
**    Rev 1.1   18 Mar 1997 09:04:56   gene
** 
**    Rev 1.0   13 Feb 1997 09:07:14   gene
** Initial revision.
** 
**    Rev 1.1   12 Jan 1996 10:57:26   kevin
** modified TmpltGetValueText() to line up dada and index
** 
**    Rev 1.0   07 Sep 1995 11:22:14   gene
** Initial revision.
** 
**    Rev 1.58   05 May 1994 15:48:24   dennis
** fixes problem with event address mask field. ppr 9110 and 8867. Tread
** address mask field as a hex value, not an address value
** 
**    Rev 1.57   03 May 1994 09:08:20   dennis
** Modified GetAddrSpace to return address space indicator of SPACE_DONT_CARE
** if the function code value (FC2-0) is invalid. Valid values are 1 (UD),
** 2 (UP), 5 (SD), 6 (SP), and 7 (CPU).
** 
**    Rev 1.56   21 Apr 1994 16:26:22   john
** Added code for the new SIM_VALID sd member
** 
**    Rev 1.55   28 Feb 1994 16:59:12   marilyn
** Moved maxOutput routines to the address server.  Updated interfaces.
** 
**    Rev 1.54   29 Jul 1993 15:47:08   john
** Fixed error in memcopy
** 
**    Rev 1.53   27 Jul 1993 14:37:28   john
** The previous fix screwed up trace display by adding the 0x.  I added a
** flag to prevent adding the 0x unless requested.  (Event window wants
** the 0x, trace does not.)
** 
**    Rev 1.52   26 Jul 1993 13:05:18   john
** The event window DATA start/end fields were giving bogus error messages
** because the hexadecimal text displayed in them did not have the 0x 
** prefix.
** 
**    Rev 1.51   22 Jul 1993 10:38:26   ernie
** (For Mindy): Initialize xilinxData before calling to fill in text
** 
**    Rev 1.50   13 Jul 1993 19:06:42   doug
** Move errors to errtext/mkerrors.h and use generic syntax error
** 
**    Rev 1.49   26 May 1993 15:35:16   ernie
** Changed TmpltRestoreTemplates() to default to pins visible if memory
** is unreadable.  This was causing a boot failure if PowerPack was
** emulating.
** 
**    Rev 1.48   25 May 1993 11:35:24   ernie
** 1. Moved calls to proc server to libmain and set module-wide statics.
**      This avoids extra overhead of calling proc.dll each time.
** 2. Changed re-read of pin function registers to happen only when
**      actually needed by the presenter.  The callback routine now just
**      sets a dirty flag.  If the information is ever needed, the chip
**      select programming registers are read at that point.
** 3. Removed registration on EVENT_TRACING_STOPPED.  Memory cannot
**      normally be read until emulation stops.  The worst that will
**      happen is that the pin function names will be their old states
**      until memory can be read again.
** 
**    Rev 1.47   07 Apr 1993 17:20:24   mindy
** a) Added a routine to handle outputting the trace using the template
**    as a guide.
** b) Moved checking of fc function after halting, trace stops and memory
**    is editted to here.
** c) added other routines to support automatic event and trace updates
** 
**    Rev 1.46   31 Mar 1993 13:34:30   ernie
** 1. Changed mechanism to construct the filename to open for the evttmplt
**    definitions.  Now uses ProcInsertSpecificProcessor() to do this.
** 2. Rearranged initialization to initialize errors before using them.
** 
**    Rev 1.45   11 Jan 1993 17:24:48   john
** processor dlls #def's changed, code mod'ed to match
** 
**    Rev 1.44   05 Jan 1993 15:18:04   courtney
** Remove MemAlloc and related macros (no longer used).
** 
**    Rev 1.43   16 Dec 1992 16:20:36   john
** changed how trace config file is found
** 
**    Rev 1.42   15 Dec 1992 12:56:36   ernie
** Changed MemRead to MemReadSized with byte specified
** 
**    Rev 1.41   02 Dec 1992 11:47:18   doug
** Memory read allocates memory buffer even if an error is returned, so the
** memory has to always be freed.
** 
**    Rev 1.40   21 Oct 1992 12:52:18   doug
** if error in whats pin function, destroy address descriptor (ppr 6990
** memory leakage problem)
** 
**    Rev 1.39   21 Oct 1992 08:10:28   mindy
** a) fixed the two zillion warning messges. Also found a bug by fixing
**    wanrings.
** 
**    Rev 1.38   23 Sep 1992 06:49:02   mindy
** beef'd up libmain to check for no EVFILE field
** 
**    Rev 1.37   16 Sep 1992 18:51:24   mindy
** changed whatspinsfunction to first read the sim_addr from the fw.  Also
** fixed a problem with using the wrong bits.  The mem server is returning
** bytes in the "wrong" order - is really doing a byte read not a word read.
** 
**    Rev 1.36   16 Sep 1992 09:47:12   mindy
** make displayAddrSpace export'd
** 
**    Rev 1.35   08 Sep 1992 15:58:06   mindy
** changes unknown (john)
** 
**    Rev 1.34   27 Aug 1992 14:46:40   mindy
** need to destroy address desc we create
** 
**    Rev 1.33   14 Aug 1992 08:41:52   mindy
** a) changed error output of libmain to use error text's routine.
** b) all cli commands first check for a valid cli id.
** 
**    Rev 1.32   11 Aug 1992 09:54:24   mindy
** cleanup warnings
** 
**    Rev 1.31   10 Aug 1992 07:44:40   tom
** CLI registration changes.
** 
**    Rev 1.30   06 Aug 1992 07:31:36   mindy
** Added support to display address space if all fc pins are defined as
** SELF.  This was done by adding a display type DISP_MOTO_ADDR.  The routines
** added to support this should be moved to a processor specific file.!!!
** 
**    Rev 1.29   03 Aug 1992 06:25:06   mindy
** added what's pin's function routine
** 
**    Rev 1.28   28 Jul 1992 06:27:20   mindy
** bumped number of template fields
** 
**    Rev 1.27   17 Jul 1992 14:18:08   mindy
** default to 332 now
** 
**    Rev 1.26   17 Jul 1992 14:09:08   mindy
** added VISIBLE_TYPE concept
** 
**    Rev 1.25   10 Jul 1992 09:00:46   mindy
** added include file(s) needed to get rid of 3.1 warnings
** 
**    Rev 1.24   07 Jul 1992 09:56:46   mindy
** a) Commented out call to get profile string and hardcoded file to
**    be the 030 !!!
** b) Changed display length of hex ranges to include two more characters
**    for the 0x.
** 
**    Rev 1.23   02 Jul 1992 08:38:34   ernie
** Changed mechanism for configuring trace signal names from compile
** time to runtime.  There are now configuration files in the evttmplt
** save format that are restored during initialization.  The name of
** the template file to restore is obtained from tbird.ini.
** 
**    Rev 1.22   15 Jun 1992 10:36:40   ernie
** Split out probe-specific event signal definitions into evsignal.c.
** 
**    Rev 1.21   15 Jun 1992 09:01:24   mindy
** added '0x' in front of hex numbers in output routine
** 
**    Rev 1.20   14 May 1992 11:38:18   mindy
** removed GetErrorText routine and added ErrInitDLL call
** 
**    Rev 1.19   13 May 1992 08:11:46   mindy
** added get error text routine
** 
**    Rev 1.18   05 May 1992 13:56:24   mindy
** Fixed ppr5462.  Event template save and restore now works and template
** is now saved in readable ascii format that the user can read and edit.
** 
**    Rev 1.17   01 May 1992 11:29:22   mindy
** fc2-0 signals were on the wrong pins
** 
**    Rev 1.16   23 Apr 1992 11:13:46   mindy
** a) changed descriptor allocation to use TMAlloc.
** b) changed cli commands to use local cliId not require use to type id
**    for every command.
** 
**    Rev 1.15   03 Apr 1992 11:59:28   mindy
** CHanged signal defination to match the new pod's signal assignment.
** 
**    Rev 1.14   05 Mar 1992 11:41:14   mindy
** changed STATUS's hw bit
** 
**    Rev 1.13   27 Feb 1992 11:28:30   mindy
** forgot to up the number of template fields
** 
**    Rev 1.12   18 Feb 1992 14:08:46   mindy
** changed order that fc2-0 appear in trace display
** 
**    Rev 1.11   17 Feb 1992 06:49:56   mindy
** added some 030 signals to template
** 
**    Rev 1.10   17 Jan 1992 11:18:14   courtney
** Revised LibMain return type.
** 
**    Rev 1.9   07 Jan 1992 10:10:52   ernie
** use BOOLEAN not BOOL
** 
**    Rev 1.8   11 Dec 1991 14:57:32   mindy
** a) event template server in its own dll now
** b) moved SendCliResults here so load path of DLLs wasn't circular.  Now
** evttmplt.dll is the first DLL to load and then the others can be loaded
** according to their import list.
** 
**    Rev 1.7   06 Dec 1991 10:19:26   mindy
** moved common routines into local.h
** 
**    Rev 1.6   02 Dec 1991 10:54:58   mindy
** for mem lock and alloc errors used global errors
** 
**    Rev 1.5   26 Nov 1991 11:35:38   mindy
** a) added the 030 template fields.
** b) two more commands added to trace cli
** 
**    Rev 1.4   20 Nov 1991 10:53:46   mindy
** cli index changed because I added the trace search routines
** 
**    Rev 1.3   15 Nov 1991 14:58:52   mindy
** a) changed errors to conform to system numbers.
** b) added cli commands
** c) fixed a few misc. problems found while debugging cli commands.
** 
**    Rev 1.2   07 Nov 1991 14:24:26   mindy
** Added routine to return the display length of a given template field.
** 
**    Rev 1.1   01 Nov 1991 09:15:42   mindy
** First real implemented version
** 
**    Rev 1.0   26 Jul 1991 15:53:24   jim
** Initial revision.
**
**  $Header:   S:/tbird/mt2_68k/trace/evttmplt.c_v   1.1   18 Mar 1997 09:04:56   gene  $
**
**  Copyright (C) 1991 Microtek International.  All rights reserved.
**
*****************************************************************************/

/****************************
*                          *
*       INCLUDE FILES      *
*                          *
****************************/
#include "ctype.h"
#include "stdlib.h"

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

#ifndef _BASEWIND_
#include "basewind.h"
#endif

#ifndef _BKROOT_
#include "bkroot.h"
#endif

#ifndef _EVTTMPLT_
#include "evttmplt.h"
#endif

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

#ifndef _ENLIB_
#include "enlib.h"
#endif

#ifndef _CLISRV_
#include "clisrv.h"
#endif

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

#ifndef _HOSTERRS_
#include "hosterrs.h"
#endif

#ifndef _LOCAL_
#include "local.h"
#endif

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

#ifndef _SDPROBE_
#include "sdprobe.h"
#endif

#ifndef _SSHARED_
#include "sshared.h"
#endif

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


			/****************************
			 *                          *
			 *     LOCAL DEFINITIONS    *
			 *                          *
			 ****************************/

#define MAX_NUM_TEMPLATES  20
#define MAX_NUM_FIELDS     75

typedef enum { NOT_VISIBLE,      /* never show field */
	       ALWAYS_VISIBLE,   /* always show field */
	       SPECIAL_VISIBLE   /* special case (for chip select support) */
} VISIBLE_TYPE;

typedef struct {
   S8 name[MAX_FIELD_NAME];
   S8 title[CHARS_IN_TITLE+1];
   DISPLAY_FORMAT displayFormat;
   ACTIVE_STATE activeState;
   U16 trailingBlanks; /* number of trailing blanks following display */
   FIELD_TYPE fieldType;
   U16 numXilinxBits;
   U16 xilinxStartBit; /* 0..127, where R=0..31, S=32..63, etc. */
   /* NOTE:  bits must be contiguous in this design */
   VISIBLE_TYPE visible;   // if visible is special_visible then the next
   U16 cs;                 // two fields are used to determine if this
   PIN_FUNC visibleType;   // entry should be displayed.
   BOOLEAN currentlyVisible; // This field is updated everytime the processor
			     // halts
} EVENT_TEMPLATE_FIELD;

typedef struct {
   S8 name[MAX_TEMPLATE_NAME];
   U16 numTemplateFields;
   EVENT_TEMPLATE_FIELD templateFields[MAX_NUM_FIELDS];
} EVENT_TEMPLATE;

typedef struct {
   LOOP_VAR activeTemplate;  /*index+1 into template list of active template*/
   LOOP_VAR activeField;     /* index+1 in field list of active field */
} EVENT_TEMPLATE_BLOCK;

STATIC U32 maxOffset;
STATIC BOOLEAN asMapable;    /* is AS pin mappable as other function */


		      /****************************
		       *                          *
		       *    EXTERNAL VARIABLES    *
		       *                          *
		       ****************************/
 
		     /****************************
		       *                          *
		       *     LOCAL STORAGE        *
		       *                          *
		       ****************************/

static HANDLE cliServerHandle = 0;       /* return address for CLI results */

U16 numTemplates = 0;                 /* number of events defined */
/* list of event template names */
EVENT_TEMPLATE templateList[MAX_NUM_TEMPLATES];
STATIC BOOLEAN displayASField = FALSE;

STATIC BOOLEAN pinFunctionCacheDirty = TRUE;
STATIC BOOLEAN currentlyVisibleStateDirty = TRUE;

		       /****************************
			*                          *
			*     LOCAL PROTOTYPES     *
			*                          *
			****************************/
BOOLEAN TemplateExists(LPSTR name, LOOP_VAR FAR *number);
RETCODE CreateNewTemplate(LPSTR name, LOOP_VAR FAR *number);
BOOLEAN FieldExists(EVENT_TEMPLATE_FIELD FAR *list, LPSTR name,
		 LOOP_VAR FAR *number);
RETCODE PositionField(U16 startBit, U16 numOfBits, 
		    XILINX_NUMBER FAR *group, U32 FAR *mask, U32 FAR *shift);
U8 CalculateNumberOfDigits(DISPLAY_FORMAT format, U16 numXilinxBits, 
			   U8 FAR *base);
VOID ShortenString(LPSTR str);
LPSTR ReadWord(int hFile, LPSTR storage);
RETCODE WriteWord(int hFile, LPSTR word);
RETCODE WriteNewLine(int hFile);
RETCODE EnumerateDisplayFormat(LPSTR ptr, DISPLAY_FORMAT FAR *format);
LPSTR TextItDisplayFormat(DISPLAY_FORMAT format, LPSTR ptr);
RETCODE EnumerateActiveState(LPSTR ptr, ACTIVE_STATE FAR *state);
LPSTR TextItActiveState(ACTIVE_STATE state, LPSTR ptr);
RETCODE EnumerateFieldType(LPSTR ptr, FIELD_TYPE FAR *type);
LPSTR TextItFieldType(FIELD_TYPE type, LPSTR ptr);
RETCODE EnumerateNumber(LPSTR ptr, U16 FAR *num);
LPSTR TextItNumber(U16 num, LPSTR ptr);
VOID FAR PASCAL TraceStopUpdate(U32 event);
ADDR_SPACE GetAddrSpace(XILINX_GROUP FAR *bits);
VOID PRIVATE UpdateCurrentlyVisible(VOID);

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


#pragma argsused
int FAR PASCAL LibMain(HANDLE hInstance, WORD wDataSeg, WORD cbHeapSize,
    LPSTR lpszCmdLine) {
   LOOP_VAR i, j;
   RETCODE err;
   S8 evfile[128];
   FARPROC lpProc;
   DESCRIPTOR desc;

   ErrInitDLL(MODULE_EVENT_TEMPLATE,"evttmplt.dll");

   /* initialize event template server information */
   for(i=0; i<MAX_NUM_TEMPLATES; i++) {
      templateList[i].name[0] = '\0';
      for(j=0; j<MAX_NUM_FIELDS; j++) 
	 templateList[i].templateFields[j].name[0] = '\0';
   }
   if (((err = ProcInsertSpecificProcessor("ev%s.cfg",evfile))!=GOOD)
       || ((err = AdrGetMaxInputAddrOffset(NULL,&maxOffset)) != GOOD)
       || ((err = ProcReturnFunctionCodesMapable(&asMapable)) != GOOD)) {
      ErrDisplayError(err, FORCE_POPUP);
      return(FALSE);
   }

   if ((err=TmpltRestoreTemplates((LPSTR )evfile)) != GOOD) {
      if (err == ER_CANT_OPEN_TEMPLATE_FILE)
	 ErrDisplayString(ER_NO_EVFILE_ENTRY,evfile,FORCE_POPUP);
      else
	 ErrDisplayError(err, FORCE_POPUP);
      return(FALSE);
   }

   lpProc = MakeProcInstance((FARPROC)TraceStopUpdate, hInstance);
   EnlRegister(EVENT_MEM_HALTED, (EVCALLBACK)lpProc,&desc);
   EnlRegister(EVENT_MEM_EDIT, (EVCALLBACK)lpProc,&desc);

   /* 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);
}

/*****************************************************************************
** 
**    TmpltOpenTemplate
** 
*****************************************************************************/
RETCODE EXPORT TmpltOpenTemplate(DESCRIPTOR FAR *id) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   if((*id = (DESCRIPTOR)TMalloc(sizeof(EVENT_TEMPLATE_BLOCK)))==NULL) 
      return(ER_OUT_OF_MEMORY);
   template = (EVENT_TEMPLATE_BLOCK FAR *)*id;
   template->activeTemplate = 0;
   template->activeField = 0;
   return(TmpltSetTemplateName(*id, "default"));
}

/*****************************************************************************
** 
**    TmpltTemplateClose
** 
*****************************************************************************/
RETCODE EXPORT TmpltTemplateClose(DESCRIPTOR id) {
   TFree((LPSTR)id);
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltSetTemplateName
** 
*****************************************************************************/
RETCODE EXPORT TmpltSetTemplateName(DESCRIPTOR id, LPSTR name) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !TemplateExists(name,&template->activeTemplate) ) {
      RETCODE err;
      if( (err=CreateNewTemplate(name,&template->activeTemplate)) != GOOD ) 
	 return(err);
      numTemplates++;
   }
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetTemplateName
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetTemplateName(DESCRIPTOR id, LPSTR name, U16 maxLen) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   if( (lstrlen(templateList[template->activeTemplate-1].name)+1) > maxLen )
      return(ER_NOT_ENOUGH_STORAGE);
   lstrcpy(name,templateList[template->activeTemplate-1].name);
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetNumOfEntries
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetNumOfEntries(DESCRIPTOR id, U16 FAR *numOfEntries) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   *numOfEntries=templateList[template->activeTemplate-1].numTemplateFields;
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetNameGivenFieldNumber
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetNameGivenFieldNumber(DESCRIPTOR id, U16 fieldNum,
					    LPSTR name, U16 maxLen) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   if((fieldNum == 0) || 
      (fieldNum > templateList[template->activeTemplate-1].numTemplateFields))
      return(ER_BAD_FIELD_NUMBER);         
   if( templateList[template->activeTemplate-1]
      .templateFields[fieldNum-1].name[0] == '\0' ) 
      return(ER_INVALID_FIELD_NUM);
   if( lstrlen(templateList[template->activeTemplate-1].
	       templateFields[fieldNum-1].name) >= maxLen ) 
      return(ER_NOT_ENOUGH_STORAGE);
   lstrcpy(name, templateList[template->activeTemplate-1].
		 templateFields[fieldNum-1].name);
   template->activeField = fieldNum;
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltSetFieldName
** 
*****************************************************************************/
RETCODE EXPORT TmpltSetFieldName(DESCRIPTOR id, LPSTR name) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   if( !FieldExists(templateList[template->activeTemplate-1].templateFields,
		    name,&template->activeField) ) 
      /* first pass can't create new template fields */
      return(ER_INVALID_FIELD);
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetFieldName
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetFieldName(DESCRIPTOR id,LPSTR name,U16 maxLen) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   if( !template->activeField ) return(ER_NO_ACTIVE_FIELD);
   if( (lstrlen(templateList[template->activeTemplate-1]
	       .templateFields[template->activeField-1].name)+1) > maxLen )
      return(ER_NOT_ENOUGH_STORAGE);
   lstrcpy(name,templateList[template->activeTemplate-1]
		.templateFields[template->activeField-1].name);
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetTitle
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetTitle(DESCRIPTOR id, LPSTR title) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   if( !template->activeField ) return(ER_NO_ACTIVE_FIELD);
   if( lstrlen(templateList[template->activeTemplate-1]
	  .templateFields[template->activeField-1].title) > CHARS_IN_TITLE )
      return(ER_NOT_ENOUGH_STORAGE);
   lstrcpy(title,templateList[template->activeTemplate-1]
		.templateFields[template->activeField-1].title);
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetDisplayFormat
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetDisplayFormat(DESCRIPTOR id,
				     DISPLAY_FORMAT FAR *displayFormat) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   if( !template->activeField ) return(ER_NO_ACTIVE_FIELD);
   *displayFormat = templateList[template->activeTemplate-1]
		    .templateFields[template->activeField-1].displayFormat;
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetDisplayLength
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetDisplayLength(DESCRIPTOR id, U8 FAR *length) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   EVENT_TEMPLATE_FIELD *field;
   U8 base;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   if( !template->activeField ) return(ER_NO_ACTIVE_FIELD);
   field = &templateList[template->activeTemplate-1]
	    .templateFields[template->activeField-1];
   *length=CalculateNumberOfDigits(field->displayFormat,field->numXilinxBits,
				   &base);
   if(field->displayFormat == DISP_MOTO_ADDR ){ 
      if (currentlyVisibleStateDirty) UpdateCurrentlyVisible();
      if( displayASField ) *length += 4;   /* for addrspace */
      if( *length < 7 ) *length = 7;
   }
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetActiveState
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetActiveState(DESCRIPTOR id,
				   ACTIVE_STATE FAR *activeState) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   if( !template->activeField ) return(ER_NO_ACTIVE_FIELD);
   *activeState = templateList[template->activeTemplate-1]
		  .templateFields[template->activeField-1].activeState;
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetTrailingBlanks
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetTrailingBlanks(DESCRIPTOR id, U16 FAR *trailingBlanks){
   EVENT_TEMPLATE_BLOCK FAR *template;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   if( !template->activeField ) return(ER_NO_ACTIVE_FIELD);
   *trailingBlanks = templateList[template->activeTemplate-1]
		  .templateFields[template->activeField-1].trailingBlanks;
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetFieldType
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetFieldType(DESCRIPTOR id, FIELD_TYPE FAR *fieldType) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   if( !template->activeField ) return(ER_NO_ACTIVE_FIELD);
   *fieldType = templateList[template->activeTemplate-1]
		.templateFields[template->activeField-1].fieldType;
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetVisible
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetVisible(DESCRIPTOR id, BOOLEAN FAR *visible) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   if( !template->activeField ) return(ER_NO_ACTIVE_FIELD);
   if (currentlyVisibleStateDirty) UpdateCurrentlyVisible();
   *visible = templateList[template->activeTemplate-1]
	      .templateFields[template->activeField-1].currentlyVisible;
   return(GOOD);
}

U8 toASCII(U8 digit) {
   if( digit <= 9 )
      return(digit+'0');
   return((digit-10)+'A');
}

U8 CalculateNumberOfDigits(DISPLAY_FORMAT format, U16 numXilinxBits, 
			   U8 FAR *base) {
   U8 numDigits;
   switch( format ) {
      case DISP_MOTO_ADDR:
      case DISP_HEX:
	 numDigits = (numXilinxBits+3) / 4;
	 *base = 16;
	 break;
      case DISP_DEC:              
	 numDigits = numXilinxBits/3;  /* not an exact count for dec */
	 *base = 10;
	 break;
      case DISP_OCT:
	 numDigits = (numXilinxBits+2) / 3;
	 *base = 8;
	 break;
      case DISP_BIN:              
      case ASSERT_DEF:            
      case ASSERT_FIRST:
	 numDigits = numXilinxBits;
	 *base = 2;
	 break;
   }
   return(numDigits);
}

/*****************************************************************************
** 
**    TmpltGetValueText
** 
**       This routine will return text in a 
**       suitible form depending upon the display type and number of bits,
**       etc.  This routine will also zero fill; thus the returned text
**       will not have any blanks.
*****************************************************************************/
RETCODE EXPORT TmpltGetValueText(DESCRIPTOR id, XILINX_GROUP FAR *bits,
				 LPSTR valueText, U16 maxLen, BOOLEAN prefix) {
   U32 value;
   U8 numDigits, base, i;
   RETCODE err;
   EVENT_TEMPLATE_BLOCK FAR *template;
   EVENT_TEMPLATE_FIELD *field;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( (err = TmpltGetValue(id, bits, &value)) != GOOD ) return(err);
   field = &templateList[template->activeTemplate-1]
	    .templateFields[template->activeField-1];
	 
   numDigits = CalculateNumberOfDigits(field->displayFormat, 
		  field->numXilinxBits, &base);
   if( (numDigits+1) > maxLen ) return(ER_NOT_ENOUGH_STORAGE);
   if( field->displayFormat == ASSERT_FIRST ) {
      if( (value && (field->activeState==ACTIVE_HIGH))
	 || (!value && (field->activeState==ACTIVE_LOW)) )
	 valueText[0] = toupper(field->name[0]);
      else
	 valueText[0] = tolower(field->name[0]);
      valueText[1] = '\0';
   }
   else if( field->displayFormat==DISP_MOTO_ADDR ) {
      ADDR_SPACE space;
      S8 *addrSpaceStr, addrText[15], cnt;
      DESCRIPTOR addr;
      space = GetAddrSpace(bits);
      switch( space ) {
	 case SPACE_UD: addrSpaceStr = " ud "; break;
	 case SPACE_UP: addrSpaceStr = " up "; break;
	 case SPACE_SD: addrSpaceStr = " sd "; break;
	 case SPACE_SP: addrSpaceStr = " sp "; break;
	 case SPACE_CPU: addrSpaceStr = " cpu"; break;
	 default:
	    /* Put this fix in to pad 4 spaces as the space designator */
			/* if were are currently displaying address space and the  */
			/* function codes are not set to a valid space designator. */
			/* Fix made by Dennis Lamb, May 5, 1994.                   */
	    if (displayASField)
	      addrSpaceStr = "    ";
	    else
	       addrSpaceStr = "";
	    break;
      }
      if ((err = AdrCreateAddress(&addr)) != GOOD) return err;
      if ((err = AdrSetAddrSpace(addr,space)) != GOOD) {
	 AdrDestroyAddress(addr);
	 return err;
      }
      if((err = AdrSetAddrOffset(addr,maxOffset&value))!=GOOD) {
	 AdrDestroyAddress(addr);
	 return err;
      }
      if((err=AdrConvAddressToTextWithParams(addr,FALSE,TRUE,addrText))
	 != GOOD ){
	 AdrDestroyAddress(addr);
	 return err;
      }
      AdrDestroyAddress(addr);
      cnt = strlen(addrText) + strlen(addrSpaceStr);
      while( cnt++ < 12 ) lstrcat(valueText," ");
      lstrcat(valueText,addrText);
      lstrcat(valueText,addrSpaceStr);
   }
   else { /*!!! Currently not supporting ASSERT_DEF just treating as binary. */
      U8 saveNumDigits;
      i = saveNumDigits = numDigits;
      valueText[i] = '\0';      
      while( numDigits-- ) {
	 U8 digit;
	 if( value ) {
	    digit = (U8)(value % (U32)base);
	    value /= base;
	 }
	 else 
	    digit = 0;
	 valueText[--i] = toASCII(digit);
      }
      if (prefix) {
	 switch(base) {
	    case 16:
	       if ((saveNumDigits + 2) > maxLen) return(ER_NOT_ENOUGH_STORAGE);
	       memmove(valueText+2, valueText, strlen(valueText)+1);
	       valueText[0] = '0';
	       valueText[1] = 'x';
	    break;
	    case 8:
	       if ((saveNumDigits + 1) > maxLen) return(ER_NOT_ENOUGH_STORAGE);
	       memmove(valueText+1, valueText, strlen(valueText)+1);
	       valueText[0] = '0';
	    break;
	    default:
	       break;
	 }
      }
   }
   return(GOOD);
}
   
/*****************************************************************************
** 
**    TmpltGetMaskValueText
** 
**       This routine will return text in a 
**       suitible form depending upon the display type and number of bits,
**       etc.  This routine will also zero fill; thus the returned text
**       will not have any blanks.
*****************************************************************************/
/* This routine was added by Dennis Lamb, May 5, 1994. This routine deals   */
/* with the event address mask field. Previously the address mask field     */
/* was handled by TmpltGetValueText, but because the display format of      */
/* the address mask field was DISP_MOTO_ADDR (the same as the address field */
/* the mask string was being returned as "FFFFFF" or "FFFFFF cpu" based on  */
/* whether or not the function codes (fc0, fc1, fc2) were active.           */
RETCODE EXPORT TmpltGetMaskValueText(DESCRIPTOR id, XILINX_GROUP FAR *bits,
				 LPSTR valueText, U16 maxLen) {
   U32 value;
   U8 numDigits, base, i;
   U8 saveNumDigits;
   RETCODE err;
   EVENT_TEMPLATE_BLOCK FAR *template;
   EVENT_TEMPLATE_FIELD *field;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( (err = TmpltGetValue(id, bits, &value)) != GOOD ) return(err);
   field = &templateList[template->activeTemplate-1]
	    .templateFields[template->activeField-1];
	 
   numDigits = CalculateNumberOfDigits(field->displayFormat, 
		  field->numXilinxBits, &base);
   if( (numDigits+1) > maxLen ) return(ER_NOT_ENOUGH_STORAGE);
   
   i = saveNumDigits = numDigits;
   valueText[i] = '\0';      
   while( numDigits-- ) {
      U8 digit;
      if( value ) {
	 digit = (U8)(value % (U32)base);
	 value /= base;
      }
      else 
	 digit = 0;
      valueText[--i] = toASCII(digit);
   }
   switch(base) {
     case 16:
	if ((saveNumDigits + 2) > maxLen) return(ER_NOT_ENOUGH_STORAGE);
	memmove(valueText+2, valueText, strlen(valueText)+1);
	valueText[0] = '0';
	valueText[1] = 'x';
	break;
     case 8:
	if ((saveNumDigits + 1) > maxLen) return(ER_NOT_ENOUGH_STORAGE);
	memmove(valueText+1, valueText, strlen(valueText)+1);
	valueText[0] = '0';
	break;
     default:
	break;
   }
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetValue
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetValue(DESCRIPTOR id, XILINX_GROUP FAR *bits,
				  U32 FAR *value) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   RETCODE err;
   LOOP_VAR aT, aF;
   XILINX_NUMBER group;
   U32 mask, shift;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !(aT=template->activeTemplate) ) return(ER_NO_ACTIVE_TEMPLATE);
   if( !(aF=template->activeField) ) return(ER_NO_ACTIVE_FIELD);
   aT--; aF--;
   if( (err=PositionField(templateList[aT].templateFields[aF].xilinxStartBit,
			  templateList[aT].templateFields[aF].numXilinxBits,
			  &group, &mask, &shift)) != GOOD ) return(err);
   *value = (bits->xilinxGroup[group] & mask) >> shift;
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltSetValue
** 
*****************************************************************************/
RETCODE EXPORT TmpltSetValue(DESCRIPTOR id, XILINX_GROUP FAR *bits,
				  U32 value) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   RETCODE err;
   LOOP_VAR aT, aF;
   XILINX_NUMBER group;
   U32 mask, shift;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !(aT=template->activeTemplate) ) return(ER_NO_ACTIVE_TEMPLATE);
   if( !(aF=template->activeField) ) return(ER_NO_ACTIVE_FIELD);
   aT--; aF--;
   if( (err=PositionField(templateList[aT].templateFields[aF].xilinxStartBit,
			  templateList[aT].templateFields[aF].numXilinxBits,
			  &group, &mask, &shift)) != GOOD ) return(err);
   value = (value << shift) & mask;
   bits->xilinxGroup[group] &= ~mask;   /* clear out old bits */
   bits->xilinxGroup[group] |= value;   /* OR in new bits */
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltGetGroup
** 
*****************************************************************************/
RETCODE EXPORT TmpltGetGroup(DESCRIPTOR id, XILINX_NUMBER FAR *group) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   U16 startGroup, endGroup;
   U16 start; 
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   if( !template->activeField ) return(ER_NO_ACTIVE_FIELD);
   start = templateList[template->activeTemplate-1]
	   .templateFields[template->activeField-1].xilinxStartBit;
   startGroup = start / 32;
   endGroup = (start + templateList[template->activeTemplate-1]
	       .templateFields[template->activeField-1].numXilinxBits - 1)/32;
   if( startGroup != endGroup ) return(ER_XILINX_BITS_OVERFLOW);
   *group = startGroup;
   return(GOOD);
}

/*****************************************************************************
** 
**    TmpltWhatsPinsFunction
** 
*****************************************************************************/
#define CSPAR0_OFFSET 0x44L

RETCODE EXPORT TmpltWhatsPinsFunction(U16 cs, PIN_FUNC FAR *func){
   RETCODE err;
   static U8 pinFunc[4];  /* copy of CSPAR0, CSPAR1 registers */
   U32 simBaseAddr;
   BOOLEAN simValid;
   if (pinFunctionCacheDirty) {
      if ((err=SdnReadMember(SDN_SIM_VALID,(U8 *)&simValid)) != GOOD)
	 return(err);
      if (!simValid) {
	 err = ER_SIM_UNREADABLE;
	 return(err);
      }
      if((err=SdnReadMember(SDN_SIM_ADDRESS,(U8 *)&simBaseAddr))!=GOOD) 
	 return(err);
      if((err=MemReadQuick((simBaseAddr&maxOffset)+CSPAR0_OFFSET, SPACE_SD,
	 sizeof(pinFunc), pinFunc, SIZE_WORD, CACHE_BYPASS)) != GOOD)
	 return(err);
      pinFunctionCacheDirty = FALSE;
   }
   switch(cs) {
      case 0:   /* cs0/br */
      case 1:   /* cs1/bg */
      case 2:   /* cs2/bgack */
	 *func = (PIN_FUNC)((pinFunc[1]>>((cs+1)*2)) & 3);
	 break;
      case 3:   /* cs3/fc0 */
      case 4:   /* cs4/fc1 */
      case 5:   /* cs5/fc2 */
	 *func = (PIN_FUNC)((pinFunc[0]>>((cs-3)*2)) & 3);
	 break; 
      case 6:   /* cs6/a19 */
      case 7:   /* cs7/a20 */
      case 8:   /* cs8/a21 */
      case 9:   /* cs9/a22 */
	 *func = (PIN_FUNC)((pinFunc[3]>>((cs-6)*2)) & 3);
	 break; 
      case 10:  /* cs10/a23 */
	 *func = (PIN_FUNC)(pinFunc[2] & 3);
	 break; 
   }
   return(GOOD);
}   

/*****************************************************************************
** 
**    TmpltRestoreTemplates
** 
*****************************************************************************/
RETCODE EXPORT TmpltRestoreTemplates(LPSTR fileName) {
   int hFile;
   S8 templateName[MAX_TEMPLATE_NAME], buf[256];
   U16 numFields;
   LOOP_VAR index, i;
   RETCODE err;
   if (fileName && (lstrlen(fileName) > 0)) {
      if((hFile = _lopen(fileName,OF_READ)) == -1) return(ER_CANT_OPEN_FILE);
      while(1) {
	 if( ReadWord(hFile,templateName) == NULL ) break;
	 if( !TemplateExists(templateName,&index) ) {
	    RETCODE err;
	    if((err=CreateNewTemplate(templateName,&index))!=GOOD) 
	       return(err);
	    numTemplates++;
	 }
	 if((err=EnumerateNumber(ReadWord(hFile,buf),&numFields))!=GOOD)
	    return(err);
	 templateList[index-1].numTemplateFields = numFields;
	 for( i = 0; i < numFields; i++ ){
	    EVENT_TEMPLATE_FIELD *p;
	    LOOP_VAR len;
	    p = &templateList[index-1].templateFields[i];
	    if( ReadWord(hFile,p->name) == NULL ) return(ER_UNEXPECTED_END);
	    if( ReadWord(hFile,p->title) == NULL ) return(ER_UNEXPECTED_END);
	    if( (len=lstrlen(p->title)) < 3 ) {
	       for( ; len < 3; len++ ) p->title[len] = ' ';
	       p->title[len] = '\0';
	    }
	    if((err=EnumerateDisplayFormat(ReadWord(hFile,buf),
				       &p->displayFormat))!=GOOD) return(err);
	    if((err=EnumerateActiveState(ReadWord(hFile,buf),
				       &p->activeState))!=GOOD) return(err);
	    if((err=EnumerateNumber(ReadWord(hFile,buf),&p->trailingBlanks))
	       !=GOOD) return(err);
	    if((err=EnumerateFieldType(ReadWord(hFile,buf),&p->fieldType))
	       !=GOOD) return(err);
	    if((err=EnumerateNumber(ReadWord(hFile,buf),&p->numXilinxBits))
	       !=GOOD) return(err);
	    if((err=EnumerateNumber(ReadWord(hFile,buf),&p->xilinxStartBit))
	       !=GOOD) return(err);
	    if((err=EnumerateNumber(ReadWord(hFile,buf),
	       (U16 FAR *)&p->visible)) != GOOD) return(err);
	    if((err=EnumerateNumber(ReadWord(hFile,buf),
	       (U16 FAR *)&p->cs)) != GOOD) return(err);
	    if((err=EnumerateNumber(ReadWord(hFile,buf),
	       (U16 FAR *)&p->visibleType)) != GOOD) return(err);
	    if(p->visible==ALWAYS_VISIBLE) p->currentlyVisible = TRUE;
	    else if(p->visible==NOT_VISIBLE) p->currentlyVisible = FALSE;
	    else if(p->visible==SPECIAL_VISIBLE) {
	       PIN_FUNC curFunc;
	       if((err=TmpltWhatsPinsFunction(p->cs,&curFunc))==GOOD) {
		  if( curFunc != p->visibleType ) p->currentlyVisible = FALSE;
		  else p->currentlyVisible = TRUE;
	       } else p->currentlyVisible = TRUE;
	    }
	 }
	 templateList[index-1].templateFields[numFields].name[0] = '\0';
      }
      if(_lclose(hFile) == -1) return(ER_CANT_CLOSE_FILE);
      return(GOOD);
   }
   return(ER_NO_TEMPLATE_FILE_GIVEN);
}

/*****************************************************************************
** 
**    TmpltSaveTemplates
** 
*****************************************************************************/
RETCODE EXPORT TmpltSaveTemplates(LPSTR fileName) {
   int hFile;
   S8 buf[256];
   LOOP_VAR tIdx, fIdx;        /* template and field index counters */
   RETCODE err;
   if (fileName && (lstrlen(fileName) > 0)) {
      if((hFile = _lcreat(fileName,0)) == -1) return(ER_CANT_OPEN_FILE);
      for( tIdx = 0; tIdx < numTemplates; tIdx++ ) {
	 if((err=WriteWord(hFile,templateList[tIdx].name))!=GOOD) return(err);
	 if((err=WriteNewLine(hFile))!=GOOD) return(err);
	 if((err=WriteWord(hFile,TextItNumber(
	    templateList[tIdx].numTemplateFields,buf)))!=GOOD) return(err);
	 if((err=WriteNewLine(hFile))!=GOOD) return(err);
	 for( fIdx = 0; fIdx < templateList[tIdx].numTemplateFields; fIdx++ ){
	    EVENT_TEMPLATE_FIELD *p;
	    p = &templateList[tIdx].templateFields[fIdx];
	    if((err=WriteWord(hFile,p->name))!=GOOD) return(err);
	    if((err=WriteWord(hFile,p->title))!=GOOD) return(err);
	    if((err=WriteWord(hFile,TextItDisplayFormat(p->displayFormat,buf))
	       )!=GOOD) return(err);
	    if((err=WriteWord(hFile,TextItActiveState(p->activeState,buf)))
	       !=GOOD) return(err);
	    if((err=WriteWord(hFile,TextItNumber(p->trailingBlanks,buf)))
	       !=GOOD) return(err);
	    if((err=WriteWord(hFile,TextItFieldType(p->fieldType,buf)))
	       !=GOOD) return(err);
	    if((err=WriteWord(hFile,TextItNumber(p->numXilinxBits,buf)))
	       !=GOOD) return(err);
	    if((err=WriteWord(hFile,TextItNumber(p->xilinxStartBit,buf)))
	       !=GOOD) return(err);
	    if((err=WriteWord(hFile,TextItNumber(p->visible,buf)))
	       !=GOOD) return(err);
	    if((err=WriteWord(hFile,TextItNumber(p->cs,buf)))
	       !=GOOD) return(err);
	    if((err=WriteWord(hFile,TextItNumber(p->visibleType,buf)))
	       !=GOOD) return(err);
	    if((err=WriteNewLine(hFile))!=GOOD) return(err);
	 }
	 if((err=WriteNewLine(hFile))!=GOOD) return(err);
      }
      if(_lclose(hFile) == -1) return(ER_CANT_CLOSE_FILE);
      return(GOOD);
   }
   return(ER_NO_FILE_GIVEN);
}

/*************************** CLI FUNCTIONS ******************************/
RETCODE EXPORT SendCliResults(LPSTR results, HANDLE server) {
   CSERVER_RESULTS FAR * msgBufPtr;
   HANDLE msgBufHandle;
   U16 msgTextSize;
   msgTextSize = lstrlen(results);
   if((msgBufHandle=GlobalAlloc(GMEM_MOVEABLE, (sizeof(CSERVER_RESULTS)+
    msgTextSize+2)))==NULL)
      return(ER_OUT_OF_MEMORY);
   if((msgBufPtr=(CSERVER_RESULTS *)GlobalLock(msgBufHandle)) == NULL)
      return(ER_WINDOWS_MEMLOCK);
   msgBufPtr->target = 0;
   msgBufPtr->variantCode = 0x401;
   msgBufPtr->resultTextLength = msgTextSize;
   lstrcpy(msgBufPtr->messageText,results);
   if( SendMessage(server, 0x401, (WORD)msgBufHandle, 0x401L) )
      return(GOOD);
   return(ER_SEND_MESSAGE_FAILED);
}
   
static DESCRIPTOR cliId=0;
RETCODE CheckCliId(VOID) {
   if( !cliId ) return(TmpltOpenTemplate(&cliId));
   return(GOOD);
}
   
#pragma argsused
RETCODE EXPORT TmpltCliOpen(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err;
   if( cliId ) 
      return(SendCliResults((LPSTR)"evttmplt already open",cliServerHandle));
   if( (err=TmpltOpenTemplate(&cliId)) != GOOD ) return(err);
   return(SendCliResults((LPSTR)"evttmplt opened",cliServerHandle));
}

#pragma argsused
RETCODE EXPORT TmpltCliClose(LPSTR cmdString, U32 argc, U32 argv[]) {
   RETCODE err=GOOD;
   if( cliId ) {
      err = TmpltTemplateClose(cliId);
      cliId = 0;
   }
   return(err);
}

RETCODE EXPORT TmpltCliTemplateName(LPSTR cmdString, U32 argc, U32 argv[]) {
   S8 resultBuff[128];
   RETCODE err;
   if((err=CheckCliId())!=GOOD) return(err);
   if( argc == 2 ) {
      if( (err=TmpltSetTemplateName(cliId, &cmdString[(U16)argv[1]]))!=GOOD ) 
	 return(err);
   }
   if((err=TmpltGetTemplateName(cliId,resultBuff,128))!=GOOD) return(err);
   return(SendCliResults(resultBuff,cliServerHandle));
}

#pragma argsused
RETCODE EXPORT TmpltCliNumOfEntries(LPSTR cmdString,U32 argc,U32 argv[]) {
   U16 num;
   S8 resultBuff[128];
   RETCODE err;
   if((err=CheckCliId())!=GOOD) return(err);
   if( (err=TmpltGetNumOfEntries(cliId,&num)) != GOOD ) return(err);
   wsprintf(resultBuff,"%d", num);
   return(SendCliResults(resultBuff,cliServerHandle));
}

RETCODE EXPORT TmpltCliNameGivenNumber(LPSTR cmdString, U32 argc, U32 argv[]){
   S8 resultBuff[128];
   RETCODE err;
   if((err=CheckCliId())!=GOOD) return(err);
   if( argc != 2 ) return(ER_CLI_SYNTAX);
   if((err=TmpltGetNameGivenFieldNumber(cliId, atoi(&cmdString[(U16)argv[1]]),
      resultBuff,128)) != GOOD ) return(err);
   return(SendCliResults(resultBuff,cliServerHandle));
}

RETCODE EXPORT TmpltCliFieldName(LPSTR cmdString, U32 argc, U32 argv[]) {
   S8 resultBuff[128];
   RETCODE err;
   if((err=CheckCliId())!=GOOD) return(err);
   if( argc == 2 ) {
      if( (err=TmpltSetFieldName(cliId, &cmdString[(U16)argv[1]])) != GOOD ) 
	 return(err);
   }
   if((err=TmpltGetFieldName(cliId, resultBuff, 128))!=GOOD) return(err);
   return(SendCliResults(resultBuff,cliServerHandle));
}

#pragma argsused
RETCODE EXPORT TmpltCliGetTitle(LPSTR cmdString, U32 argc, U32 argv[]) {
   S8 resultBuff[CHARS_IN_TITLE+1];
   RETCODE err;
   if((err=CheckCliId())!=GOOD) return(err);
   if( (err=TmpltGetTitle(cliId,resultBuff)) != GOOD ) return(err);
   return(SendCliResults(resultBuff,cliServerHandle));
}

#pragma argsused
RETCODE EXPORT TmpltCliGetDisplayFormat(LPSTR cmdString,U32 argc,U32 argv[]){
   S8 resultBuff[128];
   DISPLAY_FORMAT format;
   RETCODE err;
   if((err=CheckCliId())!=GOOD) return(err);
   if( (err=TmpltGetDisplayFormat(cliId,&format))!=GOOD ) return(err);
   TextItDisplayFormat(format, resultBuff);
   return(SendCliResults(resultBuff,cliServerHandle));
}

#pragma argsused
RETCODE EXPORT TmpltCliGetDisplayLength(LPSTR cmdString,U32 argc,U32 argv[]){
   S8 resultBuff[128];
   U8 len;
   RETCODE err;
   if((err=CheckCliId())!=GOOD) return(err);
   if( (err=TmpltGetDisplayLength(cliId,&len))!=GOOD ) return(err);
   wsprintf(resultBuff,"%d",len);
   return(SendCliResults(resultBuff,cliServerHandle));
}

#pragma argsused
RETCODE EXPORT TmpltCliGetActiveState(LPSTR cmdString,U32 argc,U32 argv[]){
   S8 resultBuff[128];
   ACTIVE_STATE state;
   RETCODE err;
   if((err=CheckCliId())!=GOOD) return(err);
   if( (err=TmpltGetActiveState(cliId,&state))!=GOOD ) return(err);
   TextItActiveState(state, resultBuff);
   return(SendCliResults(resultBuff,cliServerHandle));
}

#pragma argsused
RETCODE EXPORT TmpltCliGetTrailingBlanks(LPSTR cmdString,U32 argc,U32 argv[]){
   S8 resultBuff[128];
   U16 num;
   RETCODE err;
   if((err=CheckCliId())!=GOOD) return(err);
   if( (err=TmpltGetTrailingBlanks(cliId,&num))!=GOOD ) return(err);
   wsprintf(resultBuff,"%d",num);
   return(SendCliResults(resultBuff,cliServerHandle));
}

#pragma argsused
RETCODE EXPORT TmpltCliGetFieldType(LPSTR cmdString,U32 argc,U32 argv[]){
   S8 resultBuff[128];
   FIELD_TYPE type;
   RETCODE err;
   if((err=CheckCliId())!=GOOD) return(err);
   if( (err=TmpltGetFieldType(cliId,&type))!=GOOD ) return(err);
   TextItFieldType(type, resultBuff);
   return(SendCliResults(resultBuff,cliServerHandle));
}

#pragma argsused
RETCODE EXPORT TmpltCliGetGroup(LPSTR cmdString,U32 argc,U32 argv[]){
   S8 resultBuff[128];
   LPSTR ptr;
   XILINX_NUMBER group;
   RETCODE err;
   if((err=CheckCliId())!=GOOD) return(err);
   if( (err=TmpltGetGroup(cliId,&group))!=GOOD ) return(err);
   switch( group ) {
      case R_GROUP: ptr = "group R"; break;
      case S_GROUP: ptr = "group S"; break;
      case T_GROUP: ptr = "group T"; break;
      case U_GROUP: ptr = "group U"; break;
      default: ptr = "";
   }
   lstrcpy(resultBuff,ptr);
   return(SendCliResults(resultBuff,cliServerHandle));
}

RETCODE EXPORT TmpltCliRestoreTemplates(LPSTR cmdString,U32 argc,U32 argv[]){
   if( argc == 2 ) return(TmpltRestoreTemplates(&cmdString[(U16)argv[1]]));
   return(ER_CLI_SYNTAX);
}

RETCODE EXPORT TmpltCliSaveTemplates(LPSTR cmdString, U32 argc, U32 argv[]) {
   if( argc == 2 ) return(TmpltSaveTemplates(&cmdString[(U16)argv[1]]));
   return(ER_CLI_SYNTAX);
}

/****************************** LOCAL FUNCTIONS *****************************/
/*****************************************************************************
** 
**    TemplateExists
** 
**    Description:
**       Tells whether an template name is currently defined
**
**    Parameters:
**       input:
**          list:       list of templates
**          name:       name of template
**       output:
**          number:     number (index+1) into table of template 
**                      (if TRUE returned)
*****************************************************************************/
BOOLEAN TemplateExists(LPSTR name, LOOP_VAR FAR *number) {
   for((*number)=0; (*number) < MAX_NUM_TEMPLATES; (*number)++) {
      if(!lstrcmp(name, templateList[*number].name)) {
	 (*number)++;         
	 return(TRUE);
      }
   }
   *number = 0;   /* invalid number */
   return(FALSE);
}

/*****************************************************************************
** 
**    CreateNewTemplate
** 
**    Description:
**       Adds a new template to the template list, if there is room.
**
**    Parameters:
**       input:
**          name:       template name
**       output:
**          number:      number into table of templates (if no error returned)
*****************************************************************************/
RETCODE CreateNewTemplate(LPSTR name, LOOP_VAR FAR *number) {
   *number = 0;   /* put an invalid number value initially */
   if(lstrlen(name) >= MAX_TEMPLATE_NAME) return(ER_TEMPLATE_NAME_TOO_LONG);
   for((*number)=0; (*number)<MAX_NUM_TEMPLATES; (*number)++) {
      if(templateList[*number].name[0] == '\0') { /* available */
	 lstrcpy(templateList[*number].name, name);
	 templateList[*number].numTemplateFields = 0;
	 (*number)++;       /* return index+1 just like TemplateExists does */
	 return(GOOD);
      }
   }
   *number = 0;   /* return an invalid number */
   return(ER_TOO_MANY_TEMPLATES);
}

/*****************************************************************************
** 
**    FieldExists
** 
**    Description:
**       Tells whether an template field name is currently defined
**
**    Parameters:
**       input:
**          list:       list of fields
**          name:       name of template field
**       output:
**          number:     number (index+1) into table of template field
**                      (if TRUE returned)
*****************************************************************************/
BOOLEAN FieldExists(EVENT_TEMPLATE_FIELD FAR *list, LPSTR name, 
		    LOOP_VAR FAR *number) {
   for((*number)=0; (*number) < MAX_NUM_FIELDS; (*number)++) {
      if(!lstrcmp(name, list[*number].name)) {
	 (*number)++;         
	 return(TRUE);
      }
   }
   *number = 0;   /* invalid number */
   return(FALSE);
}


/*****************************************************************************
** 
**    PositionField
** 
**    Description:
**       Returns the group, mask and shift values for the currently
**       active template field.
**
**    Parameters:
**       input:
**          startBit:   starting xilinx bit
**          numOfBits:  number of xilinx bits in field
**       output:
**          group:      which xilinx group the template field occupies
**          mask:       mask with bits within a 32 bit xilinx set where
**                      template field resides.
**          shift:      number of bit positions to shift to get to lowest
**                      (or start + num) bit.
**
*****************************************************************************/
RETCODE PositionField(U16 startBit, U16 numOfBits, 
		    XILINX_NUMBER FAR *group, U32 FAR *mask, U32 FAR *shift) {
   U32 pos;
   U16 endGroup;
   LOOP_VAR i;
   *group = startBit / 32;
   endGroup = (startBit + numOfBits - 1) / 32;
   if( *group != endGroup ) return(ER_XILINX_BITS_OVERFLOW);

   pos = startBit - (*group * 32);
   *shift = (31 - (pos + numOfBits - 1)) & 0x1f;
   *mask = 0;
   for(i=0; i<numOfBits; i++) {
      *mask <<= 1;     /* shift up one bit position */
      *mask |= 1;      /* OR in next bit */
   }
   /* shift series of one bits into position */
   for(i=0; i<*shift; i++) *mask <<= 1;   
   return(GOOD);
}

/*****************************************************************************
** 
**    ShortenString
** 
**    Description:
**       Finds the first white space character and replaces it with a
**       NULL character to shorten string to just contain the text characters.
**
**    Parameters:
**       input:
**          str:        string to shorten
**       output:
**          none
**
*****************************************************************************/
VOID ShortenString(LPSTR str) {
   LOOP_VAR i=0;
   while(str[i] != '\0') {
      if( (str[i] == ' ') || ((str[i] >= 0x09) && (str[i] <= 0xd)) ) {
	 str[i] = '\0';
	 break;
      }
      i++;
   }
}

/*****************************************************************************
** 
**    ReadWord
** 
**    Description:
**       Reads from the given file until a "word" is found.  Leading blanks
**       are ignored.  Characters are then read until a blank character is
**       found. A word is either a number or string of characters. Caller
**       passes in the memory storage for the word that is read.  Storage
**       must be at least 256 bytes in length.
**
**    Parameters:
**       input:
**          hfile:      handle of file to read.
**       output:
**                      returns pointer of memory address of string
**
*****************************************************************************/
LPSTR ReadWord(int hFile, LPSTR storage) {
   char c=' ';
   LOOP_VAR i=0;
   while( isspace(c) ) {
      if( _lread(hFile,&c,1) != 1 ) return(NULL);
   }
   while( !isspace(c) ) {
      if( i==255 ) return(NULL);    /* hit end of storage string */
      storage[i++] = c;
      if( _lread(hFile,&c,1) != 1 ) return(NULL);
   }
   storage[i]='\0';
   return(storage);
}

RETCODE WriteWord(int hFile, LPSTR word) {
   U16 tmp;
   tmp = lstrlen(word);
   if(_lwrite(hFile,word,tmp)!=tmp) return(ER_WRITING_FILE);
   if(_lwrite(hFile,"  ",2)!=2) return(ER_WRITING_FILE);
   return(GOOD);
}

RETCODE WriteNewLine(int hFile) {
   if(_lwrite(hFile,"\n",1)!=1) return(ER_WRITING_FILE);
   return(GOOD);
}

#define DISP_HEX_TEXT "DISP_HEX"
#define MOTO_ADDR_TEXT "DISP_MOTO_ADDR"
#define DISP_DEC_TEXT "DISP_DEC"
#define DISP_OCT_TEXT "DISP_OCT"
#define DISP_BIN_TEXT "DISP_BIN"
#define ASSERT_DEF_TEXT "ASSERT_DEF"
#define ASSERT_FIRST_TEXT "ASSERT_FIRST"

RETCODE EnumerateDisplayFormat(LPSTR ptr, DISPLAY_FORMAT FAR *format) {
   if( ptr == NULL ) return(ER_UNEXPECTED_END);
   if(strncmpi(ptr, DISP_HEX_TEXT, strlen(ptr))==0) 
      *format = DISP_HEX;
   else if(strncmpi(ptr, MOTO_ADDR_TEXT, strlen(ptr))==0) 
      *format = DISP_MOTO_ADDR;
   else if(strncmpi(ptr, DISP_DEC_TEXT, strlen(ptr))==0) 
      *format = DISP_DEC;
   else if(strncmpi(ptr, DISP_OCT_TEXT, strlen(ptr))==0) 
      *format = DISP_OCT;
   else if(strncmpi(ptr, ASSERT_DEF_TEXT, strlen(ptr))==0) 
      *format = ASSERT_DEF;
   else if(strncmpi(ptr, ASSERT_FIRST_TEXT, strlen(ptr))==0) 
      *format = ASSERT_FIRST;
   else 
      *format = DISP_BIN;
   return(GOOD);
}
   
LPSTR TextItDisplayFormat(DISPLAY_FORMAT format, LPSTR ptr) {
   LPSTR localPtr;
   switch( format ) {
      case DISP_HEX: localPtr = DISP_HEX_TEXT; break;
      case DISP_MOTO_ADDR: localPtr = MOTO_ADDR_TEXT; break;
      case DISP_DEC: localPtr = DISP_DEC_TEXT; break;
      case DISP_OCT: localPtr = DISP_OCT_TEXT; break;
      case ASSERT_DEF: localPtr = ASSERT_DEF_TEXT; break;
      case ASSERT_FIRST: localPtr = ASSERT_FIRST_TEXT; break;
      case DISP_BIN: 
      default: 
	 localPtr = DISP_BIN_TEXT;
   }
   lstrcpy(ptr,localPtr);
   return(ptr);
}

#define ACTIVE_LOW_TEXT "ACTIVE_LOW"
#define ACTIVE_HIGH_TEXT "ACTIVE_HIGH"
#define ACTIVE_NA_TEXT "ACTIVE_NA"

RETCODE EnumerateActiveState(LPSTR ptr, ACTIVE_STATE FAR *state) {
   if( ptr == NULL ) return(ER_UNEXPECTED_END);
   if(strncmpi(ptr, ACTIVE_LOW_TEXT, strlen(ptr))==0) 
      *state = ACTIVE_LOW;
   else if(strncmpi(ptr, ACTIVE_HIGH_TEXT, strlen(ptr))==0) 
      *state = ACTIVE_HIGH;
   else 
      *state = ACTIVE_NA;
   return(GOOD);
}

LPSTR TextItActiveState(ACTIVE_STATE state, LPSTR ptr) {
   LPSTR localPtr;
   switch( state ) {
      case ACTIVE_LOW: localPtr = ACTIVE_LOW_TEXT; break;
      case ACTIVE_HIGH: localPtr = ACTIVE_HIGH_TEXT; break;
      case ACTIVE_NA:
      default:
	 localPtr = ACTIVE_NA_TEXT;
	 break;
   }
   lstrcpy(ptr,localPtr);
   return(ptr);
}

#define VALUE_FIELD_TEXT "VALUE_FIELD"
#define RANGE_FIELD_TEXT "RANGE_FIELD"
RETCODE EnumerateFieldType(LPSTR ptr, FIELD_TYPE FAR *type) {
   if( ptr == NULL ) return(ER_UNEXPECTED_END);
   if(strncmpi(ptr, RANGE_FIELD_TEXT, strlen(ptr))==0) 
      *type = RANGE_FIELD;
   else
      *type = VALUE_FIELD;
   return(GOOD);
}

LPSTR TextItFieldType(FIELD_TYPE type, LPSTR ptr) {
   LPSTR localPtr;
   switch( type ) {
      case RANGE_FIELD:
	 localPtr = RANGE_FIELD_TEXT;
	 break;
      default:
	 localPtr = VALUE_FIELD_TEXT;
	 break;
   }
   lstrcpy(ptr,localPtr);
   return(ptr);
}

RETCODE EnumerateNumber(LPSTR ptr, U16 FAR *num) {
   if( ptr == NULL ) return(ER_UNEXPECTED_END);
   *num = atoi(ptr);
   return(GOOD);
}

LPSTR TextItNumber(U16 num, LPSTR ptr) {
   wsprintf(ptr,"%d",num);
   return(ptr);
}


ADDR_SPACE GetAddrSpace(XILINX_GROUP FAR *bits) {
   DESCRIPTOR id;
   U32 addrSpace, fc0, fc1, fc2;
   if (currentlyVisibleStateDirty) UpdateCurrentlyVisible();
   if( !displayASField ) return(SPACE_DEFAULT);
   if( TmpltOpenTemplate(&id) != GOOD ) return(SPACE_DEFAULT);
   if( TmpltSetFieldName(id, "fc0") != GOOD ) return(SPACE_DEFAULT);
   if( TmpltGetValue(id, bits, &fc0) != GOOD ) return(SPACE_DEFAULT);
   if( TmpltSetFieldName(id, "fc1") != GOOD ) return(SPACE_DEFAULT);
   if( TmpltGetValue(id, bits, &fc1) != GOOD ) return(SPACE_DEFAULT);
   if( TmpltSetFieldName(id, "fc2") != GOOD ) return(SPACE_DEFAULT);
   if( TmpltGetValue(id, bits, &fc2) != GOOD ) return(SPACE_DEFAULT);
   addrSpace = (fc2<<2) + (fc1<<1) + fc0;
   if( TmpltTemplateClose(id) != GOOD ) return(SPACE_DEFAULT);

   /*          Code added by Dennis Lamb, May 3 1994                   */
   /* Added this code to take care of the situation where the function */
   /* code values are initialized to 0. The addrSpace value is 0, this */
   /* is not a valid space indicator. The old code would return 0 and  */
   /* this would cause an invalid space indicator error. Now if the    */
   /* addrSpace value is invalid, it is set to SPACE_DONT_CARE.        */
   switch( addrSpace ) {
      case SPACE_UD:
      case SPACE_UP:
      case SPACE_SD:
      case SPACE_SP:
      case SPACE_CPU:
	break;
      default:addrSpace = SPACE_DONT_CARE;
	break;
   } /* switch */
   return((ADDR_SPACE)addrSpace);
}

RETCODE EXPORT TmpltGetFormattedTrace(DESCRIPTOR id,
				      LPSTR buffer,
				      XILINX_GROUP FAR *bits) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   EVENT_TEMPLATE_FIELD *tPtr;
   U16 numFields;
   RETCODE err;
   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   numFields=templateList[template->activeTemplate-1].numTemplateFields;
   tPtr=templateList[template->activeTemplate-1].templateFields;
   if (currentlyVisibleStateDirty) UpdateCurrentlyVisible();
   for( template->activeField = 1; template->activeField <= numFields;
	template->activeField++, tPtr++ ) {
      U16 trailing;
      S8 xilinxData[20];
      if(!tPtr->currentlyVisible) continue;
      trailing = tPtr->trailingBlanks;
      xilinxData[0] = '\0';
      if((err=TmpltGetValueText(id,bits,xilinxData,sizeof(xilinxData), FALSE))
	  != GOOD ) return(err);
      lstrcat(buffer,xilinxData);
      while(trailing--) lstrcat(buffer," ");
   }
   return(GOOD);
}

#pragma argsused
VOID FAR PASCAL TraceStopUpdate(U32 event) {
   pinFunctionCacheDirty=TRUE;
   currentlyVisibleStateDirty=TRUE;
}

VOID PRIVATE UpdateCurrentlyVisible(VOID) {
   PIN_FUNC fc0, fc1, fc2;
   EVENT_TEMPLATE_FIELD *tPtr;
   U16 numFields;
   LOOP_VAR templates, fields;

   displayASField = !asMapable;
   if( asMapable ) {
//      if( BkProcessorMustBeHalted() != GOOD) return;  /* Leave as is */
      if( (TmpltWhatsPinsFunction(3,&fc0) != GOOD)
	  || (TmpltWhatsPinsFunction(4,&fc1) != GOOD)
	  || (TmpltWhatsPinsFunction(5,&fc2) != GOOD) )
	 displayASField = FALSE;
      else
	 displayASField =
	    ((fc0==SELF_FUNC) && (fc1==SELF_FUNC) && (fc2==SELF_FUNC));
   }
   // Every time processor stops emulation or trace and everytime
   // memory is modified we need to check the special visible type
   // fields to see if they should now be displayed.
   for(templates=0; templates<MAX_NUM_TEMPLATES; templates++) {
      if( templateList[templates].name[0] == '\0' ) continue;
      numFields=templateList[templates].numTemplateFields;
      tPtr=templateList[templates].templateFields;
      for( fields = 0; fields < numFields; fields++, tPtr++ ) {
	 if(tPtr->visible==ALWAYS_VISIBLE) tPtr->currentlyVisible = TRUE;
	 else if(tPtr->visible==NOT_VISIBLE) tPtr->currentlyVisible = FALSE;
	 else if(tPtr->visible==SPECIAL_VISIBLE) {
	    PIN_FUNC curFunc;
	    if(TmpltWhatsPinsFunction(tPtr->cs,&curFunc)!=GOOD) return;
	    if( curFunc != tPtr->visibleType ) tPtr->currentlyVisible = FALSE;
	    else tPtr->currentlyVisible = TRUE;
	 }
      }
   }
   currentlyVisibleStateDirty = FALSE;
   return;
}
      

RETCODE EXPORT TmpltDisplayAddressSpace(BOOLEAN FAR *status) {
   if (currentlyVisibleStateDirty) UpdateCurrentlyVisible();
   *status = displayASField;
   return(GOOD);
}

RETCODE EXPORT TmpltDisplayBusControlSignals(BOOLEAN FAR *status) {
   BOOLEAN mapable;
   RETCODE err;
   *status = FALSE;        // good default??
   if( (err=ProcReturnBusControlMapable(&mapable)) != GOOD ) return(err);
   *status = !mapable;
   if( mapable ) {
      PIN_FUNC br, bg, bgack;
      if( (TmpltWhatsPinsFunction(0,&br) != GOOD)
	  || (TmpltWhatsPinsFunction(1,&bg) != GOOD)
	  || (TmpltWhatsPinsFunction(2,&bgack) != GOOD) )
	 *status = FALSE;
      else {
	 *status =
	    ((br==SELF_FUNC) && (bg==SELF_FUNC) && (bgack==SELF_FUNC));
      }
   }
   return(GOOD);
}

RETCODE EXPORT TmpltGetFormattedFields(DESCRIPTOR id,
				      LPSTR buffer,
                                      XILINX_GROUP FAR *bits,
                                      U8 startField,
                                      U8 numFields) {
   EVENT_TEMPLATE_BLOCK FAR *template;
   EVENT_TEMPLATE_FIELD *tPtr;
   RETCODE err;
   U8 i;

   template = (EVENT_TEMPLATE_BLOCK FAR *)id;
   if( !template->activeTemplate ) return(ER_NO_ACTIVE_TEMPLATE);
   tPtr=templateList[template->activeTemplate-1].templateFields;
   if (currentlyVisibleStateDirty) UpdateCurrentlyVisible();

   for( i = 1; i < startField ; i++, tPtr++ );
   template->activeField = startField;
   for( i = 1; i <= numFields; i++, template->activeField++, tPtr++ ) {
      U16 trailing;
      S8 xilinxData[20];

      if(!tPtr->currentlyVisible) continue;
      trailing = tPtr->trailingBlanks;
      xilinxData[0] = '\0';
      if((err=TmpltGetValueText(id,bits,xilinxData,sizeof(xilinxData), FALSE))
	  != GOOD ) return(err);
      lstrcat(buffer,xilinxData);
      while(trailing--) lstrcat(buffer," ");
   }
   return(GOOD);
}
/******************************** E O F ***********************************/
