/***************************************************************************
**  Name:  emumem.c
**
**  Description:
**     Michelle memory-group routines.
**     include:
**        "Read N bytes memory from Michelle -- emuReadMemN()"
**        "Write N bytes memory to Michelle  -- emuSetMemN()"
**        "Memory data comparing             -- emuCompare()"
**        "Memory data coping                -- emuCopy()"
**        "Memory data filling               -- emuFill()"
**        "Memory data testing               -- emuTest()"
**        "Memory data searching             -- emuSearch()"
**        "Memory data checksum             -- emuChecksum()"
**
**  Status:  preliminary
**
**  $Log:   S:/tbird/arcppc/fw/603e/emumem.c_v  $
** 
**    Rev 1.0   11 Feb 1997 16:16:54   kevin
** Initial revision.
** 
**    Rev 1.3   25 Jun 1996 11:12:22   gene
** 
**    Rev 1.2   11 Jun 1996 17:38:36   gene
** 
**    Rev 1.1   11 Jun 1996 09:48:42   gene
** changed BDMTemp -> BDMTempB
** 
**    Rev 1.0   03 Jun 1996 14:19:04   gene
** Initial revision.
**
** Copyright (C) 1992 Microtek International, Inc.
**
****************************************************************************/

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

#ifndef _EMU_MEM_
#include "emumem.h"
#endif

#ifndef _EMU_BASE_TYPES_
#include "emutypes.h"
#endif

#ifndef _EMU_LIB_
#include "emulib.h"
#endif

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

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

#include <string.h>

                   /****************************
                   *                           *
                   *     LOCAL DEFINITIONS     *
                   *                           *
                   ****************************/
#define RET_ADDR_LEN     8
#define DATA_BUFFER_LEN  0x400

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

                   /****************************
                   *                           *
                   *     LOCAL PROTOTYPES     *
                   *                           *
                   ****************************/
STATUS processHeadPos(ADDR *addr, U16 *dataLen, U8 **buff,U16 verifyFlag,
                      BOOLEAN *processTail);
STATUS ReadMem(ADDR *addr, U8 *buff, U16 len);
STATUS WriteMem(ADDR *addr, U16 dataLen, U8 *buff);
STATUS FillMem(ADDR *addr, U32 addrLen, U8 *buff, U16 len, U16 size, U16 verifyFlag);

                   /***************************
                   *                          *
                   *      EXECUTABLE CODE     *
                   *                          *
                   ****************************/
STATUS processHeadPos(ADDR *addr, U16 *dataLen, U8 **buff,U16 verifyFlag,
          BOOLEAN *processTail) {
   STATUS status = GOOD;
   U8     tmpBuff[2];

   if (addr->pos % 2) { // process head
      DumpByteMem(addr->pos-1,addr->space,1,tmpBuff);
      tmpBuff[1] = (*buff)[0];
      status = FillWordMem(addr->pos-1,addr->space,2,2,tmpBuff,verifyFlag) & 0xff;
      addr->pos++;
      (*dataLen)--;
      (*buff)++;
   }
   if (*dataLen % 2) { // process tail at odd pos
      *processTail = TRUE;
      (*dataLen)--;
   }
   return(status);
}

STATUS processTailPos(ADDR *addr,U8 buff,U16 verifyFlag) {
   STATUS status = GOOD;
   U8     tmpBuff[2];

   DumpByteMem(addr->pos+1,addr->space,1,&tmpBuff[1]);
   tmpBuff[0] = buff;
   status = FillWordMem(addr->pos,addr->space,2,2,tmpBuff,verifyFlag) & 0xff;
   return(status);
}

