/****************************************************************************
**
**  Name:  periserv.cpp
**
**  Description:
**     Source file for peripheral server component.
**
**  $Log:   S:/tbird/mt2_68k/peri/periserv.cpv  $
** 
**    Rev 1.1   12 Dec 1997 10:40:04   hera
** For Test Report ID:129
** 
**    Rev 1.0   14 Feb 1997 14:38:30   gene
** Initial revision.
** 
**    Rev 1.6   20 Sep 1996 12:58:44   kevin
** update content after modifed MBAR
** 
**    Rev 1.5   28 Jun 1996 13:05:44   kevin
** checking group name rather than register name to make sure if the register is
** in SYS group
** 
**    Rev 1.4   25 Jun 1996 08:50:08   kevin
** added GetBaseOffset
** 
**    Rev 1.3   14 Jun 1996 11:32:52   kevin
** do with system registers
** 
**    Rev 1.2   12 Jan 1996 16:39:38   kevin
** Gene's modification: fixed a problem of getting base address of SIM in 307 
** case.
** 
**    Rev 1.0   07 Sep 1995 09:49:24   gene
** Initial revision.
** 
**    Rev 1.32   05 May 1994 18:08:16   nghia
** Fixed bug: ReadRegValue() - use local buffer to read single value registers.
** This will avoid allocating buffer that has just 1 or 2 bytes long.
** Revised check for freeing buffer returned from MemRead().
** Force Memory Server to read register value from HW not from cache.
** 
**    Rev 1.31   26 Apr 1994 11:19:14   nghia
** Moved the checking to disable menu commands outside of the reporting error
** when failed to read data. LineToText() function.
** 
**    Rev 1.30   21 Apr 1994 16:27:38   john
** Prevent the peri serv from reading memory when the sim is invalid.
** This keeps peri server from hosing a target by reading invalid areas.
** I also added a more robust way of determining if the sim is valid.
** 
**    Rev 1.29   21 Apr 1994 11:35:16   nghia
** Revised to report error with CFG file name.  Report error when close
** file handle is failed.
** 
**    Rev 1.28   20 Apr 1994 09:25:34   john
** Fixed file handle gobbling code
** 
**    Rev 1.27   19 Apr 1994 16:45:42   nghia
** Fixed PPR 9256, 9257 - Do not close Peripheral window when error occurred.
** Revised to follow coding standard.
** Use const char* instead of embedding string directly for portability.
** Divided the init process into initEvents and InitServer.
** Major clean up for memory and address descriptor leakage.
** Used disableCmds to turn off access to editing when server got error.
** Revised alot of error handling condition for server.
** 
**    Rev 1.26   08 Apr 1994 11:34:50   john
** cleaned up the code dealing with 330/340/360 sim
** 
**    Rev 1.25   06 Jan 1994 09:20:50   ron
** bug fix for 360 base address calculation
** 
**    Rev 1.24   23 Nov 1993 14:56:42   ron
** another bug found by paul
** 
**    Rev 1.23   22 Nov 1993 14:01:36   ron
** fix ppr 9111
** 
**    Rev 1.22   17 Nov 1993 14:10:42   ron
** work-arounds for brain-dead event handling fiasco
** 
**    Rev 1.21   16 Nov 1993 14:38:58   ron
** fix PPR 9092: if valid bit not set, doing silly things.
** 
**    Rev 1.20   16 Nov 1993 10:47:48   ron
** Fix for PPR 9090
** 
**    Rev 1.19   15 Nov 1993 14:27:06   ron
** byte ordering bugs fixed
** 
**    Rev 1.18   10 Nov 1993 16:06:06   ron
** fixes for 9066, 9063, 9069, 9070
** 
**    Rev 1.17   09 Nov 1993 10:48:26   ron
** debugging 340 MBAR stuff
** 
**    Rev 1.16   08 Nov 1993 15:59:28   ron
** bug fixes for pv2.1
** 
**    Rev 1.15   05 Nov 1993 10:04:24   ron
** removed unnecessary testfunc.h
** 
**    Rev 1.14   03 Nov 1993 13:43:34   ron
** ready for 2.1 initial build
** 
**    Rev 1.13   20 Oct 1993 16:49:08   ron
** refined error handling (added a bunch more specific code).
** 
**    Rev 1.12   20 Oct 1993 14:14:12   ron
** ready for pv2.1 (sorta)
** 
**    Rev 1.11   15 Oct 1993 11:14:28   ron
** ready to build and debug
** 
**    Rev 1.10   12 Oct 1993 14:43:24   ron
** checkpoint after buffer editing
** 
**    Rev 1.9   05 Oct 1993 17:48:06   marilyn
** Oops typo!
** 
**    Rev 1.8   05 Oct 1993 17:17:38   marilyn
** FormatSummary has an allocation bug with memory for buffer type registers.
** Added a little safety code.
** 
**    Rev 1.7   04 Oct 1993 16:37:32   marilyn
** Integration with Ron's reader.
** 
**    Rev 1.6   28 Sep 1993 15:29:16   ron
** revised parser (using Paul's new config format)
** 
**    Rev 1.5   01 Sep 1993 14:05:16   marilyn
** Change to interfaces for incorporating cli commands.
** 
**    Rev 1.4   30 Aug 1993 11:05:12   marilyn
** Completed implementation for display of buffers.
** 
**    Rev 1.3   14 Jul 1993 15:52:48   marilyn
** Debugging of format routines, ReadRegValue, TRegister::GetDesc,GetInfo.
** 
**    Rev 1.2   07 Jul 1993 16:52:18   marilyn
** Changed UpdateBlockLocation to take a DESCRIPTOR as a param.
** 
**    Rev 1.1   01 Jul 1993 14:07:16   marilyn
** Updated ErrDisplayFormattedErr interface.
** 
**    Rev 1.0   28 Jun 1993 15:04:24   marilyn
** Initial revision.
**
**  $Header:   S:/tbird/mt2_68k/peri/periserv.cpv   1.1   12 Dec 1997 10:40:04   hera  $
**
**  Copyright (C) 1993 Microtek International.  All rights reserved.
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/
#define STRICT
#define WIN31

#ifndef _PERISERV_
#include "periserv.h"
#endif

#ifndef _PERIPRES_
#include "peripres.h"
#endif

#ifndef _ADDR_
#include "addr.h"
#endif

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

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

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

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

#ifndef _SDNUM_
#include "sdnum.h"
#endif

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

#ifndef _SDS2ABI_
#include "sds2abi.h"
#endif

// BorlandC includes
#ifndef __CTYPE_H
#include <ctype.h>
#endif

#ifndef __STDIO_H
#include <stdio.h>
#endif

#ifndef __ASSOC_H
#include <assoc.h>
#endif

#ifndef __LIST_H
#include <list.h>
#endif



                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/
// suppress compiler warnings - parameter never use
#pragma warn -par  

const PERI_MAXNUMSTR = 20;
const PP_PCF_VERSION = 1;
const PP_CFG_VERSION = 1;

static S8 procName[PERI_MAXSTR];
static CHAR displayBuffer[PERI_MAXSTR * 2];
static BOOL setMBARtemporarily = FALSE;

PTPeriServer PeriServer;   // Peripheral server object ptr

// Constant strings use in reading configuration file
const CHAR *beginCfg   = "begin_cfg";
const CHAR *endCfg     = "end_cfg";
const CHAR *verCfg     = "version";
const CHAR *beginPeri  = "begin_peripheral";
const CHAR *endPeri    = "end_peripheral";
const CHAR *beginReg   = "begin_reg";

// Constant strings use in reporting errors
const CHAR *errReadProcStr   = "Could not read processor %s name from %s.";
const CHAR *errReadFileStr   = "Could not read file %s.";
const CHAR *errFindItemStr   = "Could not find '%s' in file %s.";
const CHAR *errFindItem2Str  = "Could not find '%s' or '%s' in file %s.";
const CHAR *errNumGroupStr   = "Could not find number of groups in file %s.";
const CHAR *errRegAddrStr    = "Could not find %s on line %d in file %s."; 
const CHAR *errLineFieldStr  = "line %d (field %d) of %s";
const CHAR *errExpected1Str  = "Expected \"%s\" on line %d of %s";
const CHAR *errExpected2Str  = "Expected \"%s %d\" on line %d of %s";
const CHAR *errExpected3Str  = "Expected \"%s\" or \"%s\" on line %d of %s"; 
const CHAR *errUnexpectedStr = "Unexpected EOF after line %d of %s";

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

#if defined (STRICT)
extern HINSTANCE hLib;
#else
extern HANDLE hLib;
#endif

PTGroup firstGroup = NULL;

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

                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/
//****************************************************************************
//  TValue
//
//  Description:
//     Value object initialized by constructor
//****************************************************************************
//-----------------------------------------------------------------------------
// TValue Ctor
//-----------------------------------------------------------------------------
TValue::TValue(U32 newValue) { value = newValue; }

//-----------------------------------------------------------------------------
// GetValue
//-----------------------------------------------------------------------------
U32 TValue::GetValue( ) { return value; }

//-----------------------------------------------------------------------------
// SetValue
//-----------------------------------------------------------------------------
VOID TValue::SetValue(U32 newValue) { value = newValue; }

//-----------------------------------------------------------------------------
// isEqual
//-----------------------------------------------------------------------------
int TValue::isEqual(const Object& toObject) const {
   return (value == ((TValue&)toObject).GetValue());
}

//****************************************************************************
//  TPeriServer
//
//  Description:
//     Server object used to contain dictionaries which control
//     access to register information.
//****************************************************************************

//-----------------------------------------------------------------------------
// TPeriServer Ctor
//-----------------------------------------------------------------------------
TPeriServer::TPeriServer() {
   debugMode = FALSE;
   status = Init();
}

//-----------------------------------------------------------------------------
// TPeriServer Dtor
//-----------------------------------------------------------------------------
TPeriServer::~TPeriServer() {
   FlushServer();
}

//-----------------------------------------------------------------------------
// FlushServer  
//-----------------------------------------------------------------------------
VOID TPeriServer::FlushServer() {
   PTGroup group, tmpGroup;
   PTRegister reg, tmpreg;
   for (group = firstGroup; group != NULL;) {
      tmpGroup = group;
      group = group->next;
      for (reg = tmpGroup->firstReg; reg != NULL; ) {
         tmpreg = reg;
         reg = reg->next;
         delete tmpreg;
      }
      delete tmpGroup;
   }
}

static BOOLEAN initHasMBAR = FALSE;
static BOOLEAN hasMBAR = FALSE;

//-----------------------------------------------------------------------------
// HasMBAR
//-----------------------------------------------------------------------------
BOOLEAN TPeriServer::HasMBAR() {
   if (!initHasMBAR && procName[0] != '\0') {
      initHasMBAR = TRUE;
      if (strcmpi("MC68340", procName) == 0 || 
          strcmpi("MC68330", procName) == 0 ||
          strcmpi("MC68302", procName) == 0 ||
          strcmpi("MC68307", procName) == 0 ||
          strcmpi("MC68328", procName) == 0 ||
          strcmpi("MC68360", procName) == 0) {
         hasMBAR = TRUE;
      }
   }
   return (hasMBAR);
}

//-----------------------------------------------------------------------------
// GetMBAR
//-----------------------------------------------------------------------------
U32 TPeriServer::GetMBAR() {
   RETCODE err = GOOD;
   S16 buttonID;
   U32 MBarAddress = 0x03FF00;
   LPU8 memBufPtr= NULL;
   DESCRIPTOR desc = NULL;
   U32 tmpValue;

   if ((err = AdrCreateAddress(&desc)) != GOOD) {
      ErrDisplayFormattedError(err, FORCE_POPUP,NULL,NULL,NULL,MB_OK,
          (S16 FAR *) &buttonID);
      return 0L;
   }
   if (((err = AdrSetAddrSpace(desc,SPACE_CPU)) != GOOD) ||
       ((err = AdrSetAddrOffset(desc,MBarAddress)) != GOOD)) {
      ErrDisplayFormattedError(err, FORCE_POPUP,NULL,NULL,NULL,MB_OK,
          (S16 FAR *) &buttonID);
      AdrDestroyAddress(desc);
      return 0L;
   }
   // 05/05/94 - Nghia
   // always re-read from HW to make sure that the value is current   
   if (MemReadSized(desc,4,&memBufPtr,
         DWORD_SIZE,CACHE_BYPASS) != GOOD) {
      AdrDestroyAddress(desc);
      // MemRead always returns a buffer even with an error
      // 05/05/94 - Nghia
      // only free if the buffer is allocated
      if (memBufPtr)
         TFree(memBufPtr);
      return (0L);
   }
   tmpValue = MemSwapBytesInLong(*((U32 *)memBufPtr));
   // 04/19/94 - Nghia
   // destroy the returned memBufPtr
   TFree(memBufPtr);
   AdrDestroyAddress(desc);
   return (tmpValue);
}

//-----------------------------------------------------------------------------
// SetMBAR
//-----------------------------------------------------------------------------
RETCODE TPeriServer::SetMBAR(U32 val) {
#ifdef LATER
   static U32 MBarAddress = 0x03FF00;
   DESCRIPTOR desc;
   RETCODE err = GOOD;
   LPU8 memBufferPtr= NULL;
   U32 tmpValueDW;

   if ((err = AdrCreateAddress(&desc)) != GOOD) {
      ErrDisplayFormattedError(err, FORCE_POPUP,NULL,NULL,NULL,MB_OK,
          (S16 FAR *) &buttonID);
      return 0L;
   }
   if (((err = AdrSetAddrSpace(desc,SPACE_CPU)) != GOOD) ||
       ((err = AdrSetAddrOffset(desc,MBarAddress)) != GOOD)) {
      ErrDisplayFormattedError(err, FORCE_POPUP,NULL,NULL,NULL,MB_OK,
          (S16 FAR *) &buttonID);
      AdrDestroyAddress(desc);
      return 0L;
   }
   // memBufferPtr is destroyed by MemWriteSized() function
   if ((memBufferPtr = (LPU8)TMalloc(sizeof(val))) == NULL)
      return ER_OUT_OF_MEMORY;

   tmpValueDW = val;
   tmpValueDW = MemSwapBytesInLong(tmpValueDW);
   *((U32 *)memBufferPtr) = tmpValueDW;
   err = MemWriteSized(desc,4,memBufferPtr,DWORD_SIZE);
   // if (err == GOOD) { // This UAE's...why?
      // err = SdnWriteMember(SDN_SIM_ADDRESS, (U8 FAR *) &MBarAddress, GOOD);
   // }
   return (err);
#endif
return (GOOD);
}

//-----------------------------------------------------------------------------
// MBARValid
//-----------------------------------------------------------------------------
BOOLEAN TPeriServer::MBARValid(U32 MBarValue) {
   return ((MBarValue & 0x1) != 0);
}

U32 SavedMBAR = 0L;
U32 LastMBAR = 0L;

//-----------------------------------------------------------------------------
// SaveMBAR
//-----------------------------------------------------------------------------
RETCODE TPeriServer::SaveMBAR() {
#ifdef LATER
   RETCODE err = GOOD;
   PTGroup tmpGroup;
   U32 tmpVal;

   // Save MBAR, stuff in a temporary value
   if (setMBARtemporarily) return (GOOD);
   SavedMBAR = GetMBAR();
   if (!MBARValid(SavedMBAR)) {
      err = (SetMBAR(SavedMBAR | 0x1)); // what about AS5 bit??
   }
   // if the address has changed (whether or not the valid bit is set)
   // reset the base address
   if ((LastMBAR | 0x1) != (SavedMBAR | 0x1)) {
      tmpVal = (~0x1) & SavedMBAR;
      for (tmpGroup = firstGroup; tmpGroup != NULL;
                           tmpGroup = tmpGroup->next) {
         tmpGroup->ResetBase(tmpVal);
      }
      LastMBAR = SavedMBAR;
   }
   // if the address is the same, but the valid bit is being set, update
   // the display
   else if ((LastMBAR & 0x1) != (SavedMBAR & 0x1)) {
      setMBARtemporarily = TRUE;
      PeriInvalidateDisplay();
      PeriRepaintDisplay();
      setMBARtemporarily = FALSE;
   }
#endif
   return (GOOD);
}

//-----------------------------------------------------------------------------
// RestoreMBAR
//-----------------------------------------------------------------------------
RETCODE TPeriServer::RestoreMBAR() {
   // Save MBAR, stuff in a temporary value
   if (setMBARtemporarily)
      return (GOOD);
#ifdef LATER
   if (!MBARValid(SavedMBAR)) {
      return(SetMBAR(SavedMBAR)); // what about AS5 bit??
   }
#endif
   return (GOOD);
}

//-----------------------------------------------------------------------------
// UpdatePeriBaseAddress
//-----------------------------------------------------------------------------
VOID TPeriServer::UpdatePeriBaseAddress() {
   U32 tmpValue,contentMBAR;
   PTRegister MBAR;
   PTGroup tmpGroup;
   BOOLEAN simValid;
   PROBE_TYPE proc;

   if (SdnReadMember(SDN_SIM_VALID, (U8 *)&simValid) != GOOD) {
      periBaseValid = FALSE;
      periBaseAddress = 0L;
      return;
   }

   if (SdnReadMember(SDN_SIM_ADDRESS,(U8 *)&tmpValue) != GOOD) {
      periBaseValid = FALSE;
      periBaseAddress = tmpValue; // FW should fill in an address
      return;
   } else {
      if (!simValid) {
         periBaseValid = FALSE;
         periBaseAddress = tmpValue;
         return;
      }
      //Hera 12/12/97
      MBAR = GetRegByName("MBAR");
      if (MBAR != NULL) {
	 if (Sds2AbiGetIntReg(MBAR->GetAddrOffset(),MBAR->GetSize(),
				      (U32 FAR*)&contentMBAR,FALSE) == GOOD) {
            contentMBAR = (contentMBAR & 0x0FFF) << 12;
	    if (contentMBAR != tmpValue) tmpValue = contentMBAR;
	 }
      }
      //Hera
      periBaseValid = TRUE;
      periBaseAddress = tmpValue;
      for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
         ProcReturnSpecificProcessor(&proc) ;
         if ((proc == M68302_MP || proc == M68307_MP) && (strcmpi(tmpGroup->GetName(),"SYS") == 0))
            continue;  // special for 68302 & 68307 SYS register group
         tmpGroup->ResetBase(periBaseAddress);
      }
   }
}

//-----------------------------------------------------------------------------
// GetGroupCount
//-----------------------------------------------------------------------------
U8 TPeriServer::GetGroupCount() const { 
   PTGroup tmpGroup;
   U8 grCnt = 0;
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      grCnt++;
   }
   return(grCnt);
}

//-----------------------------------------------------------------------------
// GetGroupInfo
//-----------------------------------------------------------------------------
PTGroup TPeriServer::GetGroupInfo(U32 grId) const {
   PTGroup tmpGroup;
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      if (tmpGroup->index == grId) break;
   }
   return(tmpGroup);
}

//-----------------------------------------------------------------------------
// GetGroupByAddress
//-----------------------------------------------------------------------------
PTGroup TPeriServer::GetGroupByAddress(DESCRIPTOR searchAddr) const {
   PTGroup tmpGroup;
   DESCRIPTOR rangeDesc = NULL;
   BOOLEAN inRange = FALSE;
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      // 04/18/94 - Nghia
      // Use the group's address range descriptor - do not destroy
      if ((rangeDesc = tmpGroup->GetRange()) != NULL) {
         AdrIsAddrInRange(searchAddr,rangeDesc,&inRange);
         if (inRange) break;
      }
   }
   return(tmpGroup);
}

//-----------------------------------------------------------------------------
// GetGroupGTAddress
//-----------------------------------------------------------------------------
PTGroup TPeriServer::GetGroupGTAddress(DESCRIPTOR searchAddr) const {
   PTGroup tmpGroup;
   DESCRIPTOR rangeDesc = NULL;
   ADDR_COMPARE result;

   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      // 04/18/94 - Nghia
      // Use the group's address range descriptor - do not destroy
      if ((rangeDesc = tmpGroup->GetRange()) != NULL) {
         AdrCompareAddresses(searchAddr,rangeDesc,&result);
         if (result == SECOND_ADDR_GREATER)
            break;
      }   
   }
   return(tmpGroup);
}

//-----------------------------------------------------------------------------
// GetGroupByName
//-----------------------------------------------------------------------------
PTGroup TPeriServer::GetGroupByName(LPSTR groupName) const {
   PTGroup tmpGroup;

   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      if (strcmpi(groupName, tmpGroup->GetName()) == 0)
         break;
   }
   return(tmpGroup);
}

//-----------------------------------------------------------------------------
// GetRegByName
//-----------------------------------------------------------------------------
PTRegister TPeriServer::GetRegByName(LPSTR regName) const {
   PTGroup tmpGroup;
   PTRegister tmpReg;

   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      for (tmpReg = tmpGroup->firstReg; tmpReg != NULL;
                                    tmpReg = tmpReg->next) {
         if (strcmpi(regName, tmpReg->GetName()) == 0) {
            return (tmpReg);
         }
      }
   }
   return(NULL);
}

//-----------------------------------------------------------------------------
// SetRegValue
//-----------------------------------------------------------------------------
RETCODE TPeriServer::SetRegValue(PTRegister reg, U32 newValue) {
   RETCODE err = GOOD;
   DESCRIPTOR addr;

   addr = NULL;
   if (!debugMode) {
      if ((err = GetRegisterAddress(reg,&addr)) != GOOD)
         return err;
   }
   err = reg->SetValue(newValue,addr);
   if (!debugMode) {
      AdrDestroyAddress(addr);
   }
   return err;
}

//-----------------------------------------------------------------------------
// SetRegValue
//-----------------------------------------------------------------------------
RETCODE TPeriServer::SetRegValue(PTRegister reg, LPU8 newValueBuffer) {
   RETCODE err = GOOD;
   DESCRIPTOR addr;

   addr = NULL;
   if (!debugMode) {
      if ((err = GetRegisterAddress(reg,&addr)) != GOOD)
         return err;
   }
   err = reg->SetValue(newValueBuffer,addr);
   if (!debugMode) {
      AdrDestroyAddress(addr);
   }
   return err;
}

//-----------------------------------------------------------------------------
// SetRegistersDirty
//-----------------------------------------------------------------------------
VOID TPeriServer::SetRegistersDirty() {
   PTGroup tmpGroup;
   PTRegister reg;
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      for (reg = tmpGroup->firstReg; reg != NULL; reg = reg->next) {
         reg->SetDirty();
      }
   }
}

//-----------------------------------------------------------------------------
// CountLine
//-----------------------------------------------------------------------------
U32 TPeriServer::CountLines() {
   PTGroup tmpGroup;
   U32 tmpCnt = 0L;

   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      tmpCnt += tmpGroup->CountLines();
   }
   return (tmpCnt);
}

//-----------------------------------------------------------------------------
// GroupToLine
//-----------------------------------------------------------------------------
U32 TPeriServer::GroupToLine(PTGroup grp) {
   PTGroup tmpGroup;
   U32 tmpCnt = 0L;
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      if (tmpGroup == grp) {
         return (tmpCnt);
      }
      tmpCnt += tmpGroup->CountLines();
   }
   return (tmpCnt);
}

//-----------------------------------------------------------------------------
// RegToLine
//-----------------------------------------------------------------------------
U32 TPeriServer::RegToLine(PTRegister reg) {
   PTGroup tmpGroup;
   PTRegister tmpReg;
   U32 tmpCnt = 0L;
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      tmpCnt++;
      for (tmpReg = tmpGroup->firstReg; tmpReg != NULL; tmpReg = tmpReg->next){
         if (tmpReg == reg) 
            return (tmpCnt);
         if (tmpGroup->expanded) 
            tmpCnt += tmpReg->CountLines();
      }
   }
   return (tmpCnt);
}

//-----------------------------------------------------------------------------
// ExpandAll
//-----------------------------------------------------------------------------
RETCODE TPeriServer::ExpandAll() {
   PTGroup tmpGroup;
   PTRegister tmpReg;
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      tmpGroup->expanded = TRUE;
      tmpGroup->nLinesDirty = TRUE;
      for (tmpReg = tmpGroup->firstReg; tmpReg != NULL; tmpReg = tmpReg->next){
         tmpReg->expanded = TRUE;
         tmpReg->nLinesDirty = TRUE;
      }
   }
   return (GOOD);
}

//-----------------------------------------------------------------------------
// CompressAll
//-----------------------------------------------------------------------------
RETCODE TPeriServer::CompressAll() {
   PTGroup tmpGroup;
   PTRegister tmpReg;
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      tmpGroup->expanded = FALSE;
      tmpGroup->nLinesDirty = TRUE;
      for (tmpReg = tmpGroup->firstReg; tmpReg != NULL; tmpReg = tmpReg->next){
         tmpReg->expanded = FALSE;
         tmpReg->nLinesDirty = TRUE;
      }
   }
   return (GOOD);
}

//-----------------------------------------------------------------------------
// LineToText
//-----------------------------------------------------------------------------
LPSTR TPeriServer::LineToText(U32 lLine) {
   PTGroup tmpGroup;
   S16 buttonID;
   U32 tmpCnt = 0L, pLines;
   HWND hWnd = NULL;

   /* Server is in bad state right now */
   if (!firstGroup) {
      strcpy(displayBuffer, "UNREADABLE");
      return ((LPSTR) displayBuffer);
   }  
   
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      tmpCnt += tmpGroup->CountLines();
   }
   
   if (lLine >= tmpCnt) lLine = tmpCnt - 1;
   tmpCnt = lLine;
   displayBuffer[0] = '\0';
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      if (tmpCnt == 0) { // print the peripheral name itself
         if (tmpGroup->expanded) {
            sprintf(displayBuffer, "(-)\t%s", tmpGroup->GetName());
         }
         else {
            sprintf(displayBuffer, "(+)\t%s", tmpGroup->GetName());
         }
         break;
      }
      else if ((pLines = tmpGroup->CountLines()) > tmpCnt) {             
         tmpGroup->LineToText(tmpCnt, (LPSTR) displayBuffer);
         if (tmpGroup->status != GOOD) { 
            // only report one error
            if ((tmpGroup->status != status) && (status == GOOD)) {
              status = tmpGroup->status; 
              ErrDisplayFormattedError(status, CHECK_MODE,
                                       NULL,NULL,NULL,MB_OK,
                                       (S16 FAR *) &buttonID);
            }
            // Disable commands - Call back will reset it back
            if (((PeriExistsWindow(&hWnd) == GOOD) && (hWnd != NULL)) &&
                (!disabledCmds))
               PeriSetCommands(hWnd, FALSE);
            // Make sure the displayBuffer has something
            if (strlen(displayBuffer) == 0)
               strcpy(displayBuffer, "UNREADABLE");
         }
         break;
      }
      else {
         tmpCnt -= pLines;
      }
   }
   
   return ((LPSTR) displayBuffer);
}

