/****************************************************************************
**
**  Name:  tgroup.cpp
**
**  Description:
**     Source file for peripheral server tgroup component.
**
**  $Log:   S:/tbird/mt2_186/peri/tgroup.cpv  $
** 
**    Rev 1.1   29 Jul 1998 12:09:40   hera
** Fixed bug of showing the register's value in peripher window.
** 
**    Rev 1.0   16 Dec 1996 16:19:46   Judy
** Initial revision.
** 
**    Rev 1.0   11 Oct 1996 18:47:18   Judy
** Initial revision.
** 
**    Rev 1.13   13 Oct 1994 10:54:42   dennis
** Modified GetRange(VOID) to return the correct range based on DOS or extended
** mode.
** 
**    Rev 1.12   11 Oct 1994 09:21:14   dennis
** Added code to support 386ex.
** 
**    Rev 1.11   10 May 1994 11:58:54   marilyn
** Fixed bug in Sort where the linked list became circular because the prev
** ptr wasn't being set back to null for each new pass of the list.
** 
**    Rev 1.10   19 Apr 1994 16:26:28   nghia
** Revised to follow coding standard.
** LineToText() only needs to report error once.
** Cleanup Address descriptor leakage.
** Added check for invalid TGroup object and destroy address range descriptor
** only if it's valid.
** 
**    Rev 1.9   22 Nov 1993 14:02:06   ron
** fix ppr 9111
** 
**    Rev 1.8   10 Nov 1993 16:06:22   ron
** fixes for 9066, 9063, 9069, 9070
** 
**    Rev 1.7   09 Nov 1993 10:48:18   ron
** debugging 340 MBAR stuff
** 
**    Rev 1.6   05 Nov 1993 10:04:44   ron
** removed unnecessary testfunc.h
** 
**    Rev 1.5   04 Nov 1993 10:35:50   ron
** misc bug fixes
** 
**    Rev 1.4   03 Nov 1993 13:47:12   ron
** ready for first build of 2.1
** 
**    Rev 1.3   21 Oct 1993 15:15:50   ron
** bug fixes based on testing for release
** 
**    Rev 1.2   15 Oct 1993 11:14:58   ron
** ready to build and debug
** 
**    Rev 1.1   04 Oct 1993 16:38:24   marilyn
** Updated member functions.
** 
**    Rev 1.0   30 Sep 1993 13:41:40   ron
** Initial revision.
** 
**  $Header:   S:/tbird/mt2_186/peri/tgroup.cpv   1.1   29 Jul 1998 12:09:40   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


// 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    *
                        *                          *
                        ****************************/


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

extern PTPeriServer PeriServer;   // Peripheral server object ptr

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

                       /****************************
                        *                          *
                        *      EXECUTABLE CODE     *
                        *                          *
                        ****************************/