/***************************************************************************
**
**  ReadMem
**
**  Description: Read a chunk memory from "addr" location.
**         Note: The data length maximum - 0x100
**
**  Parameters:
**     input:
**        addr -- unsigned long
**        len  --
**
**     output:
**        return status code(error-code) in to the output processor.
**        "O.K." -- Normal return
**        "Fatal error on emulator" -- Fatal HW error; check status-code
**
**        return data -- "length" + "N bytes data string"
**
****************************************************************************/
STATUS ReadMem(ADDR *addr, U8 *buff, U16 dataLen) {
   U16 size;
   STATUS status;
   BOOLEAN procTail = FALSE;
   U8 *buffPtr;

   if ((status = AccessIceFlag(READ_ONE,SIZE_FLAG,&size)) != GOOD)
      return status;
   if (size == S_BYTE)
      status = DumpByteMem(addr->pos,addr->space,(U32)dataLen,buff);
   else {
      buffPtr = buff;
      if (addr->pos %2) {
         status = DumpByteMem(addr->pos,addr->space,1,buffPtr);
         dataLen--;
         buffPtr++;
         addr->pos++;
      }
      if (dataLen % 2) {
         dataLen--;
         procTail = TRUE;
      }
      if (!status && dataLen != 0) {
         status = DumpWordMem(addr->pos,addr->space,(U32)dataLen,buffPtr);
         buffPtr += dataLen;
         addr->pos += dataLen;
      }
      if (procTail && !status)
         status = DumpByteMem(addr->pos,addr->space,1,buffPtr);
   }
   return(status);
}

/***************************************************************************
**
**  WriteMem
**
**  Description: Read a chunk memory from "addr" location.
**         Note: The data length maximum - 0x100
**
**  Parameters:
**     input:
**        addr -- unsigned long
**        len  --
**
**     output:
**        return status code(error-code) in to the output processor.
**        "O.K." -- Normal return
**        "Fatal error on emulator" -- Fatal HW error; check status-code
**
**        return data -- "length" + "N bytes data string"
**
**  NOTE: if interFlag is on, it will not check verifyFlag at all.
****************************************************************************/
STATUS WriteMem(ADDR *addr, U16 dataLen, U8 *buff) {
   U16 verifyFlag, size;
   //U16 lp;
   STATUS status;
   U32 length;
   BOOLEAN processTail = FALSE;

   status = GOOD;
   length = (U32)dataLen;
   AccessIceFlag(READ_ONE,VERIFY_FLAG,&verifyFlag);
   AccessIceFlag(READ_ONE,SIZE_FLAG,&size);

   if (size == S_BYTE)
      status = FillByteMem(addr->pos,addr->space,dataLen,dataLen,
                  buff,verifyFlag) & 0xff;
   else {
      status = processHeadPos(addr,&dataLen,&buff,verifyFlag,&processTail);
      if (!status && dataLen != 0)
         status = FillWordMem(addr->pos,addr->space,dataLen,
                     dataLen,buff,verifyFlag) & 0xff;
      addr->pos += dataLen;
      if (processTail && !status)
         status = processTailPos(addr,buff[dataLen],verifyFlag);
   }

   return(status);
}

/***************************************************************************
**
**  EmuReadMemN
**
**  Description: Michelle memory-group routine, to read a chunk memory
**               from "addr" location.
**         Note: The data length maximum - 0x100
**
**  Parameters:
**     input:
**        addr -- unsigned long
**        len  --
**
**     output:
**        return status code(error-code) in to the output processor.
**        "O.K." -- Normal return
**        "Fatal error on emulator" -- Fatal HW error; check status-code
**
**        return data -- "length" + "N bytes data string"
**
****************************************************************************/
VOID EmuReadMemN(ADDR *addr, U16 addrLen) {
   STATUS status;
   U8 dataLen,memBuff[MAX_STREAM_LEN];
   //U32 m68Reg[MAX_CPU_REG_NO];

   status = GOOD;
   if (addrLen > MAX_STREAM_LEN-4) {
      OutputStatus(LEN_TOO_LONG,ON);
      return;
   }
   //StopRun();
   status = ReadMem(addr,memBuff,(U16)addrLen);

   if (exceptBreak) {
      OutputExceptReason(OFF);
      OutData(RET_ADDR_LEN,&BDMTempB,ON);
      OutEnd();
   } else {
      OutputStatus(status,OFF);
      dataLen = (U8)(addrLen & 0xFF);
      OutData(dataLen,memBuff,ON);
      OutEnd();
   }
}


