/****************************************************************************
**
**  Name:  cpu.c
**
**  Description:
**     Routines to access CPU registers and signals.
**
**  Status:  TESTED
**
**  $Log:   S:/tbird/mt2_186/cpu/cpu.c_v  $
** 
**    Rev 1.3   21 Apr 1998 17:57:44   Eric
** Fixed bug of signals control.
** 
**    Rev 1.2   24 Mar 1998 11:34:20   Eric
** Add timer function for ACTOR, but not used now.
** 
**    Rev 1.0   16 Dec 1996 16:02:06   Judy
** Initial revision.
** 
**    Rev 1.61   20 Oct 1994 14:57:00   dennis
** Added code to check for setting EIP to a value greater than the CS
** register limit.
** 
**    Rev 1.60   11 Oct 1994 09:37:28   dennis
** Modified SetRegAddr to only call CpuSetRegister for the CS register if the
** segment type is not ADDR_USE_CS. We do not want to set the base, AR, etc if
** the type is ADDR_USE_CS.
** 
**    Rev 1.59   26 Sep 1994 14:39:34   ernie
** Added support for config ignoreHLDA
** 
**    Rev 1.58   20 Sep 1994 10:54:40   dennis
** Made change to SetRegAddr to set CS selector or SS selector if the PC
** or SP is changed.
** 
**    Rev 1.57   16 Sep 1994 16:01:22   dennis
** Modified SetPmode routine to specify pmode variable to be of type PMODE
** rather than U8. This caused a problem because the upper byte of information
** that the shared data member expected was full of garbage.
** 
**    Rev 1.56   14 Sep 1994 08:30:14   dennis
** Fixed PPR 9647, 9648. If the granularity bit was set, the limit was not
** being scaled. The access rights register was using info from the descriptor,
** changed the code to read the AR register and only set the AR byte, D and G
** bits.
** 
**    Rev 1.55   06 Sep 1994 10:21:30   dennis
** Modified code that set access rights to deal with 32-bit arithmetic
** instead of 16-bit. Also added code to set the PMODE shared data member
** when EFLAGS, CR0 and CS access rights D bit is modified.
** 
**    Rev 1.55   06 Sep 1994 08:48:06   dennis
** Modified the code that set the access rights to use 32-bit arithmetic
** instead of 16 bit. Also added code to set PMODE shared data member when
** EFLAGS, CR0 and the CS access right D bit is modified.
** 
**    Rev 1.54   19 Aug 1994 16:52:36   dennis
** Added protected mode checks for the cpu registers.
** 
**    Rev 1.53   28 Jul 1994 11:16:42   dennis
** Made changes to support multiplexed PMODE with CR0.PE bit and EFLAGS.VM
** bit.
** 
**    Rev 1.52   21 Jul 1994 10:50:48   marilyn
** Revised the interface to AdrGet/SetSegmentSelector routines.
** 
**    Rev 1.51   24 Jun 1994 08:22:06   dennis
** Added support for registers AX, AH, AL, BX, BH, BL, CX, CH, CL, DX, DH,
** DL, DI, SI, IP, SP, BP.
** 
**    Rev 1.50   16 Jun 1994 13:54:40   dennis
** Made changes to support new address descriptor requirements. Set descriptor
** ADDR_USE_CS or ADDR_USE_SS and set the segment value (used to be
** ADDR_USE_VALUE).
** 
**    Rev 1.49   15 Jun 1994 18:00:14   joyce
** 1. Fixed a bug where failed to update PC correctly.
** 2. Destroy address descriptors when errors happen.
** 
**    Rev 1.48   10 Jun 1994 15:48:20   dennis
** Changed GetRegAddr to get the CS or SS register value and to set the
** descriptor segment type to ADDR_USE_VALUE insead of ADDR_USE_CS or
** ADDR_USE_SS respectively. The descriptor segment value is also set to the
** register value read (instead of 0 as previously coded).
** 
**    Rev 1.47   08 Jun 1994 08:35:38   dennis
** Added code to GetRegAddr and SetRegAddr to handle 80386 real mode. This
** code sets and gets the address descriptor segment selector values.
** 
**    Rev 1.46   02 Jun 1994 14:05:20   dennis
** Added code to check for a global flag that is set if the cpu.cfg file
** could not be found. If the global flag is TRUE, don't do anything.
** Other code has been added that will cause PowerViews to exit eventually.
** 
**    Rev 1.45   19 May 1994 16:30:14   dennis
** Made changes to support register index and visibility fields added to
** the cpu.cfg file.
** 
**    Rev 1.44   18 May 1994 14:30:50   ernie
** Revised formatting to comply with coding standards.
** 
**    Rev 1.43   18 May 1994 08:27:12   dennis
** Added changes to support 386, 040 emulators. Also modified the routine that
** handles the cpu"proc".cfg file. The signal names used to have to be in the
** same order in the cpu"proc".cfg file as they were in the sdprobe.h file. I
** added a value after the signal name in the .cfg file that is an offset from
** the base signal register value (SD_SIG00).
** 
**    Rev 1.42   06 Apr 1994 16:38:24   nghia
** Fixed Address comparison problem.
** CpuGetPC, CpuGetSP and CpuGetFrameptr() will return the address descriptor
** of whataver the current address space of the processor.  Called AdrSet
** CurrentAddrSpace() to set the appropriate address space.
** Before: the default address space for MOTO is SPACE_SD - which is data space!
** 
**    Rev 1.41   30 Mar 1994 11:34:12   john
** Added changes for 360
** 
**    Rev 1.40   03 Mar 1994 15:48:18   john
** Added GetSignalName, and GetNumberOfSignals
** 
**    Rev 1.39   01 Nov 1993 09:51:40   nghia
** Fixed bug - Invalid register index for register pairs.
** Index off by 1.
** 
**    Rev 1.38   14 Jun 1993 12:22:06   MINDY
**  Added support for register pairs.  There's a new routine that the loader
**  uses to "register" register pairs.  Also now the frame pointer can change
**  since each compiler might use a different register as the frame pointer.
** 
**    Rev 1.37   30 Mar 1993 08:28:26   ernie
** 1. Added functions needed by the new generic Actor code.
** 2. Removed tab characters.
** 3. Added code to support cpu16.
** 
**    Rev 1.36   05 Jan 1993 15:51:58   courtney
** Removed MemAlloc and related macros (no longer used).
** 
**    Rev 1.35   09 Oct 1992 14:55:42   doug
** add reset of cpu only (ppr6141)
** 
**    Rev 1.34   17 Sep 1992 11:04:24   doug
** disallow setting certain registers to odd values
** 
**    Rev 1.33   10 Sep 1992 11:45:56   doug
** change edit to remove multiple events
** 
**    Rev 1.32   08 Sep 1992 14:32:24   doug
** Changed breakpoint events.
** 
**    Rev 1.31   08 Sep 1992 09:36:06   doug
** change to new events.h event structure
** 
**    Rev 1.30   02 Sep 1992 10:43:10   doug
** emulation should be halted to set registers (no run access support).
** Leave the get registers as readable; if in emulation, the value is that
** which it was when emulation began.
** 
**    Rev 1.29   02 Sep 1992 10:30:12   doug
** a) add error check for finding reg name when index out of range
** b) boundary error in checking for register index corrected
** 
**    Rev 1.28   29 Aug 1992 07:23:02   doug
** Allow registers to be set/get while emulating.  This will avoid the
** subtle multi-tasking problems of trying to read the registers when
** emulation is about to start/stop.
** 
**    Rev 1.27   19 Aug 1992 07:15:52   doug
** add new events for when registers edited (Marilyn's request)
** 
**    Rev 1.26   14 Aug 1992 16:01:14   doug
** removed warnings
** 
**    Rev 1.25   13 Aug 1992 13:59:42   doug
** do not generate events while high level breakpoints are active (lots of
** low level breaks cause many, many events)
** 
**    Rev 1.24   06 Aug 1992 08:23:14   doug
** a) remove double event notification
** b) correct FP copy/paste error
** 
**    Rev 1.23   31 Jul 1992 13:41:14   doug
** a) init server is local (called by libmain)
** b) no register commands allowed while emulating
** c) signal set is ok while emulating
** 
**    Rev 1.22   31 Jul 1992 11:34:56   mindy
** added static to prevent server from initializing twice
** 
**    Rev 1.21   29 Jul 1992 14:41:36   john
** Changed all calls to MakeProcInstance use the dll instance
** 
**    Rev 1.20   23 Jul 1992 13:08:00   doug
** reset result was not being used and event errors were being ignored
** 
**    Rev 1.19   22 Jul 1992 16:05:50   doug
** a) calling from cli should not be special (bug)
** b) reset cpu name was wrong (resetting whole box!)
** c) indices wrong
** 
**    Rev 1.18   21 Jul 1992 12:35:12   doug
** a) the program counter (PC), stack pointer (SP), and frame pointer (FP)
** are now the 1st, 2nd, and 3rd entry in list.  This position allows the
** code to be completely processor independent.
** b) alreadyTerminated was being set true in the wrong place (init) - fixed
** 
**    Rev 1.17   21 Jul 1992 08:01:38   doug
** a) removed aliasing, now handled by firmware
** b) no 68030 files (still more generic work left)
** c) no non-runtime configuration file support anymore
** d) new generic shared data members
** e) no h/w init routine (firmware sets all values on boot up)
** f) no floating point support for now
** 
**    Rev 1.16   14 Jul 1992 12:34:22   courtney
** Fixed #ifdef bracketing.
** 
**    Rev 1.15   14 Jul 1992 12:11:32   courtney
** Reads processor-configuration table at startup.
** 
**    Rev 1.14   23 Jun 1992 15:44:48   courtney
** Include new header file, bkroot.h.
** 
**    Rev 1.13   23 Jun 1992 14:29:38   courtney
** Revised breakpoint status check to be BkProcessorMustBeHalted.
** 
**    Rev 1.12   18 May 1992 12:25:44   mindy
** reset and init register commands timing out
** 
**    Rev 1.11   14 May 1992 14:47:32   nghia
** Revised for new ErrorText interface.
** 
**    Rev 1.10   13 May 1992 09:00:58   nghia
** Added CpuGetErrorText() to support centralize error text handler.
** 
**    Rev 1.9   11 May 1992 12:55:08   nghia
** Rename ProcessorMustBeHalted to BxProcessorMustBeHalted.
** 
**    Rev 1.8   23 Apr 1992 12:09:54   nghia
** Fixed bug for Special floating-point registers FPSR, FPIAR, FPCR.
** Fixed PPR 5390. Reset - Send result to CLI, before propagate events.
** 
**    Rev 1.7   17 Apr 1992 16:16:00   nghia
** Fixed PPR 5428 - Check emulation status before set register.
** Added CLI command for FindRegisterName.
** Fixed bug in CPUFindRegisterName.
** Fixed bug in send message to CLI.
** 
**    Rev 1.6   08 Apr 1992 10:47:14   nghia
** Fixed the following PPR:
** - #5391: missing FPIAR register.
** - #5392: Set CCR register will overwrite the upper byte in SR register.
** Added routine to return register width masking pattern.
** 
**    Rev 1.5   03 Apr 1992 16:44:34   nghia
** Switched order of event register.
** 
**    Rev 1.4   02 Apr 1992 16:15:42   nghia
** Added separate events for the PC and SP registers changed.
** Fixed for PPR5279.
** 
**    Rev 1.3   27 Mar 1992 10:44:16   jim
** Fixed symbol name "EVENT_HL-BKPTEXEC_EMUL_STARTED AND HALTED.
** 
**    Rev 1.2   05 Mar 1992 13:22:38   doug
** Missing end comment commented out important lines
** 
**    Rev 1.1   01 Mar 1992 08:33:36   nghia
** Fixed bug in get register width routine.
** 
**    Rev 1.0   28 Feb 1992 11:56:52   nghia
** Initial revision.
**
**  $Header:   S:/tbird/mt2_186/cpu/cpu.c_v   1.3   21 Apr 1998 17:57:44   Eric  $
**
**  Copyright (C) 1991 Microtek International.  All rights reserved.
**
*****************************************************************************/

		     /****************************
		     *                          *
		  *       INCLUDE FILES      *
		  *                          *
		  ****************************/