//-----------------------------------------------------------------------------
// LineToReg
//-----------------------------------------------------------------------------
PTRegister TPeriServer::LineToReg(U32 lLine, U32 FAR *field,
                                  BOOLEAN FAR *isPeriph) {
   PTGroup tmpGroup;
   U32 tmpCnt = 0L;
   U32 pLines;
   *field = 0;
   *isPeriph = FALSE;
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      tmpCnt += tmpGroup->CountLines();
   }
   if (lLine >= tmpCnt) lLine = tmpCnt - 1;
   tmpCnt = lLine;
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      if (tmpCnt == 0) { // print the peripheral name itself
         *isPeriph = TRUE;
         return (tmpGroup->firstReg);
      }
      else if ((pLines = tmpGroup->CountLines()) > tmpCnt) {
         return (tmpGroup->LineToReg(tmpCnt, field));
      }
      else {
         tmpCnt -= pLines;
      }
   }
   return (NULL);
}

//-----------------------------------------------------------------------------
// ToggleDisplay
//-----------------------------------------------------------------------------
VOID TPeriServer::ToggleDisplay(U32 lLine) {
   PTGroup tmpGroup;
   U32 tmpCnt = 0L;
   U32 pLines;
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      tmpCnt += tmpGroup->CountLines();
   }
   if (lLine >= tmpCnt) lLine = tmpCnt - 1;
   tmpCnt = lLine;
   for (tmpGroup = firstGroup; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
      if (tmpCnt == 0) { // the peripheral name itself
         tmpGroup->expanded = !tmpGroup->expanded;
         tmpGroup->nLinesDirty = TRUE;
         break;
      }
      else if ((pLines = tmpGroup->CountLines()) > tmpCnt) {
         tmpGroup->ToggleDisplay(tmpCnt);
         break;
      }
      else {
         tmpCnt -= pLines;
      }
   }
}

