/***************************************************************************
**
**  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/860/emuemul.c_v  $
** 
**    Rev 1.8   02 Apr 1998 08:55:10   Cathy
** disable EE (external interrupt) before step if EE enable.
** 
**    Rev 1.7   19 Jan 1998 11:30:30   kevin
** To be able to handle RSTE, EXTIE, and DECIE exceptions while stpping
** 
**    Rev 1.6   07 Nov 1997 14:04:16   kevin
** go back to previous address if system call happened
** 
**    Rev 1.5   05 Nov 1997 12:10:00   kevin
** handling with exception cases during stepping
** 
**    Rev 1.4   29 Oct 1997 14:04:12   kevin
** warning-free version
** 
**    Rev 1.3   28 Aug 1997 10:22:54   cjchen
** 
**    Rev 1.2   19 Aug 1997 16:02:34   cjchen
** 
**    Rev 1.1   28 Jul 1997 10:08:54   cjchen
** 
**    Rev 1.0   18 Jul 1997 15:12:54   cjchen
** Initial revision.
** 
**    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};

//!!!! the variable doesn't exist in FW, put dummy definition here
 U8 exceptBreak=GOOD;
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                        /****************************
                         *                          *
                         *    EXTERNAL VARIABLES    *
                         *                          *
                         ****************************/
extern U32 brokeReason;
extern U8 brokeResult;

                        /****************************
                         *                          *
                         *     LOCAL PROTOTYPES     *
                         *                          *
                         ****************************/
