/****************************************************************************
**
**  Name:  sharedat.c
**
**  Description:
**     Shared Data Server common to host and firmware
**
**  Status:  TESTED
**
**  $Log:   S:/tbird/mt2_68k/sds/sharedat.c_v  $
** 
**    Rev 1.0   13 Feb 1997 09:04:00   gene
** Initial revision.
** 
**    Rev 1.0   07 Sep 1995 11:12:16   gene
** Initial revision.
** 
**    Rev 1.52   28 Apr 1994 10:05:54   ernie
** Fixed bug in test for whether to flush the communication buffers.
** It was supposed to flush in the network, but was flushing the serial
** buffers instead.
** 
**    Rev 1.51   28 Mar 1994 11:31:22   nghia
** Removed import call to STRLIB.DLL - lmemcpy() is a local function now.
** 
**    Rev 1.50   03 Mar 1994 15:47:30   ernie
** Merged branch 1.48.1.0
** 
**    Rev 1.49   21 Dec 1993 12:19:08   tom
** Perform different processing for net, serial communication.
** 
**    Rev 1.48.1.0   03 Mar 1994 15:42:14   ernie
** Added limit check in UpdateMember.  If comm errors occur during a partial
** member write, the offset and/or length parameters could be corrupted.
** Checking will result in a cleaner error than a gp fault.
** 
**    Rev 1.48   26 Jul 1993 13:15:34   paul
** Replace CHECK_ABORT with TskCheckAbort for esc key support
** 
**    Rev 1.47   16 Jul 1993 11:58:46   ernie
** Removed obsolete includes error.h and sdserror.h
** 
**    Rev 1.46   13 Jul 1993 09:08:50   ernie
** Added support for asynchronous sd transmissions
** 
**    Rev 1.45   25 May 1993 11:40:20   ernie
** added functions SdnData() and SdnWriteMemberIfChanged()
** 
**    Rev 1.44   05 May 1993 09:24:10   doug
** support demo version (no hardware) run-time
** 
**    Rev 1.43   09 Dec 1992 10:00:50   ernie
** Moved registration of SdsCheckBreakCause to sharefw.c for PowerScope
** 
**    Rev 1.42   04 Dec 1992 12:37:32   doug
** com port is now read from .ini and passed in
** 
**    Rev 1.41   04 Dec 1992 11:33:00   ernie
** Added Sd(n)WritePartialMemberNoCallback function for PowerScope loader
** 
**    Rev 1.40   03 Dec 1992 14:48:00   ernie
** Added Sd(n)WriteMemberNoCallback functions
** 
**    Rev 1.39   02 Dec 1992 07:39:52   ernie
** Moved all comm related stuff from sharedat.c to sharefw.c for PwrScope
** 
**    Rev 1.38   03 Nov 1992 12:28:40   ernie
** 1. Fixed error message if ACK/NAK not received.  Now reports timeout.
** 2. Cleaned up warnings.
** 
**    Rev 1.37   02 Nov 1992 13:01:34   ernie
** Added Sd(n)WriteCmdChkAbortReadResponse() calls.  These take 3 additional
** arguments of a member index/descriptor, data, and error code to write
** when the ESC key is pressed while waiting for the response member to be
** written.  Consolidated all forms of WriteCmdReadResponse to call a
** common subroutine to do the work.
** 
**    Rev 1.36   29 Oct 1992 11:30:12   doug
** Use tmalloc instead of local malloc since local space too small for
** some requests (e.g. sequencer needs 16k).
** 
**    Rev 1.35   24 Oct 1992 08:08:02   ernie
** Ack timeout now retries
** 
**    Rev 1.34   23 Oct 1992 12:58:30   ernie
** 1. Changed interface to WSComRead() and WSComWrite().
** 2. Added SdsCheckBreakCause to repeatedly call SdnReadMember() when
**    processing a breakpoint so that register update packets, etc. can
**    be acknowledged faster.
** 
**    Rev 1.33   23 Oct 1992 06:58:22   ernie
** Removed trailing null at end of packet since this could be interpreted
** as the checksum byte if a character was lost.  In one of every 256
** packets with characters lost, the packet will be accepted since the
** checksum will match.  Now it is much less likely that a bad packet
** will be accepted.
** 
**    Rev 1.32   22 Oct 1992 15:24:56   ernie
** Added serial communication error recovery
** 
**    Rev 1.31   24 Sep 1992 08:35:42   doug
** Change the initial communications bytes away from 0, since the PC
** sends a break character (0) on boot up and can fool the comm link
** (ppr6004).
** 
**    Rev 1.30   14 Sep 1992 09:26:42   ernie
** Restored dummy (no hardware) support
** 
**    Rev 1.29   03 Sep 1992 13:13:10   doug
** timeout is now adjustable from debug command and a display shared data
** member routine added
** 
**    Rev 1.28   02 Sep 1992 11:45:32   courtney
** Added SdMainHWnd entry point back again.
** 
**    Rev 1.27   01 Sep 1992 14:27:14   ernie
** 1. Reverted to simple reentrant state machine approach for InputAvailable.
**    This avoids all the problems with semaphores and software delays and
**    waiting for all the data in a packet to arrive before exiting.
** 2. Fixed reentrancy bug in SdWriteCmdReadResponse() that prevented two
**    simultaneous outstanding transactions.  This occurs when tracing and
**    emulation are stopped simultaneously very soon after emulation begins,
**    such as in the step command.  Here, the step command is waiting for
**    emulation to stop when the trace stops, causing a second WCRR() call
**    to get trace buffer parameters.  Now the pending is checked on a
**    per-member basis.
** 
**    Rev 1.25   31 Aug 1992 09:24:34   courtney
** Need to back out InputAvailable semaphore since system won't 
** go into emulation with this.
** 
**    Rev 1.24   30 Aug 1992 16:35:24   courtney
** Move data from static to local for InputAvailable routine.
** Put in semaphore to guard against re-entrancy.
** 
**    Rev 1.23   27 Aug 1992 06:17:30   mindy
** a) added read member routine that doesn't check input first.
** b) changed SdatInputAvailable to to call a local routine InputAvailable
**    then all sdmember routines call InputAvailable as well this way I
**    could tell when SdatIn... was being called from the timer.  Currently
**    the shared data timer is not setup... see sharefw.c.
** 
**    Rev 1.22   25 Aug 1992 10:57:28   courtney
** WSComRead calls now return RETCODE, not number of bytes.
** 
**    Rev 1.21   25 Aug 1992 07:08:28   doug
** corrected parenthetical mismatches
** 
**    Rev 1.20   14 Aug 1992 16:00:06   doug
** removed some warnings... not all yet
** 
**    Rev 1.19   13 Aug 1992 12:45:14   doug
** always time out - use 5 seconds as default
** Also, fix logic for time out
** 
**    Rev 1.18   30 Jul 1992 15:26:38   doug
** use shared data error file
** 
**    Rev 1.17   28 Jul 1992 07:19:44   doug
** check error code on response member and report it
** 
**    Rev 1.16   08 Jun 1992 09:42:42   ernie
** Added member index access routines to improve performance
** 
**    Rev 1.15   18 May 1992 08:45:14   mindy
** missed an error - all shared data server errors need to be defined
** in error.h.
** 
**    Rev 1.14   13 May 1992 16:51:10   mindy
** SdatInternalError replaced by ErrDisplayError
** 
**    Rev 1.13   05 May 1992 14:02:34   mindy
** a) changed memory allocation to use TMalloc instead of global alloc. PPR5552
** b) Cleaned up SafeRead to return an error.  And changed calling routine to
**    check of error.
** 
**    Rev 1.12   27 Mar 1992 15:11:56   courtney
** Cleanup, cast buffer passed to WSComRead to far.
** 
**    Rev 1.11   25 Mar 1992 15:38:38   courtney
** Redesign of basic input routine (SdatInputAvailable), which is not
** a state machine any longer but reads members and their data in
** three fell swoops.
** 
** Change was preciptated by ethernet integration, and the desire to send
** as much as possible in each packet for better performance.  Serial
** (wscom) and ethernet (wsnet) both use the same interface routines, so
** the SDS proper remains independent of the method or protocol for
** communication.
** 
**    Rev 1.10   06 Feb 1992 14:38:00   doug
** removed capturing of mouse events; this was causing the trace presenter
** (and would have for others) "flaky" problems.  The reason why this was
** here in the first place is not understood, but without it... it works!
** 
**    Rev 1.9   05 Feb 1992 07:53:34   doug
** whenever a read is made, the shared data may need to be update.  The
** SdatInputAvailable routine is not driven at the interrupt level, so it
** needs to be given a time-slice of the processor
** 
**    Rev 1.8   17 Jan 1992 16:23:06   doug
** ifdef sense corrected
** 
**    Rev 1.7   17 Jan 1992 15:45:28   doug
** support for SDS version when no emulator connected
** 
**    Rev 1.6   17 Jan 1992 10:54:02   ernie
** merged 1.5.1.0
** 
**    Rev 1.5   10 Jan 1992 11:17:20   nghia
** Added SetOutputSize() routine to packetize sharedata member into 1 
** packet. - Used with WSCOM revision 2 only.
** Added display hourglass cursor to SdWriteCmdReadResponse().
** 
**    Rev 1.4   06 Nov 1991 10:05:26   doug
** Remove commented out PeekMessage section since this should not be used.
** The input available is all that is needed; with both there is a race
** condition.
** 
**    Rev 1.3   31 Oct 1991 14:48:34   doug
** removed limit check because firmware now sends the entire shared data
** memory array during initialization.  An "init" flag could be used to
** enable checks after initialization, but for now we think this extra
** freedom may be useful.
** 
**    Rev 1.2   31 Oct 1991 12:30:16   doug
** fixed compile errors
** 
**    Rev 1.1   31 Oct 1991 11:20:36   mindy
** Implemented WriteCmdReadResponse - somehow several versions of this
** file got lost.  Nghia and I worked on a version with the peekMessage
** stuff shortly after Basic System then Nghia fixed another problem
** found with the trace presenter using the caching browser which resulted
** in using SdatInputAvailible.  This is a reconstruction from Nghia's memory!
** 
**    Rev 1.0   29 Jul 1991 08:52:30   jim
** Initial revision.
**
**  $Header:   S:/tbird/mt2_68k/sds/sharedat.c_v   1.0   13 Feb 1997 09:04:00   gene  $
**
*****************************************************************************/

                       /****************************
                        *                          *
                        *       INCLUDE FILES      *
                        *                          *
                        ****************************/