#include <string.h>
#include <ctype.h>

#ifndef _BASEWIND_
#include "basewind.h"   /* basetype */
#endif

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

#ifndef _BKPTEXEC_
#include "bkptexec.h"
#endif

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

#ifndef _CLIULIB_
#include "cliulib.h"
#endif

#ifndef _CPU_
#include "cpu.h"        /* CPU Server */
#endif

#ifndef _CPUCLI_
#include "cpucli.h"     /* CPU Server cli and Internal data structures */
#endif

#ifndef _CPUERR_
#include "cpuerr.h"
#endif

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

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

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

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

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

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

#ifndef SHARED_DATA_MEMBERS
#include "members.h"
#endif

RETCODE EXPORT Sds2AbiSetReg(U16 regId, U32 regValue);
RETCODE  EXPORT Sds2AbiSetControl(U16 control);
RETCODE  EXPORT Sds2AbiGetControl(U16 *control);

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

/*
*** IMPORTANT NOTES: The order of registers in the cpuRegTable and signalTable
*** are designed to be matched exactly with the order of their enumurate types
*** (REG_NAME and SIG_NAME - defined in the 68030.h file.) Therefore, any ordering
*** change of these tables must ensure a corresponding change of its enum types
*** order. - MAX_REGISTERS and MAX_SIGNALS defined in CPUCLI.H
 */

/* static data */
REGISTER_ENTRY *regTable;   /* registers */
SIGNAL_ENTRY *signalTable;  /* signals */
U16 nRegisters;             /* number of registers */
U16 nSignals;               /* number of signales */

#define MAX_PAIRS_SUPPORTED 10      /* 10 pair should be enough */
U16 nRegPairs=0;                    /* number of assigned register pairs */
REGISTER_PAIR_ENTRY regPairTable[MAX_PAIRS_SUPPORTED]; 

REG_ID framePtrReg = 2;             /* default frame pointer is index 2 */
PROCESSOR_FAMILY procFamily = FAMILY_UNKNOWN;
	  
DESCRIPTOR emulStateDesc = NULL, resetDesc = NULL;
DESCRIPTOR emuHaltedDesc = NULL;
DESCRIPTOR pcRegDesc = NULL, spRegDesc = NULL, fpRegDesc = NULL;

/* CPU Default Notified Events */
BOOLEAN alreadyTerminated = FALSE;


	       /****************************
	       *                           *
	       *    EXTERNAL VARIABLES     *
	       *                           *
	       ****************************/
/* Flag to turn off event propagation when call reset from CLI */
extern HANDLE  hLib;
extern U16     cpuFatalError;

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

RETCODE PRIVATE GetRegAddr(REG_ID regId, DESCRIPTOR FAR *desc);
RETCODE PRIVATE SetRegAddr(REG_ID regId, DESCRIPTOR desc);
RETCODE PRIVATE HandleCpu16(U16 regIdIndex);
RETCODE PRIVATE HandleCpu32(U16 regIdIndex);
RETCODE PRIVATE HandleCpu68040(U16 regIdIndex);
RETCODE PRIVATE HandleCpu80386(U16 regIdIndex, U32 regValue, SEGMENT_DESCRIPTOR segDesc);
RETCODE PRIVATE HandleCpu80186(U16 regIdIndex, U32 regValue, SEGMENT_DESCRIPTOR segDesc);
RETCODE PRIVATE CheckEIPLimit(U32 regValue);
RETCODE PRIVATE FillInHiddenDescriptor(U16 reg, SEGMENT_DESCRIPTOR segDesc);
RETCODE PRIVATE CheckDataSegment(SEGMENT_DESCRIPTOR segDesc, U16 rpl);
RETCODE PRIVATE CheckStackSegment(SEGMENT_DESCRIPTOR segDesc);
RETCODE PRIVATE CheckCodeSegment(SEGMENT_DESCRIPTOR segDesc);
RETCODE PRIVATE CheckLdtrSegment(SEGMENT_DESCRIPTOR segDesc);
RETCODE PRIVATE CheckTrSegment(SEGMENT_DESCRIPTOR segDesc);
RETCODE PRIVATE SetPmode(VOID);

/* Internal callback routine */
VOID EXPORT CpuEmulHaltedCallback(U32 event);

RETCODE PRIVATE CpuInitIgnoreHLDA(VOID);

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

/* See CPUCLI.C for Cpu Server's CLI commands */
/*****************************************************************************
**
**    InitCpuServer
**
*****************************************************************************/
static BOOLEAN initCpuServer = FALSE;
RETCODE EXPORT InitCpuServer(VOID) {
   RETCODE err = GOOD;
   U16 i;
   FARPROC lpCallback;

   if( initCpuServer ) return(GOOD);
   initCpuServer = TRUE;

   if ((err = SdnRegister(SDN_EMULATION_STATE, NULLPTR,&emulStateDesc))!=GOOD)
      return(err) ;
   if ((err = SdnRegister(SDN_RESET_CPU, NULLPTR, &resetDesc)) != GOOD)
      return(err) ;

   /* read in processor-specific configuration, and build register table
      and signal table */
   if ((err = BuildTables()) != GOOD) return(err);

   /* Initialize all register descriptors */
   for (i = 0; i < nRegisters; i++) {
      /* Register with ShareData Server for its descriptor */
	  /* Modified code by Dennis Lamb May 19, 1994. Replaced following  */
	  /* line of code so that the register index value added to the     */
	  /* cpu.cfg file is now used rather than the position of the       */
	  /* register in the cpu.cfg file.                                  */
	  /* if ((err = SdnRegister(SDN_REG00+i, NULLPTR,                   */
      /* (DESCRIPTOR FAR *)&regTable[i].sdDesc)) != GOOD) return(err);  */

      if ((err = SdnRegister(SDN_REG00+regTable[i].regIndex, NULLPTR,
	 (DESCRIPTOR FAR *)&regTable[i].sdDesc)) != GOOD) return(err);
   }

   /* Initialize all signal descriptors */
   for (i = 0; i < nSignals; i++) {
      /*
      ** A change was made to add the "+signalTable[i].sigVal" instead of
      ** "+i". This was done to help support the 80386 signals. The      
      ** various flavors of the 80386 have different signals. The CPU.cfg
      ** file used to have to have the signals in the same order as they 
      ** were defined in the SDPROBE.H file. This would make the SDPROBE.H
      ** file a mess to maintain, there could not be any holes in the     
      ** SDN_SIG list, the 80386 signals really mess this up. So, .sigVal 
      ** was added to the signal structure. This value is read from the   
      ** CPU.cfg file and is the index from the SDN_SIG00 value for the   
      ** given signal.
      */
      if ((err = SdnRegister(SDN_SIG00+signalTable[i].sigVal, NULLPTR,
	 &signalTable[i].sdDesc)) != GOOD) return(err);
   }

   lpCallback = MakeProcInstance((FARPROC)CpuEmulHaltedCallback, hLib);
   if ((err = EnlRegister((U32)EVENT_BKPT_HALTED,
      (EVCALLBACK)lpCallback, (DESCRIPTOR FAR *)&emuHaltedDesc)) != GOOD)
      return(err);

   /* Set the global variable "family" to the processor family type */
   /* FAMILY_X86, FAMILY_68K, etc.                                  */
   if ((err = ProcReturnProcFamily(&procFamily)) != GOOD) return(err);

   if ((err = CpuInitIgnoreHLDA()) != GOOD) return(err);

   return(GOOD);
}

/*****************************************************************************
**
**    TerminateServer
**
*****************************************************************************/
RETCODE EXPORT TerminateServer(VOID) {
   RETCODE err = GOOD;
   U16 i;

   /* Check global flag to avoid terminatte being called more than one */
   if (alreadyTerminated)
      return(GOOD);
   
   /* do first, in case of error... don't try again */
   alreadyTerminated = TRUE;

   /* Unregister Event -- If can not unregister, error out */
   if (emuHaltedDesc && ((err = EnlUnRegister(emuHaltedDesc)) != GOOD))
      return(err);

   /* Unregister descriptors */
   if (emulStateDesc && ((err = SdUnRegister(emulStateDesc)) != GOOD))
      return(err);
   if (resetDesc && ((err = SdUnRegister(resetDesc)) != GOOD)) return(err);

   /* Unregister all registered members of the Cpu Server */
   for (i = 0; i<nRegisters; i++) {
      /* Unregister its descriptor */
      if (regTable[i].sdDesc &&
	 ((err = SdUnRegister((DESCRIPTOR) regTable[i].sdDesc)) != GOOD))
	 return(err);
   }
   for (i = 0; i<nSignals; i++) {
      /* Unregister its descriptor */
      if (signalTable[i].sdDesc &&
	 ((err = SdUnRegister((DESCRIPTOR) signalTable[i].sdDesc)) != GOOD))
	 return(err);
   }
   return(GOOD);
}

/*****************************************************************************
**
**    CpuGetNumRegs
**
*****************************************************************************/
RETCODE EXPORT CpuGetNumRegs(U16 FAR *numRegs) {

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   *numRegs = nRegisters;
   return(GOOD);
}

