/***************************************************************************
**
**  Name:  emuemul.c
**
**  Description:
**     Michelle emulation-group routines.
**     include:
**     "Read/write registers",
**     "Reset the emulator",
**     "Instruction step(s)"
**
**  Status:  preliminary
**
**  $Log:   S:/tbird/arcppc/fw/603e/emuemul.c_v  $
** 
**    Rev 1.0   11 Feb 1997 16:16:46   kevin
** Initial revision.
** 
**    Rev 1.7   17 Dec 1996 10:07:14   gene
** modified EmuSetIetReg/EmuGetIntReg
** 
**    Rev 1.6   29 Nov 1996 14:26:50   gene
** added EmuSetIntReg/EmuGetIntReg
** 
**    Rev 1.5   11 Sep 1996 14:24:08   gene
** modify EmuReset() error report
** 
**    Rev 1.4   27 Jun 1996 11:47:46   gene
** fixed error message return from stepOne()
** 
**    Rev 1.3   25 Jun 1996 11:03:28   gene
** 
**    Rev 1.2   17 Jun 1996 08:49:20   gene
** modify stepOne for the step time out condition
** 
**    Rev 1.1   10 Jun 1996 10:57:56   gene
** 
**    Rev 1.0   03 Jun 1996 14:18:48   gene
** Initial revision.
** 
** Copyright (C) 1992 Microtek International, Inc.
**
****************************************************************************/

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

#ifndef _EMU_EMUL_
#include "emuemul.h"
#endif

#ifndef _EMU_TRACE_
#include "emutrace.h"
#endif

#ifndef _EMU_LLFW_
#include "emullfw.h"
#endif

#ifndef _EMU_EXTERNAL_
#include "emuext.h"
#endif

#include <string.h>
                       /****************************
                        *                          *
                        *     LOCAL DEFINITIONS    *
                        *                          *
                        ****************************/
enum SPEC_INST { PUSHF = 0x9c, CLI = 0xfa, STI = 0xfb, 
                 IRET = 0xcf, POPF = 0x9d, INT3 = 0xcc};

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

                        /****************************
                         *                          *
                         *     LOCAL PROTOTYPES     *
                         *                          *
                         ****************************/
STATUS StepOne(U32 *baseReg);
U8 checkOvpReg(U16 regOffset,U16 *ptrOffset);

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

/***************************************************************************
**
**  EmuReset
**
**  Description: Michelle reset routine, to reset the CPU and set the
**               certain important registers to be default values.
**               such as "PC", "SP" ....
**
**  Parameters:
**     input:
**        none
**
**     output:
**        return status code(error-code) in to the output processor.
**        Could be : "OK" -- Normal return.
**                   "Hardware fatal error" -- error processing
**
**
****************************************************************************/
VOID EmuReset(VOID) {
   STATUS vccFlag,brokeReason;
   U32 baseReg[MAX_BASE_REG_NO];
   //U16 traceBufNo,cnt0,cnt1;
   U16 traceing;
   //U32 tmpCnt;

   vccFlag = GOOD;
   brokeReason = BROKE;
   AccessIceFlag(WRITE_ONE,RUNNING,&C_OFF);
   AccessIceFlag(WRITE_ONE,FLYING,&C_OFF);
   AccessIceFlag(WRITE_ONE,BROKE_REASON,&brokeReason);
   AccessIceFlag(READ_ONE,TRACEING,&traceing);

   vccFlag = Reset(baseReg);

   AccessBaseReg(FW_WRITE_ALL,noUse,baseReg);
   if(vccFlag != GOOD) {
      OutputExceptReason(ON);
   } else
      OutputStatus(GOOD,ON);
}


/***************************************************************************
**
**  EmuGetAllRegs
**
**  Description: Michelle Emulation routine, to get values of all registers.
**
**  Parameters:
**     input:
**        none
**
**     output:
**        return status code(error-code) in to the output processor.
**        Could be : "OK" -- Normal return.
**                   "Hardware fatal error" -- error processing
**
****************************************************************************/
VOID EmuGetAllReg(VOID) {
   STATUS status;
   U32 m68Reg[MAX_CPU_REG_NO];
   U8 dataLen;
   

   status = GOOD;
   StopRun();
   status = AccessCpuReg(READ_ALL,noUse,m68Reg);

   if (exceptBreak == GOOD) {
      status = status & 0x00ff;
      OutputStatus(status,OFF);
      if (status == OK) {
         dataLen = sizeof(m68Reg); /* each register length */
         OutData(dataLen,m68Reg,ON);
      }
      OutEnd();
   }
   else OutputExceptReason(ON);
}