#include <mem.h>

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

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

#ifndef _LISTW_
#include "listw.h"
#endif

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

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

#ifndef _SHAREDAT_
#include "sharedat.h"
#endif

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

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

#ifndef _WSCOM_
#include "wscom.h"
#endif

#ifndef __TIME_H
#include "time.h"
#endif

#ifndef _PVTASK_
#include "pvtask.h"
#endif


                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/
#define STATIC static

HWND    GlobalhWnd;

/* member response flags use by SdWriteCmdReadResp() */
STATIC U8 memberChanged[NUM_OF_SD_MEMBERS];

HANDLE mainWnd;

#define MEMBER_NAME(index) sdSharedData.member[(U16)(index)].name
#define MEMBER_INDEX(index) sdSharedData.member[(U16)(index)].memberIndex
#define MEMBER_HOST_ACCESS(index) sdSharedData.member[(U16)(index)].hostAccess
#define MEMBER_FW_ACCESS(index) sdSharedData.member[(U16)(index)].fwAccess
#define MEMBER_NUM_BYTES(index) sdSharedData.member[(U16)(index)].numBytes
#define MEMBER_RETCODE(index) sdSharedData.member[(U16)(index)].returnCode
#define MEMBER_DATA(index) \
   &sdSharedDataVal[(U16)sdSharedData.member[(U16)(index)].dataOffset]
#define MEMBER_CALLBACK(index) sdSharedData.member[(U16)(index)].updateFuncs

#define SEMAPHORE_FREE 0
#define SEMAPHORE_IN_USE 1

STATIC TIMEOUT currentTimeout = 15; /* default is 15 seconds */
STATIC BOOLEAN demoVersion = FALSE;  // is demo version running?

                        /****************************
                         *                          *
                         *    EXTERNAL VARIABLES    *
                         *                          *
                         ****************************/
extern HANDLE hLib;

/*
*** Global Shared Data Members - SDINIT.C
 */
extern SHARED_DATA sdSharedData;
extern U8 FAR *sdSharedDataVal;

                        /****************************
                         *                          *
                         *     LOCAL PROTOTYPES     *
                         *                          *
                         ****************************/
RETCODE InvokeCallbacks(SD_DESCRIPTOR FAR *sdDesc);
RETCODE InvokeCallbacksUsingIndex(MEMBER_INDEX index);
RETCODE GetIndexGivenName(MEMBER_NAME FAR *name, MEMBER_INDEX FAR *index);
RETCODE MemberLimitCheck(MEMBER_INDEX index, MEMBER_OFFSET offset,
                         MEMBER_SIZE numBytes);
U16 NumBytesInMask(MEMBER_BITS bitMask);
RETCODE TestAndSetSemaphore(MEMBER_INDEX index, BOOLEAN FAR *setOk);
RETCODE SdnWriteCmdCommon(BOOLEAN checkAbort,
          MEMBER_INDEX cmdIndex, VOID *cmdBuff, RETCODE cmdReturnCode,
          MEMBER_INDEX abortIndex, VOID *abortData, RETCODE abortReturnCode,
          MEMBER_INDEX respIndex, TIMEOUT timeout, BOOLEAN FAR *timedOut);