//-----------------------------------------------------------------------------
// FormatRegister
//-----------------------------------------------------------------------------
RETCODE TPeriServer::FormatRegister(PTRegister reg,
      TRegister::VIEWMODE formatView,BOOLEAN markFields,
      BOOLEAN showAddr,BOOLEAN showDesc,LPSTR *text, U16 *numLines) {
   RETCODE err;

   if (formatView == TRegister::DEFAULT) {
      // formatView is the overriding view mode
      if (reg->GetViewMode() == TRegister::SUMMARY) {
         err = FormatSummary(reg,markFields,showAddr,showDesc,
               text,numLines);
      }
      else {
         err = FormatVerbose(reg,markFields,showAddr,showDesc,
               text,numLines);
      }
   }
   else {
      if (formatView == TRegister::SUMMARY) {
         err = FormatSummary(reg,markFields,showAddr,showDesc,
               text,numLines);
      }
      else {
         err = FormatVerbose(reg,markFields,showAddr,showDesc,
               text,numLines);
      }
   }
   return err;
}

//-----------------------------------------------------------------------------
// InitEvents
//-----------------------------------------------------------------------------
RETCODE TPeriServer::InitEvents() {
   RETCODE err;
   FARPROC lpProc;
   DESCRIPTOR desc;
   
   // register ShareData server for SIM address updated.
   lpProc = MakeProcInstance((FARPROC)PeriUpdateBlockLocation, hLib);
   if ((err = SdnRegister(SDN_SIM_VALID,lpProc,&SDDesc)) != GOOD) return err;
   if ((err = SdnRegister(SDN_SIM_ADDRESS,lpProc,&SDDesc)) != GOOD) return err;
   // register for system events
   lpProc = MakeProcInstance((FARPROC)PeriCallback, hLib);
   if ((err = EnlRegister(EVENT_MEM_HALTED, (EVCALLBACK)lpProc,&desc))
      != GOOD) return err;
   if ((err = EnlRegister(EVENT_MEM_EDIT, (EVCALLBACK)lpProc,&desc))
         != GOOD) return err;
   if ((err = EnlRegister(EVENT_CPU_HALTED, (EVCALLBACK)lpProc,&desc))
         != GOOD) return err;
   return GOOD;
}