/***************************************************************************
**
**  EmuGetReg
**
**  Description: Michelle Emulation routine, to get values of all registers.
**
**  Parameters:
**     input:
**        none
**
**     output:
**        return status code(error-code) in to the output processor.
**        Could be : "OK" -- Normal return.
**                   "Hardware fatal error" -- error processing
**                4
****************************************************************************/
VOID EmuGetReg(REG_MODE regMode,U16 regId) {
   STATUS status;
   U16 phyCpu;
   U32 regValue;

   status = GOOD;
   StopRun();
   /* Maybe add InitLam function */
   switch (regMode) {
      case M68_REG :
         switch(regId) {
         case R_PC:
            status = AccessBaseReg(READ_ONE,BREG_PC,&regValue);
            break;
         case R_A7:
         case R_SSP:
         case R_USP:
            status = AccessBaseReg(READ_ONE,BREG_SP,&regValue);
            break;
         default:
            status = AccessCpuReg(READ_ONE,regId,&regValue);
            if (regId == R_SR)
               regValue &= 0xFFFF;
         }
         break;
      case INTERNAL_REG :
         AccessIceFlag(READ_ONE,PHY_CPU_TYPE,&phyCpu);
         break;
      default :
         status = UNKNOWN_TYPE;
   }
   if (exceptBreak == GOOD) {
//      status = status & 0x00ff;
      OutputStatus(status,OFF);
      if (status == OK) {
         OutData(sizeof(regValue),&regValue,ON);
      }
      outputStream[outputStreamLen++] = 0x00; /* Output stream length */
      OutEnd();
   }
   else OutputExceptReason(ON);
}

/***************************************************************************
**
**  EmuSetReg
**
**  Description: Michelle Emulation routine, to step certain step(s).
**
**  Parameters:
**     input:
**        reg_id -- integer,
**        value  -- unsigned integer,
**
**     output:
**        return status code(error-code) in to the output processor.
**        Could be : "OK" -- Normal return.
**                   "Hardware fatal error" -- error processing
**
****************************************************************************/
VOID EmuSetReg(REG_MODE regMode, U16 regId, U32 value) {
   STATUS status;
   //U32 srReg;

   status = GOOD;
   StopRun();
   switch (regMode) {
      case M68_REG :
         switch(regId) {
         case R_A7:
         case R_USP:
         case R_SSP:
            status = AccessBaseReg(WRITE_ONE,BREG_SP,&value);
            break;
         case R_PC:
            status = AccessBaseReg(WRITE_ONE,BREG_PC,&value);
            break;
         default:
            status = AccessCpuReg(WRITE_ONE,regId,&value);
         }
         break;
      default :
         status = UNKNOWN_TYPE;
   }
   if (exceptBreak == GOOD) {
//      status = status & 0x00ff;
      OutputStatus(status,ON);
   }
   else OutputExceptReason(ON);
}


/***************************************************************************
**
**  StepOne
**
**  Description: Michelle Emulation routine, to execute a instruction
**
**
**  Parameters:
**     input:
**
**     output:
**        return status code(error-code) in to the output processor.
**        Could be : "OK" -- Normal return.
**                   "Hardware fatal error" -- error processing
**
****************************************************************************/
STATUS StepOne(U32 *baseReg) {
   U32 curPC, curSR;
   STATUS status;
   U8 readBuf[2];
   ADDR_SPACE space;
   U16 lp;

   AccessBaseReg(READ_ONE,BREG_PC,&curPC);
   AccessCpuReg(READ_ONE,R_SR,&curSR);

   status = InstructionStep(baseReg);
   if (status) {
      if (curSR & 0x2000)
         space = SPACE_SP;
      else
         space = SPACE_UP;
      DumpWordMem(curPC,space,2,readBuf);
      if ((U16)readBuf != 0x60FE) {   // check for jum hear code
         for (lp=0 ; lp<2000 ; lp++)        // wait for ep stop
            if (EPstatus(baseReg) != 0) {
               exceptBreak = 0;       // if ep stop, clear exceptBreak
               status = GOOD;
               break;
            }
      } else {
         exceptBreak = 0;   // match jum hear code
         status = GOOD;
      }
      StopEP(baseReg);
   }
   AccessBaseReg(FW_WRITE_ALL,noUse,baseReg);

   if (status) status = STEP_ERROR;
   return(status);
}