LPSTR lmemcpy(LPSTR dest, LPSTR source, WORD len);

                        /****************************
                         *                          *
                         *      EXECUTABLE CODE     *
                         *                          *
                         ****************************/
/**************************************************************************
**
** lmemcpy
**
***************************************************************************/
LPSTR lmemcpy(LPSTR dest, LPSTR source, WORD len) {
   WORD i;
   LPSTR s, d;

   s = source; d = dest;
   for (i = 0; i < len; i++) *d++ = *s++;
   if (i == len)
      return(dest);
   return(NULL);
}

/*****************************************************************************
**
**    SdInitialize
**
*****************************************************************************/
RETCODE EXPORT SdInitialize(U16 hWnd, MEMBER_RESIDENCE residence,
      S16 *comPort) {
   RETCODE err;
   GlobalhWnd = hWnd;
   if((err = ProcReturnDemonstrationVersion(&demoVersion))!=GOOD)
      return(err);
   return(SdFwInitialize(hWnd, residence, comPort));
}

/**************************** Communications ******************************/
/*****************************************************************************
**
**  SdatInputAvailable
**
**  Description:  This function requires a Windows-message style parameter
**      list, since it is a callback on timer events.
**
*****************************************************************************/
#pragma argsused
VOID EXPORT SdatInputAvailable(U16 hWnd, U16 iMsg, U16 wParam, U32 lParam) {
   InputAvailable();
}

/*****************************************************************************
**
**  SdsInputAvailable
**
*****************************************************************************/
RETCODE EXPORT SdsInputAvailable(VOID) {
   InputAvailable();
   return(GOOD);
}

/*************************** Shared Data Services *************************/
/*****************************************************************************
**
**    SdRegister
**
*****************************************************************************/
RETCODE EXPORT SdRegister(MEMBER_NAME FAR *name, CALLBACK updateFunc,
   DESCRIPTOR FAR *desc) {
   RETCODE err;
   MEMBER_INDEX index;
   SD_DESCRIPTOR FAR *sdDesc;
   if ((*desc = (DESCRIPTOR)TMalloc((U32)sizeof(*sdDesc))) == NULL)
      return(ER_OUT_OF_MEMORY);
   sdDesc = (SD_DESCRIPTOR FAR *)*desc;
   /*
   ** check that name is valid and get the index of the member
   */
   if ((err = GetIndexGivenName((MEMBER_NAME FAR *)name,
         (MEMBER_INDEX *)&index)) != GOOD)
      return(err);

   /*
   ** register callback if specified
   */
   if(updateFunc != NULL) {
      if ((err = AppendList(&MEMBER_CALLBACK(index), updateFunc)) != GOOD) {
         ErrDisplayError(TFree((LPSTR)*desc), FORCE_POPUP);
         return(err);
      }
   }
   /*
   ** fill in the descriptor
   */
   sdDesc->index = index;
   sdDesc->updateFunc = updateFunc;
   return(GOOD);
}

/*****************************************************************************
**
**    SdUnRegister
**
*****************************************************************************/
RETCODE EXPORT SdUnRegister(DESCRIPTOR desc) {
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   RemoveList((LIST FAR *)&MEMBER_CALLBACK(sdDesc->index),sdDesc->updateFunc);
   TFree((LPSTR)desc);
   return(GOOD);
}

/*****************************************************************************
**
**    SdRegisterSemaphore
**
*****************************************************************************/
RETCODE EXPORT SdRegisterSemaphore(MEMBER_NAME FAR *name,
                                   CALLBACK updateFunc,
                                   DESCRIPTOR FAR *desc) {
   RETCODE err;
   MEMBER_INDEX index = 0, rIndex;
   MEMBER_NAME fullName[SD_MAX_NAME+1];
   BOOLEAN setOk;
   while(1) {
      /*
      ** form the full name by using the root name and adding offset starting
      ** at 0
      */
      wsprintf((MEMBER_NAME *)fullName, "%s %d", name, index);
      /*
      ** get the index corresponding to name; if fails, we have exhausted all
      ** the members with the root name
      */
      if ((err = GetIndexGivenName((MEMBER_NAME FAR *) fullName,
         (MEMBER_INDEX FAR *) &rIndex)) != GOOD) return(err);
      /*
      ** get the semaphore if it is available and register on it
      */
      if ((err = TestAndSetSemaphore(rIndex, (BOOLEAN FAR *) &setOk))!=GOOD)
         return(err);
      if (setOk) return(SdRegister(fullName,
         (CALLBACK) updateFunc, (DESCRIPTOR FAR *)desc));

      /*
      ** move on to next one
      */
      index++;
   }
}

/*****************************************************************************
**
**    SdUnRegisterSemaphore
**
*****************************************************************************/
RETCODE EXPORT SdUnRegisterSemaphore(DESCRIPTOR desc) {
   RETCODE err;
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   U8 FAR *memData;
   memData = MEMBER_DATA(sdDesc->index);
   if((err = SdatGetExclusiveSdAccess())!=GOOD) return(err);
   *memData = SEMAPHORE_FREE;
   if((err = SdatFreeExclusiveSdAccess())!=GOOD) return(err);
   return(SdUnRegister(desc));
}

/*****************************************************************************
**
**    SdReadMember
**
*****************************************************************************/
RETCODE EXPORT SdReadMember(DESCRIPTOR desc, U8 FAR *data) {
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   U8 FAR *memData;
   InputAvailable();
   memData = MEMBER_DATA(sdDesc->index);
   lmemcpy((LPSTR)data, (LPSTR)memData, (U16)MEMBER_NUM_BYTES(sdDesc->index));
   return(MEMBER_RETCODE(sdDesc->index));
}

/*****************************************************************************
**
**    SdReadMemberNoInputCheck
**
*****************************************************************************/
RETCODE EXPORT SdReadMemberNoInputCheck(DESCRIPTOR desc, U8 FAR *data) {
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   U8 FAR *memData;
   memData = MEMBER_DATA(sdDesc->index);
   lmemcpy((LPSTR)data, (LPSTR)memData, (U16)MEMBER_NUM_BYTES(sdDesc->index));
   return(MEMBER_RETCODE(sdDesc->index));
}