//-----------------------------------------------------------------------------
// InitServer
//-----------------------------------------------------------------------------
RETCODE TPeriServer::InitServer() {
   RETCODE err = GOOD;
   S8 perifile[PERI_MAXSTR];
   S16 buttonID;
   CHAR buffer[PERI_MAXSTR*2];

   periBaseAddress = 0;
   periBaseValid = FALSE;
   periDictValid = FALSE;
   disabledCmds  = FALSE;
   
   if ((err = ProcInsertSpecificProcessor("peri%s.cfg",perifile)) != GOOD)
      ErrDisplayFormattedError(err, FORCE_POPUP,NULL,NULL,NULL,MB_OK,
          (S16 FAR *) &buttonID);
   if ((err = InitDictionaries((LPSTR)perifile, (LPSTR) buffer, FALSE))
                                                                != GOOD) {
      if (err == ER_PP_UNEXPECTED_END_CFG) { // use an error with a %s
         ErrDisplayFormattedError(ER_INTERNAL, FORCE_POPUP,
            (LPSTR) "Peripheral Config File Error:", (LPSTR)buffer,
            NULL,MB_OK, (S16 FAR *) &buttonID);
      }
      else {
         ErrDisplayFormattedError(err, FORCE_POPUP,
            (LPSTR)buffer,NULL,NULL,MB_OK,
            (S16 FAR *) &buttonID);
      }
      setMBARtemporarily = FALSE;
      return err;
   }
   setMBARtemporarily = FALSE;
   if(HasMBAR()) {
      RestoreMBAR();
   }
   // restore view modes from .ini file
   // !!! GetViewModes();
   // restore watch list from .ini file
   // !!!  watch.GetWatchRegs();
   return err;
}