/***************************************************************************
**
**  EmuStepRange
**
**  Description: Michelle Emulation routine, to execute instruction
**               from addr1 to addr2.
**
**  Parameters:
**     input:
**        addr1 -- unsigned long,
**        addr2 -- unsignde long
**
**     output:
**        return status code(error-code) in to the output processor.
**        Could be : "OK" -- Normal return.
**                   "Hardware fatal error" -- error processing
**
****************************************************************************/
VOID EmuStepRange(ADDR *addr1,ADDR *addr2) {
   STATUS status;
   U32 baseReg[MAX_BASE_REG_NO];
   U32 pc,phyAddr1,phyAddr2;


   status = GOOD;
   phyAddr2 = addr2->pos;
   phyAddr1 = addr1->pos;
   if (phyAddr1 == phyAddr2) status = StepOne(baseReg); // gene
   else {
      pc = addr1->pos;
      while ( (pc <= phyAddr2) && (pc >= phyAddr1) ) {
         status = StepOne(baseReg);
         pc = baseReg[BREG_PC];
         if ((exceptBreak) || (status)) break;
      }
   }
   if (exceptBreak == GOOD) OutputStatus(status,ON);
   else OutputExceptReason(ON);
}

/***************************************************************************
**
**  EmuSetIntReg
**
**  Description: Emulation routine, to set a internal register.
**
**  Parameters:
**     input:
**        regOffset -- unsigned integer,
**        value  -- unsigned long,
**        len  -- unsigned integer.
**
**     output:
**        return status code(error-code) in to the output processor.
**        Could be : "OK" -- Normal return.
**                   "Hardware fatal error" -- error processing
**
****************************************************************************/
VOID EmuSetIntReg(U16 regOffset,U16 len,U32 value,U16 relocatable) {
   STATUS status = GOOD;
   U16 ptrOffset;

   StopRun();
   switch (len) {
   case 2:
      value = (U32)ExchangeHighLow2((U16)value);
      break;
   case 4:
      value = ExchangeHighLow4(value);
      break;
   default:;
   }
   if (checkOvpReg(regOffset,&ptrOffset)) {
      status = OvpRegister(regOffset,WRITE_ONE,2,&value,ptrOffset);
   } else {
      status = IntRegister(regOffset,WRITE_ONE,len,&value,0);
   }
   if (exceptBreak)
      OutputExceptReason(ON);
   else
      OutputStatus(GOOD,ON);
}

/***************************************************************************
**
**  EmuGetIntReg
**
**  Description: Emulation routine, to get a internal register.
**
**  Parameters:
**     input:
**        regOffset -- unsigned integer,
**        len  -- unsigned integer.
**
**     output:
**        return status code(error-code) in to the output processor.
**        Could be : "OK" -- Normal return.
**                   "Hardware fatal error" -- error processing
**
****************************************************************************/
VOID EmuGetIntReg(U16 regOffset,U16 len,U16 relocatable) {
   STATUS status = GOOD;
   U32 regValue;
   U16 ptrOffset;

   StopRun();
   if (checkOvpReg(regOffset,&ptrOffset)) {
      status = OvpRegister(regOffset,READ_ONE,2,&regValue,ptrOffset);
   } else {
      status = IntRegister(regOffset,READ_ONE,len,&regValue,0);
   }
   switch (len) {
   case 1:
      regValue &= 0xFFl;
      break;
   case 2:
      regValue = (U32)ExchangeHighLow2((U16)regValue);
      regValue &= 0xFFFFl;
      break;
   case 4:
      regValue = ExchangeHighLow4(regValue);
      break;
   default:;
   }
   if (exceptBreak)
      OutputExceptReason(ON);
   else {
      OutputStatus(GOOD,OFF);
      OutData(sizeof(regValue),&regValue,ON);
      OutEnd();
   }
}

U8 checkOvpReg(U16 regOffset,U16 *ptrOffset) {
   U16 ovpReg[] = { 0x0140 };
   U16 ovpPtrOff[] = { 0x0148 };
   U8 i, ovpRegNo = 1;

   for (i=0; i < ovpRegNo ; i++)
      if (regOffset == ovpReg[i]) {
         *ptrOffset = ovpPtrOff[i];
         break;
      }

   if (i == ovpRegNo)
      return(FALSE);
   else
      return(TRUE);
}
/******************************** E O F ***********************************/