/*****************************************************************************
**
**    SdWriteMember
**    SdWriteMemberNoCallback
**
*****************************************************************************/
RETCODE EXPORT SdWriteMember(DESCRIPTOR desc,U8 FAR *data,RETCODE returnCode){
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   RETCODE err;
   // don't check for input if on net
   if (WSComAckRequired()) {
      InputAvailable();
   }
   if ((err = UpdateMember((SD_DESCRIPTOR FAR *) sdDesc, 0,
         MEMBER_NUM_BYTES(sdDesc->index),data,returnCode,TRUE)) == GOOD) {
      err = SendFullMemberData(sdDesc->index, SDS_SYNC, NULL);
   }
   return(err);
}

RETCODE EXPORT SdWriteMemberNoCallback(DESCRIPTOR desc,U8 FAR *data,
      RETCODE returnCode){
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   RETCODE err;
   // don't check for input if on net
   if (WSComAckRequired()) {
      InputAvailable();
   }
   if ((err = UpdateMember((SD_DESCRIPTOR FAR *) sdDesc, 0,
         MEMBER_NUM_BYTES(sdDesc->index),data,returnCode,FALSE)) == GOOD) {
      err = SendFullMemberData(sdDesc->index, SDS_SYNC, NULL);
   }
   return(err);
}

/*****************************************************************************
**
**    SdReadPartialMember
**
*****************************************************************************/
RETCODE EXPORT SdReadPartialMember(DESCRIPTOR desc, MEMBER_OFFSET offset,
                                   MEMBER_SIZE numBytes, U8 FAR *data) {
   RETCODE err;
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   U8 *memData = MEMBER_DATA(sdDesc->index) + (U16)offset;
   InputAvailable();
   if((err = MemberLimitCheck(sdDesc->index, offset, numBytes))!=GOOD)
      return(err);
   lmemcpy((LPSTR)data, (LPSTR)memData, (WORD)numBytes);
   return(MEMBER_RETCODE(sdDesc->index));
}

/*****************************************************************************
**
**    SdWritePartialMember
**    SdWritePartialMemberNoCallback
**
*****************************************************************************/
RETCODE EXPORT SdWritePartialMember(DESCRIPTOR desc, MEMBER_OFFSET offset,
                 MEMBER_SIZE numBytes, U8 FAR *data, RETCODE returnCode) {
   RETCODE err;
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;

   // don't check for input if on net
   if (WSComAckRequired()) {
      InputAvailable();
   }
   if ((err = UpdateMember((SD_DESCRIPTOR FAR *)sdDesc, offset, numBytes,
      data, returnCode,TRUE)) != GOOD) return(err);
   if ((err = SendPartialMemberData(sdDesc->index, offset, numBytes, SDS_SYNC,
      NULL)) != GOOD) return(err);
   return(GOOD);
}

RETCODE EXPORT SdWritePartialMemberNoCallback(DESCRIPTOR desc,
  MEMBER_OFFSET offset,MEMBER_SIZE numBytes,U8 FAR *data,RETCODE returnCode) {
   RETCODE err;
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;

   // don't check for input if on net
   if (WSComAckRequired()) {
      InputAvailable();
   }
   if ((err = UpdateMember((SD_DESCRIPTOR FAR *)sdDesc, offset, numBytes,
      data, returnCode,FALSE)) != GOOD) return(err);
   if ((err = SendPartialMemberData(sdDesc->index, offset, numBytes, SDS_SYNC,
      NULL)) != GOOD) return(err);
   return(GOOD);
}

/*****************************************************************************
**
**    SdReadBitsOfMember
**
*****************************************************************************/
RETCODE EXPORT SdReadBitsOfMember(DESCRIPTOR desc, MEMBER_OFFSET offset,
                                  MEMBER_BITS bitMask,
                                  MEMBER_BITS FAR *bitData) {
   RETCODE err;
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   U16 i;
   U8 *memData;
   MEMBER_SIZE numBytes = NumBytesInMask(bitMask);
   InputAvailable();
   if ((err = MemberLimitCheck(sdDesc->index, offset, numBytes)) == GOOD) {
      *bitData = 0;
      for (i=0; i<numBytes; i++) {
         memData = MEMBER_DATA(sdDesc->index) + (U16)(offset+i);
         *bitData |= (MEMBER_BITS)((*memData & (U8)(bitMask>>(i*8))))>>(i*8);
      }
   }
   return(err);
}

/*****************************************************************************
**
**    SdWriteBitsOfMember
**
*****************************************************************************/
RETCODE EXPORT SdWriteBitsOfMember(DESCRIPTOR desc, MEMBER_OFFSET offset,
                                   MEMBER_BITS bitMask, MEMBER_BITS bitData,
                                   RETCODE returnCode) {
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   RETCODE err;
   U16 i, numBytes;
   U8 wrByte;

   // don't check for input if on net
   if (WSComAckRequired()) {
      InputAvailable();
   }
   numBytes = NumBytesInMask(bitMask);
   if ((err = MemberLimitCheck(sdDesc->index, offset, numBytes)) == GOOD) {
      for (i=0; i<numBytes; i++) {
         U8 *memData;
         memData = MEMBER_DATA(sdDesc->index) + (U16)(offset+i);
         wrByte = *memData;
         wrByte &= (~bitMask)>>(i*8);
         wrByte |= (bitData & bitMask)>>(i*8);
         *memData = wrByte;
      }
      MEMBER_RETCODE(sdDesc->index) = returnCode;
   }
   return(err);
}

/*****************************************************************************
**
**    SdGetMemberSize
**
*****************************************************************************/
RETCODE EXPORT SdGetMemberSize(DESCRIPTOR desc, MEMBER_SIZE FAR *numBytes) {
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   *numBytes = MEMBER_NUM_BYTES(sdDesc->index);
   return(GOOD);
}

/*****************************************************************************
**
**    SdGetMemberAccess
**
*****************************************************************************/
RETCODE EXPORT SdGetMemberAccess(DESCRIPTOR desc, MEMBER_RESIDENCE residence,
                                 MEMBER_ACCESS FAR *access) {
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   *access = (residence==MEMBER_HOST) ? MEMBER_HOST_ACCESS(sdDesc->index):
                                           MEMBER_FW_ACCESS(sdDesc->index);
   return(GOOD);
}

/*****************************************************************************
**
**    SdGetMemberName
**
*****************************************************************************/
RETCODE EXPORT SdGetMemberName(DESCRIPTOR desc, MEMBER_NAME FAR *memberName) {
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   lstrcpy((CHAR FAR *)memberName, (CHAR FAR *)MEMBER_NAME(sdDesc->index));
   return(GOOD);
}

/*****************************************************************************
**
**    SdGetMemberIndex
**
*****************************************************************************/
RETCODE EXPORT SdGetMemberIndex(DESCRIPTOR desc, MEMBER_INDEX FAR *index) {
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   *index = MEMBER_INDEX(sdDesc->index);
   return(GOOD);
}