//-----------------------------------------------------------------------------
// InitDictionaries
//-----------------------------------------------------------------------------
RETCODE TPeriServer::InitDictionaries(const LPSTR fileName,
      LPSTR buffer, BOOLEAN ignoreProc) {
   RETCODE err = GOOD;
   S8 fileProcName[PERI_MAXSTR];
   DESCRIPTOR addr;
   HFILE hFile, hGFile;
   LOOP_VAR i;
   CHAR nameBuffer[MAX_MNEMONIC];
   U32 groupIndex;
   PTGroup group = NULL;
   PTRegister reg = NULL;
   PTValue regPtr;
   PTField field;
   PString name;
   U16 line = 0;
   U16 numGroups;
   LPSTR lpTok;
   S16 buttonID;
   CHAR linebuf[MAX_INFOSTR];

   if (!fileName && (lstrlen(fileName) == 0))
      return ER_PP_CFG_FILE_NOT_FOUND;                                          
   // Read processor from HW to verify
   if ((err = ProcReturnProcessorName((LPSTR) procName)) != GOOD) 
      return err;
  
   if ((hFile = _lopen(fileName,OF_READ)) == -1) {
      strcpy(buffer, (LPSTR) fileName);  // return the file name to report
      return ER_PP_OPEN_CFG_FAIL;
   }

   if (ReadWord(hFile,fileProcName) == NULL ) {
      sprintf(buffer,(LPSTR)errReadFileStr, (LPSTR) fileName);
      _lclose(hFile);
      return ER_PP_CORRUPT_CFG_FILE;    
   }

   if (!ignoreProc) {
      if (strcmpi(procName,fileProcName) != GOOD) {
         sprintf(buffer, (LPSTR)errReadProcStr, (LPSTR)procName,
                 (LPSTR)fileName);
         _lclose(hFile);
         return ER_PP_CORRUPT_CFG_FILE;
      }
   }

   if (HasMBAR()) {
     // we need the processor name before we can do this 
     SaveMBAR();
   }
   setMBARtemporarily = TRUE;
   if (FindLine(hFile, (U16 FAR *)&line, (LPSTR)linebuf, MAX_INFOSTR)
       != GOOD) {
      sprintf(buffer, (LPSTR) errFindItemStr,
              (LPSTR)beginCfg, (LPSTR) fileName);
      _lclose(hFile);
      return ER_PP_UNEXPECTED_END_CFG;
   }
   // find "begin_cfg"
   if ((lpTok = _fstrtok(linebuf, WhiteSpace)) == NULL) {
      sprintf(buffer, (LPSTR)errFindItemStr,
              (LPSTR)beginCfg, (LPSTR) fileName);
      _lclose(hFile);
      return ER_PP_UNEXPECTED_END_CFG;
   }

   if (_fstrnicmp(lpTok, (LPSTR)beginCfg, strlen(beginCfg)) != 0) {
      sprintf(buffer, (LPSTR)errFindItemStr,
              (LPSTR)beginCfg, (LPSTR) fileName);
      _lclose(hFile);
      return ER_PP_UNEXPECTED_END_CFG;
   }
   if (FindLine(hFile, (U16 FAR *) &line, (LPSTR) linebuf, MAX_INFOSTR)
       != GOOD) {
      sprintf(buffer, (LPSTR) errFindItemStr,
              (LPSTR)verCfg, (LPSTR) fileName);
      _lclose(hFile);
      return ER_PP_UNEXPECTED_END_CFG;
   }

   // find "version 1"
   if ((lpTok = _fstrtok(linebuf, WhiteSpace)) == NULL) {
      sprintf(buffer, (LPSTR) errFindItemStr,
              (LPSTR)verCfg, (LPSTR) fileName);
      _lclose(hFile);
      return ER_PP_UNEXPECTED_END_CFG;
   }
      
   if (_fstrnicmp(lpTok, (LPSTR)verCfg, strlen(verCfg)) != 0) {
      sprintf(buffer, (LPSTR) errFindItemStr,
              (LPSTR)verCfg, (LPSTR) fileName);
      _lclose(hFile);
      return ER_PP_UNEXPECTED_END_CFG;
   }
   if (((lpTok = _fstrtok(NULL, WhiteSpace)) == NULL) ||
      (atoi(lpTok) > PP_PCF_VERSION)) {
      _lclose(hFile);
      return ER_PP_OLD_VERSION; 
   }
   //
   // process peripheral info building 
   //
   if (ReadWord(hFile, buffer) == NULL) {
      sprintf(buffer, (LPSTR)errNumGroupStr, (LPSTR) fileName);
      _lclose(hFile);
      return ER_PP_UNEXPECTED_END_CFG;
   }

   numGroups = atoi(buffer);

   for (i = 0; i < numGroups; i++) {
      if (firstGroup == NULL) {
         firstGroup = group = new TGroup(hFile, line, periBaseAddress);
      }
      else {
         group->next = new TGroup(hFile, line, periBaseAddress);
         group = group->next;
      }

      if (group->status != GOOD) {
         sprintf(buffer, (LPSTR)errLineFieldStr, group->errLine, 
                 group->errField, (LPSTR) fileName);
         ErrDisplayFormattedError(group->status, FORCE_POPUP,
                                  (LPSTR)buffer,NULL,NULL,MB_OK,
                                  (S16 FAR *) &buttonID);
         delete firstGroup;
         _lclose(hFile);
         return ER_PP_UNEXPECTED_END_CFG;
      }
   } /* for */
   
   //
   // process processor specific peripheral address info
   // if the base address is invalid, store the information
   // and continue processing the configuration file
   //
   UpdatePeriBaseAddress();
   
   // Close the configuration file
   if (_lclose(hFile) == -1) {
      strcpy(buffer, (LPSTR)fileName);
      return ER_PP_CLOSE_CFG_FAIL;
   }
   group = NULL;
   for (groupIndex = 0; groupIndex < numGroups; groupIndex++) {
      if (group == NULL) group = firstGroup;
      else group = group->next;
      //
      // process register info building the dicts
      //
      line = 0;
      // Open Group file
      if ((hGFile = group->OpenFile()) == -1) {
         strcpy(buffer, (LPSTR) group->GetFileName());
         _lclose(hGFile);
         return ER_PP_OPEN_CFG_FAIL;
      }
      if (FindLine(hGFile, (U16 FAR *) &line, (LPSTR) linebuf, MAX_INFOSTR)
                                                                  != GOOD) {
         sprintf(buffer, (LPSTR)errFindItemStr,
                 (LPSTR)beginPeri, (LPSTR) group->GetFileName());
         _lclose(hGFile);
         return ER_PP_UNEXPECTED_END_CFG;
      }
      // find "begin_peripheral"
      if (((lpTok = _fstrtok(linebuf, WhiteSpace)) == NULL) ||
         (_fstrnicmp(lpTok, (LPSTR)beginPeri, strlen((LPSTR)beginPeri)) != 0)){
         sprintf(buffer, (LPSTR) errExpected1Str, 
                 (LPSTR)beginPeri, line, (LPSTR) group->GetFileName());
         _lclose(hGFile);
         return ER_PP_UNEXPECTED_END_CFG;
      }

      if (FindLine(hGFile, (U16 FAR *) &line, (LPSTR)linebuf, MAX_INFOSTR) 
                   != GOOD) {
         sprintf(buffer, (LPSTR)errUnexpectedStr,
                 line, (LPSTR) group->GetFileName());
         _lclose(hGFile);
         return ER_PP_UNEXPECTED_END_CFG;
      }
      // find "version 1"
      if (((lpTok = _fstrtok(linebuf, WhiteSpace)) == NULL) ||
          (_fstrnicmp(lpTok, (LPSTR)verCfg, strlen(verCfg)) != 0)) {
         sprintf(buffer, (LPSTR) errExpected2Str, (LPSTR)verCfg,
                 PP_PCF_VERSION, line, (LPSTR) group->GetFileName());
         _lclose(hGFile);
         return ER_PP_READING_GROUP;
      }

      if (((lpTok = _fstrtok(NULL, WhiteSpace)) == NULL) ||
          (atoi(lpTok) > PP_PCF_VERSION)) {
         sprintf(buffer, (LPSTR)errExpected2Str, (LPSTR)verCfg,
                 PP_PCF_VERSION, line, (LPSTR) group->GetFileName());
         _lclose(hGFile);
         return ER_PP_READING_GROUP;
      }

      while (1) {
         if (FindLine(hGFile, (U16 FAR *) &line, (LPSTR) linebuf,
                      MAX_INFOSTR) != GOOD) {
            sprintf(buffer, (LPSTR)errFindItem2Str, (LPSTR)beginReg,
                    (LPSTR)endPeri, (LPSTR) group->GetFileName());
            _lclose(hGFile);
            return ER_PP_UNEXPECTED_END_CFG;
         }

         // find "begin_reg" or "end_peripheral"
         if ((lpTok = _fstrtok(linebuf, WhiteSpace)) == NULL) {
            sprintf(buffer, (LPSTR) errExpected3Str, (LPSTR)beginReg,
                    (LPSTR)endPeri, line, (LPSTR) group->GetFileName());
            _lclose(hGFile);
            return ER_PP_READING_REG;
         }

         if (_fstrnicmp(lpTok, (LPSTR)beginReg, strlen(beginReg)) != 0) {
            if (_fstrnicmp(lpTok, (LPSTR)endPeri, strlen(endPeri)) != 0) {
               sprintf(buffer, (LPSTR) errExpected3Str, (LPSTR)beginReg,
                       (LPSTR)endPeri, line, (LPSTR) group->GetFileName());
               ErrDisplayFormattedError(ER_PP_READING_REG, FORCE_POPUP,
                                        (LPSTR)buffer,NULL,NULL,MB_OK,
                                        (S16 FAR *) &buttonID);
               while (1) {
                  if (FindLine(hGFile, (U16 FAR *) &line, (LPSTR) linebuf,
                                                     MAX_INFOSTR) != GOOD) {
                     sprintf(buffer, (LPSTR)errExpected3Str, (LPSTR)beginReg,
                             (LPSTR)endPeri,line, (LPSTR)group->GetFileName());
                     _lclose(hGFile);
                     return ER_PP_UNEXPECTED_END_CFG;
                  }

                  // find beginReg or endPeri token
                  if ((lpTok = _fstrtok(linebuf, WhiteSpace)) == NULL) {
                     sprintf(buffer, (LPSTR)errExpected3Str,
                             (LPSTR)beginReg,(LPSTR)endPeri,     
                             line, (LPSTR) group->GetFileName());
                     _lclose(hGFile);
                     return ER_PP_UNEXPECTED_END_CFG;
                  }
                  if (_fstrnicmp(lpTok, (LPSTR)beginReg,
                                 strlen(beginReg)) == 0) break;
                  if (_fstrnicmp(lpTok, (LPSTR)endPeri,
                                 strlen(endPeri)) == 0) break;
               } // while
               if (_fstrnicmp(lpTok, (LPSTR)endPeri,
                              strlen(endPeri)) == 0) break;
            }
            else {
               break;
            }
         }
         if (group->firstReg == NULL) {
            group->firstReg = reg = new TRegister(hGFile, groupIndex, line);
         }
         else {
            reg->next = new TRegister(hGFile, groupIndex, line);
            reg = reg->next;
         }
         reg->parent = group;
         line = reg->errLine;
         
         if (reg->status != GOOD) {
            sprintf(buffer, (LPSTR)errLineFieldStr, line, 
                    reg->errField, (LPSTR) group->GetFileName());
            ErrDisplayFormattedError(ER_PP_READING_REG, FORCE_POPUP,
                                     (LPSTR)buffer,NULL,NULL,MB_OK,
                                     (S16 FAR *) &buttonID);
            // destroy reg object
            delete reg;
            _lclose(hGFile);
            return ER_PP_UNEXPECTED_END_CFG;
         }
         err = GetRegisterAddress(reg,&addr);
         if (addr) AdrDestroyAddress(addr);
         if (err != GOOD) {
            sprintf(buffer, (LPSTR) errRegAddrStr, (LPSTR)"reg address",
                    line, (LPSTR) group->GetFileName());
            _lclose(hGFile);
            return err;
         }
      } // while
      
      // Now Sort all register in the group
      group->Sort();
      // close the group file
      if (_lclose(hGFile) == -1) {
         strcpy(buffer, (LPSTR) group->GetFileName());
         return ER_PP_CLOSE_CFG_FAIL;
      }
   } /* for */
   
   // set server dictionary valid flag to TRUE
   periDictValid = TRUE;
   return GOOD;
}