/****************************************************************************
**
**  EmuSetMemN
**
**  Description: Michelle memory-group routine, to write a chunk data
**               from "buff" to "addr" location. The driver program can
**               use this function to implement to download code from a
**               file.
**               The length should be limitted up to 1024 bytes(1KB).
**         Note: if the set area is not in the internal register area
**               then do: ModifyRegister();
**                          set memory
**                        RestoreRegister();
**               else set memory only.
**
**  Parameters:
**     input:
**        addr  -- unsigned long,
**        *buff -- unsigned char,
**        len   -- int
**
**     output:
**        return status code(error-code) in to the output processor.
**        "O.K." -- Normal return
**        "Fatal error on emulator" -- Fatal HW error; check status-code
**
**
****************************************************************************/
VOID EmuSetMemN(ADDR *addr, U8 *buff, U16 len) {
   STATUS status;
   //ADDR addr2;
   //U16 Len1, Len2;

   status = GOOD;
   if (len > MAX_STREAM_LEN-4) {
      OutputStatus(LEN_TOO_LONG,ON);
      return;
   }
   else if (len == 0) {
      OutputStatus(LEN_EQUAL_ZERO,ON);
      return;
   }
   //StopRun();

   status = WriteMem(addr,len,buff);

   if (exceptBreak) {
      OutputExceptReason(OFF);
      OutData(RET_ADDR_LEN*2,&BDMTempB,ON);
      OutEnd();
   } else {
      switch (status) {
      case GOOD :
         OutputStatus(GOOD,ON);
         break;
      case 1 :
         OutputStatus(ERROR_WRITE,OFF);
         OutData(RET_ADDR_LEN*2,&BDMTempB,ON);
         break;
      default :
         OutputExceptReason(OFF);
         OutData(RET_ADDR_LEN*2,&BDMTempB,ON);  //NOT_READY
      }
      OutEnd();
   }
}

/***************************************************************************
**
** EmuCompare
**
**  Description: Michelle memory-group routine, to compare two memory
**               blocks.
**               Return the locations and its contents, if there has
**               difference.
**               The "addr1" is the start addres of the first block.
**               The "addrLen" is the addres length of the first block.
**               The "addr2" is the start address of the second block.
**
**  Parameters:
**     input:
**        addr1 -- unsigned long,
**        addrLen -- unsigned long,
**        addr2 -- unsigned long
**
**     output:
**        return status code(error-code) in to the output processor.
**        "O.K." -- Normal return
**        "Fatal error on emulator" -- Fatal HW error; check status-code
**
**        return data -- "length" + "location" + "contents"
**
****************************************************************************/
VOID EmuCompare(ADDR *addr1, U32 addrLen, ADDR *addr2) {
   STATUS status;
   //U32 m68Reg[MAX_CPU_REG_NO];
   U16 size;

   AccessIceFlag(READ_ONE,SIZE_FLAG,&size);
   switch ((SIZE)size) {
      case S_WORD :
         ReduceEven(addr1->pos);
         IncreaseEven(addrLen);
         ReduceEven(addr2->pos);
         break;
      case S_LONG :
         ReduceQuad(addr1->pos);
         IncreaseQuad(addrLen);
         ReduceQuad(addr2->pos);
         break;
   }
   //StopRun();
   status = CompareMem(addr1->pos,addr1->space,addrLen,
           addr2->pos,addr2->space,size) & 0xff;

   switch (status) {
      case 0 :
         OutputStatus(NO_DIFF,ON);
         break;
      case 1 :
         OutputStatus(GOOD,OFF);
         OutData(RET_ADDR_LEN*2,&BDMTempB,ON);
         break;
      case 2 :
         OutputStatus(NOT_READY,OFF);
         OutData(RET_ADDR_LEN,&BDMTempB,ON);
         break;
      case 3 :
         OutputStatus(NOT_READY,OFF);
         OutData(RET_ADDR_LEN,&BDMTempB + 8,ON); // gene
         break;
      default :
         OutputExceptReason(ON);
   }
   OutEnd();
}