/*****************************************************************************
**
**    SdGetMemberReturnCode
**
*****************************************************************************/
RETCODE EXPORT SdGetMemberReturnCode(DESCRIPTOR desc, RETCODE FAR *retCode) {
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   *retCode = MEMBER_RETCODE(sdDesc->index);
   return(GOOD);
}

/*****************************************************************************
**
**    SdTestAndSetSemaphore
**
*****************************************************************************/
RETCODE EXPORT SdTestAndSetSemaphore(DESCRIPTOR desc, BOOLEAN FAR *setOk) {
   SD_DESCRIPTOR FAR *sdDesc = (SD_DESCRIPTOR FAR *)desc;
   RETCODE err;
   err = TestAndSetSemaphore(sdDesc->index, (BOOLEAN FAR *)setOk);
   return(err);
}

/*****************************************************************************
**
**    SdWriteCmdReadResponse
**
*****************************************************************************/
/* Windows - turn off warning */
#pragma argsused
RETCODE EXPORT SdWriteCmdChkAbortReadResponse(DESCRIPTOR cmdDesc,
                                   VOID FAR *cmdBuff,
                                   RETCODE cmdReturnCode,
                                   DESCRIPTOR abortDesc, VOID FAR *abortData,
                                   RETCODE abortReturnCode,
                                   DESCRIPTOR respDesc, TIMEOUT timeout,
                                   BOOLEAN FAR *timedOut) {
   SD_DESCRIPTOR FAR *cDesc = (SD_DESCRIPTOR FAR *)cmdDesc;
   SD_DESCRIPTOR FAR *aDesc = (SD_DESCRIPTOR FAR *)abortDesc;
   SD_DESCRIPTOR FAR *rDesc = (SD_DESCRIPTOR FAR *)respDesc;
   return(SdnWriteCmdCommon(TRUE,
      cDesc->index,cmdBuff,cmdReturnCode,
      aDesc->index,abortData,abortReturnCode,
      rDesc->index,timeout,timedOut));
}

RETCODE EXPORT SdWriteCmdReadResponse(DESCRIPTOR cmdDesc, VOID FAR *cmdBuff,
                                      RETCODE cmdReturnCode,
                                      DESCRIPTOR respDesc, TIMEOUT timeout,
                                      BOOLEAN FAR *timedOut) {
   SD_DESCRIPTOR FAR *cDesc = (SD_DESCRIPTOR FAR *)cmdDesc;
   SD_DESCRIPTOR FAR *rDesc = (SD_DESCRIPTOR FAR *)respDesc;
   return(SdnWriteCmdCommon(FALSE,
      cDesc->index,cmdBuff,cmdReturnCode,
      NULL, NULL, NULL,
      rDesc->index,timeout,timedOut));
}

/*********************** Shared Data Services, numeric access  *************/

/*****************************************************************************
**
**    SdnRegister
**
*****************************************************************************/
RETCODE EXPORT SdnRegister(MEMBER_INDEX index, CALLBACK updateFunc,
                          DESCRIPTOR FAR *desc) {
   RETCODE err;
   SD_DESCRIPTOR FAR *sdDesc;
   if ((*desc = (DESCRIPTOR)TMalloc((U32)sizeof(*sdDesc))) == NULL)
      return(ER_OUT_OF_MEMORY);
   sdDesc = (SD_DESCRIPTOR FAR *)*desc;
   /*
   ** register callback if specified
   */
   if(updateFunc != NULL) {
      if ((err = AppendList(&MEMBER_CALLBACK(index), updateFunc)) != GOOD) {
         ErrDisplayError(TFree((LPSTR)*desc), FORCE_POPUP);
         return(err);
      }
   }
   /*
   ** fill in the descriptor
   */
   sdDesc->index = index;
   sdDesc->updateFunc = updateFunc;
   return(GOOD);
}  

/*****************************************************************************
**
**    SdnData
**
*****************************************************************************/
U8 * EXPORT SdnData(MEMBER_INDEX index) {
   return(MEMBER_DATA(index));
}

/*****************************************************************************
**
**    SdnRetcode
**
*****************************************************************************/
RETCODE * EXPORT SdnRetcode(MEMBER_INDEX index) {
   return(&MEMBER_RETCODE(index));
}

/*****************************************************************************
**
**    SdnReadMember
**
*****************************************************************************/
RETCODE EXPORT SdnReadMember(MEMBER_INDEX index, U8 FAR *data) {
   U8 FAR *memData;
   InputAvailable();
   memData = MEMBER_DATA(index);
   lmemcpy((LPSTR)data, (LPSTR)memData, (U16)MEMBER_NUM_BYTES(index));
   return(MEMBER_RETCODE(index));
}

/*****************************************************************************
**
**    SdnWriteMember
**    SdnWriteMemberIfChanged
**    SdnWriteMemberNoCallback
**    SdnWriteMemberAsync
**
*****************************************************************************/
RETCODE EXPORT SdnWriteMember(MEMBER_INDEX index, U8 FAR *data,
                              RETCODE returnCode){
   RETCODE err;

   // don't check for input if on net
   if (WSComAckRequired()) {
      InputAvailable();
   }
   if ((err = UpdateMemberUsingIndex(index, 0, MEMBER_NUM_BYTES(index),
         data, returnCode,TRUE)) != GOOD) return(err);
   return(SendFullMemberData(index, SDS_SYNC, NULL));
}

RETCODE EXPORT SdnWriteMemberIfChanged(MEMBER_INDEX index, U8 FAR *data,
                              RETCODE returnCode){
   RETCODE err;
   if (memcmp(MEMBER_DATA(index), data, (U16)MEMBER_NUM_BYTES(index)) == 0)
      return(GOOD);  /* Value is the same...do not update */
   if ((err = UpdateMemberUsingIndex(index, 0, MEMBER_NUM_BYTES(index),
         data, returnCode,TRUE)) != GOOD) return(err);
   return(SendFullMemberData(index, SDS_SYNC, NULL));
}

RETCODE EXPORT SdnWriteMemberNoCallback(MEMBER_INDEX index, U8 FAR *data,
                              RETCODE returnCode){
   RETCODE err;

   // don't check for input if on net
   if (WSComAckRequired()) {
      InputAvailable();
   }
   if ((err = UpdateMemberUsingIndex(index, 0, MEMBER_NUM_BYTES(index),
         data, returnCode,FALSE)) != GOOD) return(err);
   return(SendFullMemberData(index, SDS_SYNC, NULL));
}

RETCODE EXPORT SdnWriteMemberAsync(MEMBER_INDEX index, U8 FAR *data,
                              RETCODE returnCode, FARPROC func){
   RETCODE err;

   // don't check for input if on net
   if (WSComAckRequired()) {
      InputAvailable();
   }
   if ((err = UpdateMemberUsingIndex(index, 0, MEMBER_NUM_BYTES(index),
         data, returnCode,TRUE)) != GOOD) return(err);
   return(SendFullMemberData(index, SDS_ASYNC, func));
}