//-----------------------------------------------------------------------------
// Init
//-----------------------------------------------------------------------------
RETCODE TPeriServer::Init(VOID) {
   RETCODE err;
   if ((err = InitEvents()) != GOOD) return err;
   return InitServer();
}

//-----------------------------------------------------------------------------
// GetRegisterAddress
//-----------------------------------------------------------------------------
RETCODE TPeriServer::GetRegisterAddress(const PTRegister reg,
                                        DESCRIPTOR *desc) {
   RETCODE err = GOOD, err2 = GOOD;
   PTGroup group;

   *desc = NULL;

   if(HasMBAR()) {
      err2 = SaveMBAR();
   }
   group = GetGroupInfo(reg->GetGroupId());
   // duplication of group descriptor
   // caller need to destroy the returned address descriptor
   if ((err = group->GetRange(desc)) != GOOD) {
      return err;
   }
   err = AdrAddToAddress(*desc,reg->GetAddrOffset());
   if(HasMBAR()) {
      err2 = RestoreMBAR();
   }
   if (err != GOOD) return (err);
   return (err2);
}

//-----------------------------------------------------------------------------
// FormatVerbose
//-----------------------------------------------------------------------------
RETCODE TPeriServer::FormatVerbose(const PTRegister reg,BOOLEAN markFields,
      BOOLEAN showAddr,BOOLEAN showDesc,LPSTR *text, U16 *numLines) {
   // Not Yet Implemented
   return GOOD;
}