//*****************************************************************************
//  TGroup
//
//  Description:
//     Peripheral group object used to contain group information.
//*****************************************************************************
//-----------------------------------------------------------------------------
// TGroup CTor
//-----------------------------------------------------------------------------
TGroup::TGroup(HFILE hFile, U16 line, U32 periBaseAddr) {
   LPSTR lpTok;
   S32 lTmp;
   U32 uTmp, startRangeOffset, endRangeOffset;
   CHAR *endptr; // not really used
   CHAR linebuf[MAX_INFOSTR];

   status       = ER_PP_READING_GROUP; // error if we return early...
   next         = NULL;
   errLine      = line;
   errField     = 0;
   firstReg     = NULL;
   nLines       = 1;
   expanded     = FALSE;
   nLinesDirty  = FALSE;
   addressRange = NULL;
   addressSelector = 0;
   
   if(FindLine(hFile, (U16 FAR *) &errLine, (LPSTR) linebuf, MAX_INFOSTR)
      != GOOD) return;
   // group id
   lpTok = _fstrtok(linebuf, WhiteSpace);
   if (lpTok == NULL) return;
   lTmp = strtol(lpTok, &endptr, 0);
   if (lTmp < 0) return;
   index = lTmp;

   // group name
   lpTok = _fstrtok(NULL, WhiteSpace);
   if (lpTok == NULL) return;
   _fstrncpy((LPSTR)name, lpTok, MAX_MNEMONIC - 1);
   name[MAX_MNEMONIC - 1] = '\0';

   // base
   errField = 1;
   lpTok = _fstrtok(NULL, WhiteSpace);
   if (lpTok == NULL) return;
   lTmp = strtol(lpTok, &endptr, 0); // NOT PORTABLE
   addressBase = (S32) lTmp;

   // startOffset
   errField = 2;
   lpTok = _fstrtok(NULL, WhiteSpace);
   if (lpTok == NULL) return;
   uTmp = strtoul(lpTok, &endptr, 0); // NOT PORTABLE
   startOffset = (U32) uTmp;

   // endOffset
   errField = 3;
   lpTok = _fstrtok(NULL, WhiteSpace);
   if (lpTok == NULL) return;
   uTmp = strtoul(lpTok, &endptr, 0);  // NOT PORTABLE
   endOffset = (U32) uTmp;

   // file name
   errField = 4;
   lpTok = _fstrtok(NULL, WhiteSpace);
   if (lpTok == NULL) return;
   _fstrncpy((LPSTR)file_name, lpTok, MAX_MNEMONIC - 1);
   file_name[MAX_MNEMONIC - 1] = '\0';
   // create address range for TGroup
   if ((status = AdrCreateAddressWithType(ADDR_PHYSICAL, &addressRange)) != GOOD) {
      addressRange = NULL;
      return;
   }
  
   // Intel 2nd base
   errField = 5;
   lpTok = _fstrtok(NULL, WhiteSpace);
   if (lpTok != NULL) {
      lTmp = strtol(lpTok, &endptr, 0); // NOT PORTABLE
      addressBase2 = (S32) lTmp;
   } /* end */
   else
      addressBase2 = addressBase; 

   // set address start and end offset for TGroup address range
   startRangeOffset = (periBaseAddr+addressBase)+startOffset;
   endRangeOffset   = (periBaseAddr+addressBase)+endOffset;
   /* Group address can be negative - Do not check for error */
   AdrSetAddrOffset(addressRange, startRangeOffset);
   AdrSetEndAddrOffset(addressRange, endRangeOffset);
   status = GOOD;
}

//-----------------------------------------------------------------------------
// TGroup Dtor
//-----------------------------------------------------------------------------
TGroup::~TGroup() {
   // Only destroy address descriptor if it's valid
   if (addressRange)
      AdrDestroyAddress(addressRange); 
}

//-----------------------------------------------------------------------------
// OpenFile
//-----------------------------------------------------------------------------
HFILE TGroup::OpenFile() {
   return(_lopen(file_name,OF_READ));
}

//-----------------------------------------------------------------------------
// CountLines
//-----------------------------------------------------------------------------
U32 TGroup::CountLines() {
   PTRegister tmpReg;
   U32 tmpCnt = 1L; // always one line for the peripheral name itself
   if (!expanded) {
      nLines = 1;
      nLinesDirty = FALSE;
      return (1L);
   }
   if (nLinesDirty) {
      for (tmpReg = firstReg; tmpReg != NULL; tmpReg = tmpReg->next) {
         tmpCnt += tmpReg->CountLines();
      }
      nLines = tmpCnt;
      nLinesDirty = FALSE;
   }
   return (nLines);
}

//-----------------------------------------------------------------------------
// LineToText
//-----------------------------------------------------------------------------
LPSTR TGroup::LineToText(U32 lLine, LPSTR buffer) {
   RETCODE err;
   PTRegister tmpReg;
   U32 tmpCnt = 0L;
   U32 pLines;
   S16 buttonID;

   status = GOOD;
   lLine--; // subtract one for the name of the peripheral itself
   tmpCnt = CountLines();
   if (lLine >= tmpCnt) lLine = tmpCnt - 1;
   tmpCnt = lLine;
   for (tmpReg = firstReg; tmpReg != NULL; tmpReg = tmpReg->next) {
      if (tmpCnt == 0) {
         // 04/15/93 - Nghia
         // Record error reading register for the group.
         if ((err = tmpReg->FormatRegister(buffer)) != GOOD) {
            status = (status == GOOD) ? err : status;
         }
         break;
      }
      else if ((pLines = tmpReg->CountLines()) > tmpCnt) {
         tmpReg->LineToText(tmpCnt, buffer);
         break;
      }
      else {
         tmpCnt -= pLines;
      }
   }
   return ((LPSTR) buffer);
}