/*****************************************************************************
**
**    SdnReadPartialMember
**
*****************************************************************************/
RETCODE EXPORT SdnReadPartialMember(MEMBER_INDEX index, MEMBER_OFFSET offset,
                                   MEMBER_SIZE numBytes, U8 FAR *data) {
   RETCODE err;
   U8 *memData = MEMBER_DATA(index) + (U16)offset;
   InputAvailable();
   if((err = MemberLimitCheck(index, offset, numBytes))!=GOOD) return(err);
   lmemcpy((LPSTR)data, (LPSTR)memData, (WORD)numBytes);
   return(MEMBER_RETCODE(index));
}

/*****************************************************************************
**
**    SdnWritePartialMember
**    SdnWritePartialMemberNoCallback
**    SdnWritePartialMemberAsync
**
*****************************************************************************/
RETCODE EXPORT SdnWritePartialMember(MEMBER_INDEX index, MEMBER_OFFSET offset,
                 MEMBER_SIZE numBytes, U8 FAR *data, RETCODE returnCode) {
   RETCODE err;

   // don't check for input if on net
   if (WSComAckRequired()) {
      InputAvailable();
   }
   if((err = UpdateMemberUsingIndex(index,offset, numBytes, data, returnCode,
      TRUE))!=GOOD) return(err);
   if((err = SendPartialMemberData(index, offset, numBytes, SDS_SYNC, NULL))
      !=GOOD) return(err);
   return(GOOD);
}

RETCODE EXPORT SdnWritePartialMemberNoCallback(MEMBER_INDEX index,
  MEMBER_OFFSET offset,MEMBER_SIZE numBytes,U8 FAR *data,RETCODE returnCode) {
   RETCODE err;

   // don't check for input if on net
   if (WSComAckRequired()) {
      InputAvailable();
   }
   if((err = UpdateMemberUsingIndex(index,offset, numBytes, data, returnCode,
      FALSE)) != GOOD) return(err);
   if((err = SendPartialMemberData(index, offset, numBytes, SDS_SYNC, NULL))
      != GOOD) return(err);
   return(GOOD);
}

RETCODE EXPORT SdnWritePartialMemberAsync(MEMBER_INDEX index,
      MEMBER_OFFSET offset, MEMBER_SIZE numBytes, U8 FAR *data,
      RETCODE returnCode, FARPROC func) {
   RETCODE err;

   // don't check for input if on net
   if (WSComAckRequired()) {
      InputAvailable();
   }
   if((err = UpdateMemberUsingIndex(index,offset, numBytes, data, returnCode,
      TRUE))!=GOOD) return(err);
   if((err = SendPartialMemberData(index, offset, numBytes, SDS_ASYNC, func))
      !=GOOD) return(err);
   return(GOOD);
}

/*****************************************************************************
**
**    SdnGetMemberSize
**
*****************************************************************************/
RETCODE EXPORT SdnGetMemberSize(MEMBER_INDEX index,
                                MEMBER_SIZE FAR *numBytes) {
   *numBytes = MEMBER_NUM_BYTES(index);
   return(GOOD);
}

/*****************************************************************************
**
**    SdnGetMemberAccess
**
*****************************************************************************/
RETCODE EXPORT SdnGetMemberAccess(MEMBER_INDEX index,
                     MEMBER_RESIDENCE residence, MEMBER_ACCESS FAR *access) {
   *access = (residence==MEMBER_HOST) ? MEMBER_HOST_ACCESS(index):
                                        MEMBER_FW_ACCESS(index);
   return(GOOD);
}

/*****************************************************************************
**
**    SdnGetMemberName
**
*****************************************************************************/
RETCODE EXPORT SdnGetMemberName(MEMBER_INDEX index,
                                MEMBER_NAME FAR *memberName) {
   lstrcpy((CHAR FAR *)memberName, (CHAR FAR *)MEMBER_NAME(index));
   return(GOOD);
}

/*****************************************************************************
**
**    SdnGetMemberIndex
**
*****************************************************************************/
RETCODE EXPORT SdnGetMemberIndex(MEMBER_INDEX index,
                                 MEMBER_INDEX FAR *basedIndex) {
   *basedIndex = MEMBER_INDEX(index);
   return(GOOD);
}

/*****************************************************************************
**
**    SdnGetMemberReturnCode
**
*****************************************************************************/
RETCODE EXPORT SdnGetMemberReturnCode(MEMBER_INDEX index,
                                      RETCODE FAR *retCode) {
   *retCode = MEMBER_RETCODE(index);
   return(GOOD);
}

/*****************************************************************************
**
**    SdnWriteCmdReadResponse
**
*****************************************************************************/
RETCODE EXPORT SdnWriteCmdReadResponse(MEMBER_INDEX cmdIndex, VOID *cmdBuff,
                                      RETCODE cmdReturnCode,
                                      MEMBER_INDEX respIndex, TIMEOUT timeout,
                                      BOOLEAN FAR *timedOut) {
   return(SdnWriteCmdCommon(FALSE,
      cmdIndex, cmdBuff, cmdReturnCode,
      NULL, NULL, NULL,
      respIndex, timeout, timedOut));
}

RETCODE EXPORT SdnWriteCmdChkAbortReadResponse(MEMBER_INDEX cmdIndex,
                                   VOID *cmdBuff,
                                   RETCODE cmdReturnCode,
                                   MEMBER_INDEX abortIndex, VOID *abortData,
                                   RETCODE abortReturnCode,
                                   MEMBER_INDEX respIndex, TIMEOUT timeout,
                                   BOOLEAN FAR *timedOut) {
   return(SdnWriteCmdCommon(TRUE,
      cmdIndex, cmdBuff, cmdReturnCode,
      abortIndex, abortData, abortReturnCode,
      respIndex, timeout, timedOut));
}