/*****************************************************************************
**
**    CpuGetRegister
**
*****************************************************************************/
RETCODE EXPORT CpuGetRegister(REG_ID regId, U32 FAR *regValue) {
   REG_ID curId=regId;
   RETCODE err;
   U32 curVal=0L;
   U32 size, val;

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);
   if (regId >= (nRegisters+nRegPairs)) return(ER_INVALID_REGID);
   if( regId >= nRegisters ) {
      if((err=SdReadMember(
	 regTable[regPairTable[(regId-nRegisters)][0]].sdDesc,
	 (U8 FAR *)&curVal))!=GOOD) return(err);
      curId = regPairTable[(regId-nRegisters)][1];
      curVal <<= 16;
   }
   if (regTable[regId].multiplexed == TRUE) {
      curId = regTable[regId].regIndex;
   }
   if((err=SdReadMember(regTable[curId].sdDesc,(U8 FAR *)regValue))!=GOOD)
      return(err);

   if (regTable[regId].multiplexed == TRUE) {
      size = (U32) ((1L << regTable[regId].regWidth) - 1);
      val = *regValue;
      *regValue = ((val >> regTable[regId].startBit) & size);
   }

   // if handling register pairs...
   if (regId >= nRegisters) *regValue = curVal | (*regValue & 0xffffL);
   return(GOOD);
}

/*****************************************************************************
**
**    CpuSetRegister
**
*****************************************************************************/
RETCODE EXPORT CpuSetRegister(REG_ID regId, U32 regValue) {
   RETCODE err;
   REG_ID curReg;
   PROC_CPU cpu;
   U8 width;
   U16 regIdIndex;
   U32 oldValue, mask, val;
   PMODE   pmode;
   U16     regIdValue;
   U16     reg16Value;
   U16     rpl;
   S8      buf[80];
   DESCRIPTOR desc = NULL;
   SEGMENT_DESCRIPTOR segDesc;

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   /* Modifed by Dennis Lamb May 19, 1994. regIdIndex was added, all */
   /* references to regIdIndex used to be regId. regId is an index   */
   /* into the regTable. Code has been added to use the register     */
   /* index value that was added to the cpu.cfg file format. This    */
   /* means that the register list in the cpu.cfg file does not have */
   /* to match that in the sdprobe.h file (that is to say that the   */
   /* same number of registers defined in the sdprobe.h file does    */
   /* not have to be matched by the cpu.cfg file.                    */
   regIdIndex = regTable[regId].regIndex;

   if (regId >= (nRegisters+nRegPairs)) return(ER_INVALID_REGID);
   if((err=CpuGetRegisterWidth(regId, &width))!=GOOD) return(err);
   if( width < 32) {
      if (regValue >= (1L << width)) return(ER_REG_OUT_OF_RANGE);
   }
   /* emulation must be halted to set registers */
   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD) return(err);
 
   if ((err = ProcReturnCpu(&cpu)) != GOOD) return(err);
   switch(cpu) {
      case PROC_CPU_68000:
      case PROC_CPU_CPU32:
      case PROC_CPU_CPU32P:
	 /* PC, A7, SSP, and USP cannot be set to odd address */
	 if(( (regIdIndex==SDN_PC-SDN_REG00) || (regIdIndex==SDN_A7-SDN_REG00)
	     || (regIdIndex==SDN_USP-SDN_REG00) || (regIdIndex==SDN_SSP-SDN_REG00))
	     && ((regValue&1)!=0)) return(ER_CPU_CANT_BE_ODD);
	 break;

      case PROC_CPU_68040:
	 /* PC, A7, SSP, and USP cannot be set to odd address */
	 if(( (regIdIndex==SDN_PC-SDN_REG00) || (regIdIndex==SDN_A7-SDN_REG00)
	   || (regIdIndex==SDN_ISP-SDN_REG00) || (regIdIndex==SDN_MSP-SDN_REG00))
	   && ((regValue&1)!=0)) return(ER_CPU_CANT_BE_ODD);
	 break;

      case PROC_CPU_CPU16:
	 /* PK:PC and IP cannot be set to odd address */
	 if(( (regIdIndex==SDN_PCR-SDN_REG00) || (regIdIndex==SDN_IP-SDN_REG00))
	   && ((regValue&1)!=0)) return(ER_CPU_CANT_BE_ODD);
	 break;

      case PROC_CPU_80386:
	 /* We are dealing with Intel processors, get the processor mode. */
	 if ((err = AdrGetPmode(&pmode,buf)) != GOOD) return(err);
	 regIdValue = regIdIndex+SDN_REG00;

	 if (pmode == PMODE_REAL) {
	    /* Can not set the TR register if in REAL mode */
	    if (regIdValue == SDN_TR)
	       return(ER_TR_REAL_MODE_ERR);
	    /* Can not set the LDTR register if in REAL mode */
	    if (regIdValue == SDN_LDTR)
	       return(ER_LDTR_REAL_MODE_ERR);
	 } /* if */

	 if (regIdValue == SDN_EIP) {
	    if ((err = CheckEIPLimit(regValue)) != GOOD)
	       return(err);
	 }

	 if ((pmode != PMODE_V86) && (pmode != PMODE_REAL)) {
	    /* Only do these checks if in protected mode */
	    reg16Value = regValue & 0xFFFF;

	    /* We only want to do the following checking if */
	    /* we are setting CS, SS, DS, ES, FS or GS.     */
	    if ((regIdValue >= SDN_CS) && (regIdValue <= SDN_SS)
	       || (regIdValue == SDN_TR) || (regIdValue == SDN_LDTR)) {           
			    
	       /* Check for null selector. */
	       if (reg16Value < 4) {
		  /* CS register can not be set to a null selector. */
		  if (regIdValue == SDN_CS)
		     return(ER_CS_HAS_NULL_SELECTOR);
		  /* SS register can not be set to a null selector. */
		  if (regIdValue == SDN_SS)
		     return(ER_SS_HAS_NULL_SELECTOR);
	       } else {
		  /* Make sure the selector points to GDT for LDTR reg */
		  if (((reg16Value & 4) != 0) && (regIdValue == SDN_LDTR))
		     return(ER_LDTR_DESC_NOT_IN_GDT);

		  /* Make sure the selector points to GDT for TR reg */
		  if (((reg16Value & 4) != 0) && (regIdValue == SDN_TR))
		     return(ER_TR_DESC_NOT_IN_GDT);

		  /* Allocate an address descriptor for the Caller */
		  if ((err = AdrCreateAddress(&desc)) != GOOD) {
		     desc = NULL;
		     return(err);
		  } /* if */

		  /* Set the descriptor selector information */
		  if ((err = AdrSetAddrSegmentSelector (desc, ADDR_USE_VALUE,
			      (U16 FAR *)&reg16Value)) != GOOD) {
		     AdrDestroyAddress(desc);
		     return(err);
		  } /* if */

		  /* Now read the segment descriptor information */
		  if ((err = AdrGetSegmentDescCopy(desc, &segDesc)) != GOOD) {
		     AdrDestroyAddress(desc);
		     return(err);
		  } /* if */

		  /* We do not need the address descriptor, so get rid of it */
		  AdrDestroyAddress(desc);
 
		  switch (regIdValue) {
		     case SDN_CS: {
			/* Now check to make sure the selector points */
			/* to a valid descriptor for the CS.          */
			if ((err = CheckCodeSegment(segDesc)) != GOOD)
			   return(err);
			break;
		    } /* end case */

		     case SDN_SS: {
			/* Now check to make sure the selector points */
			/* to a valid descriptor for the SS.          */
			if ((err = CheckStackSegment(segDesc)) != GOOD)
			   return(err);
			break;
		     } /* end case */

		     case SDN_DS:
		     case SDN_ES:
		     case SDN_GS:
		     case SDN_FS: {
			/* Now check to make sure the selector points */
			/* to a valid descriptor for the segment.     */
			rpl = regValue & 3;
			if ((err = CheckDataSegment(segDesc,rpl)) != GOOD)
			   return(err);
			break;
		     } /* end case */

		     case SDN_LDTR: {
			/* Now check to make sure the selector points */
			/* to a valid descriptor for the CS.          */
			if ((err = CheckLdtrSegment(segDesc)) != GOOD)
			   return(err);
			break;
		    } /* end case */

		     case SDN_TR: {
			/* Now check to make sure the selector points */
			/* to a valid descriptor for the CS.          */
			if ((err = CheckTrSegment(segDesc)) != GOOD)
			   return(err);
			break;
		    } /* end case */
		  } /* end switch */
	       } /* else */
	    } /* if */
	 } /* if */
	 break;
      }  /* end switch */
   
   /* Write the shared data member */
   curReg = regId;
   if( regId >= nRegisters ) {
      // write MSB into first register
      U32 tmpVal = regValue >> 16; 
      if((err = SdWriteMember(
	 regTable[regPairTable[(regId-nRegisters)][0]].sdDesc,
	 (U8 FAR *) &tmpVal,GOOD)) != GOOD) return(err);
      // setup common variables to write LSB into second register.
      curReg = regPairTable[(regId-nRegisters)][1];
      regValue &= 0xffffL;
   }

   /* See if we are dealing with a multiplexed register (i.e. AX, AH and */
   /* AL are registers multiplexed with EAX).                            */
   if (regTable[regId].multiplexed == TRUE) {
      /* Get the register value, for example EAX */
      if((err=SdReadMember(regTable[regId].sdDesc,(U8 FAR *)&oldValue))!=GOOD)
	 return(err);
      /* Now set the mask for masking out bits we will set */
      mask = 0L;
      if (width == 16)
	 /* Dealing with 16 bit register, for example AX */
	 mask = 0xffff0000L;
      else if (width == 8) {
	 if (regTable[regId].startBit == 8)
	    /* 8 bit register, for example AH */
	    mask = 0xffff00ffL;
	 else
	    /* 8 bit register, for example AL */
	    mask = 0xffffff00L;
      }
      /* Mask out the bits we will set later */
      oldValue &= mask;

      /* get the register value in the correct bit position (AH needs to */
      /* be shifted over 8 bits, for all others the startBit will be 0.  */
      val = (regValue << regTable[regId].startBit);

      /* Or in the register information */
      regValue = val | oldValue;
   }

   if((err = SdWriteMember(regTable[curReg].sdDesc, (U8 FAR *) &regValue,
      GOOD)) != GOOD) return(err);

   /* check frame ptr register */
   if( regIdIndex == framePtrReg ) {
      if((err = EnlEventNotify(EVENT_CPU_FP_EDIT))!=GOOD) return(err);
   }

   /* check special registers */
   switch (cpu) {
      /*
      ** cpu16 register coupling
      **   ip -> pc, ccr
      **   pc -> ip, ccr
      **  ccr -> ip, pc
      */
      case PROC_CPU_CPU16:
	 if ((err = HandleCpu16(regIdIndex)) != GOOD)
	    return(err);
	 break;  /* end of cpu16 case */

      /*
      ** cpu32,cpu32p, 68000 register coupling
      **   a7 -> ssp or usp
      **  ssp -> a7
      **  usp -> a7
      **   sr -> a7
      */
      case PROC_CPU_68000:
      case PROC_CPU_CPU32:
      case PROC_CPU_CPU32P:
	 if ((err = HandleCpu32(regIdIndex)) != GOOD)
	    return(err);
	 break;   /* end of cpu32 case */
 
      /*
      ** 68040 register coupling
      **   a7 -> usp, isp or msp
      **  usp -> a7
      **  isp -> a7
      **  msp -> a7
      **   sr -> a7
      */
      case PROC_CPU_68040:
	 if ((err = HandleCpu68040(regIdIndex)) != GOOD)
	    return(err);
	 break;   /* end of 68040 case */

     /*
      ** 80386 segment registers (CS, DS, ES, FS, GS, SS)
      **   When ever a segment register is modified, its corresponding
      **   AR (access rights), LIMIT and BASE registers need to be updated.
      **   These registers are hidden registers that the 80386 automatically
      **   updates when a segment register is loaded, the emulator needs to
      **   simulate this operation.
      */
      case PROC_CPU_80386:
	 if ((err = HandleCpu80386(regIdIndex, regValue, segDesc)) != GOOD)
	    return(err);
	 break;  /* end of 80386 case */
      case PROC_CPU_80186:
	 if ((err = HandleCpu80186(regIdIndex, regValue, segDesc)) != GOOD)
	    return(err);
	 break;  /* end of 80186 case */
   }  /* end of switch(cpu) */
 
   /* always generate register edited */
   if((err = EnlEventNotify(EVENT_CPU_EDIT))!=GOOD) return(err);
      
   return(GOOD);
}