//-----------------------------------------------------------------------------
// LineToReg
//-----------------------------------------------------------------------------
PTRegister TGroup::LineToReg(U32 lLine, U32 FAR *field) {
   PTRegister tmpReg;
   U32 tmpCnt = 0L;
   U32 pLines;

   lLine--; // subtract one for the name of the peripheral itself
   tmpCnt = CountLines();
   if (lLine >= tmpCnt) lLine = tmpCnt - 1;
   tmpCnt = lLine;
   for (tmpReg = firstReg; tmpReg != NULL; tmpReg = tmpReg->next) {
      if (tmpCnt == 0) { 
         return (tmpReg);
      }
      else if ((pLines = tmpReg->CountLines()) > tmpCnt) {
         *field = tmpReg->GetFieldCnt() - (pLines - tmpCnt);
         return (tmpReg);
      }
      else {
         tmpCnt -= pLines;
      }
   }
   return (NULL);
}

//-----------------------------------------------------------------------------
// ToggleDisplay
//-----------------------------------------------------------------------------
VOID TGroup::ToggleDisplay(U32 lLine) {
   PTRegister tmpReg;
   U32 tmpCnt = 0L;
   U32 pLines;

   lLine--; // subtract one for the name of the peripheral itself
   tmpCnt = CountLines();
   if (lLine >= tmpCnt) lLine = tmpCnt - 1;
   tmpCnt = lLine;
   for (tmpReg = firstReg; tmpReg != NULL; tmpReg = tmpReg->next) {
      if (tmpCnt == 0) { 
         tmpReg->expanded = !tmpReg->expanded;
         tmpReg->nLinesDirty = TRUE;
         nLinesDirty = TRUE;
         break;
      }
      else if ((pLines = tmpReg->CountLines()) > tmpCnt) {
         break;
      }
      else {
         tmpCnt -= pLines;
      }
   }
}


//-----------------------------------------------------------------------------
// GetName
//-----------------------------------------------------------------------------
LPSTR TGroup::GetName(LPSTR nameBuffer) const {
   if (nameBuffer != NULL)
      strcpy(nameBuffer,name);
   return ((LPSTR)name);
}

//-----------------------------------------------------------------------------
// GetRange
//-----------------------------------------------------------------------------
RETCODE TGroup::GetRange(DESCRIPTOR *addrRange,const PTRegister reg) {
   RETCODE err;

   if (PeriServer->IsIntel()) {
      if((err = AdrDuplicateAddress(addressRange, addrRange)) != GOOD)
         return(err);
      if((err = AdrSetAddrSpace(*addrRange,SPACE_IO)) != GOOD)
         return(err);
      if ((addressSelector == 0) && (!reg->IsExtended())) {
         AdrSetAddrOffset(*addrRange, addressBase+startOffset);
         AdrSetEndAddrOffset(*addrRange, addressBase+endOffset);
      }
      else {
         AdrSetAddrOffset(*addrRange, addressBase2+startOffset);
         AdrSetEndAddrOffset(*addrRange, addressBase2+endOffset);
      }
   }
   else
      return(AdrDuplicateAddress(addressRange, addrRange));
   return(GOOD);
}

//-----------------------------------------------------------------------------
// GetBaseOffset
//-----------------------------------------------------------------------------
U32 TGroup::GetBaseOffset(VOID) {
   return(addressBase);
}