STATUS StepOne(void);
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;
   U32 InfoPtr[4];
   //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(1,InfoPtr);

   //!!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;
   status = AccessCpuReg(READ_ALL,noUse,m68Reg);

   if (exceptBreak == GOOD) {
      status = status & 0x00ff;
      OutputStatus(status,OFF);
      if (status == OK) {
         dataLen = (U8)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;
   /* Maybe add InitLam function */
   brokeResult = EPStatus(&brokeReason);
   if (brokeResult != 0x20 && brokeResult != 0x21) {//not running
      status = AccessCpuReg(READ_ONE,regId,&regValue);
      brokeResult = EPStatus(&brokeReason);
      if (brokeResult == 0x10) {
         OutputStatus(status,OFF);
         if (status == OK) {
            OutData(sizeof(regValue),&regValue,ON);
         }
         outputStream[outputStreamLen++] = 0x00; /* Output stream length */
         OutEnd();
      }
      else OutputExceptReason(ON);
   }
   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;
   status = AccessCpuReg(WRITE_ONE,regId,&value);
   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(void) {
   U32 curPC;
   STATUS status=GOOD;
//   U32 curSR;
//   U8 readBuf[2];
//   ADDR_SPACE space;
//   U16 lp;
//   U32 baseReg[MAX_BASE_REG_NO];

   AccessCpuReg(READ_ONE,R_SRR0,&curPC);

   status = InstructionStep();

   //if (status) {
   //   DumpWordMem(curPC,2,readBuf);
   //   if ((U16)readBuf != 0x60FE) {   ** check for jum hear code
   //      for (lp=0 ; lp<2000 ; lp++)        ** wait for ep stop
   //         if (EPStatus() != 0) {
   //            exceptBreak = 0;       ** if ep stop, clear exceptBreak
   //            status = GOOD;
   //            break;
   //         }
   //   } else {
   //      exceptBreak = 0;   ** match jum hear code
   //      status = GOOD;
   //   }
   //   StopEP();
   //}

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

STATUS StepException(U32 brokeReason) {
   U32 curPC;

   switch (brokeReason) {
      // bit order   22222233 11112222 00111111 00000000
      //             45678901 67890123 89012345 01234567
      case 0x00000400:  // bit 13, System call
         AccessCpuReg(READ_ONE,R_SRR0,&curPC);
         curPC -= 4;  // go back to previous PC
         AccessCpuReg(WRITE_ONE,R_SRR0,&curPC);

         return(RBW_HALT);   //!! using RBW_HALT to stand for exception error

      case 0x00000010:  // bit  3, Machine Check
      case 0x00000001:  // bit  7, Alignment
      case 0x00008000:  // bit  8, Trap
      case 0x00004000:  // bit  9, Floating-point
      case 0x00400000:  // bit 17, S/W exception
      case 0x00200000:  // bit 18, I_TLB miss
      case 0x00100000:  // bit 19, I-TLB error
      case 0x00080000:  // bit 20, D-TLB miss
      case 0x00040000:  // bit 21, D-TLB error
         return(RBW_HALT);   //!! using RBW_HALT to stand for exception error

      case 0x00000040:  // bit  1, Reset Interrupt Enable Bit
      case 0x00000020:  // bit  6, External Interrupt Enable Bit
      case 0x00002000:  // bit 10, Decrementer Interrupt Enable Bit
         return(EP_PENDING); //!! using EP_PENDING to stand for interrput exception

      default:
         return GOOD;  // assume GOOD==0
   }
}

/***************************************************************************
**
**  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;
   U8  flag ;


   status = GOOD;
   phyAddr2 = addr2->pos;
   phyAddr1 = addr1->pos;
   if (phyAddr1 == phyAddr2) {
       flag = DisableExtInt() ;
       status = StepOne(); // gene          // EPStatus must follow StepOne to get reg
       brokeResult = EPStatus(&brokeReason);
       if ( (brokeResult == 0x10 ) && flag ) EnableExtInt() ;
       //if (StepException(brokeReason) == EP_PENDING) { // interrupt exception
       //   status = StepOne(); // gene
       //   brokeResult = EPStatus(&brokeReason);
       //}
   }
   else {
      pc = addr1->pos;
      while ( (pc <= phyAddr2) && (pc >= phyAddr1) ) {
         status = StepOne();
         AccessCpuReg(READ_ONE,R_SRR0,&pc);
         brokeResult = EPStatus(&brokeReason);
         if (brokeResult!=0x10 || (StepException(brokeReason) == RBW_HALT))
            break;
      }
   }

   if (brokeResult==0x10) { // EP stoped
      status = StepException(brokeReason);
      OutputStatus(status,ON);
   }
   else
      OutputExceptReason(ON); //not ok in frozen
}

//  if msr(EE) =1 ,disable msr(EE) before step. then set flag = 1 .
//  and check next if instruction = "mtmsr r? "   ?
//      yes -> flag = 0 ,not to recover msr(EE) ,
//      no  -> flag = 1 ,recover msr(EE)
U8 DisableExtInt(void){
   U32 curPC,readBuf ;
   U8  flag, brokeResult ;
   ADDR addr ;
   STATUS status ;

   flag = 0 ;
   AccessCpuReg(READ_ONE,R_SRR1,&readBuf) ; // get swaped reg value
   if ( ( readBuf & 0x00008000 ) != 0 )     // check EE = 1 ?
   {
        flag = 1 ;
        readBuf &= 0xffff7fff ;
        AccessCpuReg(WRITE_ONE,R_SRR1,&readBuf) ;   // clear msr(EE)

        AccessCpuReg(READ_ONE,R_SRR0,&curPC) ;
        addr.pos = curPC ;
        status = ReadMem(&addr,&(U8)readBuf,4) ;
        if (status==1)
        brokeResult = EPStatus(&brokeReason);

        readBuf &= 0xffff1ffc ;
        if ( readBuf == 0x2401007c )  // not to recover EE if cur instruction is
            flag = 0 ;                // mtmsr-> 0b011111xxxxx00000000000100100100
    }
    else flag = 0 ;

   return(flag) ;  // if flag = 1 , then set msr(EE) after stepone
}
void EnableExtInt(){
    U32 readBuf ;

    AccessCpuReg(READ_ONE,R_SRR1,&readBuf) ;
    readBuf |= 0x00008000 ;
    AccessCpuReg(WRITE_ONE,R_SRR1,&readBuf) ;
}
/***************************************************************************
**
**  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;
   U16 verifyFlag;
   U32 swapAddr,regValue;
   U32 info[3];

   status = AccessCpuReg(READ_ONE,R_IMMR,&regValue);
   swapAddr=regOffset+(regValue & 0xffff0000l);
   Swap((U8 *)&swapAddr,4);
   Swap((U8 *)&value,4);
   value=value>>((4-len)*8);

   switch (len) {
   case 1:
      status = FillByteMem(swapAddr,len,len,(U8 *)&value,verifyFlag,info) & 0xff;
      break;
   case 2:
      status = FillHalfWordMem(swapAddr,len,len,(U8 *)&value,verifyFlag,info) & 0xff;
      break;
   case 4:
      status = FillWordMem(swapAddr,len,len,(U8 *)&value,verifyFlag,info) & 0xff;
      break;
   default:;
   }
   if (status == 1) {
      brokeResult = EPStatus(&brokeReason);
      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;
   U8 regValue[4];
   U32 regImmr;
//   U16 ptrOffset;
   U32 swapAddr;

   status = AccessCpuReg(READ_ONE,R_IMMR,&regImmr);
   swapAddr=regOffset+(regImmr & 0xffff0000l);
   Swap((U8 *)&swapAddr,4);
   status = DumpByteMem(swapAddr,len,regValue);
   Swap((U8 *)regValue, (U8)len);
   /*
   switch (len) {
   case 1:
      regValue &= 0xFFl;
      break;
   case 2:
      regValue &= 0xFFFFl;
      break;
   case 4:
      break;
   default:;
   }
   */
   if (status == 1) {
      brokeResult = EPStatus(&brokeReason);
      OutputExceptReason(ON);
   }
   else {
      OutputStatus(GOOD,OFF);
      OutData((U8)len,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 ***********************************/