/*****************************************************************************
**
**    CpuGetFpRegister
**
*****************************************************************************/
#pragma argsused /* !!! temporary until implemented */
RETCODE EXPORT CpuGetFpRegister(REG_ID regId, FPREG_VAL FAR *regValue) {
   return(ER_FP_NOT_SUPPORTED); /* !!! */
}

/*****************************************************************************
**
**    CpuSetFpRegister
**
*****************************************************************************/
#pragma argsused /* !!! temporary until implemented */
RETCODE EXPORT CpuSetFpRegister(REG_ID regId, FPREG_VAL regValue) {
   return(ER_FP_NOT_SUPPORTED); /* !!! */
}
 
/*****************************************************************************
**
**    CpuGetPC
**
*****************************************************************************/
RETCODE EXPORT CpuGetPC(DESCRIPTOR FAR *desc) {
   RETCODE err;
   
   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   if ((err = GetRegAddr(0, desc)) != GOOD)
      return err;
   return AdrSetCurrentAddrSpace(*desc);
}

/*****************************************************************************
**
**    CpuSetPC
**
*****************************************************************************/
RETCODE EXPORT CpuSetPC(DESCRIPTOR desc) {
   RETCODE err;

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   if ((err = SetRegAddr(0, desc)) != GOOD)
      AdrDestroyAddress(desc);
   return err;
}

/*****************************************************************************
**
**    CpuGetSP
**
*****************************************************************************/
RETCODE EXPORT CpuGetSP(DESCRIPTOR FAR *desc) {
   RETCODE err;

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   if ((err = GetRegAddr(1, desc)) != GOOD)
      return err;
   return AdrSetCurrentAddrSpace(*desc);
}

/*****************************************************************************
**
**    CpuSetSP
**
*****************************************************************************/
RETCODE EXPORT CpuSetSP(DESCRIPTOR desc) {
   RETCODE err;

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   if ((err = SetRegAddr(1, desc)) != GOOD)
      AdrDestroyAddress(desc);
   return err;
}

/*****************************************************************************
**
**    CpuGetFramePtr
**
*****************************************************************************/
RETCODE EXPORT CpuGetFrameptr(DESCRIPTOR FAR *desc) {
   RETCODE err;

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   if ((err = GetRegAddr(framePtrReg, desc)) != GOOD)
      return err;
   return AdrSetCurrentAddrSpace(*desc);
}

/*****************************************************************************
**
**    CpuResetCPU
**
*****************************************************************************/
RETCODE EXPORT CpuResetCPU() {
   RETCODE        err;
   CPU_RESET      reset = RESET_CPU_AND_UPDATE_STATUS;
   BOOLEAN        timedOut = FALSE;

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   /* Write reset command and wait for reset result form the box */
   if((err = SdWriteCmdReadResponse(resetDesc, (U8 FAR *)&reset, GOOD,
      emulStateDesc, 0L, &timedOut))!=GOOD) return(err);

   /* Generate the HALTED event on reset; this will update everyone */
   if((err = EnlEventNotify(EVENT_CPU_HALTED))!=GOOD) return(err);

   return (GOOD);
}

/*****************************************************************************
**
**    CpuResetCPUOnly
**
*****************************************************************************/
RETCODE EXPORT CpuResetCPUOnly() {
   RETCODE err;
   CPU_RESET reset = ONLY_RESET_CPU;

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   /* emulation must be halted to reset only the cpu */
   if((err = BkProcessorMustBeHaltedAbsolute())!=GOOD) return(err);

   /* Write reset command and do not wait for any response */
   return(SdWriteMember(resetDesc, (U8 FAR *)&reset, GOOD));
}

/*****************************************************************************
**
**    CpuGetSignal
**
*****************************************************************************/
RETCODE EXPORT CpuGetSignal(SIG_ID sigId, U8 FAR *signalState) {
   RETCODE err;
   U16 tmpSignal;
   PROBE_TYPE specificProcessor;

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   if (sigId >= nSignals)
         return(ER_INVALID_SIGID);

   //Fixed bug for signals control by Eric 4/17/98
   if ((err = ProcReturnSpecificProcessor(&specificProcessor)) != GOOD)
      return(err);
   switch(specificProcessor) {
      case I80C186EB_MP:
      case I80C188EB_MP:
         if(sigId >= 7) sigId++;
         break;
      case I80C186EC_MP:
      case I80C188EC_MP:
         if(sigId >= 3) sigId += 5;
         break;
   }
   //end Eric 4/17/98

   /* Get signal state and return */
   if ((err=Sds2AbiGetControl(&tmpSignal)) != GOOD) return(err);
   *signalState = (tmpSignal >> sigId) & 1;
   return(GOOD);
}

/*****************************************************************************
**
**    CpuSetSignal
**
*****************************************************************************/
RETCODE EXPORT CpuSetSignal(SIG_ID sigId, U8 signalState) {
   RETCODE err;
   U16 tmpSignal;
   PROBE_TYPE specificProcessor;

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   if (sigId >= nSignals)
      return(ER_INVALID_SIGID);

   //Fixed bug for signals control by Eric 4/17/98
   if ((err = ProcReturnSpecificProcessor(&specificProcessor)) != GOOD)
      return(err);
   switch(specificProcessor) {
      case I80C186EB_MP:
      case I80C188EB_MP:
         if(sigId >= 7) sigId++;
         break;
      case I80C186EC_MP:
      case I80C188EC_MP:
         if(sigId >= 3) sigId += 5;
         break;
   }
   //end Eric 4/17/98

   /* Set the signal state and return */
   if ((err=Sds2AbiGetControl(&tmpSignal)) != GOOD) return(err);
   if (signalState) tmpSignal |= 1 << sigId;
   else tmpSignal &= ~(1 << sigId);
   return (Sds2AbiSetControl(tmpSignal));
}

/*****************************************************************************
**
**    CpuGetTimer //not supported
**
*****************************************************************************/
RETCODE EXPORT CpuGetTimer(U16 timerId, U8 FAR *timerState) {
   return(GOOD);
}

/*****************************************************************************
**
**    CpuSetTimer //not supported
**
*****************************************************************************/
RETCODE EXPORT CpuSetTimer(U16 timerId, U8 timerState) {
   return (GOOD);
}

/*****************************************************************************
**
**    CpuFindRegisterName
**
*****************************************************************************/
RETCODE EXPORT CpuFindRegisterName(REG_ID regId, LPSTR regName) {

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   if (regId < nRegisters) {
      if (lstrcpy(regName, (LPSTR)regTable[regId].regName) == NULL)
	 return(ER_FAILED_STRCPY);
   } else if (regId < (nRegisters+nRegPairs)) {
      if (lstrcpy(regName,
	 (LPSTR)regTable[regPairTable[(regId-nRegisters)][0]].regName)
	 == NULL) return(ER_FAILED_STRCPY);
      lstrcat(regName,":");
      if (lstrcat(regName,
	 (LPSTR)regTable[regPairTable[(regId-nRegisters)][1]].regName)
	 == NULL) return(ER_FAILED_STRCPY);
   }
   else return(ER_INVALID_REGID);
   return(GOOD);          
}

/*****************************************************************************
**
**    CpuGetRegisterInfo
**
*****************************************************************************/
RETCODE EXPORT CpuGetRegisterInfo(REG_ID regId, LPSTR regName,
      U16 FAR *sortIndex, U16 FAR *type, U16 FAR *width, U16 FAR *regIndex,
      U16 FAR *visibility) {

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   if (regId >= nRegisters) return(ER_INVALID_REGID);
   if (regName && (lstrcpy(regName, (LPSTR)regTable[regId].regName) == NULL))
      return(ER_FAILED_STRCPY);
   if (sortIndex) *sortIndex = regTable[regId].presenterLine;
   if (type) *type = regTable[regId].numberOfFlags;
   if (width) *width = regTable[regId].regWidth;

   /* Modified code by Dennis Lamb May 19, 1994.                     */
   /* Added these two parameters. The register index and visibility     */
   /* variables were added to the REGISTER_ENTRY structure.          */
   if (regIndex) *regIndex = regTable[regId].regIndex;
   if (visibility) *visibility = regTable[regId].visible;

   return(GOOD);
}