//-----------------------------------------------------------------------------
// GetRange - DO NOT DESTROY THE RETURN DESCRIPTOR
//-----------------------------------------------------------------------------
const DESCRIPTOR TGroup::GetRange(VOID) {

   DESCRIPTOR tmpAddr;
   RETCODE    err;

   if (PeriServer->IsIntel()) {
      if((err = AdrDuplicateAddress(addressRange, &tmpAddr)) != GOOD)
         return(NULL);
      if (addressSelector == 0) {
         AdrSetAddrOffset(tmpAddr, addressBase+startOffset);
         AdrSetEndAddrOffset(tmpAddr, addressBase+endOffset);
      }
      else {
         AdrSetAddrOffset(tmpAddr, addressBase2+startOffset);
         AdrSetEndAddrOffset(tmpAddr, addressBase2+endOffset);
      }
      return tmpAddr;
   }
   else
      return addressRange;
}

//-----------------------------------------------------------------------------
// ResetBase
//-----------------------------------------------------------------------------
VOID TGroup::ResetBase(U32 newPeriBaseAddr) {
   if (GetAddrSelector() == 1) {
      AdrSetAddrOffset(addressRange,newPeriBaseAddr+addressBase2+startOffset);
      AdrSetEndAddrOffset(addressRange,newPeriBaseAddr+addressBase2+endOffset);
   } else {
      AdrSetAddrOffset(addressRange,newPeriBaseAddr+addressBase+startOffset);
      AdrSetEndAddrOffset(addressRange,newPeriBaseAddr+addressBase+endOffset);
   }
}

//-----------------------------------------------------------------------------
// GetId
//-----------------------------------------------------------------------------
U32 TGroup::GetId() { return index; }

//-----------------------------------------------------------------------------
// GetAddrSelector
//-----------------------------------------------------------------------------
U8 TGroup::GetAddrSelector() { return addressSelector; }

//-----------------------------------------------------------------------------
// SetAddrSelector
//-----------------------------------------------------------------------------
VOID TGroup::SetAddrSelector(U8 selector) {
   addressSelector = selector;
}

//-----------------------------------------------------------------------------
// GetFileName
//-----------------------------------------------------------------------------
LPSTR TGroup::GetFileName(VOID) { return ((LPSTR)file_name);}

//-----------------------------------------------------------------------------
// FindClosestRegByAddress
//-----------------------------------------------------------------------------
PTRegister TGroup::FindClosestRegByAddress(DESCRIPTOR searchAddr) {
   PTRegister reg = NULL;
   PTRegister lastreg = NULL;
   DESCRIPTOR regAddrDesc = NULL;
   ADDR_COMPARE result;
   
   if (firstReg == NULL)
      return reg;
   for (reg = firstReg; reg != NULL; reg = reg->next) {
      lastreg = reg;
      PeriServer->GetRegisterAddress(reg,&regAddrDesc);
      AdrCompareAddresses(regAddrDesc,(DESCRIPTOR)searchAddr,&result);
      AdrDestroyAddress(regAddrDesc);
      if ((result == EQUAL_ADDRS) || (result == FIRST_ADDR_GREATER))
         break;
   }
   if (reg != NULL)
      return reg;
   else 
      return lastreg;
}

//-----------------------------------------------------------------------------
// Sort 
//-----------------------------------------------------------------------------
VOID TGroup::Sort(VOID) {
   PTRegister prevreg;
   PTRegister reg1;
   PTRegister reg2;
   U32 reg1addr;
   U32 reg2addr;
   BOOLEAN switched = TRUE;

   while (switched) {
      switched = FALSE;
      prevreg = NULL;
      for (reg1 = firstReg; reg1 != NULL; reg1 = reg1->next) {
         reg2 = reg1->next;
         if (reg2 == NULL) break;
         reg1addr = reg1->GetAddrOffset();
         reg2addr = reg2->GetAddrOffset();
         if (reg1addr > reg2addr) {
            switched = TRUE;
            if (prevreg != NULL) {
               prevreg->next = reg2;
            }
            else {
               firstReg = reg2;
            }
            reg1->next = reg2->next;
            reg2->next = reg1;
         }
         prevreg = reg1;
      }
   }
}

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