RETCODE SdnWriteCmdCommon(BOOLEAN checkAbort,
          MEMBER_INDEX cmdIndex, VOID *cmdBuff, RETCODE cmdReturnCode,
          MEMBER_INDEX abortIndex, VOID *abortData, RETCODE abortReturnCode,
          MEMBER_INDEX respIndex, TIMEOUT timeout, BOOLEAN FAR *timedOut) {
   RETCODE err = GOOD;
   time_t startTime; /* time.h */
   HCURSOR hSaveCursor, hHourGlass;
   BOOLEAN abortFromEsc;


   *timedOut = FALSE;
   /*
   ** Set up member response flag to be notified
   ** If cmd and response member are the same, updatemember will be
   ** called twice for this member.
   */
   if( respIndex == cmdIndex ) memberChanged[(U16)respIndex] = 2;
   else memberChanged[(U16)respIndex] = 1;

   if ((err = SdnWriteMember(cmdIndex, cmdBuff, cmdReturnCode)) != GOOD)
      return(err);

if(demoVersion) {
   return(GOOD);
} else {
   /* put up an hour glass cursor to wait */
   hHourGlass = LoadCursor(NULL, IDC_WAIT);
   hSaveCursor = SetCursor(hHourGlass);

   /* flush buffered writes for network */
   if (!WSComAckRequired())
      WSComFlush();

   if (timeout==0) timeout = currentTimeout;

   startTime = time(NULL);
   do {
      InputAvailable();
      /* wait for response - UpdateMember will set flag if responded */
      if( memberChanged[(U16)respIndex] == 0) {
         RETCODE retCode;
         SetCursor(hSaveCursor);
         if((err = SdnGetMemberReturnCode(respIndex, &retCode))!=GOOD)
            return(err);
         return(retCode);
      }

      if (checkAbort) {
         err = TskCheckAbort(&abortFromEsc);
         if((err!=GOOD) || (abortFromEsc!=0) ) {
             if ((err = SdnWriteMember(abortIndex,abortData,abortReturnCode))
                  != GOOD) {
               SetCursor(hSaveCursor);
               return(err);
             }
         }
       }

   } while((startTime + timeout) >= time(NULL));
   memberChanged[(U16)respIndex] = 0;
   *timedOut = TRUE;
   SetCursor(hSaveCursor);
   return(ER_TIMED_OUT);
}
}


/****************************** Local Routines ****************************/

/****************************************************************************
**
**  UpdateMember
**  UpdateMemberUsingIndex
**
**  Description:
**     This routine updates one member (or part of a member) with new data.
**     It also invokes the callbacks associated with the member.
**
**  Parameters:
**     input:
**        index:  index of member in the shared data table
**        offset:  offset where data is to be written (use 0 for full write)
**        length:  length of data to write (use MEMBER_NUM_BYTES for
**                 full write)
**        buff:  buffer containing bytes to write for member (or partial)
**        callCallbacks: true to call callbacks
**     output:
**        none
**
*****************************************************************************/
RETCODE UpdateMember(SD_DESCRIPTOR FAR *sdDesc, MEMBER_OFFSET offset,
  MEMBER_SIZE numBytes,U8 FAR *buff,RETCODE returnCode,BOOLEAN callCallbacks){
   RETCODE err=GOOD;
   U8 FAR *memData;

   if (buff != NULL) {
      if (sdSharedData.member[(U16)sdDesc->index].dataOffset + offset
          + numBytes > BYTES_IN_SHARED_DATA) return(ER_SD_INDEX);
      memData = MEMBER_DATA(sdDesc->index) + (U16)offset;
/*!!!    if ((err = MemberLimitCheck(sdDesc->index, offset, numBytes))!=GOOD)
         return(err); !!! on boot up, whole memory image sent, don't check */
      lmemcpy((LPSTR)memData, (LPSTR)buff, (U16)numBytes);
   }
   MEMBER_RETCODE(sdDesc->index) = returnCode;
   if (callCallbacks) err = InvokeCallbacks(sdDesc); /* !!! */

   /* set response flag of updating member - SdWriteCmdReadResponse() */
   if( memberChanged[(U16)(sdDesc->index)] )
      memberChanged[(U16)(sdDesc->index)]--;
   return((err != GOOD) ? err : GOOD);
}

RETCODE UpdateMemberUsingIndex(MEMBER_INDEX index, MEMBER_OFFSET offset,
  MEMBER_SIZE numBytes,U8 FAR *buff,RETCODE returnCode,BOOLEAN callCallbacks){
   RETCODE err=GOOD;
   U8 FAR *memData;

   if (buff != NULL) {
      if (sdSharedData.member[(U16)index].dataOffset + offset
          + numBytes > BYTES_IN_SHARED_DATA) return(ER_SD_INDEX);
      memData = MEMBER_DATA(index) + (U16)offset;
/*!!!    if ((err = MemberLimitCheck(index, offset, numBytes))!=GOOD)
         return(err); !!! on boot up, whole memory image sent, don't check */
      lmemcpy((CHAR FAR *)memData, (CHAR FAR *)buff, (U16)numBytes);
   }
   MEMBER_RETCODE(index) = returnCode;
   if (callCallbacks) err = InvokeCallbacksUsingIndex(index); /* !!! */

   /* set response flag of updating member - SdWriteCmdReadResponse() */
   if( memberChanged[(U16)index]) memberChanged[(U16)index]--;
   return((err != GOOD) ? err : GOOD);
}

/****************************************************************************
**
**  InvokeCallbacks
**  InvokeCallbacksUsingIndex
**
**  Description:
**     This routine calls all routines registered with a particular member.
**
**  Parameters:
**     input:
**        index:  index of member in the shared data table
**     output:
**        none
**
*****************************************************************************/
RETCODE InvokeCallbacks(SD_DESCRIPTOR FAR *sdDesc) {
   CALLBACK func;
   SD_DESCRIPTOR FAR *sdDescCallback;
   /* start at the head of linked list */
   HeadList(&MEMBER_CALLBACK(sdDesc->index));
   while (!IsTailList(&MEMBER_CALLBACK(sdDesc->index))) {
      func = (CALLBACK) GetList(&MEMBER_CALLBACK(sdDesc->index));
      /* !!! Watch for Endless loop */
      if (func != sdDesc->updateFunc) {
         if((sdDescCallback=(SD_DESCRIPTOR FAR *)
            TMalloc(sizeof(*sdDescCallback)))==NULL) return(ER_OUT_OF_MEMORY);
         sdDescCallback->index = sdDesc->index;
         sdDescCallback->updateFunc = func;
         SdatInvokeCallback((CALLBACK)func,(DESCRIPTOR)sdDescCallback);/*!!!*/
         TFree((LPSTR)sdDescCallback);
      }
      NextList(&MEMBER_CALLBACK(sdDesc->index));
   }
   return(GOOD);
}

RETCODE InvokeCallbacksUsingIndex(MEMBER_INDEX index) {
   CALLBACK func;
   HeadList(&MEMBER_CALLBACK(index));
   while (!IsTailList(&MEMBER_CALLBACK(index))) {
      func = (CALLBACK) GetList(&MEMBER_CALLBACK(index));
      {
         SD_DESCRIPTOR FAR *sdDescCallback;
         if((sdDescCallback=(SD_DESCRIPTOR FAR *)
            TMalloc(sizeof(*sdDescCallback)))==NULL) return(ER_OUT_OF_MEMORY);
         sdDescCallback->index = index;
         sdDescCallback->updateFunc = func;
         SdatInvokeCallback((CALLBACK)func,(DESCRIPTOR)sdDescCallback);
         TFree((LPSTR)sdDescCallback);
      }
      NextList(&MEMBER_CALLBACK(index));
   }
   return(GOOD);
}