/*****************************************************************************
**
**    CpuGetRegisterValueText
**
*****************************************************************************/
RETCODE EXPORT CpuGetRegisterValueText(REG_ID regId,U16 type,LPSTR valueText) {
   RETCODE err;
   U16 i;
   U32 regValue;
   static S8 hex[] =
      { '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
   if ((err = CpuGetRegister(regId, &regValue)) != GOOD) return(err);
   if (type == 0) {
      U8 width;
      if((err=CpuGetRegisterWidth(regId, &width))!=GOOD) return(err);
      sprintf(valueText,"%0*lX",(width + 3)/4,regValue);
   } else {
      S8 *valuePtr = valueText;
      for (i=0; i<regTable[regId].numberOfFlags; i++) {
	 if (regTable[regId].flagTable[i].displayAsChar) {
	    *valuePtr++ = (regValue & regTable[regId].flagTable[i].mask)
	       ? regTable[regId].flagTable[i].assertedChar
	       : regTable[regId].flagTable[i].negatedChar;
	 } else {
	    *valuePtr++ =
	       hex[(U16)((regValue & regTable[regId].flagTable[i].mask)
	       >> regTable[regId].flagTable[i].shift)];
	 }
      }
      *valuePtr = '\0';
   }
   return(GOOD);
}

/*****************************************************************************
**
**    CpuFindRegisterId
**
*****************************************************************************/
RETCODE EXPORT CpuFindRegisterId(U8 FAR *refRegName, REG_ID FAR *regId) {
   U16 i;

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   /* Go through the register table and check for refRegName */
   for (i = 0; i<nRegisters; i++) {
      if (!lstrcmpi((LPSTR)refRegName, (LPSTR)regTable[i].regName)) {
	 *regId = i;
	 return(GOOD);
      }
   }
   return(ER_REGNAME_NOT_FOUND);
}

/*****************************************************************************
**
**    CpuGetRegisterWidth
**
*****************************************************************************/
RETCODE EXPORT CpuGetRegisterWidth(REG_ID regId, U8 FAR *regWidth) {

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   /* Check for valid register id */
   if (regId < nRegisters) {
      *regWidth = regTable[regId].regWidth;
      return(GOOD);
   }
   // Is it a register pair?
   if (regId < (nRegisters+nRegPairs)) {
      *regWidth = (regTable[regPairTable[regId-nRegisters][0]].regWidth
	 + regTable[regPairTable[regId-nRegisters][1]].regWidth);
      return(GOOD);
   }
   /* Bad register Id */
   return(ER_INVALID_REGID);
}

/****************************************************************************
**
**  CpuRegisterMultipleRegisters
**
*****************************************************************************/
RETCODE EXPORT CpuRegisterMultipleRegisters(REG_ID regList[2],
		   REG_ID FAR *assignedRegNum) {

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   if (nRegPairs == MAX_PAIRS_SUPPORTED )
      return(ER_TOO_MANY_PAIRS);
   regPairTable[nRegPairs][0] = regList[0];
   regPairTable[nRegPairs][1] = regList[1];
   // NOTES: Nghia - 10/28/93
   // Fixed bug - Index for register pairs is off by 1.
   // Max index for regular registers will be nRegister-1
   // Max index for register pairs is the (nRegisters + nRegPairs-1)
   // Increment the nRegPairs after assignment will keep track number of
   // register correctly without assigning an invalid register Index.
   *assignedRegNum = (nRegisters + nRegPairs);
   nRegPairs++;
   return(GOOD);
}

/****************************************************************************
**
**  CpuSetFramePtrRegister
**
*****************************************************************************/
RETCODE EXPORT CpuSetFramePtrRegister(REG_ID regId) {

   /* Don't do anything if the cpu.cfg file was not found. */
   if (cpuFatalError != 0)
      return(ER_BADGET);

   // Don't let register pairs be used as frame pointer.
   if (regId >= nRegisters) return(ER_INVALID_REGID);
   framePtrReg = regId;
   return(GOOD);
}

/*****************************************************************************
**
**    CpuGetFatalFlag
**
*****************************************************************************/
RETCODE EXPORT CpuGetFatalFlag(U16 FAR *fatalFlag) {
   *fatalFlag = cpuFatalError;
   return(GOOD);
}
		       /**************************** 
			*                          *
			*      LOCAL ROUTINES      *
			*                          *
			****************************/
/*****************************************************************************
**
**  CheckEIPLimit
**
**  Description:
**     Checks to make sure the EIP is not set to a value greater than its
**     limit.
***
**  Parameters:
**     input:
**        regValue value to set EIP to.
**
*****************************************************************************/
RETCODE PRIVATE CheckEIPLimit(U32 regValue)
{

   U32 limit;
   RETCODE err;

   /* Fill in the AR information */
   if ((err = SdnReadMember(SDN_CSLIMIT,(LPU8)&limit)) != GOOD)
      return(err);
   if (regValue > limit)
      return(ER_EIP_EXCEED_LIMIT);
   return(GOOD);
}

/*****************************************************************************
**
**  FillInHiddenDescriptor
**
**  Description:
**     Fills in the Access Rights (AR), Base, and limit values for
**     the hidden portions of the descriptors.
***
**  Parameters:
**     input:
**        reg      Id of the AR register.
**        segDesc  Structure that holds descriptor information
**
*****************************************************************************/
RETCODE PRIVATE FillInHiddenDescriptor(U16 reg, SEGMENT_DESCRIPTOR segDesc)
{

   U32 accessRights;
   U32 base;
   U32 limit;
   RETCODE err;

   /* Fill in the AR information */
   if ((err = SdnReadMember(reg,(LPU8)&accessRights)) != GOOD)
      return(err);
   accessRights = accessRights & 0xFF3F00FFL;
   accessRights |= (segDesc.segmentType << 8);
   accessRights |= (segDesc.descType << 12);
   accessRights |= (segDesc.descPrivLevel << 13);
   accessRights |= (segDesc.segmentPresent << 15);
   accessRights |= ((U32) segDesc.opSize << 22);
   accessRights |= ((U32) segDesc.granularity << 23);
	      
   if((err = SdnWriteMember(reg, (LPU8)&accessRights,GOOD)) != GOOD)
      return(err);

   /* Now write out the BASE information */
   reg++;
   base = (U32) segDesc.base15_0 | ((U32) segDesc.base23_16 << 16)
	  | ((U32) segDesc.base31_24 << 24);
   if((err = SdnWriteMember(reg, (LPU8)&base,GOOD)) != GOOD)
      return(err);

   /* Now write out the LIMIT information */
   reg++;
   limit = (U32) segDesc.limit15_0 | ((U32) segDesc.limit19_16 << 16);

   /* If the granularity bit is set, then scale the limit value by */
   /* a factor of 2 to the twelveth (0x0FFF). Otherwise the limit  */
   /* is just the 20 bit limit field.                              */
   if (segDesc.granularity == 1)
      limit = (limit << 12) + 0x0FFFL;
   if((err = SdnWriteMember(reg, (LPU8)&limit,GOOD)) != GOOD)
      return(err);
   return(GOOD);
}

/*****************************************************************************
**
**  CheckTrSegment
**
**  Description:
**     Checks for any error conditions
***
**  Parameters:
**     input:
**        segDesc  Structure that holds descriptor information
**
*****************************************************************************/
RETCODE PRIVATE CheckTrSegment(SEGMENT_DESCRIPTOR segDesc)
{

   BOOLEAN notTss;
   BOOLEAN TssBusy;
   BOOLEAN notPresent;
 
   /* Set some boolean flags that provide us with */
   /* some informtion about the descriptor.       */
   notTss = ((segDesc.descType == 1) ||
	  ((segDesc.segmentType != 1) && (segDesc.segmentType != 3)
	  && (segDesc.segmentType != 9) && (segDesc.segmentType != 11)));
   TssBusy = ((segDesc.segmentType == 3) || (segDesc.segmentType == 11));
   notPresent = (segDesc.segmentPresent == 0);

   /* Now check to see if the segment descriptor is data or */
   /* readable code.                                        */
   if (notTss)
      return(ER_NOT_A_TSS);

   if (TssBusy)
      return(ER_TSS_BUSY);
	    
   /* Now check to see if the segment descriptor is present */
   if (notPresent)
      return(ER_SEGMENT_NOT_PRESENT);

   return(GOOD);
}

/*****************************************************************************
**
**  CheckLdtrSegment
**
**  Description:
**     Checks for any error conditions
***
**  Parameters:
**     input:
**        segDesc  Structure that holds descriptor information
**
*****************************************************************************/
RETCODE PRIVATE CheckLdtrSegment(SEGMENT_DESCRIPTOR segDesc)
{

   BOOLEAN notLdt;
   BOOLEAN notPresent;
 
   /* Set some boolean flags that provide us with */
   /* some informtion about the descriptor.       */
   notLdt = ((segDesc.descType == 1) ||
	  (segDesc.segmentType != 2));
   notPresent = (segDesc.segmentPresent == 0);

   /* Now check to see if the segment descriptor is data or */
   /* readable code.                                        */
   if (notLdt)
      return(ER_NOT_A_LDT);
	    
   /* Now check to see if the segment descriptor is present */
   if (notPresent)
      return(ER_SEGMENT_NOT_PRESENT);

   return(GOOD);
}

/*****************************************************************************
**
**  CheckDataSegment
**
**  Description:
**     Checks for any error conditions
***
**  Parameters:
**     input:
**        segDesc  Structure that holds descriptor information
**
*****************************************************************************/
RETCODE PRIVATE CheckDataSegment(SEGMENT_DESCRIPTOR segDesc, U16 rpl)
{

   BOOLEAN data;
   BOOLEAN code;
   BOOLEAN notConforming;
   BOOLEAN readable;
   BOOLEAN notPresent;
 
   /* Set some boolean flags that provide us with */
   /* some informtion about the descriptor.       */
   data = ((segDesc.descType == 1) &&
	  ((segDesc.segmentType & 8) == 0));
   code = ((segDesc.descType == 1) &&
	  ((segDesc.segmentType & 8) != 0));
   readable = ((segDesc.segmentType & 2) != 0);
   notConforming = ((segDesc.segmentType & 4) == 0);
   notPresent = (segDesc.segmentPresent == 0);

   /* Now check to see if the segment descriptor is data or */
   /* readable code.                                        */
   if (!data && !(code && readable))
      return(ER_NOT_DATA_OR_CODE_READ);
	    
   if ((data || (code && notConforming)) &&
      (rpl > segDesc.descPrivLevel))
      return(ER_RPL_GREATER_THEN_DPL);
 
   /* Now check to see if the segment descriptor is present */
   if (notPresent)
      return(ER_SEGMENT_NOT_PRESENT);

   return(GOOD);
}

/*****************************************************************************
**
**  CheckStackSegment
**
**  Description:
**     Checks for any error conditions
***
**  Parameters:
**     input:
**        segDesc  Structure that holds descriptor information
**
*****************************************************************************/
RETCODE PRIVATE CheckStackSegment(SEGMENT_DESCRIPTOR segDesc)
{

   BOOLEAN notData;
   BOOLEAN notWritable;
   BOOLEAN notPresent;

   /* Set some boolean flags that provide us with */
   /* some informtion about the descriptor.       */
   notData = ((segDesc.descType != 1) ||
	  ((segDesc.segmentType & 8) != 0));
   notWritable = ((segDesc.segmentType & 2) == 0);
   notPresent = (segDesc.segmentPresent == 0);

   /* Check to make sure the descriptor is a data segment descriptor */
   if (notData)
      return(ER_DESC_NOT_DATA);

   /* Now check to see if the segment descriptor is writeable */
   if (notWritable)
      return(ER_DESC_NOT_WRITABLE);
	     
   /* Now check to see if the segment descriptor is present */
   if (notPresent)
      return(ER_SEGMENT_NOT_PRESENT);

   return(GOOD);
}

/*****************************************************************************
**
**  CheckCodeSegment
**
**  Description:
**     Checks for any error conditions
***
**  Parameters:
**     input:
**        segDesc  Structure that holds descriptor information
**
*****************************************************************************/
RETCODE PRIVATE CheckCodeSegment(SEGMENT_DESCRIPTOR segDesc)
{

   BOOLEAN notCode;
   BOOLEAN notPresent;

   /* Set some boolean flags that provide us with */
   /* some informtion about the descriptor.       */
   notCode = ((segDesc.descType != 1) ||
	     ((segDesc.segmentType & 8) == 0));
   notPresent = (segDesc.segmentPresent == 0);

   /* Check to make sure the descriptor is a code segment descriptor */
   if (notCode)
      return(ER_DESC_NOT_CODE);

   /* Now check to see if the segment descriptor is present */
   if (notPresent)
      return(ER_SEGMENT_NOT_PRESENT);

   return(GOOD);
}

/*****************************************************************************
**
**  SetPmode
**
**  Description:
**     Sets the shared data member pmode to the correct processor operating
**     mode (REAL, Virtual REAL, Protected-32, Protected-16).
***
**  Parameters:
**     none
**
*****************************************************************************/
RETCODE PRIVATE SetPmode(VOID) {

   PMODE   pMode;
   U32     cr0;
   U32     flags;
   U32     csAccess;
   RETCODE err;

   /* We need to set the PMODE to be the correct type. If the */
   /* CR0.PE bit (bit 0) is not set (0), then we are in real  */
   /* mode and it does not matter what the EFLAGS.VM bit is,  */
   /* we will do nothing. However if the CR0.PE bit is set (1)*/
   /* then we will set the PMODE to virtual 86 if the VM bit  */
   /* is set. If the VM bit is 0, then we have to check the CS*/
   /* access rights (AR) register's D bit (default bit) to    */
   /* see if we are in protected-32 (1) or protect-16 (0).    */
   if ((err = SdnReadMember(SDN_CR0,(LPU8)&cr0)) != GOOD)
      return(err);

   if ((cr0 & 1) == 0)
      /* CR0.PE bit is 0, set to REAL mode. */
      pMode = PMODE_REAL;
   else {
      if ((err = SdnReadMember(SDN_EFLAGS,(LPU8)&flags)) != GOOD)
	 return(err);
      if ((flags & 0x20000L) != 0)
	 /* EFLAGS.VM bit is 1, set to virtual 86 mode. */
	 pMode = PMODE_V86;
      else {
	 if ((err = SdnReadMember(SDN_CSAR,(LPU8)&csAccess)) != GOOD)
	    return(err);
	 if ((csAccess & 0x400000L) != 0)
	    /* CS AR.D bit is 1, set to protected 32 mode. */
	    pMode = PMODE_P32;
	 else
	    /* CS AR.D bit is not set, set to protected 16 mode. */
	    pMode = PMODE_P16;
      } /* else */
   } /* else */

   if((err = SdnWriteMember(SDN_PMODE, (LPU8)&pMode,GOOD)) != GOOD)
      return(err);

   return(GOOD);
}

/*****************************************************************************
**
**  HandleCpu80386
**
**  Description:
**     Handles coupling for 80386 processor
***
**  Parameters:
**     input:
**        regIdIndex      Id of the register.
**        regValue        Value to set register to
**
*****************************************************************************/
RETCODE PRIVATE HandleCpu80386(U16 regIdIndex, U32 regValue,
			       SEGMENT_DESCRIPTOR segDesc) {
   /*
   ** 80386 segment registers (CS, DS, ES, FS, GS, SS)
   **   When ever a segment register is modified, its corresponding
   **   AR (access rights), LIMIT and BASE registers need to be updated.
   **   These registers are hidden registers that the 80386 automatically
   **   updates when a segment register is loaded, the emulator needs to
   **   simulate this operation.
   */
   U16     regIdValue;
   RETCODE err;
   PMODE   pmode;
   S8      buf[80];
   U32     base;
   U16     reg;
   U16     reg16Value;
   U32     accessRights;

   /* We are dealing with Intel processors, get the processor mode. */
   if ((err = AdrGetPmode(&pmode,buf)) != GOOD) return(err);

   regIdValue = regIdIndex+SDN_REG00;
   switch (regIdValue) {
      case SDN_EIP:
	 /* Notify tasks about instruction pointer change */
	 if((err = EnlEventNotify(EVENT_CPU_PC_EDIT))!=GOOD) return(err);
	 break;

      case SDN_ESP:
	 /* Notify tasks about stack pointer change */
	 if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD) return(err);
	 break;

      case SDN_CS: {

		if ((pmode == PMODE_V86) || (pmode == PMODE_REAL)) {
	    /*
	    ** For REAL or VIRTUAL86 modes, the AR and LIMIT registers are
	    ** fixed. The BASE is calculated by shifting the segment  
	    ** register value to the left by 4 (multiply by 16).
	    */
	    base = regValue << 4;

	    if((err = SdnWriteMember(SDN_CSBASE, (LPU8)&base,GOOD)) != GOOD)
	       return(err);
	 } else {
	    if ((err = FillInHiddenDescriptor(SDN_CSAR, segDesc)) != GOOD)
	       return(err);
	    /* We need to set the PMODE to be the correct type. If the */
	    /* CR0.PE bit (bit 0) is not set (0), then we are in real  */
	    /* mode and it does not matter what the EFLAGS.VM bit is,  */
	    /* we will do nothing. However if the CR0.PE bit is set (1)*/
	    /* then we will set the PMODE to virtual 86 if the VM bit  */
	    /* is set. If the VM bit is 0, then we have to check the CS*/
	    /* access rights (AR) register's D bit (default bit) to    */
	    /* see if we are in protected-32 (1) or protect-16 (0).    */
	    if ((err = SetPmode()) != GOOD)
	       return(err);
	 } /* else */

	 /*
	 ** If the CS register was modified, notify tasks
	 ** about PC change.
	 */
	 if((err = EnlEventNotify(EVENT_CPU_PC_EDIT)) != GOOD)
	    return(err);
	 break;
      } /* end CS case */

      case SDN_SS: {
		   if ((pmode == PMODE_V86) || (pmode == PMODE_REAL)) {
	    /*
	    ** For REAL or VIRTUAL86 modes, the AR and LIMIT registers are
	    ** fixed. The BASE is calculated by shifting the segment  
	    ** register value to the left by 4 (multiply by 16).
	    */
	    base = regValue << 4;

	    if((err = SdnWriteMember(SDN_SSBASE, (LPU8)&base,GOOD)) != GOOD)
	       return(err);
	 } else {
	    if ((err = FillInHiddenDescriptor(SDN_SSAR, segDesc)) != GOOD)
	       return(err);
	 } /* else */
	 /*
	 ** If the SS register was modified, notify tasks
	 ** about SP change.
	 */
	 if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD)
	    return(err);
	 break;
      } /* end SS case */

      case SDN_DS:
      case SDN_ES:
      case SDN_GS:
      case SDN_FS: {

		   if ((pmode == PMODE_V86) || (pmode == PMODE_REAL)) {
	    /*
	    ** For REAL or VIRTUAL86 modes, the AR and LIMIT registers are
	    ** fixed. The BASE is calculated by shifting the segment  
	    ** register value to the left by 4 (multiply by 16).
	    */
	    base = regValue << 4;

	   /*
	    ** Now calculate the BASE register (SDN_CSBASE, SDN_DSBASE,
	    ** etc.) index so we know which BASE register to modify.
	    */
	    reg = SDN_CSBASE + ((regIdValue - SDN_CS) * 3);  
	    if((err = SdnWriteMember(reg, (LPU8)&base,GOOD)) != GOOD)
	       return(err);
	 } else {
	    reg16Value = regValue & 0xFFFF;

	   /*
	    ** Now calculate the AR register (SDN_CSAR, SDN_DSAR, etc.)
	    ** index so we know which AR, BASE, LIMIT registers to modify.
	    */
	    reg = SDN_CSAR + ((regIdValue - SDN_CS) * 3);
 
	    /* See if the selector is the null selector */
	    if (reg16Value < 4) {
	       /* We have a null selector */
	       if ((err = SdnReadMember(reg,(LPU8)&accessRights)) != GOOD)
		  return(err);
	       /* Clear the Segment Present bit */
	       accessRights &= 0xFFFF7FFFL;
	       if((err = SdnWriteMember(reg, (LPU8)&accessRights,GOOD)) != GOOD)
		  return(err);
	    } else {
	       if ((err = FillInHiddenDescriptor(reg, segDesc)) != GOOD)
		  return(err);
	     } /* else, not null descriptor */
	  } /* else, not REAL or VIRTUAL86 modes */
	 break;
      }/* end case */

      case SDN_LDTR: { 
	 reg16Value = regValue & 0xFFFF;
	 /* See if the selector is the null selector */
	 if (reg16Value < 4) {
	    /* We have a null selector */
	    if ((err = SdnReadMember(SDN_LDTAR,(LPU8)&accessRights)) != GOOD)
	       return(err);
	    /* Clear the Segment Present bit */
	    accessRights &= 0xFFFF7FFFL;
	    if((err = SdnWriteMember(SDN_LDTAR, (LPU8)&accessRights,GOOD)) != GOOD)
	       return(err);
	 } else {
	    if ((err = FillInHiddenDescriptor(SDN_LDTAR, segDesc)) != GOOD)
	       return(err);
	 } /* else */
	 break;
      } /* end LDTR case */

      case SDN_TR: { 
	 if ((err = FillInHiddenDescriptor(SDN_TSSAR, segDesc)) != GOOD)
	       return(err);
	 break;
      } /* end TR case */

      case SDN_EFLAGS:
      case SDN_CR0: {
	 /* We need to set the PMODE to be the correct type. If the */
	 /* CR0.PE bit (bit 0) is not set (0), then we are in real  */
	 /* mode and it does not matter what the EFLAGS.VM bit is,  */
	 /* we will do nothing. However if the CR0.PE bit is set (1)*/
	 /* then we will set the PMODE to virtual 86 if the VM bit  */
	 /* is set. If the VM bit is 0, then we have to check the CS*/
	 /* access rights (AR) register's D bit (default bit) to    */
	 /* see if we are in protected-32 (1) or protect-16 (0).    */
	 if ((err = SetPmode()) != GOOD)
	    return(err);
	 break;
      } /* end case */
   } /* end of switch */
   return(GOOD);
}

