//! Insert breakpoint
//!
//! @param type     one of memoryBreak, hardBreak, writeWatch, readWatch, accessWatch
//! @param address  address for breakpoint/watchpoint
//! @param size     range to watch (watchpoint only)
//!
//! @return true  => OK\n
//!         false => error
//!
//! @note writeWatch, readWatch, accessWatch not currently working
//!
int GdbBreakpoints::insertBreakpoint(BreakType type, uint32_t address, unsigned size) {
   LOGGING_Q;
   log.print("(%s, a=%08X, s=%d)\n", getBreakpointName(type), address, size);

   switch (type) {
   case memoryBreak: {
      if (findMemoryBreakpoint(address)) {
         log.print("- already set - ignored\n");
         return true; // Already set - ignore
      }
      MemoryBreakInfo *bpPtr = findFreeMemoryBreakpoint();
      if (bpPtr == NULL) {
         return false; // Too many breakpoints
      }
      bpPtr->address = address;
      bpPtr->inUse   = true;
      return true; // Done
      }
      break;
   case hardBreak: {
      if (findHardwareBreakpoint(address)) {
         log.print("- already set - ignored\n");
         return true; // Already set - ignore
      }
      HardwareBreakInfo *bpPtr = findFreeHardwareBreakpoint();
      if (bpPtr == NULL) {
         return false; // Too many breakpoints
      }
      bpPtr->address = address;
      bpPtr->inUse   = true;
      return true; // Done
      }
      break;
   case writeWatch:
   case readWatch:
   case accessWatch: {
      DataBreakInfo *bpPtr = findDataWatchPoint(address);
      if (bpPtr != NULL) {
         if (size > bpPtr->size)
            bpPtr->size = size;
         if (type != bpPtr->type)
            bpPtr->type = accessWatch;
         return true; // Already set - update
      }
      bpPtr = findFreeDataWatchPoint();
      if (bpPtr == NULL) {
         return false; // Too many breakpoints
      }
      bpPtr->address = address;
      bpPtr->size    = size;
      bpPtr->type    = (BreakType)type;
      bpPtr->inUse   = true;
      return true; // Done
      }
      break;
   default:
      return false; // Unknown type
   }
}
//! RAM based breakpoints leave the PC pointing at the instruction following
//  the HALT instruction.  This routine checks for this situation and adjusts
//! the target PC.
//!
void checkAndAdjustBreakpointHalt(void) {
   // Processor halted
   unsigned long pcAddress = 0;
   USBDM_ReadPC(&pcAddress);
   pcAddress -= 2;
   if (breakpointsActive && (findMemoryBreakpoint(pcAddress) != NULL)) {
      print("checkAndAdjustBreakpointHalt() - adjusting PC=%08lX\n", pcAddress);
      USBDM_WritePC(pcAddress);
   }
}
//! Remove breakpoint
//!
//! @param type     one of memoryBreak, hardBreak, writeWatch, readWatch, accessWatch
//! @param address  address for breakpoint/watchpoint
//! @param size     not used
//!
int GdbBreakpoints::removeBreakpoint(BreakType type, uint32_t address, unsigned size) {
   LOGGING_Q;
   log.print("(%s, a=%08X, s=%d)\n", getBreakpointName(type), address, size);
   switch (type) {
   case memoryBreak: {
      MemoryBreakInfo *bpPtr = findMemoryBreakpoint(address);
      if (bpPtr == NULL) {
         return false; // Non-existent breakpoint
      }
      bpPtr->inUse     = false;
      bpPtr->address   = 0;
      bpPtr->opcode[0] = 0;
      bpPtr->opcode[1] = 0;
      log.print("(t=MEM, a=%08X, s=%d) - done\n", address, size);
      return true; // Done
   }
   break;
   case hardBreak: {
      HardwareBreakInfo *bpPtr = findHardwareBreakpoint(address);
      if (bpPtr == NULL) {
         return false; // Non-existent breakpoint
      }
      bpPtr->inUse     = false;
      log.print("(t=HW, a=%08X, s=%d) - done\n", address, size);
      return true; // Done
      }
      break;
   case writeWatch:
   case readWatch:
   case accessWatch: {
      DataBreakInfo *bpPtr = findDataWatchPoint(address);
      if (bpPtr == NULL) {
         return false; // Non-existent breakpoint
      }
      bpPtr->inUse     = false;
      return true; // Done
      }
      break;
   default:
      return false; // Unknown type
   }
}
int atMemoryBreakpoint() {
   unsigned long pcAddress = 0;
   USBDM_ReadPC(&pcAddress);
   return (findMemoryBreakpoint(pcAddress) != NULL);
}
bool GdbBreakpoints::atMemoryBreakpoint(unsigned long  pcAddress) {
   return (findMemoryBreakpoint(pcAddress) != NULL);
}
bool GdbBreakpoints::atBreakpoint(uint32_t address) {
   return (findHardwareBreakpoint(address) != 0) || (findMemoryBreakpoint(address) != 0);
}