//! 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));
      }
   }
}
//!  2.2.8.2 Execute a Single Step
//!
//! @param dnNrInstructions
//!
USBDM_GDI_DECLSPEC
DiReturnT DiExecSingleStep ( DiUInt32T dnNrInstructions ) {
   LOGGING_Q;
   log.print("(%d)\n", dnNrInstructions);

   USBDM_ErrorCode BDMrc;
   long unsigned ccrValue;
   long unsigned pcValue;
   unsigned char currentOpcode;
   const int interruptMask = (1<<3);
   const int tapOpcode  = 0x84;
   const int tpaOpcode  = 0x85;
   const int seiOpcode  = 0x9B;
   const int cliOpcode  = 0x9A;
   const int waitOpcode = 0x8F;
   const int rtiOpcode  = 0x80;
   const int swiOpcode  = 0x83;
   const int stopOpcode = 0x8E;

   CHECK_ERROR_STATE();
#if (TARGET == MC56F80xx)
   BDMrc = DSC_TargetStepN(dnNrInstructions);
#else
   if (dnNrInstructions>1) {
      log.print("DiExecSingleStep() - Only a single step is supported!\n");
      return setErrorState(DI_ERR_PARAM, ("Only a single step is allowed"));
   }
/*
 * Cases to consider when masking interrupts during step
 *
 * +--------+-----------+---------+-----------------------------------------------------+
 * | Opcode | Initial I | Final I | Problem - action                                    |
 * +--------+-----------+---------+-----------------------------------------------------+
 * | ---    |     1     |    X    | None - no action (interrupts already masked)        |
 * +--------+-----------+---------+-----------------------------------------------------+
 * | CLI    |     0     |    ?    | It may be possible for an interrupt to occur,       |
 * | WAIT   |           |         | setting I-flag which is then incorrectly cleared.   |
 * | STOP   |           |         | (I don't think it applies to CLI but be safe.)      |
 * | SWI    |           |         | - don't 'fix' CCR                                   |
 * +--------+-----------+---------+-----------------------------------------------------+
 * | RTI    |     0     |    1    | Contrived but possible situation. I flag            |
 * |        |           |         | incorrectly cleared - don't 'fix' CCR               |
 * +--------+-----------+---------+-----------------------------------------------------+
 * | SEI    |     0     |    1    | The instruction may set I-flag which is then        |
 * | TAP    |     0     |    1    | incorrectly cleared - don't 'fix' CCR               |
 * +--------+-----------+---------+-----------------------------------------------------+
 * | TPA    |     0     |    X    | The wrong value is transferred to A - fix A         |
 * +--------+-----------+---------+-----------------------------------------------------+
 * | ---    |     0     |    0    | CCR change - clear I-flag in new CCR                |
 * +--------+-----------+---------+-----------------------------------------------------+
 */
   if (bdmOptions.maskInterrupts) {
      log.print("DiExecSingleStep() - checking if interrupt masking needed\n");
      USBDM_ReadReg(HCS08_RegCCR, &ccrValue);
      if ((ccrValue&interruptMask) != 0) {
         // Interrupts already masked - just step
         BDMrc = USBDM_TargetStep();
      }
      else {
         // Mask interrupts
         log.print("DiExecSingleStep() - masking interrupts\n");
         USBDM_WriteReg(HCS08_RegCCR, ccrValue|interruptMask);
         // Get current instruction opcode
         USBDM_ReadReg(HCS08_RegPC, &pcValue);
         USBDM_ReadMemory(1,1,pcValue,&currentOpcode);
         // Do a step
         BDMrc = USBDM_TargetStep();
         switch(currentOpcode) {
            case cliOpcode  :
            case waitOpcode :
            case seiOpcode  :
            case tapOpcode  :
            case rtiOpcode  :
            case swiOpcode  : // Not ever stepped - treated as subroutine?
            case stopOpcode :
               log.print("DiExecSingleStep() - skipping CCR restore\n");
               // Don't 'fix' CCR as updated by instruction or int ack
               break;
            case tpaOpcode :
               // Fix A & CCR  (clear I flag)
               log.print("DiExecSingleStep() - fixing A & CCR reg\n");
               USBDM_WriteReg(HCS08_RegA, ccrValue&~interruptMask);
               USBDM_WriteReg(HCS08_RegCCR, ccrValue&~interruptMask);
               break;
            default :
               // Fix CCR (clear I flag)
               // Unmask interrupts
               log.print("DiExecSingleStep() - fixing CCR reg\n");
               USBDM_ReadReg(HCS08_RegCCR, &ccrValue);
               USBDM_WriteReg(HCS08_RegCCR, ccrValue&~interruptMask);
               break;
         }
      }
   }
   else
      BDMrc = USBDM_TargetStep();
#endif
   if (BDMrc != BDM_RC_OK) {
      return setErrorState(DI_ERR_NONFATAL, BDMrc);
   }
   return setErrorState(DI_OK);
}
//! 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);
}
//! \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;
}
//!  Determines the trim value for the target internal clock.
//!  The target clock is left trimmed for a bus freq. of targetBusFrequency.
//!
//!     Target clock has been suitably configured.
//!
//!  @param      trimAddress           Address of trim register.
//!  @param      targetBusFrequency    Target Bus Frequency to trim to.
//!  @param      returnTrimValue       Resulting trim value (9-bit number)
//!  @param      measuredBusFrequency  Resulting Bus Frequency
//!  @param      do9BitTrim            True to do 9-bit trim (rather than 8-bit)
//!
//!  @return
//!   == \ref PROGRAMMING_RC_OK  => Success \n
//!   != \ref PROGRAMMING_RC_OK  => Various errors
//!
USBDM_ErrorCode FlashProgrammer::trimTargetClock(uint32_t       trimAddress,
                                                 unsigned long  targetBusFrequency,
                                                 uint16_t      *returnTrimValue,
                                                 unsigned long *measuredBusFrequency,
                                                 int            do9BitTrim){
   LOGGING;
   uint8_t          mask;
   uint8_t          trimMSB, trimLSB, trimCheck;
   int              trimValue;
   int              maxRange;
   int              minRange;
   unsigned         long bdmSpeed;
   int              index;
   USBDM_ErrorCode  rc = PROGRAMMING_RC_OK;

#if TARGET == RS08
   mask = RS08_BDCSCR_CLKSW;
#elif TARGET == HCS08
   mask = HC08_BDCSCR_CLKSW;
#elif TARGET == HCS12
   mask = HC12_BDMSTS_CLKSW;
#elif TARGET == CFV1
   mask = CFV1_XCSR_CLKSW;
#endif

   unsigned long BDMStatusReg;
   rc = USBDM_ReadStatusReg(&BDMStatusReg);
   if ((BDMStatusReg&mask) == 0) {
      Logging::print("Setting CLKSW\n");
      BDMStatusReg |= mask;
#if TARGET == CFV1
      // Make sure we don't accidently do a mass erase
      mask &= ~CFV1_XCSR_ERASE;
#endif
      rc = USBDM_WriteControlReg(BDMStatusReg);
      rc = USBDM_Connect();
   }
   static const int maxTrim        = 505;   // Maximum acceptable trim value
   static const int minTrim        =   5;   // Minimum acceptable trim value
   static const int SearchOffset   =   8;   // Linear sweep range is +/- this value
   static const unsigned char zero =   0;
   const unsigned long targetBDMFrequency = targetBusFrequency/parameters.getBDMtoBUSFactor();
   int numAverage;     // Number of times to repeat measurements

   double sumX          = 0.0;
   double sumY          = 0.0;
   double sumXX         = 0.0;
   double sumYY         = 0.0;
   double sumXY         = 0.0;
   double num           = 0.0;
   double alpha, beta;
   double trimValueF;

   Logging::print("targetBusFrequency=%ld, targetBDMFrequency=%ld)\n", targetBusFrequency, targetBDMFrequency);

   flashReady = FALSE; // Not configured for Flash access

   // Set safe defaults
   *returnTrimValue      = 256;
   *measuredBusFrequency = 10000;
   trimMSB               = 0;

   // Set LSB trim value = 0
   if (writeClockRegister(trimAddress+1, zero) != BDM_RC_OK) {
      return PROGRAMMING_RC_ERROR_BDM_WRITE;
   }
   // Initial binary search (MSB only)
   for (mask = 0x80; mask > 0x0; mask>>=1) {
      trimMSB |= mask;
      // Set trim value (MSB only)
      if (writeClockRegister(trimAddress, trimMSB) != BDM_RC_OK) {
         return PROGRAMMING_RC_ERROR_BDM_WRITE;
      }
      // Check target speed
      if (USBDM_Connect() != BDM_RC_OK) {
         return PROGRAMMING_RC_ERROR_BDM_CONNECT;
      }
      if (USBDM_GetSpeed(&bdmSpeed) != BDM_RC_OK) {
         return PROGRAMMING_RC_ERROR_BDM_WRITE;
      }
      bdmSpeed *= 1000; // convert to Hz

    Logging::print("Binary search: trimMSB=0x%02X (%d), bdmSpeed=%ld%c\n",
            trimMSB, trimMSB, bdmSpeed, (bdmSpeed<targetBDMFrequency)?'-':'+');

      // Adjust trim value
      if (bdmSpeed<targetBDMFrequency) {
         trimMSB &= ~mask; // too slow
      }
      if (trimMSB > maxTrim/2) {
         trimMSB = maxTrim/2;
      }
      if (trimMSB < minTrim/2) {
         trimMSB = minTrim/2;
      }
   }

   // Binary search value is middle of range to sweep

   trimValue = trimMSB<<1;  // Convert to 9-bit value

   // Linear sweep +/-SEARCH_OFFSET, starting at higher freq (smaller Trim)
   // Range is constrained to [minTrim..maxTrim]

   maxRange = trimValue + SearchOffset;
   if (maxRange > maxTrim) {
      maxRange = maxTrim;
   }
   minRange = trimValue - SearchOffset;
   if (minRange < minTrim) {
      minRange = minTrim;
   }
//   Logging::print("trimTargetClock(): Linear sweep, f=%6ld    \n"
//                   "trimTargetClock():    Trim       frequency \n"
//                   "========================================== \n",
//                   targetBDMFrequency/1000);

   if (do9BitTrim) {
      numAverage = 2;
   }
   else {
      numAverage = 4;
   }
   for(trimValue=maxRange; trimValue>=minRange; trimValue--) {
      trimLSB = trimValue&0x01;
      trimMSB = (uint8_t)(trimValue>>1);
      if (do9BitTrim) {
         // Write trim LSB
         if (writeClockRegister(trimAddress+1, trimLSB) != BDM_RC_OK)
            return PROGRAMMING_RC_ERROR_BDM_WRITE;
         if (USBDM_ReadMemory(1, 1, trimAddress+1,   &trimCheck) != BDM_RC_OK)
            return PROGRAMMING_RC_ERROR_BDM_WRITE;
         if ((trimCheck&0x01) != trimLSB)
            return PROGRAMMING_RC_ERROR_BDM_WRITE;
      }
      else if (trimValue&0x01) {
         // skip odd trim values if 8-bit trim
         continue;
      }
      // Write trim MSB
      if (writeClockRegister(trimAddress,  trimMSB) != BDM_RC_OK) {
         return PROGRAMMING_RC_ERROR_BDM_WRITE;
      }
      if (USBDM_ReadMemory(1, 1, trimAddress,   &trimCheck) != BDM_RC_OK) {
         return PROGRAMMING_RC_ERROR_BDM_WRITE;
      }
      if (trimCheck != trimMSB) {
         return PROGRAMMING_RC_ERROR_BDM_WRITE;
      }
      //milliSleep(100);
      // Measure sync multiple times
      for(index=numAverage; index>0; index--) {
         // Check target speed
         if (USBDM_Connect() != BDM_RC_OK)
            return PROGRAMMING_RC_ERROR_BDM_CONNECT;
         if (USBDM_GetSpeedHz(&bdmSpeed) != BDM_RC_OK)
            return PROGRAMMING_RC_ERROR_BDM_CONNECT;
         sumX  += trimValue;
         sumY  += bdmSpeed;
         sumXX += trimValue*trimValue;
         sumYY += bdmSpeed*bdmSpeed;
         sumXY += bdmSpeed*trimValue;
         num   += 1.0;
//         Logging::print("trimTargetClock(): %6d    %10ld %10ld\n", trimValue, bdmSpeed, targetBDMFrequency-bdmSpeed);
      }
   }
   for(trimValue=minRange; trimValue<=maxRange; trimValue++) {
      trimLSB = trimValue&0x01;
      trimMSB = (uint8_t)(trimValue>>1);
      if (do9BitTrim) {
         // Write trim LSB
         if (writeClockRegister(trimAddress+1, trimLSB) != BDM_RC_OK) {
            return PROGRAMMING_RC_ERROR_BDM_WRITE;
         }
         if (USBDM_ReadMemory(1, 1, trimAddress+1,   &trimCheck) != BDM_RC_OK) {
            return PROGRAMMING_RC_ERROR_BDM_WRITE;
         }
         if ((trimCheck&0x01) != trimLSB) {
            return PROGRAMMING_RC_ERROR_BDM_WRITE;
         }
      }
      else if (trimValue&0x01) {
         // skip odd trim values if 8-bit trim
         continue;
      }
      // Write trim MSB
      if (writeClockRegister(trimAddress, trimMSB) != BDM_RC_OK) {
         return PROGRAMMING_RC_ERROR_BDM_WRITE;
      }
      if (USBDM_ReadMemory(1, 1, trimAddress,   &trimCheck) != BDM_RC_OK) {
         return PROGRAMMING_RC_ERROR_BDM_WRITE;
      }
      if (trimCheck != trimMSB) {
         return PROGRAMMING_RC_ERROR_BDM_WRITE;
      }
      //milliSleep(100);
      // Measure sync multiple times
      for(index=numAverage; index>0; index--) {
         // Check target speed
         if (USBDM_Connect() != BDM_RC_OK) {
            return PROGRAMMING_RC_ERROR_BDM_CONNECT;
         }
         if (USBDM_GetSpeedHz(&bdmSpeed) != BDM_RC_OK) {
            return PROGRAMMING_RC_ERROR_BDM_CONNECT;
         }
         sumX  += trimValue;
         sumY  += bdmSpeed;
         sumXX += trimValue*trimValue;
         sumYY += bdmSpeed*bdmSpeed;
         sumXY += bdmSpeed*trimValue;
         num   += 1.0;
//         Logging::print("trimTargetClock(): %6d    %10ld %10ld\n", trimValue, bdmSpeed, targetBDMFrequency-bdmSpeed);
      }
   }

//   Logging::print("N=%f, sumX=%f, sumXX=%f, sumY=%f, sumYY=%f, sumXY=%f\n",
//                    num, sumX, sumXX, sumY, sumYY, sumXY);

   // Calculate linear regression co-efficients
   beta  = (num*sumXY-sumX*sumY)/(num*sumXX-sumX*sumX);
   alpha = (sumY-beta*sumX)/num;

   // Estimate required trim value
   trimValueF = ((targetBDMFrequency-alpha)/beta);

   if ((trimValueF <= 5.0) || (trimValue >= 505.0)) { // resulted in extreme value
      trimValueF = 256.0;                             // replace with 'Safe' trim value
      rc = PROGRAMMING_RC_ERROR_TRIM;
   }

   trimValue = (int)round(trimValueF);
   trimMSB   = trimValue>>1;
   trimLSB   = trimValue&0x01;

//   Logging::print("alpha= %f, beta= %f, trimF= %f, trimMSB= %d (0x%02X), trimLSB= %d\n",
//           alpha, beta, trimValueF, trimMSB, trimMSB, trimLSB);

//   Logging::print("trimTargetClock() Result: trim=0x%3.3x (%d), measured bdmSpeed=%ld\n",
//           savedTrimValue, savedTrimValue, bestFrequency);

   *returnTrimValue  = trimValue;

   // Set trim value (LSB first)
   if ((do9BitTrim && (writeClockRegister(trimAddress+1, trimLSB) != BDM_RC_OK)) ||
       (writeClockRegister(trimAddress, trimMSB) != BDM_RC_OK)) {
      return PROGRAMMING_RC_ERROR_BDM_WRITE;
   }
   // Check connection at that speed
   if (USBDM_Connect() != BDM_RC_OK) {
      return PROGRAMMING_RC_ERROR_BDM_CONNECT;
   }
   if (USBDM_GetSpeedHz(&bdmSpeed) != BDM_RC_OK) {
      return PROGRAMMING_RC_ERROR_BDM_CONNECT;
   }
   *measuredBusFrequency = bdmSpeed*parameters.getBDMtoBUSFactor();

   return rc;
}
//! (HCS12, HC08 & RS08) Reads block from memory
//!
//! @param addr    16-bit memory address
//! @param count   8-bit byte count
//! @param data    Pointer to buffer to contain data
//!
OSBDM_API void _opensourcebdm_read_block( unsigned int  addr,
                                          unsigned int  count,
                                          unsigned char *data) {

   USBDM_ReadMemory( 1, count, addr, data);
}
//! (HCS12, HC08 & RS08) Reads one byte from memory
//!
//! @param address 16-bit memory address
//!
//! @return 8-bit value
//!
OSBDM_API unsigned char _opensourcebdm_read_byte(unsigned int address) {
U8 value;

   USBDM_ReadMemory( 1, 1, address, &value);
   return value;
}