/*****************************************************************************
**
**  HandleCpu80186
**
**  Description:
**     Handles coupling for 80386 processor
***
**  Parameters:
**     input:
**        regIdIndex      Id of the register.
**        regValue        Value to set register to
**
*****************************************************************************/
RETCODE PRIVATE HandleCpu80186(U16 regIdIndex, U32 regValue,
			       SEGMENT_DESCRIPTOR segDesc) {
   U16     regIdValue;
   U32     base;
   RETCODE err;

   regIdValue = regIdIndex+SDN_REG00;
   switch (regIdValue) {
      case SDN_EIP:
	 if((err = EnlEventNotify(EVENT_CPU_PC_EDIT))!=GOOD) return(err);
	 break;
      case SDN_CS: 
	 /* Notify tasks about instruction pointer change */
     base = regValue << 4;

	 if((err = SdnWriteMember(SDN_CSBASE, (LPU8)&base,GOOD)) != GOOD)
	       return(err);
	 if((err = EnlEventNotify(EVENT_CPU_PC_EDIT))!=GOOD) return(err);
	 break;

      case SDN_ESP:
	 if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD) return(err);
	 break;
      case SDN_SS: 
	 /* Notify tasks about stack pointer change */
     base = regValue << 4;

	 if((err = SdnWriteMember(SDN_SSBASE, (LPU8)&base,GOOD)) != GOOD)
	       return(err);
	 if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD) return(err);
	 break;

   } /* end of switch */
   err = Sds2AbiSetReg(regIdIndex, regValue);
   return(err);
}