/***************************************************************************
**
**  EmuCopy
**
**  Description: Michelle memory-group routine, to copy a memory block
**               data to another block's.
**               The "addr1" and "addr2" is the addres range of the
**               first block.
**               The "addr3" is the start address of the destination
**               block.
**
**         Note: If the range is partial in the internal register area
**               then reduce the length and do:
**                     ModifyRegisterCS()
**                     copy command
**                     RestoreRegisterCS()
**               If one of the source area or destination area is all in the
**               internal register area then don't do copy.
**  Parameters:
**     input:
**        addr1 -- unsigned long,
**        addr2 -- unsigned long,
**        addr3 -- unsigned long
**
**     output:
**        return status code(error-code) in to the output processor.
**        "O.K." -- Normal return
**        "Fatal error on Emulator" -- Fatal HW error; check status-code
**
**
****************************************************************************/
VOID EmuCopy(ADDR *addr1, U32 addrLen, ADDR *addr2) {
   STATUS status;
   //U32 m68Reg[MAX_CPU_REG_NO];
   U16 size,verifyFlag;
   
   status = GOOD;
   AccessIceFlag(READ_ONE,SIZE_FLAG,&size);
   AccessIceFlag(READ_ONE,VERIFY_FLAG,&verifyFlag);
   switch ((SIZE)size) {
      case S_WORD :
         ReduceEven(addr1->pos);
         IncreaseEven(addrLen);
         ReduceEven(addr2->pos);
         break;
      case S_LONG :
         ReduceQuad(addr1->pos);
         IncreaseQuad(addrLen);
         ReduceEven(addr2->pos);
         break;
   }
   //StopRun();

   if (addr1->pos >= addr2->pos)
      status = PreCopyMem(addr1->pos,addr1->space,addrLen,
         addr2->pos,addr2->space,verifyFlag) & 0xff;
   else {
      status = PostCopyMem(addr1->pos,addr1->space,addrLen,
     addr2->pos,addr2->space,verifyFlag) & 0xff;
   }

   if (exceptBreak) {
      OutputExceptReason(OFF);
      OutData(RET_ADDR_LEN*2,&BDMTempB,ON);
      OutEnd();
   } else {
      switch(status) {
         case 0 :
            OutputStatus(GOOD,OFF);
            break;
         case 1 :
            OutputStatus(ERROR_WRITE,OFF);
            // error data allways byte, mask high byte
            *(BDMTempB+7) = *(BDMTemp+15) = 0;
            OutData(RET_ADDR_LEN*2,&BDMTempB,ON);
            break;
         case 2 :
            OutputStatus(NOT_READY,OFF);
            OutData(RET_ADDR_LEN,&BDMTempB,ON);
            break;
         case 3 :
            OutputStatus(NOT_READY,OFF);
            OutData(RET_ADDR_LEN,&BDMTempB+8,ON);
            break;
         default :
            OutputExceptReason(OFF);
            OutData(RET_ADDR_LEN*2,&BDMTempB,ON);
            break;
      }
      OutEnd();
   }
}

/***************************************************************************
**
**  FillMem
**
**  Description: Fill a memory range with a specified pattern.
**               Data pattern is specified in "buff" array.
**               The lengh of the data pattern is "len".
**
**  Parameters:
**     input:
**        addr -- unsigned long(address), int (space)
**        addrLen -- unsigned long,
**        *buff -- unsigned char, Max 16 bytes;
**        len   -- int
**        size --int
**        verifyFlag -- int
**     output:
**        return status code(error-code) in to the output processor.
**        "O.K." -- Normal return
**        "Fatal error on Emulator" -- Fatal HW error; check status-code
**
**
****************************************************************************/
STATUS FillMem(ADDR *addr, U32 addrLen, U8 *buff, U16 len, U16 size, U16 verifyFlag) {
STATUS status=GOOD;
U16 pos;
   switch ((SIZE)size) {
      case S_BYTE :
         status = FillByteMem(addr->pos,addr->space,addrLen,len,buff,verifyFlag) & 0xff;
         break;
      case S_WORD :
      case S_LONG :
         if (addrLen%2) {
            pos = (len == 1 ? 0 : addrLen%len ? (U16)(addrLen%len)-1 : len-1);
            addrLen--;
            status = FillByteMem(addr->pos+addrLen,addr->space,1,1,
                        &buff[pos],verifyFlag) & 0xff;
         }
         if (addrLen != 0 && status == GOOD)
            status = FillWordMem(addr->pos,addr->space,addrLen,
                        len,buff,verifyFlag) & 0xff;
         break;
      default :
         status = FillByteMem(addr->pos,addr->space,addrLen,len,buff,verifyFlag) & 0xff;
         break;
   }
   return(status);
}
/***************************************************************************
**
**  EmuFill
**
**  Description: Michelle memory-group routine, to fill a memory range
**               with a specified pattern.
**               Fill a memory range with a specified pattern.
**               Data pattern is specified in "buff" array.
**               The lengh of the data pattern is "len".
**
**  Parameters:
**     input:
**        addr1 -- unsigned long,
**        addr2 -- unsigned long,
**        *buff -- unsigned char, Max 16 bytes;
**        len   -- int
**
**     output:
**        return status code(error-code) in to the output processor.
**        "O.K." -- Normal return
**        "Fatal error on Emulator" -- Fatal HW error; check status-code
**
** 
****************************************************************************/
VOID EmuFill(ADDR *addr, U32 addrLen, U8 *buff, U16 len) {
   STATUS status;
   U16 size;
   //U32 m68Reg[MAX_CPU_REG_NO];
   U16 verifyFlag;

   status = GOOD;
   AccessIceFlag(READ_ONE,SIZE_FLAG,&size);
   AccessIceFlag(READ_ONE,VERIFY_FLAG,&verifyFlag);
   //StopRun();

   status = FillMem(addr,addrLen,buff,len,size,verifyFlag);

   if (exceptBreak) {
      OutputExceptReason(OFF);
      OutData(RET_ADDR_LEN*2,&BDMTempB,ON); // NOT READY
      OutEnd();
   } else {
      switch (status) {
      case GOOD :
         OutputStatus(GOOD,OFF);
         break;
      case 1 :
         OutputStatus(ERROR_WRITE,OFF);
         OutData(RET_ADDR_LEN*2,&BDMTempB,ON);
         break;
      default :
         OutputExceptReason(OFF);
         OutData(RET_ADDR_LEN*2,&BDMTempB,ON); // NOT READY
      }
      OutEnd();
   }
}

/***************************************************************************
**
**  EmuTest
**
**  Description: Michelle memory-group routine, to test a spcified
**               address range working O.K. or not? Is RAM or not?
**         Note: If the range is partial in the internal register area
**               then reduce the length and do:
**                     ModifyRegisterCS()
**                     copy command
**                     RestoreRegisterCS()
**               If one of the source area or destination area is all in the
**               internal register area then don't do copy (InterFlag=YES).
**
**  Parameters:
**     input:
**        addr1 -- unsigned long,
**        addr2 -- unsigned long,
**
**    *** *pattern[n] -- char ===> optional
**
**     output:
**        return status code(error-code) in to the output processor.
**        "O.K." -- Normal return
**        "Fatal error on Emulator" -- Fatal HW error; check status-code
**
**  NOTE: I have three ways to do this testing, you can decide how
**        to implement it by one of these.
**        1. (Read data from specified address location) -->
**           (Write pre-read data back to the same memory location) -->
**           (Compare these result then check it to report the status)
**        2. Write the defined special data pattern to the specified
**           memory loaction, then check the virolation.
**        3. Like method 2, but driver can select the "data pattern"
**           as she like. In this case, to add a parameter to indicate
**           the data pattern is necessary.
**
****************************************************************************/
VOID EmuTest(ADDR *addr, U32 addrLen) {
   STATUS status = GOOD;
   //U32 m68Reg[MAX_CPU_REG_NO];
   U16 size;

   AccessIceFlag(READ_ONE,SIZE_FLAG,&size);
   switch ((SIZE)size) {
      case S_WORD :
         ReduceEven(addr->pos);
         IncreaseEven(addrLen);
         break;
      case S_LONG :
         ReduceQuad(addr->pos);
         IncreaseQuad(addrLen);
         break;
   }
   //StopRun();
   status = TestMem(addr->pos,addr->space,addrLen,size) & 0xff;

   if (exceptBreak == GOOD) {
      if (status == GOOD) OutputStatus(GOOD,ON);
      else {
         OutputStatus(TEST_FAIL,OFF);
         OutData(RET_ADDR_LEN*2,&BDMTempB,ON);
         OutEnd();
      }
   } else {
      OutputExceptReason(OFF);
      OutData(RET_ADDR_LEN,&BDMTempB,ON);
      OutEnd();
   }
}

/***************************************************************************
**
**  EmuSearch
**
**  Description: Michelle memory-group routine, to search data pattern
**               in the specified address range.
**               Data pattern is specified in "buff" array.
**               The lengh of the data pattern is "len".
**
**  Parameters:
**     input:
**        addr1 -- unsigned long,
**        addr2 -- unsigned long,
**        *buff -- unsigned char,  Max 32 Bytes;
**        len   -- int
**
**     output:
**        return status code(error-code) in to the output processor.
**        "O.K." -- Normal return
**        "Fatal error on Emulator" -- Fatal HW error; check status-code
**
**
****************************************************************************/
VOID EmuSearch(ADDR *addr, U32 addrLen, U8 *buff, U16 len) {
#define MATCH  0xff /* template data, it will redefine. */
   STATUS status = GOOD;
   //U32 m68Reg[MAX_CPU_REG_NO];

   //StopRun();
   status = SearchMem(addr->pos,addr->space,addrLen,len,buff) & 0xff;

   if (exceptBreak == GOOD) {
      if (status == GOOD) {
         OutputStatus(GOOD,OFF);
         OutData(RET_ADDR_LEN,&BDMTempB,ON);
         OutEnd();
      }
      else OutputStatus(NO_FOUND,ON);
   }
   else {
      OutputExceptReason(OFF);
      OutData(RET_ADDR_LEN,&BDMTempB,ON);
      OutEnd();
   }
}

/***************************************************************************
**
**  EmuChecksum
**
**  Description: Michelle memory-group routine, to caculate the data
**               sum value of the specified address range.
**               The "addr1" and "addr2" is the address range.
**               The "size" is the sum-width = 0 --> caculate in byte
**                                           = 1 --> caculate in word
**                                           = 2 --> caculate in long word
**
**  Parameters:
**     input:
**        addr1 -- unsigned long,
**        addr2 -- unsigned long,
**        size  -- int
**
**     output:
**        return status code(error-code) in to the output processor.
**        "O.K." -- Normal return
**        "Fatal error on Emulator" -- Fatal HW error; check status-code
**
**
****************************************************************************/
VOID EmuChecksum(ADDR *addr, U32 addrLen) {
   //U32 m68Reg[MAX_CPU_REG_NO];
   U16 size,checksumData;
   STATUS status;

   AccessIceFlag(READ_ONE,SIZE_FLAG,&size);
   //StopRun();
   if (size == S_WORD) {
      ReduceEven(addr->pos);
      IncreaseEven(addrLen);
   }
   status = Checksum(addr->pos,addr->space,addrLen,size);

   if (exceptBreak == GOOD) {
      OutputStatus(GOOD,OFF);
      checksumData = *(U16 *)(BDMTempB+6);
      if (size == S_BYTE) 
         checksumData &= 0x00FF;   /* byte checksum */
      OutData(2,&checksumData,ON);
      OutEnd();
   }
   else {
      OutputExceptReason(OFF);
      OutData(RET_ADDR_LEN,&BDMTempB,ON);
      OutEnd();
   }
}

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