//-----------------------------------------------------------------------------
// FormatSummary
//-----------------------------------------------------------------------------
RETCODE TPeriServer::FormatSummary(const PTRegister reg,BOOLEAN markFields,
      BOOLEAN showAddr,BOOLEAN showDesc,LPSTR *text, U16 *numLines) {
   RETCODE err = GOOD;
   S8 regAddr[PERI_MAXNUMSTR];
   S8 mnemonic[MAX_MNEMONIC];
   S8 value[PERI_MAXNUMSTR];
   LPSTR valuePtr = NULL;  
   LPU8 bufPtr = NULL;
   LPU8 tempPtr = NULL;
   LOOP_VAR i;
   S8 desc[MAX_INFOSTR];
   DESCRIPTOR tmpDesc = NULL;
   U32 bufferSize = 0;
   U32 fcnt = 0;

   // Register summary format is as follows:
   // register address,mnemonic,value,description
   // Buffer summary format is as follows:
   // register address,mnemonic,buffer bytes
   *numLines = 1;
   regAddr[0] = '\0';
   mnemonic[0] = '\0';
   value[0] = '\0';
   desc[0] = '\0';
   if (showAddr) {
      if ((err = GetRegisterAddress(reg,&tmpDesc)) != GOOD)
         return err;
      if ((err = AdrConvAddressToTextWithParams(tmpDesc,FALSE,FALSE,
            regAddr) ) != GOOD) {
         // 04/15/94 - Nghia
         // Destroy the tmpDesc before exit
         if (tmpDesc != NULL) AdrDestroyAddress(tmpDesc);
         return err;
      }
      bufferSize += (strlen(regAddr)+1); //add 1 for the tab char
   }
   bufferSize += (strlen(reg->GetName(mnemonic))+1);
   // Read memory for teh latest value of register
   if (reg->IsDirty())       
      err = ReadRegValue(reg, tmpDesc);
   // 04/14/94 - Nghia
   // Destroy the tmpDesc before exit
   if (tmpDesc != NULL) AdrDestroyAddress(tmpDesc);
   if (err != GOOD)
      return err;
   
   bufferSize += (strlen(reg->GetDesc(desc))+1); //add 1 for terminator
   if (!reg->IsBuffer()) {
      sprintf(value,"%#04lx",reg->GetValue());
      bufferSize += (strlen(value)+1);
      valuePtr = value;
   }
   else {
      // format for buffer
      U8 tempVal[10];
      tempVal[0] = '\0';
      fcnt = reg->GetFieldCnt();
      if (fcnt == 0) {
         return ER_OUT_OF_MEMORY;
      }
      valuePtr = (LPSTR)TMalloc(fcnt*PERI_MAXNUMSTR);
      memset(valuePtr,'\0',(fcnt*PERI_MAXNUMSTR));
      tempPtr = valuePtr;
      bufPtr = reg->GetBufferValue();
      for (i = fcnt - 1; i >= 0; i--) {
         sprintf(tempVal,"%#02x",*bufPtr);
         strcat(tempPtr,tempVal);
         if (i != 0) {
            if ((i % 8) == 0) {  // eight bytes formatted, start new line
               bufferSize += (strlen(tempPtr)+3);
               tempPtr += (strlen(tempPtr)+1);
               *tempPtr = '\0';
               strcat(tempPtr,"\t\t");
               (*numLines)++;
            }
            else
               strcat(tempPtr," ");
         }
         bufPtr++;
      }
      bufferSize += (strlen(tempPtr)+1);
   }
   if ((*text = (LPSTR)TMalloc(bufferSize)) == NULL)
      return ER_OUT_OF_MEMORY;
   memset(*text,'\0',bufferSize);
   if (showAddr) {
      strcat(*text,regAddr);
      strcat(*text,"\t");
   }
   strcat(*text,mnemonic);
   strcat(*text,"\t");
   if (markFields) {
      // mark the value as beginning the editable text
   }
   tempPtr = *text;
   tempPtr += (strlen(*text));
   bufPtr = valuePtr;
   for (i = *numLines; i > 0; i--) {
      strcat(tempPtr,bufPtr);
      bufPtr += (strlen(bufPtr) + 1);       // point to the next line
      tempPtr += (strlen(tempPtr) + 1);     // point to past the terminator
      *tempPtr = '\0';
   }
   if (markFields) {
      // mark the value as ending the editable text
   }
   if (!reg->IsBuffer())
      strcat(*text,"\t");
   if (!reg->IsBuffer() && showDesc)
      strcat(*text,desc);
   if (reg->IsBuffer()) {
      // clean up
      if (valuePtr != NULL)
         TFree(valuePtr);
   }
   return err;
}