/*****************************************************************************
**
**  HandleCpu68040
**
**  Description:
**     Handles coupling for 68040 processor
***
**  Parameters:
**     input:
**        regIdIndex      Id of the register.
**
*****************************************************************************/
RETCODE PRIVATE HandleCpu68040(U16 regIdIndex) {
   RETCODE err;

   /*
   ** 68040 register coupling
   **   a7 -> usp, isp or msp
   **  usp -> a7
   **  isp -> a7
   **  msp -> a7
   **   sr -> a7
   */
   switch (regIdIndex+SDN_REG00) {
      case SDN_PC:
	 if((err = EnlEventNotify(EVENT_CPU_PC_EDIT))!=GOOD) return(err);
	 break;

      case SDN_A7: {
	 U32 SR, A7;
	 if((err = SdnReadMember(SDN_SR,(U8 FAR *)&SR)) != GOOD)
	    return(err);
	 if((err = SdnReadMember(SDN_A7,(U8 FAR *)&A7)) != GOOD)
	    return(err);
	 if((SR & 0x2000) != 0) { /* supervisor mode */
	    if((SR & 0x1000) != 0) {
	       /* supervisor master mode: A7 -> MSP */
	       if((err = SdnWriteMember(SDN_MSP,(U8 FAR *)&A7,GOOD))
		  !=GOOD) return(err);
	    } else {
	       /* supervisor interrupt mode: A7 -> ISP */
	       if((err = SdnWriteMember(SDN_ISP,(U8 FAR *)&A7,GOOD))
		  !=GOOD) return(err);
	    } /* else */
	 } else {                    /* user mode: A7 -> USP */
	    if(( err = SdnWriteMember(SDN_USP,(U8 FAR *)&A7,GOOD))!=GOOD)
	       return(err);
	 } /* else */
	 if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD) return(err);
	 break;
      } /* end case */

      case SDN_SR: {
	 U32 SR, A7;
	 if((err = SdnReadMember(SDN_SR,(U8 FAR *)&SR)) != GOOD)
	    return(err);
	 if((SR & 0x2000) != 0) {  /* supervisor state */
	    if((SR & 0x1000) != 0) {
	       /* supervisor master mode */
	       if((err = SdnReadMember(SDN_MSP,(U8 FAR *)&A7)) != GOOD)
		  return(err);
	    } else {
	       /* supervisor interrupt mode */
	       if((err = SdnReadMember(SDN_ISP,(U8 FAR *)&A7)) != GOOD)
		  return(err);
	    }   /* else */
	 } else {
	    if((err = SdnReadMember(SDN_USP,(U8 FAR *)&A7)) != GOOD)
	       return(err);
	 } /* else */
	 if((err = SdnWriteMember(SDN_A7,(U8 FAR *)&A7,GOOD)) != GOOD)
	    return(err);
	 if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD) return(err);
	 break;
      } /* end case */

      case SDN_MSP: {
	 U32 SR, Ssp;
	 if((err = SdnReadMember(SDN_SR,(U8 FAR *)&SR)) != GOOD)
	    return(err);
	 if((err = SdnReadMember(SDN_MSP,(U8 FAR *)&Ssp)) != GOOD)
	    return(err);
	 if((SR & 0x3000) == 0x3000) {
	    if((err = SdnWriteMember(SDN_A7,(U8 FAR *)&Ssp,GOOD))!=GOOD)
	       return(err);
	    if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD)
	       return(err);
	 } /* if */
	 break;
      } /* end case */

      case SDN_ISP: {
	 U32 SR, Ssp;
	 if((err = SdnReadMember(SDN_SR,(U8 FAR *)&SR)) != GOOD)
	    return(err);
	 if((err = SdnReadMember(SDN_ISP,(U8 FAR *)&Ssp)) != GOOD)
	    return(err);
	 if((SR & 0x3000) == 0x2000) {
	    if((err = SdnWriteMember(SDN_A7,(U8 FAR *)&Ssp,GOOD))!=GOOD)
	       return(err);
	    if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD)
	       return(err);
	 } /* if */
	 break;
      } /* end case */

      case SDN_USP: {
	 U32 SR, Usp;
	 if((err = SdnReadMember(SDN_SR,(U8 FAR *)&SR)) != GOOD)
	    return(err);
	 if((err = SdnReadMember(SDN_USP,(U8 FAR *)&Usp)) != GOOD)
	    return(err);
	 if((SR & 0x2000) == 0) {
	    if((err = SdnWriteMember(SDN_A7,(U8 FAR *)&Usp,GOOD))!=GOOD)
	       return(err);
	    if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD)
	       return(err);
	 }/* if */
	 break;
      } /* end case */
   } /* end switch */
   return(GOOD);
}

/*****************************************************************************
**
**  HandleCpu32
**
**  Description:
**     Handles coupling for CPU32, CPU32+ and 68000 processors
***
**  Parameters:
**     input:
**        regIdIndex      Id of the register.
**
*****************************************************************************/
RETCODE PRIVATE HandleCpu32(U16 regIdIndex) {
  
   RETCODE err;
  
  /*
   ** cpu32,cpu32p, 68000 register coupling
   **   a7 -> ssp or usp
   **  ssp -> a7
   **  usp -> a7
   **   sr -> a7
   */
   switch (regIdIndex+SDN_REG00) {
      case SDN_PC:
	 if((err = EnlEventNotify(EVENT_CPU_PC_EDIT))!=GOOD) return(err);
	    break;

      case SDN_A7: {
	 U32 SR, A7;
	 if((err = SdnReadMember(SDN_SR,(U8 FAR *)&SR)) != GOOD)
	    return(err);
	 if((err = SdnReadMember(SDN_A7,(U8 FAR *)&A7)) != GOOD)
	    return(err);
	 if((SR & 0x2000) != 0) {    /* supervisor mode: A7 -> SSP */
	    if((err = SdnWriteMember(SDN_SSP,(U8 FAR *)&A7,GOOD))!=GOOD)
	       return(err);
	 } else {                    /* user mode: A7 -> USP */
	    if(( err = SdnWriteMember(SDN_USP,(U8 FAR *)&A7,GOOD))!=GOOD)
	       return(err);
	 } /* else */
	 if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD) return(err);
	 break;
      } /* end case */

      case SDN_SR: {
	 U32 SR, A7;
	 if((err = SdnReadMember(SDN_SR,(U8 FAR *)&SR)) != GOOD)
	    return(err);
	 if((SR & 0x2000) != 0) {  /* supervisor state */
	    if((err = SdnReadMember(SDN_SSP,(U8 FAR *)&A7)) != GOOD)
	       return(err);
	 } else {
	    if((err = SdnReadMember(SDN_USP,(U8 FAR *)&A7)) != GOOD)
	       return(err);
	 } /* else */
	 if((err = SdnWriteMember(SDN_A7,(U8 FAR *)&A7,GOOD)) != GOOD)
	    return(err);
	 if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD) return(err);
	 break;
      } /* end case */

      case SDN_SSP: {
	 U32 SR, Ssp;
	 if((err = SdnReadMember(SDN_SR,(U8 FAR *)&SR)) != GOOD)
	    return(err);
	 if((err = SdnReadMember(SDN_SSP,(U8 FAR *)&Ssp)) != GOOD)
	    return(err);
	 if((SR & 0x2000) != 0) {
	    if((err = SdnWriteMember(SDN_A7,(U8 FAR *)&Ssp,GOOD))!=GOOD)
	       return(err);
	    if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD)
	       return(err);
	 } /* if */
	 break;
      }/* end case */
 
      case SDN_USP: {
	 U32 SR, Usp;
	 if((err = SdnReadMember(SDN_SR,(U8 FAR *)&SR)) != GOOD)
	    return(err);
	 if((err = SdnReadMember(SDN_USP,(U8 FAR *)&Usp)) != GOOD)
	    return(err);
	 if((SR & 0x2000) == 0) {
	    if((err = SdnWriteMember(SDN_A7,(U8 FAR *)&Usp,GOOD))!=GOOD)
	       return(err);
	    if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD)
	       return(err);
	 } /* if */
	 break;
      } /* end case */
   }/* end switch */
   return(GOOD);
}

/*****************************************************************************
**
**  HandleCpu16
**
**  Description:
**     Handles coupling for CPU16 processors
***
**  Parameters:
**     input:
**        regIdIndex      Id of the register.
**
*****************************************************************************/
RETCODE PRIVATE HandleCpu16(U16 regIdIndex) {
  
   RETCODE err;

  /*
   ** cpu16 register coupling
   **   ip -> pc, ccr
   **   pc -> ip, ccr
   **  ccr -> ip, pc
   */
   switch (regIdIndex+SDN_REG00) {
      case SDN_IP: {
	 U32 IP, CCR;
	 if((err = SdnReadMember(SDN_IP,(U8 FAR *)&IP)) != GOOD)
	    return(err);
	 if((err = SdnReadMember(SDN_CCR,(U8 FAR *)&CCR)) != GOOD)
	    return(err);
	 IP+=6;  /* pk:pc = ip+6 */
	 CCR = (CCR & 0xfff0L) | ((IP>>16) & 0xfL);
	 if((err = SdnWriteMember(SDN_PCR,(U8 FAR *)&IP,GOOD)) != GOOD)
	    return(err);
	 if((err = SdnWriteMember(SDN_CCR,(U8 FAR *)&CCR,GOOD)) != GOOD)
	    return(err);
	 if((err = EnlEventNotify(EVENT_CPU_PC_EDIT))!=GOOD) return(err);
	 break;
      } /* end case */

      case SDN_PCR: {
	 U32 PC,CCR;
	 if((err = SdnReadMember(SDN_PCR,(U8 FAR *)&PC)) != GOOD)
	    return(err);
	 if((err = SdnReadMember(SDN_CCR,(U8 FAR *)&CCR)) != GOOD)
	    return(err);
	 CCR = (CCR & 0xfff0L) | ((PC>>16) & 0xfL);
	 PC-=6;  /* ip = pk:pc-6 */
	 if((err = SdnWriteMember(SDN_IP,(U8 FAR *)&PC,GOOD)) != GOOD)
	    return(err);
	 if((err = SdnWriteMember(SDN_CCR,(U8 FAR *)&CCR,GOOD)) != GOOD)
	    return(err);
	 if((err = EnlEventNotify(EVENT_CPU_PC_EDIT))!=GOOD) return(err);
	 break;
      } /* end case */

      case SDN_CCR: {
	 U32 PC,CCR;
	 if((err = SdnReadMember(SDN_PCR,(U8 FAR *)&PC)) != GOOD)
	    return(err);
	 if((err = SdnReadMember(SDN_CCR,(U8 FAR *)&CCR)) != GOOD)
	    return(err);
	 PC = (PC & 0xffffL) | ((CCR & 0xfL) << 16);
	 if((err = SdnWriteMember(SDN_PCR,(U8 FAR *)&PC,GOOD)) != GOOD)
	    return(err);
	 PC -= 6;
	 if((err = SdnWriteMember(SDN_IP,(U8 FAR *)&PC,GOOD)) != GOOD)
	    return(err);
	 if((err = EnlEventNotify(EVENT_CPU_PC_EDIT))!=GOOD) return(err);
	 break;
      } /* end case */

      case SDN_SP:
	 if((err = EnlEventNotify(EVENT_CPU_SP_EDIT))!=GOOD) return(err);
	 break;
   } /* end switch */
   return(GOOD);
}

/****************************************************************************
**
**  GetRegAddr
**
**  Status:  TESTED
**
**  Description:
**     Retrieve the current value of the specified register and return it
**     in an Address descriptor.
**
**  Parameters:
**     input:
**        regId      Id of the register.
**     output:
**        desc       Pointer to an Address descriptor.
**
*****************************************************************************/
RETCODE PRIVATE GetRegAddr(REG_ID regId, DESCRIPTOR FAR *desc) {
   RETCODE err = GOOD;
   U32    regValue;
   S8     buf[80];
   PMODE  pmode;
   U32    segValue;

   /* Check if regId is a valid id */
   if (regId >= nRegisters)
      return(ER_INVALID_REGID);

   /* Added by Dennis Lamb, June 8, 1994 */
   /* If the processor family is Intel (FAMILY_X86) and the processor */
   /* mode is virtual 86 (PMODE_V86) or real (PMODE_REAL), then the   */
   /* address descriptor needs to have the segment selector set. For  */
   /* the PC address, use CS, for the stack pointer or framepointer   */
   /* use SS.                                                         */

   /* If we are dealing with Intel processors, get the processor mode. */
   if (procFamily == FAMILY_X86) {
	  if ((err = AdrGetPmode(&pmode,buf)) != GOOD) return(err);
   } /* if */

   /* Get value of register */
   if ((err = CpuGetRegister(regId, (U32 FAR *) &regValue)) != GOOD) 
      return err;
   /* Allocate an address descriptor for the Caller */
   if ((err = AdrCreateAddress(desc)) != GOOD) {
      *desc = NULL;
      return(err);
   }
   /* Fill in the segment information if we are dealing with Intel */
   if (procFamily == FAMILY_X86) {
      if (regId == 0) {
	 /* We are dealing with the program counter, set address */
			/* descriptor segment selector to CS.                   */
	 if (((err = CpuGetRegister(10, (U32 FAR *) &segValue)) != GOOD) ||
	     ((err = AdrSetAddrSegmentSelector (*desc, ADDR_USE_CS,
			 (U16 FAR *)&segValue)) != GOOD)) {
	    AdrDestroyAddress(*desc);
	    return(err);
	 }
      }
		else {
	 /* !!! THIS WILL WORK FOR ALL REGISTERS - REG_ID IS NOT 0 */         
	 /* We are dealing with either the stack pointer or frame  */
			/* pointer, set address descriptor segment selector to SS */
	 if (((err = CpuGetRegister(15, (U32 FAR *) &segValue)) != GOOD) ||
		       ((err = AdrSetAddrSegmentSelector (*desc, ADDR_USE_SS,
			 (U16 FAR *)&segValue)) !=      GOOD)) {
	    AdrDestroyAddress(*desc);
	    return(err);
	 }
      }
		/* If we are in real mode, then only use 16 bits of the register */
		if ((pmode == PMODE_V86) || (pmode == PMODE_REAL))
		   regValue &= 0xFFFF;
   }
   /* Fill in Address descriptor information */
   if ((err = AdrSetAddrOffset(*desc, regValue)) != GOOD) {
      /* error, destroy the address descriptor */
      AdrDestroyAddress(*desc);
      *desc = NULL;
   }
   return(err);
}
/****************************************************************************
**
**  SetRegAddr
**
**  Status:  TESTED
**
**  Description:
**     Set an address value to the specified register.
**
**  Parameters:
**     input:
**        regId      Id of register.
**        desc       An address descriptor.
**     output:
**
**
*****************************************************************************/
RETCODE PRIVATE SetRegAddr(REG_ID regId, DESCRIPTOR desc) {
   RETCODE err = GOOD;
   U32 regValue;
   S8     buf[80];
   PMODE  pmode;
   ADDR_SEGSEL_TYPE type;
   U32 segValue=0L;

   /* Check if regId is a valid id */
   if (regId >= nRegisters)
      return(ER_INVALID_REGID);

   /* Added by Dennis Lamb, June 8, 1994 */
   /* If the processor family is Intel (FAMILY_X86) and the processor */
   /* mode is virtual 86 (PMODE_V86) or real (PMODE_REAL), then the   */
   /* CS or SS register needs to be set to the value in the address   */
   /* descriptor segment selector.                                    */

   /* If we are dealing with Intel processors, get the processor mode */
   /* and the address descriptor segment selector.                    */
   if (procFamily == FAMILY_X86) {
      /* Get the processor mode */
	  if ((err = AdrGetPmode(&pmode,buf)) != GOOD) return(err);

      /* Get the address descriptor segment selector. */
	  if ((err = AdrGetAddrSegmentSelector (desc, &type, 
		 (U16 FAR *)&segValue)) != GOOD) return(err);
 
     /* Fill in the segment information. If the PC was set, then   */
	  /* set the CS register, otherwise set the SS register. The    */
	  /* values 10 and 15 are indexes into the register table, they */
	  /* match the order that the registers are specified in the    */
	  /* cpu.cfg file.                                              */
     if (regId == 0) {
	/* CS register */
	if ((type != ADDR_USE_CS) &&
		((err = CpuSetRegister (10, segValue)) != GOOD))
		return(err);
	  }
	  else {
	     /* SS register*/
	     if ((err = CpuSetRegister (15, segValue)) != GOOD) return(err);
	  } /* else */
   } /* if */

   /* Get the address offset in the descriptor */
   if ((err = AdrGetAddrOffset(desc, (U32 FAR *)&regValue)) != GOOD)
      return(err);
   if ((err = CpuSetRegister(regId, regValue)) != GOOD)
      return(err);
   return(AdrDestroyAddress(desc));
}

/****************************************************************************
**
**  CpuEmulHaltedCallback
**
**  Status:  TESTED
**
**  Description:
**     Callback routine when emulation halts.
**
**  Parameters:
**     input:
**        event      Event of this callback.
**
**     output:
**        none.
**
*****************************************************************************/
VOID EXPORT CpuEmulHaltedCallback(U32 event) {
   RETCODE err;

   /*
   *** Event emulation halted occurred, registers contents changed.
   *** So notify Servers registered on CPU Server event.
   */
   if(event == EVENT_BKPT_HALTED) {
      if((err = EnlEventNotify(EVENT_CPU_HALTED)) != GOOD)
    ErrDisplayError(err, CHECK_MODE);
   }
}

/*****************************************************************************
**
**    CpuGetNumberOfSignals
**
*****************************************************************************/
RETCODE EXPORT CpuGetNumberOfSignals(U16 *numSignals) {
   *numSignals = nSignals;
   return(GOOD);
}

/*****************************************************************************
**
**    CpuGetSignalName
**
*****************************************************************************/
RETCODE EXPORT CpuGetSignalName(SIG_ID signalId, LPSTR name) {
   if (signalId > nSignals) return(1);
   strcpy(name, signalTable[signalId].signalName);
   return(GOOD);
}

#define INI_FILENAME "pwrviews.ini"
#define INI_SECTION_NAME "SystemInfo"

RETCODE CpuSetIgnoreHLDA(BOOLEAN ignoreHLDA) {
   RETCODE err;
   BOOLEAN value;
   PROBE_TYPE probeType;
   if ((err = ProcReturnSpecificProcessor(&probeType)) != GOOD) return(err);
   if (probeType != I80386EX_TB) return(ER_ADR_SHELL_NOT_SUPPORTED);
   value = !ignoreHLDA;
   if ((err = SdnWriteMember(SDN_HLDA_SIG, &value, GOOD)) != GOOD)
      return(err);
   WritePrivateProfileString(INI_SECTION_NAME, "ignoreHLDA",
      (ignoreHLDA)?"on":"off", INI_FILENAME);
   return(GOOD);
}

RETCODE CpuGetIgnoreHLDA(BOOLEAN *ignoreHLDA) {
   RETCODE err;
   BOOLEAN value;
   if ((err = SdnReadMember(SDN_HLDA_SIG, &value)) != GOOD) return(err);
   *ignoreHLDA = !value;
   return(GOOD);
}

RETCODE PRIVATE CpuInitIgnoreHLDA(VOID) {
   BOOLEAN value;
   RETCODE err;
   S8 buf[32];
   GetPrivateProfileString(INI_SECTION_NAME, "ignoreHLDA", "off", buf,
      sizeof(buf), INI_FILENAME);
   if (strcmpi(buf,"off")==0) value = TRUE;
   else value = FALSE;   // SDS state is ON for ignoreHLDA=off.
   if ((err = SdnWriteMember(SDN_HLDA_SIG, &value, GOOD)) != GOOD)
      return(err);
   return(GOOD);
}

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