--- hatari/src/debug/breakcond.c 2019/04/09 08:54:20 1.1.1.6 +++ hatari/src/debug/breakcond.c 2019/04/09 08:58:01 1.1.1.9 @@ -1,7 +1,7 @@ /* Hatari - breakcond.c - Copyright (c) 2009-2012 by Eero Tamminen + Copyright (c) 2009-2016 by Eero Tamminen This file is distributed under the GNU General Public License, version 2 or at your option any later version. Read the file gpl.txt for details. @@ -23,8 +23,7 @@ const char BreakCond_fileid[] = "Hatari #include "dsp.h" #include "stMemory.h" #include "str.h" -#include "screen.h" /* for defines needed by video.h */ -#include "video.h" /* for Hatari video variable addresses */ +#include "vars.h" #include "debug_priv.h" #include "breakcond.h" @@ -46,26 +45,6 @@ const char BreakCond_fileid[] = "Hatari #define BC_DEFAULT_DSP_SPACE 'P' -typedef enum { - /* plain number */ - VALUE_TYPE_NUMBER = 0, - - /* functions to call to get value */ - VALUE_TYPE_FUNCTION32 = 2, - - /* internal Hatari value variables */ - VALUE_TYPE_VAR32 = 4, - - /* size must match register size used in BreakCond_ParseRegister() */ - VALUE_TYPE_REG16 = 16, - VALUE_TYPE_REG32 = 32 -} value_t; - -static inline bool is_register_type(value_t vtype) { - /* type used for CPU/DSP registers */ - return (vtype == VALUE_TYPE_REG16 || vtype == VALUE_TYPE_REG32); -} - typedef struct { bool is_indirect; char dsp_space; /* DSP has P, X, Y address spaces, zero if not DSP */ @@ -96,6 +75,7 @@ typedef struct { bool trace; /* trace mode, don't break */ bool noinit; /* prevent debugger inits on break */ bool lock; /* tracing + show locked info */ + bool deleted; /* delayed delete flag */ } bc_options_t; typedef struct { @@ -106,14 +86,29 @@ typedef struct { int hits; /* how many times breakpoint hit */ } bc_breakpoint_t; -static bc_breakpoint_t *BreakPointsCpu; -static bc_breakpoint_t *BreakPointsDsp; -static int BreakPointCpuCount, BreakPointCpuAllocated; -static int BreakPointDspCount, BreakPointDspAllocated; +typedef struct { + bc_breakpoint_t *breakpoint; + bc_breakpoint_t *breakpoint2delete; /* delayed delete of old alloc */ + const char *name; + int count; + int allocated; + bool delayed_change; + const debug_reason_t reason; +} bc_breakpoints_t; + +static bc_breakpoints_t CpuBreakPoints = { + .name = "CPU", + .reason = REASON_CPU_BREAKPOINT +}; +static bc_breakpoints_t DspBreakPoints = { + .name = "DSP", + .reason = REASON_DSP_BREAKPOINT +}; /* forward declarations */ -static bool BreakCond_Remove(int position, bool bForDsp); +static void BreakCond_DoDelayedActions(bc_breakpoints_t *bps); +static bool BreakCond_Remove(bc_breakpoints_t *bps, int position); static void BreakCond_Print(bc_breakpoint_t *bp); @@ -126,7 +121,7 @@ bool BreakCond_Save(const char *filename FILE *fp; int i; - if (!(BreakPointCpuCount || BreakPointDspCount)) { + if (!(CpuBreakPoints.count || DspBreakPoints.count)) { if (File_Exists(filename)) { if (remove(filename)) { perror("ERROR"); @@ -143,11 +138,11 @@ bool BreakCond_Save(const char *filename return false; } /* save conditional breakpoints as debugger input file */ - for (i = 0; i < BreakPointCpuCount; i++) { - fprintf(fp, "b %s\n", BreakPointsCpu[i].expression); + for (i = 0; i < CpuBreakPoints.count; i++) { + fprintf(fp, "b %s\n", CpuBreakPoints.breakpoint[i].expression); } - for (i = 0; i < BreakPointDspCount; i++) { - fprintf(fp, "db %s\n", BreakPointsDsp[i].expression); + for (i = 0; i < DspBreakPoints.count; i++) { + fprintf(fp, "db %s\n", DspBreakPoints.breakpoint[i].expression); } fclose(fp); return true; @@ -190,11 +185,6 @@ static Uint32 BreakCond_ReadDspMemory(Ui */ static Uint32 BreakCond_ReadSTMemory(Uint32 addr, const bc_value_t *bc_value) { - /* Mask to a 24 bit address. With this e.g. $ffff820a is also - * recognized as IO mem $ff820a (which is the same in the 68000). - */ - addr &= 0x00ffffff; - switch (bc_value->bits) { case 32: return STMemory_ReadLong(addr); @@ -299,7 +289,7 @@ static bool BreakCond_MatchConditions(bc condition->comparison); abort(); } - if (!hit) { + if (likely(!hit)) { return false; } if (condition->track) { @@ -312,19 +302,25 @@ static bool BreakCond_MatchConditions(bc /** - * Show all breakpoints which conditions matched and return which matched - * @return index to last matching (non-tracing) breakpoint, - * or zero if none matched + * Check and show which breakpoints' conditions matched + * @return true if (non-tracing) breakpoint was hit, + * or false if none matched */ -static int BreakCond_MatchBreakPoints(bc_breakpoint_t *bp, int count, const char *name) +static bool BreakCond_MatchBreakPoints(bc_breakpoints_t *bps) { - int i, ret = 0; + bc_breakpoint_t *bp; + bool changes = false; + bool hit = false; + int i; - for (i = 0; i < count; bp++, i++) { + /* array should not be changed while it's being traversed */ + assert(likely(!bps->delayed_change)); + bps->delayed_change = true; - if (BreakCond_MatchConditions(bp->conditions, bp->ccount)) { - bool for_dsp; + bp = bps->breakpoint; + for (i = 0; i < bps->count; bp++, i++) { + if (BreakCond_MatchConditions(bp->conditions, bp->ccount)) { bp->hits++; if (bp->options.skip) { if (bp->hits % bp->options.skip) { @@ -334,15 +330,10 @@ static int BreakCond_MatchBreakPoints(bc } if (!bp->options.quiet) { fprintf(stderr, "%d. %s breakpoint condition(s) matched %d times.\n", - i+1, name, bp->hits); + i+1, bps->name, bp->hits); BreakCond_Print(bp); } - for_dsp = (bp-i == BreakPointsDsp); - if (for_dsp) { - History_Mark(REASON_DSP_BREAKPOINT); - } else { - History_Mark(REASON_CPU_BREAKPOINT); - } + History_Mark(bps->reason); if (bp->options.lock || bp->options.filename) { bool reinit = !bp->options.noinit; @@ -357,50 +348,59 @@ static int BreakCond_MatchBreakPoints(bc } if (bp->options.filename) { DebugUI_ParseFile(bp->options.filename, reinit); + changes = true; } } if (bp->options.once) { - BreakCond_Remove(i+1, for_dsp); - count--; + BreakCond_Remove(bps, i+1); + changes = true; } if (!bp->options.trace) { - /* index for current hit (BreakCond_Remove() indexes start from 1) */ - ret = i + 1; + /* index for current hit, they start from 1 */ + hit = true; } /* continue checking breakpoints to make sure all relevant actions get performed */ } } - return ret; + bps->delayed_change = false; + if (unlikely(changes)) { + BreakCond_DoDelayedActions(bps); + } + return hit; } /* ------------- breakpoint condition checking, public API ------------- */ /** - * Return matched CPU breakpoint index or zero for no hits. + * Return true if there were CPU breakpoint hits, false otherwise. */ -int BreakCond_MatchCpu(void) +bool BreakCond_MatchCpu(void) { - return BreakCond_MatchBreakPoints(BreakPointsCpu, BreakPointCpuCount, "CPU"); + return BreakCond_MatchBreakPoints(&CpuBreakPoints); } /** - * Return matched DSP breakpoint index or zero for no hits. + * Return true if there were DSP breakpoint hits, false otherwise. */ -int BreakCond_MatchDsp(void) +bool BreakCond_MatchDsp(void) { - return BreakCond_MatchBreakPoints(BreakPointsDsp, BreakPointDspCount, "DSP"); + return BreakCond_MatchBreakPoints(&DspBreakPoints); } /** - * Return number of condition breakpoints + * Return number of CPU condition breakpoints */ -int BreakCond_BreakPointCount(bool bForDsp) +int BreakCond_CpuBreakPointCount(void) { - if (bForDsp) { - return BreakPointDspCount; - } else { - return BreakPointCpuCount; - } + return CpuBreakPoints.count; +} + +/** + * Return number of DSP condition breakpoints + */ +int BreakCond_DspBreakPointCount(void) +{ + return DspBreakPoints.count; } @@ -415,238 +415,28 @@ typedef struct { } parser_state_t; -/* Hatari variable name & address array items */ -typedef struct { - const char *name; - Uint32 *addr; - value_t vtype; - size_t bits; - const char *constraints; -} var_addr_t; - -/* Accessor functions for calculated Hatari values */ -static Uint32 GetLineCycles(void) -{ - int dummy1, dummy2, lcycles; - Video_GetPosition(&dummy1, &dummy2 , &lcycles); - return lcycles; -} -static Uint32 GetFrameCycles(void) -{ - int dummy1, dummy2, fcycles; - Video_GetPosition(&fcycles, &dummy1, &dummy2); - return fcycles; -} - -/* helpers for TOS OS call opcode accessor functions */ -#define INVALID_OPCODE 0xFFFFu - -static inline Uint16 getLineOpcode(Uint8 line) -{ - Uint32 pc; - Uint16 instr; - pc = M68000_GetPC(); - instr = STMemory_ReadWord(pc); - /* for opcode X, Line-A = 0xA00X, Line-F = 0xF00X */ - if ((instr >> 12) == line) { - return instr & 0xFF; - } - return INVALID_OPCODE; -} -static inline bool isTrap(Uint8 trap) -{ - Uint32 pc; - Uint16 instr; - pc = M68000_GetPC(); - instr = STMemory_ReadWord(pc); - return (instr == (Uint16)0x4e40u + trap); -} -static inline Uint16 getControlOpcode(void) -{ - /* Control[] address from D1, opcode in Control[0] */ - return STMemory_ReadWord(STMemory_ReadLong(Regs[REG_D1])); -} -static inline Uint16 getStackOpcode(void) -{ - return STMemory_ReadWord(Regs[REG_A7]); -} - -/* Actual TOS OS call opcode accessor functions */ -static Uint32 GetLineAOpcode(void) -{ - return getLineOpcode(0xA); -} -static Uint32 GetLineFOpcode(void) -{ - return getLineOpcode(0xF); -} -static Uint32 GetGemdosOpcode(void) -{ - if (isTrap(1)) { - return getStackOpcode(); - } - return INVALID_OPCODE; -} -static Uint32 GetBiosOpcode(void) -{ - if (isTrap(13)) { - return getStackOpcode(); - } - return INVALID_OPCODE; -} -static Uint32 GetXbiosOpcode(void) -{ - if (isTrap(14)) { - return getStackOpcode(); - } - return INVALID_OPCODE; -} -static Uint32 GetAesOpcode(void) -{ - if (isTrap(2)) { - Uint16 d0 = Regs[REG_D0]; - if (d0 == 0xC8) { - return getControlOpcode(); - } else if (d0 == 0xC9) { - /* same as appl_yield() */ - return 0x11; - } - } - return INVALID_OPCODE; -} -static Uint32 GetVdiOpcode(void) -{ - if (isTrap(2)) { - Uint16 d0 = Regs[REG_D0]; - if (d0 == 0x73) { - return getControlOpcode(); - } else if (d0 == 0xFFFE) { - /* -2 = vq_[v]gdos() */ - return 0xFFFE; - } - } - return INVALID_OPCODE; -} - -static Uint32 GetNextPC(void) -{ - return Disasm_GetNextPC(M68000_GetPC()); -} - -/* sorted by variable name so that this can be bisected */ -static const var_addr_t hatari_vars[] = { - { "AesOpcode", (Uint32*)GetAesOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }, - { "BiosOpcode", (Uint32*)GetBiosOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }, - { "BSS", (Uint32*)DebugInfo_GetBSS, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" }, - { "CpuInstr", (Uint32*)DebugCpu_InstrCount, VALUE_TYPE_FUNCTION32, 0, "CPU instructions count" }, - { "CpuOpcodeType", (Uint32*)DebugCpu_OpcodeType, VALUE_TYPE_FUNCTION32, 0, "CPU instruction type" }, - { "DATA", (Uint32*)DebugInfo_GetDATA, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" }, -#if ENABLE_DSP_EMU - { "DspInstr", (Uint32*)DebugDsp_InstrCount, VALUE_TYPE_FUNCTION32, 0, "DSP instructions count" }, - { "DspOpcodeType", (Uint32*)DebugDsp_OpcodeType, VALUE_TYPE_FUNCTION32, 0, "DSP instruction type" }, -#endif - { "FrameCycles", (Uint32*)GetFrameCycles, VALUE_TYPE_FUNCTION32, 0, NULL }, - { "GemdosOpcode", (Uint32*)GetGemdosOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }, - { "HBL", (Uint32*)&nHBL, VALUE_TYPE_VAR32, sizeof(nHBL)*8, NULL }, - { "LineAOpcode", (Uint32*)GetLineAOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }, - { "LineCycles", (Uint32*)GetLineCycles, VALUE_TYPE_FUNCTION32, 0, "is always divisable by 4" }, - { "LineFOpcode", (Uint32*)GetLineFOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }, - { "NextPC", (Uint32*)GetNextPC, VALUE_TYPE_FUNCTION32, 0, NULL }, - { "TEXT", (Uint32*)DebugInfo_GetTEXT, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" }, - { "TEXTEnd", (Uint32*)DebugInfo_GetTEXTEnd, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" }, - { "VBL", (Uint32*)&nVBLs, VALUE_TYPE_VAR32, sizeof(nVBLs)*8, NULL }, - { "VdiOpcode", (Uint32*)GetVdiOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }, - { "XbiosOpcode", (Uint32*)GetXbiosOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" } -}; - - -/** - * Readline match callback for CPU variable/symbol name completion. - * STATE = 0 -> different text from previous one. - * Return next match or NULL if no matches. - */ -char *BreakCond_MatchCpuVariable(const char *text, int state) -{ - static int i, len; - const char *name; - - if (!state) { - /* first match */ - len = strlen(text); - i = 0; - } - /* next match */ - while (i < ARRAYSIZE(hatari_vars)) { - name = hatari_vars[i++].name; - if (strncasecmp(name, text, len) == 0) - return (strdup(name)); - } - /* no variable match, check all CPU symbols */ - return Symbols_MatchCpuAddress(text, state); -} - -/** - * Readline match callback for DSP variable/symbol name completion. - * STATE = 0 -> different text from previous one. - * Return next match or NULL if no matches. - */ -char *BreakCond_MatchDspVariable(const char *text, int state) -{ - /* currently no DSP variables, check all DSP symbols */ - return Symbols_MatchDspAddress(text, state); -} - - /** * If given string is a Hatari variable name, set bc_value * fields accordingly and return true, otherwise return false. */ static bool BreakCond_ParseVariable(const char *name, bc_value_t *bc_value) { - /* left, right, middle, direction */ - int l, r, m, dir; + const var_addr_t *hvar; ENTERFUNC(("BreakCond_ParseVariable('%s')\n", name)); - /* bisect */ - l = 0; - r = ARRAYSIZE(hatari_vars) - 1; - do { - m = (l+r) >> 1; - dir = strcasecmp(name, hatari_vars[m].name); - if (dir == 0) { - bc_value->value.reg32 = hatari_vars[m].addr; - bc_value->valuetype = hatari_vars[m].vtype; - bc_value->bits = hatari_vars[m].bits; - assert(bc_value->bits == 32 || bc_value->valuetype != VALUE_TYPE_VAR32); - EXITFUNC(("-> true\n")); - return true; - } - if (dir < 0) { - r = m-1; - } else { - l = m+1; - } - } while (l <= r); + hvar = Vars_ParseVariable(name); + if (hvar) { + bc_value->value.reg32 = hvar->addr; + bc_value->valuetype = hvar->vtype; + bc_value->bits = hvar->bits; + assert(bc_value->bits == 32 || bc_value->valuetype != VALUE_TYPE_VAR32); + EXITFUNC(("-> true\n")); + return true; + } EXITFUNC(("-> false\n")); return false; } -/** - * If given string is a Hatari variable name, set value to given - * variable value and return true, otherwise return false. - */ -bool BreakCond_GetHatariVariable(const char *name, Uint32 *value) -{ - bc_value_t bc_value; - if (!BreakCond_ParseVariable(name, &bc_value)) { - return false; - } - bc_value.mask = 0xffffffff; - bc_value.is_indirect = false; - *value = BreakCond_GetValue(&bc_value); - return true; -} - /** * If given string matches a suitable symbol, set bc_value @@ -773,28 +563,19 @@ static bool BreakCond_ParseRegister(cons */ static bool BreakCond_CheckAddress(bc_value_t *bc_value) { - Uint32 highbyte, bit23, addr = bc_value->value.number; + Uint32 addr = bc_value->value.number; + int size = bc_value->bits >> 8; ENTERFUNC(("BreakCond_CheckAddress(%x)\n", addr)); if (bc_value->dsp_space) { - if (addr > 0xFFFF) { + if (addr+size > 0xFFFF) { EXITFUNC(("-> false (DSP)\n")); return false; } EXITFUNC(("-> true (DSP)\n")); return true; } - - bit23 = (addr >> 23) & 1; - highbyte = (addr >> 24) & 0xff; - if ((bit23 == 0 && highbyte != 0) || - (bit23 == 1 && highbyte != 0xff)) { - fprintf(stderr, "WARNING: address 0x%x 23th bit isn't extended to bits 24-31.\n", addr); - } - /* use a 24-bit address */ - addr &= 0x00ffffff; - if ((addr > STRamEnd && addr < 0xe00000) || - (addr >= 0xff0000 && addr < 0xff8000)) { + if (!STMemory_CheckAreaType(addr, size, ABFLAG_RAM | ABFLAG_ROM | ABFLAG_IO)) { EXITFUNC(("-> false (CPU)\n")); return false; } @@ -824,7 +605,8 @@ static bool BreakCond_ParseAddressModifi return true; } if (!bc_value->is_indirect) { - pstate->error = "space/width modifier makes sense only for an address (register)"; + pstate->error = "space/width modifier can be used only with an (address) expression\n" + "(note that you can use a mask instead of width, for example: 'd0 & 0xff')"; EXITFUNC(("arg:%d -> false\n", pstate->arg)); return false; } @@ -1304,42 +1086,42 @@ static char *BreakCond_TokenizeExpressio /** - * Set corrent breakpoint list and name for given CPU type. + * Select corrent breakpoints struct and provide name for it. * Make sure there's always space for at least one additional breakpoint. - * Return pointer to breakpoint list count + * Return pointer to the breakpoints struct */ -static int* BreakCond_GetListInfo(bc_breakpoint_t **bp, - const char **name, bool bForDsp) +static bc_breakpoints_t* BreakCond_GetListInfo(bool bForDsp) { - int *allocated, *bcount; + bc_breakpoints_t *bps; if (bForDsp) { - allocated = &BreakPointDspAllocated; - bcount = &BreakPointDspCount; - *bp = BreakPointsDsp; - *name = "DSP"; + bps = &DspBreakPoints; } else { - allocated = &BreakPointCpuAllocated; - bcount = &BreakPointCpuCount; - *bp = BreakPointsCpu; - *name = "CPU"; + bps = &CpuBreakPoints; } /* allocate (more) space for breakpoints when needed */ - if (*bcount + 1 >= *allocated) { - if (!*allocated) { + if (bps->count + 1 >= bps->allocated) { + if (!bps->allocated) { /* initial count of available breakpoints */ - *allocated = 16; + bps->allocated = 16; } else { - *allocated *= 2; + bps->allocated *= 2; } - *bp = realloc(*bp, *allocated * sizeof(bc_breakpoint_t)); - assert(*bp); - if (bForDsp) { - BreakPointsDsp = *bp; + if (bps->delayed_change) { + if(bps->breakpoint2delete) { + /* getting second re-alloc within same breakpoint handler is really + * unlikely, this would require adding dozens of new breakpoints. + */ + fprintf(stderr, "ERROR: too many new breakpoints added within single breakpoint hit!\n"); + abort(); + } + bps->breakpoint2delete = bps->breakpoint; + bps->breakpoint = malloc(bps->allocated * sizeof(bc_breakpoint_t)); } else { - BreakPointsCpu = *bp; + bps->breakpoint = realloc(bps->breakpoint, bps->allocated * sizeof(bc_breakpoint_t)); } + assert(bps->breakpoint); } - return bcount; + return bps; } @@ -1391,15 +1173,14 @@ static void BreakCond_CheckTracking(bc_b static bool BreakCond_Parse(const char *expression, bc_options_t *options, bool bForDsp) { parser_state_t pstate; + bc_breakpoints_t *bps; bc_breakpoint_t *bp; - const char *name; char *normalized; - int *bcount; int ccount; - bcount = BreakCond_GetListInfo(&bp, &name, bForDsp); + bps = BreakCond_GetListInfo(bForDsp); - bp += *bcount; + bp = bps->breakpoint + bps->count; memset(bp, 0, sizeof(bc_breakpoint_t)); normalized = BreakCond_TokenizeExpression(expression, &pstate); @@ -1423,10 +1204,10 @@ static bool BreakCond_Parse(const char * free(pstate.argv); } if (ccount > 0) { - (*bcount)++; + bps->count++; if (!options->quiet) { fprintf(stderr, "%s condition breakpoint %d with %d condition(s) added:\n\t%s\n", - name, *bcount, ccount, bp->expression); + bps->name, bps->count, ccount, bp->expression); if (options->skip) { fprintf(stderr, "-> Break only on every %d hit.\n", options->skip); } @@ -1497,6 +1278,9 @@ static void BreakCond_Print(bc_breakpoin if (bp->options.once) { fprintf(stderr, " :once"); } + if (bp->options.quiet) { + fprintf(stderr, " :quiet"); + } if (bp->options.trace) { if (bp->options.lock) { fprintf(stderr, " :lock"); @@ -1510,26 +1294,27 @@ static void BreakCond_Print(bc_breakpoin if (bp->options.filename) { fprintf(stderr, " :file %s", bp->options.filename); } + if (bp->options.deleted) { + fprintf(stderr, " (deleted)"); + } fprintf(stderr, "\n"); } /** * List condition breakpoints */ -static void BreakCond_List(bool bForDsp) +static void BreakCond_List(bc_breakpoints_t *bps) { - const char *name; bc_breakpoint_t *bp; - int i, bcount; - - bcount = *BreakCond_GetListInfo(&bp, &name, bForDsp); - if (!bcount) { - fprintf(stderr, "No conditional %s breakpoints.\n", name); + int i; + + if (!bps->count) { + fprintf(stderr, "No conditional %s breakpoints.\n", bps->name); return; } - - fprintf(stderr, "%d conditional %s breakpoints:\n", bcount, name); - for (i = 1; i <= bcount; bp++, i++) { + fprintf(stderr, "%d conditional %s breakpoints:\n", bps->count, bps->name); + bp = bps->breakpoint; + for (i = 1; i <= bps->count; bp++, i++) { fprintf(stderr, "%4d:", i); BreakCond_Print(bp); } @@ -1539,51 +1324,81 @@ static void BreakCond_List(bool bForDsp) /** * Remove condition breakpoint at given position */ -static bool BreakCond_Remove(int position, bool bForDsp) +static bool BreakCond_Remove(bc_breakpoints_t *bps, int position) { - const char *name; bc_breakpoint_t *bp; - int *bcount, offset; - bcount = BreakCond_GetListInfo(&bp, &name, bForDsp); - if (!*bcount) { - fprintf(stderr, "No (more) %s breakpoints to remove.\n", name); + if (!bps->count) { + fprintf(stderr, "No (more) %s breakpoints to remove.\n", bps->name); return false; } - if (position < 1 || position > *bcount) { - fprintf(stderr, "ERROR: No such %s breakpoint.\n", name); + if (position < 1 || position > bps->count) { + fprintf(stderr, "ERROR: No such %s breakpoint.\n", bps->name); return false; } - offset = position - 1; - if (!bp[offset].options.quiet) { - fprintf(stderr, "Removed %s breakpoint %d:\n", name, position); - BreakCond_Print(&(bp[offset])); + bp = bps->breakpoint + (position - 1); + if (bps->delayed_change) { + bp->options.deleted = true; + return true; + } + if (!bp->options.quiet) { + fprintf(stderr, "Removed %s breakpoint %d:\n", bps->name, position); + BreakCond_Print(bp); } - free(bp[offset].expression); - free(bp[offset].conditions); - bp[offset].expression = NULL; - bp[offset].conditions = NULL; + free(bp->expression); + free(bp->conditions); + bp->expression = NULL; + bp->conditions = NULL; - if (bp[offset].options.filename) { - free(bp[offset].options.filename); + if (bp->options.filename) { + free(bp->options.filename); } - if (position < *bcount) { - memmove(bp+offset, bp+position, - (*bcount-position)*sizeof(bc_breakpoint_t)); + if (position < bps->count) { + memmove(bp, bp + 1, (bps->count - position) * sizeof(bc_breakpoint_t)); } - (*bcount)--; + bps->count--; return true; } /** - * Remove all condition breakpoints + * Remove all conditional breakpoints */ -static void BreakCond_RemoveAll(bool bForDsp) +static void BreakCond_RemoveAll(bc_breakpoints_t *bps) { - while (BreakCond_Remove(1, bForDsp)) - ; + bool removed; + int i; + + for (i = bps->count; i > 0; i--) { + removed = BreakCond_Remove(bps, i); + ASSERT_VARIABLE(removed); + } + fprintf(stderr, "%s breakpoints: %d\n", bps->name, bps->count); +} + +/** + * Do delayed breakpoint actions, remove breakpoints and old array alloc + */ +static void BreakCond_DoDelayedActions(bc_breakpoints_t *bps) +{ + bc_options_t *options; + bool removed; + int i; + + assert(!bps->delayed_change); + if (bps->breakpoint2delete) { + free(bps->breakpoint2delete); + bps->breakpoint2delete = NULL; + } + for (i = bps->count; i > 0; i--) { + options = &(bps->breakpoint[i-1].options); + if (options->deleted) { + options->deleted = false; + removed = BreakCond_Remove(bps, i); + ASSERT_VARIABLE(removed); + } + } } @@ -1593,24 +1408,19 @@ static void BreakCond_RemoveAll(bool bFo */ int BreakCond_MatchCpuExpression(int position, const char *expression) { - if (position < 1 || position > BreakPointCpuCount) { + if (position < 1 || position > CpuBreakPoints.count) { return false; } - if (strcmp(expression, BreakPointsCpu[position-1].expression)) { + if (strcmp(expression, CpuBreakPoints.breakpoint[position-1].expression)) { return false; } return true; } -/** - * help - */ -static void BreakCond_Help(void) -{ - Uint32 value; - int i; - fputs( +/* ------------- breakpoint condition parsing, public API ------------ */ + +static const char BreakCond_Help[] = " condition = [.mode] [& ] [.mode]\n" "\n" " where:\n" @@ -1633,37 +1443,13 @@ static void BreakCond_Help(void) " DSP addresses belong to different address spaces: P, X or Y. Note that\n" " on DSP only R0-R7 registers can be used for memory addressing.\n" "\n" -" Valid Hatari variable names (and their current values) are:\n", stderr); - for (i = 0; i < ARRAYSIZE(hatari_vars); i++) { - switch (hatari_vars[i].vtype) { - case VALUE_TYPE_FUNCTION32: - value = ((Uint32(*)(void))(hatari_vars[i].addr))(); - break; - case VALUE_TYPE_VAR32: - value = *(hatari_vars[i].addr); - break; - default: - fprintf(stderr, "ERROR: variable '%s' has unsupported type '%d'\n", - hatari_vars[i].name, hatari_vars[i].vtype); - continue; - } - fprintf(stderr, " - %s ($%x)", hatari_vars[i].name, value); - if (hatari_vars[i].constraints) { - fprintf(stderr, ", %s\n", hatari_vars[i].constraints); - } else { - fprintf(stderr, "\n"); - } - } - fputs( -"\n" " Examples:\n" " pc = $64543 && ($ff820).w & 3 = (a0) && d0 = %1100\n" " ($ffff9202).w ! ($ffff9202).w :trace\n" -" (r0).x = 1 && (r0).y = 2\n", stderr); -} - +" (r0).x = 1 && (r0).y = 2\n" +"\n" +" For breakpoint options, see 'help b'.\n"; -/* ------------- breakpoint condition parsing, public API ------------ */ const char BreakCond_Description[] = " [&& ...] [: