//! (HCS12) Writes one word to memory (address must be aligned)
//!
//! @param address 16-bit memory address
//! @param data 16-bit data value
//!
TBDML_API void _tbdml_write_word(unsigned int address, unsigned int data) {
uint8_t buffer[2];

   buffer[0] = data>>8;
   buffer[1] = data;
   USBDM_WriteMemory( 2, 2, address, buffer);
}
//!  (CFv1) Memory write
//!
//! @param addr         24-bit address
//! @param count        8-bit block size (in bytes)
//! @param elementSize  Memory transfer size (1,2 or 4)
//! @param data         Pointer to buffer containing data
//!
//! @return TRUE => Success,\n FALSE => Fail
//!
OSBDM_API BOOL  _opensourcebdm_write_mem( unsigned int address,
                                          unsigned int count,
                                          unsigned int elementSize,
                                          unsigned char *data) {

   return (USBDM_WriteMemory( elementSize, count, address, data) == BDM_RC_OK);
}
//! Clock register write with retry
//!
//! @param addr : clock register address
//! @param data : byte to write
//!
//! @return BDM_RC_OK => success
//!
//! @note writes are retried after a re-connect to cope
//!  with a possible clock speed change.
//!
USBDM_ErrorCode writeClockRegister(uint32_t addr, uint8_t data) {
   LOGGING_Q;
   USBDM_ErrorCode rc;

   rc = USBDM_WriteMemory(1,1,addr,&data);
   if (rc != BDM_RC_OK) {
      Logging::print("Failed write 0x%04X <= 0x%02X) - retrying\n", addr, data);
      rc = USBDM_Connect();
      if (rc == BDM_RC_OK) {
         rc = USBDM_WriteMemory(1,1,addr,&data);
         if (rc != BDM_RC_OK) {
         }
      }
   }
   if (rc != BDM_RC_OK) {
      Logging::print("Failed write 0x%04X <= 0x%02X), rc = %s\n", addr, data, USBDM_GetErrorString(rc));
   }
   return rc;
}
//! De-activate breakpoints. \n
//! This may involve changing target code for RAM breakpoints or
//! modifying target breakpoint hardware.
//!
void deactivateBreakpoints(void) {
   print("deactivateBreakpoints()\n");
   memoryBreakInfo *bpPtr;
   if (!breakpointsActive)
      return;
   for (bpPtr = memoryBreakpoints;
        bpPtr < memoryBreakpoints+MAX_MEMORY_BREAKPOINTS;
        bpPtr++) {
      if (bpPtr->inUse) {
         print("deactivateBreakpoints(MEM@%08X)\n", bpPtr->address);
         USBDM_WriteMemory(2,2,bpPtr->address,bpPtr->opcode);
      }
   }
   USBDM_WriteDReg(CFV1_DRegTDR, TDR_DISABLE);
   breakpointsActive = false;
}
//! De-activate breakpoints. \n
//! This may involve changing target code for RAM breakpoints or
//! modifying target breakpoint hardware.
//!
void deactivateBreakpoints(void) {
   print("deactivateBreakpoints()\n");
   memoryBreakInfo *bpPtr;
   if (!breakpointsActive)
      return;
   // Memory breakpoints
   for (bpPtr = memoryBreakpoints;
        bpPtr < memoryBreakpoints+MAX_MEMORY_BREAKPOINTS;
        bpPtr++) {
      if (bpPtr->inUse) {
         print("deactivateBreakpoints(MEM@%08X)\n", bpPtr->address);
         USBDM_WriteMemory(sizeof(haltOpcode),sizeof(haltOpcode),bpPtr->address,bpPtr->opcode);
      }
   }
   // Hardware breakpoints
   ARM_WriteMemory(4, 4, FP_CTRL, getData4x8Le(FP_CTRL_DISABLE));
   breakpointsActive = false;
}
//! (HCS12, HC08 & RS08) Writes a block of data to memory
//!
//! For HCS12 & HC08 this writes to RAM.
//!
//! @param addr    16-bit memory address
//! @param count   8-bit byte count
//! @param data    Pointer to buffer containing data
//!
TBDML_API void _tbdml_write_block( unsigned int addr,
                                   unsigned int count,
                                   unsigned char *data) {
   USBDM_WriteMemory( 1, count, addr, data);
}
TBDML_API void _tbdml_write_byte( unsigned int  address,
                                   unsigned char data) {
   USBDM_WriteMemory( 1, 1, address, &data);
}
//! Activate breakpoints. \n
//! This may involve changing target code for RAM breakpoints or
//! modifying target breakpoint hardware
//!
void activateBreakpoints(void) {
   print("activateBreakpoints()\n");
   memoryBreakInfo *bpPtr;
   if (breakpointsActive)
      return;
   // Memory breakpoints
   for (bpPtr = memoryBreakpoints;
        bpPtr < memoryBreakpoints+MAX_MEMORY_BREAKPOINTS;
        bpPtr++) {
      if (bpPtr->inUse) {
         print("activateBreakpoints(%s@%08X)\n", getBreakpointName(memoryBreak), bpPtr->address);
         USBDM_ReadMemory(sizeof(haltOpcode),sizeof(haltOpcode),bpPtr->address,bpPtr->opcode);
         USBDM_WriteMemory(sizeof(haltOpcode),sizeof(haltOpcode),bpPtr->address,haltOpcode);
         breakpointsActive = true;
      }
   }
   // Hardware breakpoints
   uint32_t fp_ctrl = FP_CTRL_DISABLE;
   for (int breakPtNum=0; breakPtNum<MAX_HARDWARE_BREAKPOINTS; breakPtNum++) {
      if (hardwareBreakpoints[breakPtNum].inUse) {
         print("activateBreakpoints(%s@%08X)\n", getBreakpointName(hardBreak),
                                                 hardwareBreakpoints[breakPtNum].address&~0x1);
         fp_ctrl = FP_CTRL_ENABLE;
         ARM_WriteMemory(4, 4, FP_COMP0+4*breakPtNum, getFpCompAddress(hardwareBreakpoints[breakPtNum].address));
         breakpointsActive = true;
      }
      else {
         ARM_WriteMemory(4, 4, FP_COMP0+4*breakPtNum, getData4x8Le(FP_COMP_DISABLE));
      }
   }
   ARM_WriteMemory(4, 4, FP_CTRL, getData4x8Le(fp_ctrl));
   // Hardware watches
   for (int watchPtNum=0; watchPtNum<MAX_DATA_WATCHES; watchPtNum++) {
      if (dataWatchPoints[watchPtNum].inUse) {
         unsigned size       = dataWatchPoints[watchPtNum].size;
         unsigned bpSize     = 1;
         unsigned sizeValue  = 0;
         while ((bpSize<size) && (sizeValue<15)) {
            sizeValue++;
            bpSize <<= 1;
         }
         int mode = DWT_FUNCTION_READ_WATCH;
         switch (dataWatchPoints[watchPtNum].type) {
         case readWatch:   mode = DWT_FUNCTION_READ_WATCH;  break;
         case writeWatch:  mode = DWT_FUNCTION_WRITE_WATCH; break;
         case accessWatch: mode = DWT_FUNCTION_RW_WATCH;    break;
         default : break;
         }
         print("activateBreakpoints(%s@%08X)\n", getBreakpointName(dataWatchPoints[watchPtNum].type),
                                                 dataWatchPoints[watchPtNum].address);
         print("activateBreakpoints(%s@%08X, bpSize=%d, sizeValue=%d)\n",
               getBreakpointName(dataWatchPoints[watchPtNum].type),
               dataWatchPoints[watchPtNum].address&(~(bpSize-1)),
               bpSize, sizeValue );
         ARM_WriteMemory(4, 4, DWT_COMP0+16*watchPtNum,     getData4x8Le(dataWatchPoints[watchPtNum].address));
         ARM_WriteMemory(4, 4, DWT_MASK0+16*watchPtNum,     getData4x8Le(sizeValue));
         ARM_WriteMemory(4, 4, DWT_FUNCTION0+16*watchPtNum, getData4x8Le(mode));
         breakpointsActive = true;
      }
      else {
         ARM_WriteMemory(4, 4, DWT_FUNCTION0+16*watchPtNum, getData4x8Le(DWT_FUNCTION_NONE));
      }
   }
}
//! Activate breakpoints. \n
//! This may involve changing target code for RAM breakpoints or
//! modifying target breakpoint hardware
//!
void activateBreakpoints(void) {
   print("activateBreakpoints()\n");
   static const uint8_t haltOpcode[] = {0x4a, 0xc8};
   memoryBreakInfo *bpPtr;
   if (breakpointsActive)
      return;
   for (bpPtr = memoryBreakpoints;
        bpPtr < memoryBreakpoints+MAX_MEMORY_BREAKPOINTS;
        bpPtr++) {
      if (bpPtr->inUse) {
         print("activateBreakpoints(%s@%08X)\n", getBreakpointName(memoryBreak), bpPtr->address);
         USBDM_ReadMemory(2,2,bpPtr->address,bpPtr->opcode);
         USBDM_WriteMemory(2,2,bpPtr->address,haltOpcode);
         breakpointsActive = true;
      }
   }
   uint32_t tdrValue = TDR_DISABLE;
   if (hardwareBreakpoints[0].inUse) {
      tdrValue |= TDR_TRC_HALT|TDR_L1T|TDR_L1EBL|TDR_L1EPC;
      USBDM_WriteDReg(CFVx_DRegPBR0, hardwareBreakpoints[0].address&~0x1);
      USBDM_WriteDReg(CFVx_DRegPBMR, 0x00000000);
      breakpointsActive = true;
      print("activateBreakpoints(%s@%08X)\n", getBreakpointName(hardBreak),
                                              hardwareBreakpoints[0].address&~0x1);
   }
   if (hardwareBreakpoints[1].inUse) {
      tdrValue |= TDR_TRC_HALT|TDR_L1T|TDR_L1EBL|TDR_L1EPC;
      USBDM_WriteDReg(CFVx_DRegPBR1, hardwareBreakpoints[1].address|0x1);
      breakpointsActive = true;
      print("activateBreakpoints(%s@%08X)\n", getBreakpointName(hardBreak),
                                              hardwareBreakpoints[1].address&~0x1);
   }
   else {
      USBDM_WriteDReg(CFVx_DRegPBR1,0);
   }
   if (hardwareBreakpoints[2].inUse) {
      tdrValue |= TDR_TRC_HALT|TDR_L1T|TDR_L1EBL|TDR_L1EPC;
      USBDM_WriteDReg(CFVx_DRegPBR2, hardwareBreakpoints[2].address|0x1);
      breakpointsActive = true;
      print("activateBreakpoints(%s@%08X)\n", getBreakpointName(hardBreak),
                                              hardwareBreakpoints[2].address&~0x1);
   }
   else {
      USBDM_WriteDReg(CFVx_DRegPBR2,0);
   }
   if (hardwareBreakpoints[3].inUse) {
      tdrValue |= TDR_TRC_HALT|TDR_L1T|TDR_L1EBL|TDR_L1EPC;
      USBDM_WriteDReg(CFVx_DRegPBR3, hardwareBreakpoints[3].address|0x1);
      breakpointsActive = true;
      print("activateBreakpoints(%s@%08X)\n", getBreakpointName(hardBreak),
                                              hardwareBreakpoints[3].address&~0x1);
   }
   else {
      USBDM_WriteDReg(CFV1_DRegPBR3,0);
   }
   if (dataWatchPoints[0].inUse) {
      tdrValue |= TDR_TRC_HALT|TDR_L1T|TDR_L1EBL|TDR_L1EA_INC;
      USBDM_WriteDReg(CFVx_DRegABLR, dataWatchPoints[0].address);
      USBDM_WriteDReg(CFVx_DRegABHR, dataWatchPoints[0].address+dataWatchPoints[0].size-1);
      breakpointsActive = true;
   }
   USBDM_WriteDReg(CFVx_DRegTDR, tdrValue);
}
USBDM_ErrorCode HCS12Unsecure::programSecurityLocation() {

   uint8_t unsecureFlashValue[] = {0xFF, 0xFE};
   const uint8_t allOnes   = 0xFF;
   const uint8_t allZeroes = 0x00;
   int timeout;
   uint8_t statValue;
   uint8_t Hex_02 = 0x02;
   uint8_t Hex_30 = 0x30;

   // Unsecure Chip - re-programming flash NVSEC
   print( "HCS12Unsecure::programSecurityLocation() - unsecuring target\n");

   USBDM_WriteMemory(1, 1, HCS12DeviceData::getCOPCTL(), &COPCTL_value);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFTSTMODAddress(), &allZeroes);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFSTATAddress(),   &Hex_02);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFCLKDIVAddress(), &fcdivValue);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFCLKDIVAddress(), &fcdivValue);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFPROTAddress(),   &allOnes);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFSTATAddress(),   &Hex_30);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFTSTMODAddress(), &allZeroes);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFSTATAddress(),   &Hex_02);
   USBDM_WriteMemory(2, 2, HCS12DeviceData::getNVSECAddress()-1, unsecureFlashValue);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFCMDAddress(),    &mWordProg);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFSTATAddress(),   &startFlashCommand);


   // Program non-volatile Flash security location
//   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFPROTAddress(),   &allOnes);
//   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFSTATAddress(),   &HCS12_clearFlashErrors);
//   USBDM_WriteMemory(2, 2, HCS12DeviceData::getNVSECAddress()-1, unsecureFlashValue);
//   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFCMDAddress(),    &mWordProg);
//   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFSTATAddress(),   &startFlashCommand);
   // Wait for flash command to complete
   timeout = 10;
   do {
      // Programming should take ???
      wxMilliSleep(100);
      if (USBDM_ReadMemory(1,1,HCS12DeviceData::getFSTATAddress(),&statValue) != BDM_RC_OK)
         return BDM_RC_FAIL;
      if (timeout-- == 0)
         return BDM_RC_FAIL;
   } while ((statValue & 0xC0) != 0xC0);
   return BDM_RC_OK;
}
//! \brief Does Bulk Erase of Target Flash.
//!
//! @return error code, see \ref FlashError_t
//!
//! @note The target is not reset so current security state persists after erase.
//!
USBDM_ErrorCode HCS12Unsecure::bulkEraseMemory() {
   const uint8_t allOnes        = 0xFF;
   const uint8_t allZeroes      = 0x00;
   uint8_t dummyFlashAddress[]  = {0xFF, 0xFE};
   uint8_t dummyEepromAddress[] = {0x0C, 0x00};
   uint8_t dummyFlashData[]     = {0xFF, 0xFF};
   int timeout;
   uint8_t statValue;
   USBDM_ErrorCode rc;

   print("HCS12Unsecure::bulkEraseMemory():Bulk erasing target...\n");

   // Erase chip
   //=============================

   // Set up flash & eeprom
   rc = initialiseTargetFlash();
   if (rc != BDM_RC_OK) {
      print("HCS12Unsecure::bulkEraseMemory(): initialiseTargetFlash() failed, reason=%s\n",
            USBDM_GetErrorString(rc));
      return rc;
   }

   print("HCS12Unsecure::bulkEraseMemory():Bulk erasing target Flash...\n");
   // Apply Bulk Erase operation to all Flash banks
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFPROTAddress(),   &allOnes);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFSTATAddress(),   &HCS12_clearFlashErrors);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFTSTMODAddress(), &allZeroes);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFSTATAddress(),   &HCS12_clearFlashErrors);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFTSTMODAddress(), &HCS12_FTSTMOD_WRALL);
   USBDM_WriteMemory(2, 2, HCS12DeviceData::getFADDRAddress(),   dummyFlashAddress);
   USBDM_WriteMemory(2, 2, HCS12DeviceData::getFDATAAddress(),   dummyFlashData);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFCMDAddress(),    &mMassErase);
   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFSTATAddress(),   &startFlashCommand);

   // Wait for flash command to complete
   timeout = 10;
   do {
      // Mass erase should take ~100ms
      wxMilliSleep(100);
      if (USBDM_ReadMemory(1,1,HCS12DeviceData::getFSTATAddress(),&statValue) != BDM_RC_OK)
         return BDM_RC_FAIL;
      if (timeout-- == 0)
         return BDM_RC_FAIL;
   } while ((statValue & 0xC0) != 0xC0);

   USBDM_WriteMemory(1, 1, HCS12DeviceData::getFTSTMODAddress(), &allZeroes);

   if (hasEEPROM) {
      print("HCS12Unsecure::bulkEraseMemory():Bulk erasing target EEPROM...\n");
      // Apply Bulk Erase operation to EEPROM
      USBDM_WriteMemory(1, 1, HCS12DeviceData::getEPROTAddress(),   &allOnes);
      USBDM_WriteMemory(1, 1, HCS12DeviceData::getESTATAddress(),   &HCS12_clearFlashErrors);
      USBDM_WriteMemory(2, 2, HCS12DeviceData::getEADDRAddress(),   dummyEepromAddress);
      USBDM_WriteMemory(2, 2, HCS12DeviceData::getEDATAAddress(),   dummyFlashData);
      USBDM_WriteMemory(1, 1, HCS12DeviceData::getECMDAddress(),    &mMassErase);
      USBDM_WriteMemory(1, 1, HCS12DeviceData::getESTATAddress(),   &startFlashCommand);

      // Wait for flash command to complete
      timeout = 10;
      do {
         // Mass erase should take ~100ms
         wxMilliSleep(100);
         if (USBDM_ReadMemory(1,1,HCS12DeviceData::getESTATAddress(),&statValue) != BDM_RC_OK)
            return BDM_RC_FAIL;
         if (timeout-- == 0)
            return BDM_RC_FAIL;
      } while ((statValue & 0xC0) != 0xC0);
   }
   USBDM_TargetReset((TargetMode_t)(RESET_HARDWARE|RESET_SPECIAL));

   unsigned long usbdmStatus;
   USBDM_ReadStatusReg(&usbdmStatus);

   if ((usbdmStatus & HC12_BDMSTS_UNSEC) != 0) {
      print("HCS12Unsecure::bulkEraseMemory():Bulk erasing target memory...Complete\n");
      return BDM_RC_OK;
   }
   else {
      print("HCS12Unsecure::bulkEraseMemory():Bulk erasing target memory...Failed!\n");
      return BDM_RC_FAIL;
   }
}
//! \brief Sets the FCLKDIV/ECLKDIV register value based on
//! the BDM communication speed.
//!
//! Prepares the target for Flash and eeprom operations. \n
//! - Unprotects flash & eeprom [FPROT/EPROT registers]
//! - Set Flash clock divider [FCLKDIV/ECLKDIV registers] based busFrequency
//! - Clear flash & eeprom errors [FSTAT/ESTAT registers]
//! - Unprotects flash (FPROT)
//! - Set Flash clock divider (FCDIV)
//! - Disable COP
//!
//! @return error code, see \ref FlashError_t
//!
//! @note Assumes target has been reset & connected
//!
USBDM_ErrorCode HCS12Unsecure::initialiseTargetFlash() {
   uint8_t  fcdivCheckValue;
   static const unsigned char allOnes   = 0xFF;
   static const unsigned char allZeroes = 0x00;
   USBDM_ErrorCode rc;

   print("HCS12Unsecure::initialiseTargetFlash()\n");

   // Determine the FCLKDIV/ECLKDIV value
   //=========================================

   // Set user supplied speed
   rc = USBDM_SetSpeed(busFrequency);
   if ((rc != BDM_RC_OK) && (rc != BDM_RC_BDM_EN_FAILED))
      return rc;

   // Calculate the required FCDIV value
   fcdivValue = findFlashDividerValue((long)round(busFrequency), 150, 200);

   print("HCS12Unsecure::initialiseTargetFlash(): fBus = %ld kHz, FCDIV=0x%2.2X (%d)\n",
                   (long)round(busFrequency/1000.0), fcdivValue, fcdivValue);

   if (fcdivValue == 0xFF)
      return BDM_RC_UNKNOWN_SPEED;

   // Disable COP
   if (USBDM_WriteMemory(1, 1, HCS12DeviceData::getCOPCTL(), &COPCTL_value) != BDM_RC_OK)
      return BDM_RC_FAIL;

   //   fcdivValue  |= 0x80;

   // Unprotect the Flash
   if (USBDM_WriteMemory(1,1,HCS12DeviceData::getFPROTAddress(),&allOnes) != BDM_RC_OK)
      return BDM_RC_FAIL;

   // Clear any Flash errors (set FTSTMOD_WALL so all banks are affected)
   if (USBDM_WriteMemory(1, 1, HCS12DeviceData::getFTSTMODAddress(), &HCS12_FTSTMOD_WRALL) != BDM_RC_OK)
      return BDM_RC_FAIL;
   if (USBDM_WriteMemory(1,1,HCS12DeviceData::getFSTATAddress(),&HCS12_clearFlashErrors) != BDM_RC_OK)
      return BDM_RC_FAIL;
   if (USBDM_WriteMemory(1, 1, HCS12DeviceData::getFTSTMODAddress(), &allZeroes) != BDM_RC_OK)
      return BDM_RC_FAIL;

   // Set FCLKDIV value & verify
   if (USBDM_WriteMemory(1,1,HCS12DeviceData::getFCLKDIVAddress(),&fcdivValue) != BDM_RC_OK)
      return BDM_RC_FAIL;
   if (USBDM_ReadMemory(1,1,HCS12DeviceData::getFCLKDIVAddress(),&fcdivCheckValue) != BDM_RC_OK)
      return BDM_RC_FAIL;
   if (fcdivCheckValue != (fcdivValue|FCLKDIV_FDIVLD))
      return BDM_RC_FAIL;


   if (hasEEPROM) {
      // Set up the eeprom
      //====================================

      // Assume errors here mean there isn't actually any EEPROM
      // Unprotect the eeprom
      if (USBDM_WriteMemory(1,1,HCS12DeviceData::getEPROTAddress(),&allOnes) != BDM_RC_OK)
         hasEEPROM = false;

      // Clear any eeprom errors
      if (USBDM_WriteMemory(1,1,HCS12DeviceData::getESTATAddress(),&HCS12_clearFlashErrors) != BDM_RC_OK)
         hasEEPROM = false;

      // Set ECLKDIV value & verify
      if (USBDM_WriteMemory(1,1,HCS12DeviceData::getECLKDIVAddress(),&fcdivValue) != BDM_RC_OK)
         hasEEPROM = false;
      if (USBDM_ReadMemory(1,1,HCS12DeviceData::getECLKDIVAddress(),&fcdivCheckValue) != BDM_RC_OK)
         hasEEPROM = false;
      if (fcdivCheckValue != (fcdivValue|ECLKDIV_EDIVLD))
         hasEEPROM = false;
   }
   return BDM_RC_OK;
}
//! (HCS12, HC08 & RS08) Writes a block of data to memory
//!
//! For HCS12 & HC08 this writes to RAM.
//! For RS08 this may write to RAM or Flash if preceded by _opensourcebdm_mem_dlstart().
//!
//! @param addr    16-bit memory address
//! @param count   8-bit byte count
//! @param data    Pointer to buffer containing data
//!
OSBDM_API void _opensourcebdm_write_block( unsigned int addr,
                                           unsigned int count,
                                           unsigned char *data) {
   USBDM_WriteMemory( 1, count, addr, data);
}
OSBDM_API void _opensourcebdm_write_byte( unsigned int  address,
                                          unsigned char data) {
   USBDM_WriteMemory( 1, 1, address, &data);
}