/****************************************************************************
**
**  GetIndexGivenName
**
**  Description:
**     This routine checks the list of members by ASCII name and returns
**     the index if found.
**
**  Parameters:
**     input:
**        name:  string containing members name
**     output:
**        index:  index of member in the shared data table
**
*****************************************************************************/
RETCODE GetIndexGivenName(MEMBER_NAME FAR *name, MEMBER_INDEX FAR *index) {
   for ((*index) = 0; (*index) < NUM_OF_SD_MEMBERS; (*index)++) {
      if(!lstrcmp((MEMBER_NAME FAR *)name, (CHAR FAR *)MEMBER_NAME(*index)))
         return(GOOD);
   }
   return(ER_MEMBER_NAME_NOT_FOUND);
}

/****************************************************************************
**
**  MemberLimitCheck
**
**  Description:
**     Checks to see if an offset and a number of bytes exceeds the size
**     of a member.
**
**  Parameters:
**     input:
**        index:  index of member in shared data array
**        offset:  starting offset (from 0)
**        length:  number of bytes from offset
**     output:
**        none
**
*****************************************************************************/
RETCODE MemberLimitCheck(MEMBER_INDEX index, MEMBER_OFFSET offset,
                         MEMBER_SIZE numBytes) {
   return(((offset+numBytes) > MEMBER_NUM_BYTES(index)) ?
      ER_SHARED_DATA_LIMIT_EXCEEDED: GOOD);
}

/****************************************************************************
**
**  NumBytesInMask
**
**  Description:
**     This routine takes a bit mask and returns the number of bytes that
**     will be affected by using the mask.  If one bit is used in any of
**     the bytes, it is being used.  The bit set in the highest byte
**     determines the total.
**
**  Parameters:
**     input:
**        bitMask:  mask
**     output:
**        none
**
*****************************************************************************/
U16 NumBytesInMask(MEMBER_BITS bitMask) {
   if((bitMask & 0xFF000000L)!=0) return(4);
   if((bitMask & 0x00FF0000L)!=0) return(3);
   if((bitMask & 0x0000FF00L)!=0) return(2);
   if((bitMask & 0x000000FFL)!=0) return(1);
   return(0);
}

/****************************************************************************
**
**  TestAndSetSemaphore
**
**  Description:
**     This routine sees if the data field of a member is available
**     and if so, sets it to unavailable.
**
**  Parameters:
**     input:
**        index:  index of shared data member
**        setOk:  caller declares storage
**     output:
**        setOk:  TRUE if semaphore was gotten ok, FALSE if not available
**
*****************************************************************************/
RETCODE TestAndSetSemaphore(MEMBER_INDEX index, BOOLEAN FAR *setOk) {
   RETCODE err;
   U8 *memData = MEMBER_DATA(index);

   *setOk = FALSE;
   if((err = SdatGetExclusiveSdAccess())!=GOOD) return(err);
   if((*memData)==SEMAPHORE_FREE) {
      *memData = SEMAPHORE_IN_USE;
      *setOk = TRUE;
   }
   if((err = SdatFreeExclusiveSdAccess())!=GOOD) return(err);
   return(GOOD);
}

/****************************************************************************
**
**  SdatGetTimeout
**
*****************************************************************************/
RETCODE SdatGetTimeout(TIMEOUT *timeout) {
   *timeout = currentTimeout;
   return(GOOD);
}

/****************************************************************************
**
**  SdatSetTimeout
**
*****************************************************************************/
RETCODE SdatSetTimeout(TIMEOUT timeout) {
   currentTimeout = timeout;
   return(GOOD);
}

/****************************************************************************
**
**  SdatDisplayMember
**
*****************************************************************************/
RETCODE SdatDisplayMember(U32 index) {
   S8 buff[256], buff1[300];
   U16 i;
   BOOLEAN   abortFromEsc;
   RETCODE  err;


   if(index >= NUM_OF_SD_MEMBERS) return(ER_SD_INDEX);
   wsprintf(buff,
      "%lx: %s, h:%s, f:%s, err: %ld",
      index, MEMBER_NAME(index),
         (MEMBER_HOST_ACCESS(index)==MEMBER_READ_ONLY) ? "rd":
         (MEMBER_HOST_ACCESS(index)==MEMBER_WRITE_ONLY) ? "wr":
         (MEMBER_HOST_ACCESS(index)==MEMBER_READ_WRITE) ? "rd/wr":
         "unknown",
         (MEMBER_FW_ACCESS(index)==MEMBER_READ_ONLY) ? "rd":
         (MEMBER_FW_ACCESS(index)==MEMBER_WRITE_ONLY) ? "wr":
         (MEMBER_FW_ACCESS(index)==MEMBER_READ_WRITE) ? "rd/wr":
         "unknown",
         MEMBER_RETCODE(index));
   switch(MEMBER_NUM_BYTES(index)) {
      case 1:
         wsprintf(buff1, "%s, data: %02.2x",
            buff, *((U8 *)MEMBER_DATA(index)));
         SendMessageToCli(buff1);
         break;
      case 2:
         wsprintf(buff1, " %s, data: %04x",
            buff, *((U16 *)MEMBER_DATA(index)));
         SendMessageToCli(buff1);
         break;
      case 4:
         wsprintf(buff1, "%s, data: %08lx",
            buff, *((U32 *)MEMBER_DATA(index)));
         SendMessageToCli(buff1);
         break;
      default:
         SendMessageToCli(buff);
         for(i=0; i<(U16)MEMBER_NUM_BYTES(index); i++) {
            wsprintf(buff, " data: %02.2x", *(((U8*)MEMBER_DATA(index))+i));
            SendMessageToCli(buff);

            err = TskCheckAbort(&abortFromEsc);
            if(err!=GOOD)  break;
            if (abortFromEsc!=0) break;
      }
   }
   return(GOOD);
}

/* get main window handle for application, for app-fatal errors */
RETCODE EXPORT SdSetMainHWnd(HANDLE hwnd) {
    mainWnd = hwnd;
    return(GOOD);
}

#pragma argsused
VOID FAR PASCAL SdsCheckBreakCause(DESCRIPTOR desc) {
   EMULATION_STATE emuState;
   time_t startTime;
   if (memberChanged[SDN_EMULATION_STATE] == 0) {
      startTime = time(NULL);
      do {
         if (SdnReadMember(SDN_EMULATION_STATE, (U8 FAR*)&emuState) != GOOD)
            return;
         if (emuState != EM_RUNNING) break;
      } while ((startTime + currentTimeout) >= time(NULL));
   }
}

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