//********************************************************************
//   
//      ReadWord : Originally from evttmplt.c for evttmplt.dll
//   
//      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 (in):      handle of file to read.
//  
//      Output:
//            returns pointer of memory address of string
//  
//*********************************************************************
LPSTR TPeriServer::ReadWord(const 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);
   }
   if (i <= 255)
      storage[i]='\0';
   return(storage);
}

//********************************************************************
// 
//    ReadString :
// 
//    Description:
//       Reads from the given file until a "string of words" is found.
//       Strings are denoted in the file by quote marks.
//       Characters are then read until a second quote mark is located.
//       The caller passes in the memory storage for the string that is
//       read.  Storage must be 256 bytes in length.
//
//    Parameters:
//       input:
//          hfile (in):      handle of file to read.
//
//    Output:
//          returns pointer of memory address of string
//
//*********************************************************************
LPSTR TPeriServer::ReadString(const int hFile, LPSTR storage) {
   char c=' ';
   LOOP_VAR i=0;

   while( c != '"') {  // look for the quote mark
      if( _lread(hFile,&c,1) != 1 ) return(NULL);
   }
   if( _lread(hFile,&c,1) != 1 ) return(NULL);  // skip the quote mark

   while( c != '"') {      // read till end of string
      if( i==255 ) return(NULL);    // hit end of storage string
      storage[i++] = c;
      if( _lread(hFile,&c,1) != 1 ) return(NULL);
   }
   if (i <= 255 )
      storage[i]='\0';       // null terminate the string
   return(storage);
}


//-----------------------------------------------------------------------------
// ReadRegValue
//-----------------------------------------------------------------------------
RETCODE TPeriServer::ReadRegValue(const PTRegister reg, DESCRIPTOR desc) {
   RETCODE err = GOOD;
   LPU8 memBufPtr = NULL;
   BOOLEAN destroyAddr = FALSE;
   U32 tmpValue;
   U8  tmpRegBuff[20];  // use for reading single value register
   U16 offset16;
   PROBE_TYPE proc;
   BOOL relative = TRUE;
   PTGroup group;

   if ((err = ProcReturnSpecificProcessor(&proc)) != GOOD)
      return(err);

   if (proc != M68306_MP) {
      if (reg->GetReadPermission()) {
         group = GetGroupInfo(reg->GetGroupId());
         offset16 = (U16)(reg->GetAddrOffset() & 0xFFFF)+
                    (U16)(group->GetBaseOffset() & 0xFFFF) ;
         if (stricmp(group->GetName(tmpRegBuff), "SYS") == 0)
            relative = FALSE;
         if ((err = Sds2AbiGetIntReg(offset16,reg->GetSize(),(U32 FAR *)&tmpValue,relative)) != GOOD)
            return(err);

         if ((err = reg->SetValue(tmpValue)) != GOOD)
            return(err);
      }
   } else {

      if (!periBaseValid) return(ER_SIM_UNREADABLE);
   
      if (desc == NULL) {
         destroyAddr = TRUE;
         if ((err = GetRegisterAddress(reg,&desc)) != GOOD) {
            if (desc != NULL) AdrDestroyAddress(desc);
            return err;
         }
      }
      // 05/05/94 - Nghia
      // There are two types of register value - single or buffer.
      // Use MemReadSized() for buffer and MemReadSizedNoAlloc() for
      // single value, this will reduce the overhead of allocate just
      // 1, 2 bytes for the single value registers.
      if (!reg->IsBuffer()) {
         memBufPtr = tmpRegBuff;
         // MemReadSizedNoAlloc does not returns buffer
         // Always re-read value from HW
         if ((err = MemReadSizedNoAlloc(desc, reg->GetSize(), (U8*)memBufPtr,
               (ACCESS_SIZE)reg->GetSize(), CACHE_BYPASS)) != GOOD) {
            if (destroyAddr == TRUE)
               AdrDestroyAddress(desc);
            return err;
         }      
         // translate the memory read buffer into register value
         switch (reg->GetSize()) {
            case  (sizeof(U8)): 
               tmpValue = (U32)(*memBufPtr);
               break;
            case  (sizeof(U16)):
               tmpValue = MemSwapBytesInWord(*((U16 *)memBufPtr));
               break;
            case  (sizeof(U32)):
               tmpValue = MemSwapBytesInLong(*((U32 *)memBufPtr));
               break;
         }
         // save the value to the register
         if ((err = reg->SetValue(tmpValue)) != GOOD)
            return err;
      }
      else {  // is buffer
         // MemRead always returns a buffer even with an error - must destroy
         // 05/05/94 - Nghia
         // re-read from HW
         if ((err = MemReadSized(desc,reg->GetFieldCnt(), &memBufPtr,
                    (ACCESS_SIZE)reg->GetSize(), CACHE_BYPASS)) != GOOD) {
            if (destroyAddr == TRUE)
               AdrDestroyAddress(desc);
            // only free buffer if it's allocated   
            if (memBufPtr)   
               TFree(memBufPtr);
            return err;
         }
         // Save the memBufPtr to register - default argument desc = NULL
         err = reg->SetValue(memBufPtr);
         TFree(memBufPtr);
         if (err != GOOD) return err;
      }
      if (destroyAddr)
         AdrDestroyAddress(desc);
   }
   return err;
}

//-----------------------------------------------------------------------------
// CheckForEOF
//-----------------------------------------------------------------------------
BOOLEAN TPeriServer::CheckForEOF(LPSTR lpBuf) {
   return (BOOLEAN)(strcmpi(lpBuf,"CFG_END") == 0);
}

//-----------------------------------------------------------------------------
// FormatErrorValue
//-----------------------------------------------------------------------------
LPSTR FormatErrorValue(LPSTR buffer, U16 size) {
   U16 i = 0;
   LPSTR tmpPtr = buffer;
   if (!buffer) return (NULL);
   for (i = 0; i < size; i++) {
      *tmpPtr = (CHAR)'?';
   }
   return (buffer);
}


//***************************** C Interface *********************************
static BOOLEAN inCallBack = FALSE;

/*****************************************************************************
**
**  PeriCallback
**
*****************************************************************************/
RETCODE EXPORT PeriCallback(U32 eventNum) {  

   // reset PeriServer status on call back
   PeriServer->status = GOOD;
   PeriServer->disabledCmds = FALSE;
   if (inCallBack) return (GOOD);
   inCallBack = TRUE;
   switch(eventNum)  {
      case EVENT_MEM_HALTED:
      case EVENT_CPU_HALTED:
      case EVENT_MEM_EDIT: {
         if(PeriServer->HasMBAR()) { 
            PeriServer->UpdatePeriBaseAddress();
            PeriServer->SaveMBAR();
         }
         setMBARtemporarily = TRUE;
         PeriInvalidateDisplay();
         PeriRepaintDisplay();
         setMBARtemporarily = FALSE;
         if(PeriServer->HasMBAR()) {
            PeriServer->RestoreMBAR();
         }
         break;
      }
   }
   inCallBack = FALSE;
   return GOOD;
}

/*****************************************************************************
**
**  PeriUpdateBlockLocation
**
*****************************************************************************/
VOID EXPORT PeriUpdateBlockLocation(DESCRIPTOR) {
   // make sure PeriServer is valid
   if (PeriServer) {
      PeriServer->status = GOOD;
      PeriServer->disabledCmds = FALSE;
      PeriServer->UpdatePeriBaseAddress();
      PeriServer->SetRegistersDirty();
      PeriInvalidateDisplay();
      PeriRepaintDisplay();
   }
}

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

