Annotation of hatari/src/debug/breakcond.c, revision 1.1.1.7

1.1       root        1: /*
                      2:   Hatari - breakcond.c
                      3: 
1.1.1.5   root        4:   Copyright (c) 2009-2012 by Eero Tamminen
1.1       root        5: 
1.1.1.5   root        6:   This file is distributed under the GNU General Public License, version 2
                      7:   or at your option any later version. Read the file gpl.txt for details.
1.1       root        8: 
                      9:   breakcond.c - code for breakpoint conditions that can check variable
                     10:   and memory values against each other, mask them etc. before deciding
                     11:   whether the breakpoint should be triggered.  See BreakCond_Help()
                     12:   for the syntax.
                     13: */
                     14: const char BreakCond_fileid[] = "Hatari breakcond.c : " __DATE__ " " __TIME__;
                     15: 
                     16: #include <ctype.h>
                     17: #include <stdlib.h>
                     18: #include "config.h"
                     19: #include "main.h"
1.1.1.2   root       20: #include "file.h"
1.1       root       21: #include "m68000.h"
                     22: #include "memorySnapShot.h"
                     23: #include "dsp.h"
                     24: #include "stMemory.h"
                     25: #include "str.h"
1.1.1.2   root       26: #include "screen.h"    /* for defines needed by video.h */
1.1       root       27: #include "video.h"     /* for Hatari video variable addresses */
                     28: 
                     29: #include "debug_priv.h"
                     30: #include "breakcond.h"
                     31: #include "debugcpu.h"
1.1.1.6   root       32: #include "debugdsp.h"
1.1.1.2   root       33: #include "debugInfo.h"
                     34: #include "debugui.h"
1.1       root       35: #include "evaluate.h"
1.1.1.3   root       36: #include "history.h"
1.1       root       37: #include "symbols.h"
1.1.1.2   root       38: #include "68kDisass.h"
1.1       root       39: 
                     40: 
                     41: /* set to 1 to enable parsing function tracing / debug output */
                     42: #define DEBUG 0
                     43: 
                     44: /* needs to go through long long to handle x=32 */
                     45: #define BITMASK(x)      ((Uint32)(((unsigned long long)1<<(x))-1))
                     46: 
                     47: #define BC_DEFAULT_DSP_SPACE 'P'
                     48: 
1.1.1.2   root       49: typedef enum { 
1.1       root       50:        /* plain number */
                     51:        VALUE_TYPE_NUMBER     = 0,
                     52: 
                     53:        /* functions to call to get value */
                     54:        VALUE_TYPE_FUNCTION32 = 2,
                     55: 
                     56:        /* internal Hatari value variables */
                     57:        VALUE_TYPE_VAR32      = 4,
                     58: 
                     59:        /* size must match register size used in BreakCond_ParseRegister() */
                     60:        VALUE_TYPE_REG16      = 16,
                     61:        VALUE_TYPE_REG32      = 32
1.1.1.2   root       62: } value_t;
1.1       root       63: 
                     64: static inline bool is_register_type(value_t vtype) {
                     65:        /* type used for CPU/DSP registers */
                     66:        return (vtype == VALUE_TYPE_REG16 || vtype == VALUE_TYPE_REG32);
                     67: }
                     68: 
                     69: typedef struct {
                     70:        bool is_indirect;
                     71:        char dsp_space; /* DSP has P, X, Y address spaces, zero if not DSP */
                     72:        value_t valuetype;      /* Hatari value variable type */
                     73:        union {
                     74:                Uint32 number;
                     75:                Uint16 (*func16)(void);
                     76:                Uint32 (*func32)(void);
                     77:                Uint16 *reg16;
                     78:                Uint32 *reg32;
                     79:        } value;
                     80:        Uint32 bits;    /* CPU has 8/16/32 bit address widths */
                     81:        Uint32 mask;    /* <width mask> && <value mask> */
                     82: } bc_value_t;
                     83: 
                     84: typedef struct {
                     85:        bc_value_t lvalue;
                     86:        bc_value_t rvalue;
                     87:        char comparison;
                     88:        bool track;     /* track value changes */
                     89: } bc_condition_t;
                     90: 
                     91: typedef struct {
1.1.1.2   root       92:        char *filename; /* file where to read commands to do on hit */
                     93:        int skip;       /* how many times to hit before breaking */
                     94:        bool once;      /* remove after hit&break */
1.1.1.5   root       95:        bool quiet;     /* no output from setting & hitting */
1.1.1.2   root       96:        bool trace;     /* trace mode, don't break */
1.1.1.5   root       97:        bool noinit;    /* prevent debugger inits on break */
1.1.1.2   root       98:        bool lock;      /* tracing + show locked info */
1.1.1.7 ! root       99:        bool deleted;   /* delayed delete flag */
1.1.1.2   root      100: } bc_options_t;
                    101: 
                    102: typedef struct {
1.1       root      103:        char *expression;
1.1.1.2   root      104:        bc_options_t options;
1.1.1.6   root      105:        bc_condition_t *conditions;
1.1       root      106:        int ccount;     /* condition count */
                    107:        int hits;       /* how many times breakpoint hit */
                    108: } bc_breakpoint_t;
                    109: 
1.1.1.7 ! root      110: typedef struct {
        !           111:        bc_breakpoint_t *breakpoint;
        !           112:        bc_breakpoint_t *breakpoint2delete;     /* delayed delete of old alloc */
        !           113:        const char *name;
        !           114:        int count;
        !           115:        int allocated;
        !           116:        bool delayed_change;
        !           117:        const debug_reason_t reason;
        !           118: } bc_breakpoints_t;
        !           119: 
        !           120: static bc_breakpoints_t CpuBreakPoints = {
        !           121:        .name = "CPU",
        !           122:        .reason = REASON_CPU_BREAKPOINT
        !           123: };
        !           124: static bc_breakpoints_t DspBreakPoints = {
        !           125:        .name = "DSP",
        !           126:        .reason = REASON_DSP_BREAKPOINT
        !           127: };
1.1       root      128: 
                    129: 
                    130: /* forward declarations */
1.1.1.7 ! root      131: static int BreakCond_DoDelayedActions(bc_breakpoints_t *bps, int triggered);
        !           132: static bool BreakCond_Remove(bc_breakpoints_t *bps, int position);
1.1       root      133: static void BreakCond_Print(bc_breakpoint_t *bp);
                    134: 
                    135: 
                    136: /**
                    137:  * Save breakpoints as debugger input file
                    138:  * return true for success, false for failure
                    139:  */
                    140: bool BreakCond_Save(const char *filename)
                    141: {
                    142:        FILE *fp;
                    143:        int i;
                    144: 
1.1.1.7 ! root      145:        if (!(CpuBreakPoints.count || DspBreakPoints.count)) {
1.1.1.2   root      146:                if (File_Exists(filename)) {
                    147:                        if (remove(filename)) {
                    148:                                perror("ERROR");
                    149:                                return false;
                    150:                        }
1.1       root      151:                }
                    152:                return true;
                    153:        }
                    154: 
                    155:        fprintf(stderr, "Saving breakpoints to '%s'...\n", filename);
                    156:        fp = fopen(filename, "w");
                    157:        if (!fp) {
                    158:                perror("ERROR");
                    159:                return false;
                    160:        }
                    161:        /* save conditional breakpoints as debugger input file */
1.1.1.7 ! root      162:        for (i = 0; i < CpuBreakPoints.count; i++) {
        !           163:                fprintf(fp, "b %s\n", CpuBreakPoints.breakpoint[i].expression);
1.1       root      164:        }
1.1.1.7 ! root      165:        for (i = 0; i < DspBreakPoints.count; i++) {
        !           166:                fprintf(fp, "db %s\n", DspBreakPoints.breakpoint[i].expression);
1.1       root      167:        }
                    168:        fclose(fp);
                    169:        return true;
                    170: }
                    171: 
                    172: 
                    173: /* --------------------- debugging code ------------------- */
                    174: 
                    175: #if DEBUG
                    176: /* see parsing code for usage examples */
                    177: static int _traceIndent;
                    178: static void _spaces(void)
                    179: {
                    180:        int spaces = _traceIndent;
                    181:        while(spaces-- > 0) {
                    182:                putchar(' ');   /* fputc(' ',stdout); */
                    183:        }
                    184: }
                    185: #define ENTERFUNC(args) { _traceIndent += 2; _spaces(); printf args ; fflush(stdout); }
                    186: #define EXITFUNC(args) { _spaces(); printf args ; fflush(stdout); _traceIndent -= 2; }
                    187: #else
                    188: #define ENTERFUNC(args)
                    189: #define EXITFUNC(args)
                    190: #endif
                    191: 
                    192: 
                    193: /* ------------- breakpoint condition checking, internals ------------- */
                    194: 
                    195: /**
                    196:  * Return value from given DSP memory space/address
                    197:  */
                    198: static Uint32 BreakCond_ReadDspMemory(Uint32 addr, const bc_value_t *bc_value)
                    199: {
                    200:        const char *dummy;
                    201:        return DSP_ReadMemory(addr, bc_value->dsp_space, &dummy) & BITMASK(24);
                    202: }
                    203: 
                    204: /**
                    205:  * Return value of given size read from given ST memory address
                    206:  */
                    207: static Uint32 BreakCond_ReadSTMemory(Uint32 addr, const bc_value_t *bc_value)
                    208: {
                    209:        switch (bc_value->bits) {
                    210:        case 32:
                    211:                return STMemory_ReadLong(addr);
                    212:        case 16:
                    213:                return STMemory_ReadWord(addr);
                    214:        case 8:
                    215:                return STMemory_ReadByte(addr);
                    216:        default:
                    217:                fprintf(stderr, "ERROR: unknown ST address size %d!\n", bc_value->bits);
                    218:                abort();
                    219:        }
                    220: }
                    221: 
                    222: 
                    223: /**
                    224:  * Return Uint32 value according to given bc_value_t specification
                    225:  */
                    226: static Uint32 BreakCond_GetValue(const bc_value_t *bc_value)
                    227: {
                    228:        Uint32 value;
                    229: 
                    230:        switch (bc_value->valuetype) {
                    231:        case VALUE_TYPE_NUMBER:
                    232:                value = bc_value->value.number;
                    233:                break;
                    234:        case VALUE_TYPE_FUNCTION32:
                    235:                value = bc_value->value.func32();
                    236:                break;
                    237:        case VALUE_TYPE_REG16:
                    238:                value = *(bc_value->value.reg16);
                    239:                break;
                    240:        case VALUE_TYPE_VAR32:
                    241:        case VALUE_TYPE_REG32:
                    242:                value = *(bc_value->value.reg32);
                    243:                break;
                    244:        default:
                    245:                fprintf(stderr, "ERROR: unknown condition value size/type %d!\n", bc_value->valuetype);
                    246:                abort();
                    247:        }
                    248:        if (bc_value->is_indirect) {
                    249:                if (bc_value->dsp_space) {
                    250:                        value = BreakCond_ReadDspMemory(value, bc_value);
                    251:                } else {
                    252:                        value = BreakCond_ReadSTMemory(value, bc_value);
                    253:                }
                    254:        }
                    255:        return (value & bc_value->mask);
                    256: }
                    257: 
                    258: 
                    259: /**
1.1.1.5   root      260:  * Show & update rvalue for a tracked breakpoint condition to lvalue
                    261:  */
                    262: static void BreakCond_UpdateTracked(bc_condition_t *condition, Uint32 value)
                    263: {
                    264:        Uint32 addr;
                    265: 
                    266:        /* next monitor changes to this new value */
                    267:        condition->rvalue.value.number = value;
                    268: 
                    269:        if (condition->lvalue.is_indirect &&
                    270:            condition->lvalue.valuetype == VALUE_TYPE_NUMBER) {
                    271:                /* simple memory address */
                    272:                addr = condition->lvalue.value.number;
                    273:                fprintf(stderr, "  $%x = $%x\n", addr, value);
                    274:        } else {
                    275:                /* register tms. */
                    276:                fprintf(stderr, "  $%x\n", value);
                    277:        }
                    278: }
                    279: 
                    280: 
                    281: /**
1.1       root      282:  * Return true if all of the given breakpoint's conditions match
                    283:  */
1.1.1.5   root      284: static bool BreakCond_MatchConditions(bc_condition_t *condition, int count)
1.1       root      285: {
                    286:        Uint32 lvalue, rvalue;
                    287:        bool hit = false;
                    288:        int i;
                    289:        
                    290:        for (i = 0; i < count; condition++, i++) {
                    291: 
                    292:                lvalue = BreakCond_GetValue(&(condition->lvalue));
                    293:                rvalue = BreakCond_GetValue(&(condition->rvalue));
                    294: 
                    295:                switch (condition->comparison) {
                    296:                case '<':
                    297:                        hit = (lvalue < rvalue);
                    298:                        break;
                    299:                case '>':
                    300:                        hit = (lvalue > rvalue);
                    301:                        break;
                    302:                case '=':
                    303:                        hit = (lvalue == rvalue);
                    304:                        break;
                    305:                case '!':
                    306:                        hit = (lvalue != rvalue);
                    307:                        break;
                    308:                default:
                    309:                        fprintf(stderr, "ERROR: Unknown breakpoint value comparison operator '%c'!\n",
                    310:                                condition->comparison);
                    311:                        abort();
                    312:                }
1.1.1.7 ! root      313:                if (likely(!hit)) {
1.1       root      314:                        return false;
                    315:                }
1.1.1.6   root      316:                if (condition->track) {
                    317:                        BreakCond_UpdateTracked(condition, lvalue);
                    318:                }
1.1       root      319:        }
                    320:        /* all conditions matched */
                    321:        return true;
                    322: }
                    323: 
                    324: 
                    325: /**
1.1.1.5   root      326:  * Show all breakpoints which conditions matched and return which matched
                    327:  * @return     index to last matching (non-tracing) breakpoint,
                    328:  *             or zero if none matched
1.1       root      329:  */
1.1.1.7 ! root      330: static int BreakCond_MatchBreakPoints(bc_breakpoints_t *bps)
1.1       root      331: {
1.1.1.7 ! root      332:        bc_breakpoint_t *bp;
        !           333:        bool changes = false;
1.1.1.5   root      334:        int i, ret = 0;
                    335: 
1.1.1.7 ! root      336:        /* array should not be changed while it's being traversed */
        !           337:        assert(likely(!bps->delayed_change));
        !           338:        bps->delayed_change = true;
1.1.1.2   root      339: 
1.1.1.7 ! root      340:        bp = bps->breakpoint;
        !           341:        for (i = 0; i < bps->count; bp++, i++) {
1.1.1.2   root      342: 
1.1.1.7 ! root      343:                if (BreakCond_MatchConditions(bp->conditions, bp->ccount)) {
1.1       root      344:                        bp->hits++;
1.1.1.5   root      345:                        if (bp->options.skip) {
                    346:                                if (bp->hits % bp->options.skip) {
                    347:                                        /* check next */
                    348:                                        continue;
                    349:                                }
                    350:                        }
                    351:                        if (!bp->options.quiet) {
                    352:                                fprintf(stderr, "%d. %s breakpoint condition(s) matched %d times.\n",
1.1.1.7 ! root      353:                                        i+1, bps->name, bp->hits);                      
1.1.1.5   root      354:                                BreakCond_Print(bp);
1.1       root      355:                        }
1.1.1.7 ! root      356:                        History_Mark(bps->reason);
1.1.1.3   root      357: 
1.1.1.5   root      358:                        if (bp->options.lock || bp->options.filename) {
                    359:                                bool reinit = !bp->options.noinit;
1.1.1.3   root      360: 
1.1.1.5   root      361:                                if (reinit) {
                    362:                                        DebugCpu_InitSession();
                    363:                                        DebugDsp_InitSession();
                    364:                                }
                    365: 
                    366:                                if (bp->options.lock) {
                    367:                                        DebugInfo_ShowSessionInfo();
                    368:                                }
                    369:                                if (bp->options.filename) {
                    370:                                        DebugUI_ParseFile(bp->options.filename, reinit);
1.1.1.7 ! root      371:                                        changes = true;
1.1.1.5   root      372:                                }
1.1.1.3   root      373:                        }
                    374:                        if (bp->options.once) {
1.1.1.7 ! root      375:                                BreakCond_Remove(bps, i+1);
        !           376:                                changes = true;
1.1.1.2   root      377:                        }
1.1.1.5   root      378:                        if (!bp->options.trace) {
1.1.1.7 ! root      379:                                /* index for current hit, they start from 1 */
1.1.1.5   root      380:                                ret = i + 1;
                    381:                        }
                    382:                        /* continue checking breakpoints to make sure all relevant actions get performed */
1.1       root      383:                }
                    384:        }
1.1.1.7 ! root      385:        bps->delayed_change = false;
        !           386:        if (unlikely(changes)) {
        !           387:                ret = BreakCond_DoDelayedActions(bps, ret);
        !           388:        }
1.1.1.5   root      389:        return ret;
1.1       root      390: }
                    391: 
                    392: /* ------------- breakpoint condition checking, public API ------------- */
                    393: 
                    394: /**
1.1.1.3   root      395:  * Return matched CPU breakpoint index or zero for no hits.
1.1       root      396:  */
                    397: int BreakCond_MatchCpu(void)
                    398: {
1.1.1.7 ! root      399:        return BreakCond_MatchBreakPoints(&CpuBreakPoints);
1.1       root      400: }
                    401: 
                    402: /**
1.1.1.3   root      403:  * Return matched DSP breakpoint index or zero for no hits.
1.1       root      404:  */
                    405: int BreakCond_MatchDsp(void)
                    406: {
1.1.1.7 ! root      407:        return BreakCond_MatchBreakPoints(&DspBreakPoints);
1.1       root      408: }
                    409: 
                    410: /**
1.1.1.7 ! root      411:  * Return number of CPU condition breakpoints
1.1       root      412:  */
1.1.1.7 ! root      413: int BreakCond_CpuBreakPointCount(void)
1.1       root      414: {
1.1.1.7 ! root      415:        return CpuBreakPoints.count;
        !           416: }
        !           417: 
        !           418: /**
        !           419:  * Return number of DSP condition breakpoints
        !           420:  */
        !           421: int BreakCond_DspBreakPointCount(void)
        !           422: {
        !           423:        return DspBreakPoints.count;
1.1       root      424: }
                    425: 
                    426: 
                    427: /* -------------- breakpoint condition parsing, internals ------------- */
                    428: 
                    429: /* struct for passing around breakpoint conditions parsing state */
                    430: typedef struct {
                    431:        int arg;                /* current arg */
                    432:        int argc;               /* arg count */
                    433:        const char **argv;      /* arg pointer array (+ strings) */
                    434:        const char *error;      /* error from parsing args */
                    435: } parser_state_t;
                    436: 
                    437: 
                    438: /* Hatari variable name & address array items */
                    439: typedef struct {
                    440:        const char *name;
                    441:        Uint32 *addr;
                    442:        value_t vtype;
                    443:        size_t bits;
                    444:        const char *constraints;
                    445: } var_addr_t;
                    446: 
                    447: /* Accessor functions for calculated Hatari values */
                    448: static Uint32 GetLineCycles(void)
                    449: {
                    450:        int dummy1, dummy2, lcycles;
                    451:        Video_GetPosition(&dummy1, &dummy2 , &lcycles);
                    452:        return lcycles;
                    453: }
                    454: static Uint32 GetFrameCycles(void)
                    455: {
                    456:        int dummy1, dummy2, fcycles;
                    457:        Video_GetPosition(&fcycles, &dummy1, &dummy2);
                    458:        return fcycles;
                    459: }
                    460: 
1.1.1.2   root      461: /* helpers for TOS OS call opcode accessor functions */
                    462: #define INVALID_OPCODE 0xFFFFu
                    463: 
                    464: static inline Uint16 getLineOpcode(Uint8 line)
                    465: {
                    466:        Uint32 pc;
                    467:        Uint16 instr;
                    468:        pc = M68000_GetPC();
                    469:        instr = STMemory_ReadWord(pc);
                    470:        /* for opcode X, Line-A = 0xA00X, Line-F = 0xF00X */
                    471:        if ((instr >> 12) == line) {
                    472:                return instr & 0xFF;
                    473:        }
                    474:        return INVALID_OPCODE;
                    475: }
                    476: static inline bool isTrap(Uint8 trap)
                    477: {
                    478:        Uint32 pc;
                    479:        Uint16 instr;
                    480:        pc = M68000_GetPC();
                    481:        instr = STMemory_ReadWord(pc);
                    482:        return (instr == (Uint16)0x4e40u + trap);
                    483: }
                    484: static inline Uint16 getControlOpcode(void)
                    485: {
                    486:        /* Control[] address from D1, opcode in Control[0] */
                    487:        return STMemory_ReadWord(STMemory_ReadLong(Regs[REG_D1]));
                    488: }
                    489: static inline Uint16 getStackOpcode(void)
                    490: {
                    491:        return STMemory_ReadWord(Regs[REG_A7]);
                    492: }
                    493: 
                    494: /* Actual TOS OS call opcode accessor functions */
                    495: static Uint32 GetLineAOpcode(void)
                    496: {
                    497:        return getLineOpcode(0xA);
                    498: }
                    499: static Uint32 GetLineFOpcode(void)
                    500: {
                    501:        return getLineOpcode(0xF);
                    502: }
                    503: static Uint32 GetGemdosOpcode(void)
                    504: {
                    505:        if (isTrap(1)) {
                    506:                return getStackOpcode();
                    507:        }
                    508:        return INVALID_OPCODE;
                    509: }
                    510: static Uint32 GetBiosOpcode(void)
                    511: {
                    512:        if (isTrap(13)) {
                    513:                return getStackOpcode();
                    514:        }
                    515:        return INVALID_OPCODE;
                    516: }
                    517: static Uint32 GetXbiosOpcode(void)
                    518: {
                    519:        if (isTrap(14)) {
                    520:                return getStackOpcode();
                    521:        }
                    522:        return INVALID_OPCODE;
                    523: }
                    524: static Uint32 GetAesOpcode(void)
                    525: {
                    526:        if (isTrap(2)) {
                    527:                Uint16 d0 = Regs[REG_D0];
                    528:                if (d0 == 0xC8) {
                    529:                        return getControlOpcode();
                    530:                } else if (d0 == 0xC9) {
                    531:                        /* same as appl_yield() */
                    532:                        return 0x11;
                    533:                }
                    534:        }
                    535:        return INVALID_OPCODE;
                    536: }
                    537: static Uint32 GetVdiOpcode(void)
                    538: {
                    539:        if (isTrap(2)) {
                    540:                Uint16 d0 = Regs[REG_D0];
                    541:                if (d0 == 0x73) {
                    542:                        return getControlOpcode();
                    543:                } else if (d0 == 0xFFFE) {
                    544:                        /* -2 = vq_[v]gdos() */
                    545:                        return 0xFFFE;
                    546:                }
                    547:        }
                    548:        return INVALID_OPCODE;
                    549: }
                    550: 
1.1.1.5   root      551: static Uint32 GetNextPC(void)
                    552: {
                    553:        return Disasm_GetNextPC(M68000_GetPC());
                    554: }
                    555: 
1.1       root      556: /* sorted by variable name so that this can be bisected */
                    557: static const var_addr_t hatari_vars[] = {
1.1.1.2   root      558:        { "AesOpcode", (Uint32*)GetAesOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" },
1.1.1.7 ! root      559:        { "Basepage", (Uint32*)DebugInfo_GetBASEPAGE, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" },
1.1.1.2   root      560:        { "BiosOpcode", (Uint32*)GetBiosOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" },
                    561:        { "BSS", (Uint32*)DebugInfo_GetBSS, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" },
1.1.1.6   root      562:        { "CpuInstr", (Uint32*)DebugCpu_InstrCount, VALUE_TYPE_FUNCTION32, 0, "CPU instructions count" },
                    563:        { "CpuOpcodeType", (Uint32*)DebugCpu_OpcodeType, VALUE_TYPE_FUNCTION32, 0, "CPU instruction type" },
1.1.1.2   root      564:        { "DATA", (Uint32*)DebugInfo_GetDATA, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" },
1.1.1.6   root      565: #if ENABLE_DSP_EMU
                    566:        { "DspInstr", (Uint32*)DebugDsp_InstrCount, VALUE_TYPE_FUNCTION32, 0, "DSP instructions count" },
                    567:        { "DspOpcodeType", (Uint32*)DebugDsp_OpcodeType, VALUE_TYPE_FUNCTION32, 0, "DSP instruction type" },
                    568: #endif
1.1       root      569:        { "FrameCycles", (Uint32*)GetFrameCycles, VALUE_TYPE_FUNCTION32, 0, NULL },
1.1.1.2   root      570:        { "GemdosOpcode", (Uint32*)GetGemdosOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" },
1.1       root      571:        { "HBL", (Uint32*)&nHBL, VALUE_TYPE_VAR32, sizeof(nHBL)*8, NULL },
1.1.1.2   root      572:        { "LineAOpcode", (Uint32*)GetLineAOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" },
1.1       root      573:        { "LineCycles", (Uint32*)GetLineCycles, VALUE_TYPE_FUNCTION32, 0, "is always divisable by 4" },
1.1.1.2   root      574:        { "LineFOpcode", (Uint32*)GetLineFOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" },
1.1.1.5   root      575:        { "NextPC", (Uint32*)GetNextPC, VALUE_TYPE_FUNCTION32, 0, NULL },
1.1.1.2   root      576:        { "TEXT", (Uint32*)DebugInfo_GetTEXT, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" },
1.1.1.5   root      577:        { "TEXTEnd", (Uint32*)DebugInfo_GetTEXTEnd, VALUE_TYPE_FUNCTION32, 0, "invalid before Desktop is up" },
1.1.1.2   root      578:        { "VBL", (Uint32*)&nVBLs, VALUE_TYPE_VAR32, sizeof(nVBLs)*8, NULL },
                    579:        { "VdiOpcode", (Uint32*)GetVdiOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" },
                    580:        { "XbiosOpcode", (Uint32*)GetXbiosOpcode, VALUE_TYPE_FUNCTION32, 16, "by default FFFF" }
1.1       root      581: };
                    582: 
                    583: 
                    584: /**
                    585:  * Readline match callback for CPU variable/symbol name completion.
                    586:  * STATE = 0 -> different text from previous one.
                    587:  * Return next match or NULL if no matches.
                    588:  */
                    589: char *BreakCond_MatchCpuVariable(const char *text, int state)
                    590: {
                    591:        static int i, len;
                    592:        const char *name;
                    593:        
                    594:        if (!state) {
                    595:                /* first match */
                    596:                len = strlen(text);
                    597:                i = 0;
                    598:        }
                    599:        /* next match */
                    600:        while (i < ARRAYSIZE(hatari_vars)) {
                    601:                name = hatari_vars[i++].name;
                    602:                if (strncasecmp(name, text, len) == 0)
                    603:                        return (strdup(name));
                    604:        }
                    605:        /* no variable match, check all CPU symbols */
                    606:        return Symbols_MatchCpuAddress(text, state);
                    607: }
                    608: 
                    609: /**
                    610:  * Readline match callback for DSP variable/symbol name completion.
                    611:  * STATE = 0 -> different text from previous one.
                    612:  * Return next match or NULL if no matches.
                    613:  */
                    614: char *BreakCond_MatchDspVariable(const char *text, int state)
                    615: {
                    616:        /* currently no DSP variables, check all DSP symbols */
                    617:        return Symbols_MatchDspAddress(text, state);
                    618: }
                    619: 
                    620: 
                    621: /**
                    622:  * If given string is a Hatari variable name, set bc_value
                    623:  * fields accordingly and return true, otherwise return false.
                    624:  */
                    625: static bool BreakCond_ParseVariable(const char *name, bc_value_t *bc_value)
                    626: {
1.1.1.7 ! root      627:        const var_addr_t *hvar;
1.1       root      628:        /* left, right, middle, direction */
                    629:         int l, r, m, dir;
                    630: 
                    631:        ENTERFUNC(("BreakCond_ParseVariable('%s')\n", name));
                    632:        /* bisect */
                    633:        l = 0;
                    634:        r = ARRAYSIZE(hatari_vars) - 1;
                    635:        do {
                    636:                m = (l+r) >> 1;
1.1.1.7 ! root      637:                hvar = hatari_vars + m;
        !           638:                dir = strcasecmp(name, hvar->name);
1.1       root      639:                if (dir == 0) {
1.1.1.7 ! root      640:                        bc_value->value.reg32 = hvar->addr;
        !           641:                        bc_value->valuetype = hvar->vtype;
        !           642:                        bc_value->bits = hvar->bits;
1.1       root      643:                        assert(bc_value->bits == 32 || bc_value->valuetype !=  VALUE_TYPE_VAR32);
                    644:                        EXITFUNC(("-> true\n"));
                    645:                        return true;
                    646:                }
                    647:                if (dir < 0) {
                    648:                        r = m-1;
                    649:                } else {
                    650:                        l = m+1;
                    651:                }
                    652:        } while (l <= r);
                    653:        EXITFUNC(("-> false\n"));
                    654:        return false;
                    655: }
                    656: 
1.1.1.2   root      657: /**
                    658:  * If given string is a Hatari variable name, set value to given
                    659:  * variable value and return true, otherwise return false.
                    660:  */
                    661: bool BreakCond_GetHatariVariable(const char *name, Uint32 *value)
                    662: {
                    663:        bc_value_t bc_value;
                    664:        if (!BreakCond_ParseVariable(name, &bc_value)) {
                    665:                return false;
                    666:        }
                    667:        bc_value.mask = 0xffffffff;
                    668:        bc_value.is_indirect = false;
                    669:        *value = BreakCond_GetValue(&bc_value);
                    670:        return true;
                    671: }
                    672: 
1.1       root      673: 
                    674: /**
                    675:  * If given string matches a suitable symbol, set bc_value
                    676:  * fields accordingly and return true, otherwise return false.
                    677:  */
                    678: static bool BreakCond_ParseSymbol(const char *name, bc_value_t *bc_value)
                    679: {
                    680:        symtype_t symtype;
                    681:        Uint32 addr;
                    682: 
                    683:        ENTERFUNC(("BreakCond_ParseSymbol('%s')\n", name));
                    684:        if (bc_value->is_indirect) {
                    685:                /* indirect use of address makes sense only for data */
                    686:                symtype = SYMTYPE_DATA|SYMTYPE_BSS;
                    687:        } else {
                    688:                /* direct value can be compared for anything */
                    689:                symtype = SYMTYPE_ALL;
                    690:        }
                    691:        
                    692:        if (bc_value->dsp_space) {
                    693:                if (!Symbols_GetDspAddress(symtype, name, &addr)) {
                    694:                        EXITFUNC(("-> false (DSP)\n"));
                    695:                        return false;
                    696:                }
                    697:                /* all DSP memory values are 24-bits */
                    698:                bc_value->bits = 24;
                    699:                bc_value->value.number = addr;
                    700:                bc_value->valuetype = VALUE_TYPE_NUMBER;
                    701:                EXITFUNC(("-> true (DSP)\n"));
                    702:                return true;
                    703:        }
                    704:        
                    705:        if (!Symbols_GetCpuAddress(symtype, name, &addr)) {
                    706:                EXITFUNC(("-> false (CPU)\n"));
                    707:                return false;
                    708:        }
                    709:        if (addr & 1) {
                    710:                /* only bytes can be at odd addresses */
                    711:                bc_value->bits = 8;
                    712:        } else {
                    713:                bc_value->bits = 32;
                    714:        }
                    715:        bc_value->value.number = addr;
                    716:        bc_value->valuetype = VALUE_TYPE_NUMBER;
                    717:        EXITFUNC(("-> true (CPU)\n"));
                    718:        return true;
                    719: }
                    720: 
                    721: 
                    722: /**
                    723:  * Helper function to get CPU PC register value with static inline as Uint32
                    724:  */
                    725: static Uint32 GetCpuPC(void)
                    726: {
                    727:        return M68000_GetPC();
                    728: }
                    729: /**
                    730:  * Helper function to get CPU SR register value with static inline as Uint32
                    731:  */
                    732: static Uint32 GetCpuSR(void)
                    733: {
                    734:        return M68000_GetSR();
                    735: }
                    736: 
                    737: /**
                    738:  * If given string is register name (for DSP or CPU), set bc_value
                    739:  * fields accordingly and return true, otherwise return false.
                    740:  */
                    741: static bool BreakCond_ParseRegister(const char *regname, bc_value_t *bc_value)
                    742: {
                    743:        int regsize;
                    744:        ENTERFUNC(("BreakCond_ParseRegister('%s')\n", regname));
                    745:        if (bc_value->dsp_space) {
                    746:                regsize = DSP_GetRegisterAddress(regname,
                    747:                                              &(bc_value->value.reg32),
                    748:                                              &(bc_value->mask));
                    749:                if (regsize) {
1.1.1.6   root      750:                        if (bc_value->is_indirect
                    751:                            && toupper((unsigned char)regname[0]) != 'R') {
1.1       root      752:                                fprintf(stderr, "ERROR: only R0-R7 DSP registers can be used for indirect addressing!\n");
                    753:                                EXITFUNC(("-> false (DSP)\n"));
                    754:                                return false;
                    755:                        }
                    756:                        /* all DSP memory values are 24-bits */
                    757:                        bc_value->bits = 24;
                    758:                        bc_value->valuetype = regsize;
                    759:                        EXITFUNC(("-> true (DSP)\n"));
                    760:                        return true;
                    761:                }
                    762:                EXITFUNC(("-> false (DSP)\n"));
                    763:                return false;
                    764:        }
                    765:        regsize = DebugCpu_GetRegisterAddress(regname, &(bc_value->value.reg32));
                    766:        if (regsize) {
                    767:                bc_value->bits = regsize;
                    768:                /* valuetypes for registers are 16 & 32 */
                    769:                bc_value->valuetype = regsize;
                    770:                EXITFUNC(("-> true (CPU)\n"));
                    771:                return true;
                    772:        }
                    773:        /* Exact UAE core 32-bit PC & 16-bit SR register values
1.1.1.4   root      774:         * can be gotten only through UAE accessors, not directly
1.1       root      775:         */
                    776:        if (strcasecmp(regname, "PC") == 0) {
                    777:                bc_value->bits = 32;
                    778:                bc_value->value.func32 = GetCpuPC;
                    779:                bc_value->valuetype = VALUE_TYPE_FUNCTION32;
                    780:                EXITFUNC(("-> true (CPU)\n"));
                    781:                return true;
                    782:        }
                    783:        if (strcasecmp(regname, "SR") == 0) {
                    784:                bc_value->bits = 16;
                    785:                bc_value->value.func32 = GetCpuSR;
                    786:                bc_value->valuetype = VALUE_TYPE_FUNCTION32;
                    787:                EXITFUNC(("-> true (CPU)\n"));
                    788:                return true;
                    789:        }
                    790:        EXITFUNC(("-> false (CPU)\n"));
                    791:        return false;
                    792: }
                    793: 
                    794: /**
                    795:  * If given address is valid (for DSP or CPU), return true.
                    796:  */
                    797: static bool BreakCond_CheckAddress(bc_value_t *bc_value)
                    798: {
1.1.1.7 ! root      799:        Uint32 addr = bc_value->value.number;
        !           800:        int size = bc_value->bits >> 8;
1.1       root      801: 
                    802:        ENTERFUNC(("BreakCond_CheckAddress(%x)\n", addr));
                    803:        if (bc_value->dsp_space) {
1.1.1.7 ! root      804:                if (addr+size > 0xFFFF) {
1.1       root      805:                        EXITFUNC(("-> false (DSP)\n"));
                    806:                        return false;
                    807:                }
                    808:                EXITFUNC(("-> true (DSP)\n"));
                    809:                return true;
                    810:        }
1.1.1.7 ! root      811:        if (!STMemory_CheckAreaType(addr, size, ABFLAG_RAM | ABFLAG_ROM | ABFLAG_IO)) {
1.1       root      812:                EXITFUNC(("-> false (CPU)\n"));
                    813:                return false;
                    814:        }
                    815:        EXITFUNC(("-> true (CPU)\n"));
                    816:        return true;
                    817: }
                    818: 
                    819: 
                    820: /**
                    821:  * Check for and parse a condition value address space/width modifier.
                    822:  * Modify pstate according to parsing (arg index and error string).
                    823:  * Return false for error and true for no or successfully parsed modifier.
                    824:  */
                    825: static bool BreakCond_ParseAddressModifier(parser_state_t *pstate, bc_value_t *bc_value)
                    826: {
                    827:        char mode;
                    828: 
                    829:        ENTERFUNC(("BreakCond_ParseAddressModifier()\n"));
                    830:        if (pstate->arg+2 > pstate->argc ||
                    831:            strcmp(pstate->argv[pstate->arg], ".") != 0) {
                    832:                if (bc_value->dsp_space && bc_value->is_indirect) {
                    833:                        pstate->error = "DSP memory addresses need to specify address space";
                    834:                        EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    835:                        return false;
                    836:                }
                    837:                EXITFUNC(("arg:%d -> true (missing)\n", pstate->arg));
                    838:                return true;
                    839:        }
                    840:        if (!bc_value->is_indirect) {
                    841:                pstate->error = "space/width modifier makes sense only for an address (register)";
                    842:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    843:                return false;
                    844:        }
                    845:        pstate->arg++;
                    846:        if (bc_value->dsp_space) {
                    847:                switch (pstate->argv[pstate->arg][0]) {
                    848:                case 'p':
                    849:                case 'x':
                    850:                case 'y':
1.1.1.6   root      851:                        mode = toupper((unsigned char)pstate->argv[pstate->arg][0]);
1.1       root      852:                        break;
                    853:                default:
                    854:                        pstate->error = "invalid address space modifier";
                    855:                        EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    856:                        return false;
                    857:                }
                    858:        } else {
                    859:                switch (pstate->argv[pstate->arg][0]) {
                    860:                case 'l':
                    861:                        mode = 32;
                    862:                        break;
                    863:                case 'w':
                    864:                        mode = 16;
                    865:                        break;
                    866:                case 'b':
                    867:                        mode = 8;
                    868:                        break;
                    869:                default:
                    870:                        pstate->error = "invalid address width modifier";
                    871:                        EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    872:                        return false;
                    873:                }
                    874:        }
                    875:        if (pstate->argv[pstate->arg][1]) {
                    876:                pstate->error = "invalid address space/width modifier";
                    877:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    878:                return false;
                    879:        }
                    880:        if (bc_value->dsp_space) {
                    881:                bc_value->dsp_space = mode;
                    882:                EXITFUNC(("arg:%d -> space:%c, true\n", pstate->arg, mode));
                    883:        } else {
                    884:                bc_value->bits = mode;
                    885:                EXITFUNC(("arg:%d -> width:%d, true\n", pstate->arg, mode));
                    886:        }
                    887:        pstate->arg++;
                    888:        return true;
                    889: }
                    890: 
                    891: 
                    892: /**
                    893:  * Check for and parse a condition value mask.
                    894:  * Modify pstate according to parsing (arg index and error string).
                    895:  * Return false for error and true for no or successfully parsed modifier.
                    896:  */
                    897: static bool BreakCond_ParseMaskModifier(parser_state_t *pstate, bc_value_t *bc_value)
                    898: {
                    899:        ENTERFUNC(("BreakCond_ParseMaskModifier()\n"));
                    900:        if (pstate->arg+2 > pstate->argc ||
                    901:            strcmp(pstate->argv[pstate->arg], "&") != 0) {
                    902:                EXITFUNC(("arg:%d -> true (missing)\n", pstate->arg));
                    903:                return true;
                    904:        }
                    905:        if (bc_value->valuetype == VALUE_TYPE_NUMBER &&
                    906:            !bc_value->is_indirect) {
                    907:                fprintf(stderr, "WARNING: plain numbers shouldn't need masks.\n");
                    908:        }
                    909:        pstate->arg++;
                    910:        if (!Eval_Number(pstate->argv[pstate->arg], &(bc_value->mask))) {
                    911:                pstate->error = "invalid dec/hex/bin value";
                    912:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    913:                return false;
                    914:        }
                    915:        if (bc_value->mask == 0 ||
                    916:            (bc_value->valuetype == VALUE_TYPE_NUMBER && !bc_value->is_indirect &&
                    917:             bc_value->value.number && !(bc_value->value.number & bc_value->mask))) {
                    918:                pstate->error = "mask zeroes value";
                    919:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    920:                return false;
                    921:        }
                    922:        EXITFUNC(("arg:%d -> true (%x)\n", pstate->arg, bc_value->mask));
                    923:        pstate->arg++;
                    924:        return true;
                    925: }
                    926: 
                    927: 
                    928: /**
                    929:  * Parse a breakpoint condition value.
                    930:  * Modify pstate according to parsing (arg index and error string).
                    931:  * Return true for success and false for error.
                    932:  */
                    933: static bool BreakCond_ParseValue(parser_state_t *pstate, bc_value_t *bc_value)
                    934: {
                    935:        const char *str;
                    936:        int skip = 1;
                    937: 
                    938:        ENTERFUNC(("BreakCond_Value()\n"));
                    939:        if (pstate->arg >= pstate->argc) {
                    940:                pstate->error = "value missing";
                    941:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    942:                return false;
                    943:        }
                    944:        /* parse indirection */
                    945:        if (pstate->arg+3 <= pstate->argc) {
                    946:                if (strcmp(pstate->argv[pstate->arg+0], "(") == 0 &&
                    947:                    strcmp(pstate->argv[pstate->arg+2], ")") == 0) {
                    948:                        bc_value->is_indirect = true;
                    949:                        pstate->arg++;
                    950:                        skip = 2;
                    951:                }
                    952:        }
                    953:        
                    954:        str = pstate->argv[pstate->arg];
1.1.1.6   root      955:        if (isalpha((unsigned char)*str) || *str == '_') {
1.1       root      956:                /* parse direct or indirect variable/register/symbol name */
                    957:                if (bc_value->is_indirect) {
                    958:                        /* a valid register or data symbol name? */
                    959:                        if (!BreakCond_ParseRegister(str, bc_value) &&
                    960:                            !BreakCond_ParseSymbol(str, bc_value)) {
                    961:                                pstate->error = "invalid register/symbol name for indirection";
                    962:                                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    963:                                return false;
                    964:                        }
                    965:                } else {
                    966:                        /* a valid Hatari variable or register name?
                    967:                         * variables cannot be used for ST memory indirection.
                    968:                         */
                    969:                        if (!BreakCond_ParseVariable(str, bc_value) &&
                    970:                            !BreakCond_ParseRegister(str, bc_value) &&
                    971:                            !BreakCond_ParseSymbol(str, bc_value)) {
                    972:                                pstate->error = "invalid variable/register/symbol name";
                    973:                                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    974:                                return false;
                    975:                        }
                    976:                }
                    977:        } else {
                    978:                /* a number */
                    979:                if (!Eval_Number(str, &(bc_value->value.number))) {
                    980:                        pstate->error = "invalid dec/hex/bin value";
                    981:                        EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    982:                        return false;
                    983:                }
                    984:        }
                    985:        /* memory address (indirect value) -> OK as address? */
                    986:        if (bc_value->is_indirect &&
                    987:            bc_value->valuetype == VALUE_TYPE_NUMBER &&
                    988:            !BreakCond_CheckAddress(bc_value)) {
                    989:                pstate->error = "invalid address";
                    990:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    991:                return false;
                    992:        }
                    993:        pstate->arg += skip;
                    994: 
                    995:        /* parse modifiers */
                    996:        if (!BreakCond_ParseAddressModifier(pstate, bc_value)) {
                    997:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    998:                return false;
                    999:        }
                   1000:        if (!BreakCond_ParseMaskModifier(pstate, bc_value)) {
                   1001:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                   1002:                return false;
                   1003:        }
                   1004:        EXITFUNC(("arg:%d -> true (%s value)\n", pstate->arg,
                   1005:                  (bc_value->is_indirect ? "indirect" : "direct")));
                   1006:        return true;
                   1007: }
                   1008: 
                   1009: 
                   1010: /**
                   1011:  * Parse a breakpoint comparison character.
                   1012:  * Modify pstate according to parsing (arg index and error string).
                   1013:  * Return the character or nil for an error.
                   1014:  */
                   1015: static char BreakCond_ParseComparison(parser_state_t *pstate)
                   1016: {
                   1017:        const char *comparison;
                   1018:        
                   1019:        ENTERFUNC(("BreakCond_ParseComparison(), arg:%d\n", pstate->arg));
                   1020:        if (pstate->arg >= pstate->argc) {
                   1021:                pstate->error = "breakpoint comparison missing";
                   1022:                EXITFUNC(("-> false\n"));
                   1023:                return false;
                   1024:        }
                   1025:        comparison = pstate->argv[pstate->arg];
                   1026:        switch (comparison[0]) {
                   1027:        case '<':
                   1028:        case '>':
                   1029:        case '=':
                   1030:        case '!':
                   1031:                break;
                   1032:        default:
                   1033:                pstate->error = "invalid comparison character";
                   1034:                EXITFUNC(("-> false\n"));
                   1035:                return false;
                   1036:        }
                   1037:        if (comparison[1]) {
                   1038:                pstate->error = "trailing comparison character(s)";
                   1039:                EXITFUNC(("-> false\n"));
                   1040:                return false;
                   1041:        }
                   1042: 
                   1043:        pstate->arg++;
                   1044:        if (pstate->arg >= pstate->argc) {
                   1045:                pstate->error = "right side missing";
                   1046:                EXITFUNC(("-> false\n"));
                   1047:                return false;
                   1048:        }
                   1049:        EXITFUNC(("-> '%c'\n", *comparison));
                   1050:        return *comparison;
                   1051: }
                   1052: 
                   1053: 
                   1054: /**
                   1055:  * If no value, use the other value, if that also missing, use default
                   1056:  */
                   1057: static void BreakCond_InheritDefault(Uint32 *value1, Uint32 value2, Uint32 defvalue)
                   1058: {
                   1059:        if (!*value1) {
                   1060:                if (value2) {
                   1061:                        *value1 = value2;
                   1062:                } else {
                   1063:                        *value1 = defvalue;
                   1064:                }
                   1065:        }
                   1066: }
                   1067: 
                   1068: /**
                   1069:  * Check & ensure that the masks and address sizes are sane
                   1070:  * and allow comparison with the other side.
                   1071:  * If yes, return true, otherwise false.
                   1072:  */
                   1073: static bool BreakCond_CrossCheckValues(parser_state_t *pstate,
                   1074:                                       bc_value_t *bc_value1,
                   1075:                                       bc_value_t *bc_value2)
                   1076: {
                   1077:        Uint32 mask1, mask2, defbits;
                   1078:        ENTERFUNC(("BreakCond_CrossCheckValues()\n"));
                   1079: 
                   1080:        /* make sure there're valid bit widths and that masks have some value */
                   1081:        if (bc_value1->dsp_space) {
                   1082:                defbits = 24;
                   1083:        } else {
                   1084:                defbits = 32;
                   1085:        }
                   1086:        BreakCond_InheritDefault(&(bc_value1->bits), bc_value2->bits, defbits);
                   1087:        BreakCond_InheritDefault(&(bc_value2->bits), bc_value1->bits, defbits);
                   1088:        BreakCond_InheritDefault(&(bc_value1->mask), bc_value2->mask, BITMASK(bc_value1->bits));
                   1089:        BreakCond_InheritDefault(&(bc_value2->mask), bc_value1->mask, BITMASK(bc_value2->bits));
                   1090: 
                   1091:        /* check first value mask & bit width */
                   1092:        mask1 = BITMASK(bc_value1->bits) & bc_value1->mask;
                   1093:        
                   1094:        if (mask1 != bc_value1->mask) {
                   1095:                fprintf(stderr, "WARNING: mask 0x%x doesn't fit into %d address/register bits.\n",
                   1096:                        bc_value1->mask, bc_value1->bits);
                   1097:        }
                   1098:        if (!bc_value1->dsp_space &&
                   1099:            bc_value1->is_indirect &&
                   1100:            (bc_value1->value.number & 1) && bc_value1->bits > 8) {
                   1101:                fprintf(stderr, "WARNING: odd CPU address 0x%x given without using byte (.b) width.\n",
                   1102:                        bc_value1->value.number);
                   1103:        }
                   1104:        
                   1105:        /* cross-check both values masks */
                   1106:        mask2 = BITMASK(bc_value2->bits) & bc_value2->mask;
                   1107: 
                   1108:        if ((mask1 & mask2) == 0) {
                   1109:                pstate->error = "values masks cancel each other";
                   1110:                EXITFUNC(("-> false\n"));
                   1111:                return false;
                   1112:        }
                   1113:        if (bc_value2->is_indirect ||
                   1114:            bc_value2->value.number == 0 ||
                   1115:            bc_value2->valuetype != VALUE_TYPE_NUMBER) {
                   1116:                EXITFUNC(("-> true (no problematic direct types)\n"));
                   1117:                return true;
                   1118:        }
                   1119:        if ((bc_value2->value.number & mask1) != bc_value2->value.number) {
                   1120:                pstate->error = "number doesn't fit the other side address width&mask";
                   1121:                EXITFUNC(("-> false\n"));
                   1122:                return false;
                   1123:        }
                   1124:        EXITFUNC(("-> true\n"));
                   1125:        return true;
                   1126: }
                   1127: 
                   1128: 
                   1129: /**
                   1130:  * Parse given breakpoint conditions and append them to breakpoints.
                   1131:  * Modify pstate according to parsing (arg index and error string).
                   1132:  * Return number of added conditions or zero for failure.
                   1133:  */
                   1134: static int BreakCond_ParseCondition(parser_state_t *pstate, bool bForDsp,
1.1.1.6   root     1135:                                    bc_breakpoint_t *bp, int ccount)
1.1       root     1136: {
                   1137:        bc_condition_t condition;
                   1138: 
                   1139:        ENTERFUNC(("BreakCond_ParseCondition(...)\n"));
                   1140: 
                   1141:        /* setup condition */
                   1142:        memset(&condition, 0, sizeof(bc_condition_t));
                   1143:        if (bForDsp) {
                   1144:                /* used also for checking whether value is for DSP */
                   1145:                condition.lvalue.dsp_space = BC_DEFAULT_DSP_SPACE;
                   1146:                condition.rvalue.dsp_space = BC_DEFAULT_DSP_SPACE;
                   1147:        }
                   1148: 
                   1149:        /* parse condition */
                   1150:        if (!BreakCond_ParseValue(pstate, &(condition.lvalue))) {
                   1151:                EXITFUNC(("-> 0\n"));
                   1152:                return 0;
                   1153:        }
                   1154:        condition.comparison = BreakCond_ParseComparison(pstate);
                   1155:        if (!condition.comparison) {
                   1156:                EXITFUNC(("-> 0\n"));
                   1157:                return 0;
                   1158:        }
                   1159:        if (!BreakCond_ParseValue(pstate, &(condition.rvalue))) {
                   1160:                EXITFUNC(("-> 0\n"));
                   1161:                return 0;
                   1162:        }
                   1163:        if (!(BreakCond_CrossCheckValues(pstate, &(condition.lvalue), &(condition.rvalue)) &&
                   1164:              BreakCond_CrossCheckValues(pstate, &(condition.rvalue), &(condition.lvalue)))) {
                   1165:                EXITFUNC(("-> 0\n"));
                   1166:                return 0;
                   1167:        }
1.1.1.6   root     1168:        /* copy new condition */
                   1169:        ccount += 1;
                   1170:        bp->conditions = realloc(bp->conditions, sizeof(bc_condition_t)*(ccount));
                   1171:        if (!bp->conditions) {
                   1172:                pstate->error = "failed to allocate space for breakpoint condition";
                   1173:                EXITFUNC(("-> 0\n"));
                   1174:                return 0;
                   1175:        }
                   1176:        bp->conditions[ccount-1] = condition;
1.1       root     1177: 
                   1178:        /* continue with next condition? */
                   1179:        if (pstate->arg == pstate->argc) {
1.1.1.6   root     1180:                EXITFUNC(("-> %d conditions (all args parsed)\n", ccount));
1.1       root     1181:                return ccount;
                   1182:        }
                   1183:        if (strcmp(pstate->argv[pstate->arg], "&&") != 0) {
                   1184:                pstate->error = "trailing content for breakpoint condition";
                   1185:                EXITFUNC(("-> 0\n"));
                   1186:                return 0;
                   1187:        }
                   1188:        pstate->arg++;
                   1189: 
                   1190:        /* recurse conditions parsing */
1.1.1.6   root     1191:        ccount = BreakCond_ParseCondition(pstate, bForDsp, bp, ccount);
1.1       root     1192:        if (!ccount) {
                   1193:                EXITFUNC(("-> 0\n"));
                   1194:                return 0;
                   1195:        }
1.1.1.6   root     1196:        EXITFUNC(("-> %d conditions (recursed)\n", ccount));
1.1       root     1197:        return ccount;
                   1198: }
                   1199: 
                   1200: 
                   1201: /**
                   1202:  * Tokenize given breakpoint expression to given parser struct.
                   1203:  * Return normalized expression string that corresponds to tokenization
                   1204:  * or NULL on error. On error, pstate->error contains the error message
                   1205:  * and pstate->arg index to invalid character (instead of to token like
                   1206:  * after parsing).
                   1207:  */
                   1208: static char *BreakCond_TokenizeExpression(const char *expression,
                   1209:                                          parser_state_t *pstate)
                   1210: {
                   1211:        char separator[] = {
                   1212:                '=', '!', '<', '>',  /* comparison operators */
                   1213:                '(', ')', '.', '&',  /* other separators */
                   1214:                '\0'                 /* terminator */
                   1215:        };
                   1216:        bool is_separated, has_comparison;
                   1217:        char sep, *dst, *normalized;
                   1218:        const char *src;
                   1219:        int i, tokens;
                   1220: 
                   1221:        memset(pstate, 0, sizeof(parser_state_t));
                   1222: 
                   1223:        /* _minimum_ safe size for normalized expression is 2x+1 */
                   1224:        normalized = malloc(2*strlen(expression)+1);
                   1225:        if (!normalized) {
                   1226:                pstate->error = "alloc failed";
                   1227:                return NULL;
                   1228:        }
                   1229: 
                   1230:        /* check characters & normalize string */
                   1231:        dst = normalized;
                   1232:        is_separated = false;
                   1233:        has_comparison = false;
                   1234:        for (src = expression; *src; src++) {
                   1235:                /* discard white space in source */
1.1.1.6   root     1236:                if (isspace((unsigned char)*src)) {
1.1       root     1237:                        continue;
                   1238:                }
                   1239:                /* separate tokens with single space in destination */
                   1240:                for (i = 0; (sep = separator[i]); i++) {
                   1241:                        if (*src == sep) {
                   1242:                                if (dst > normalized) {
                   1243:                                        /* don't separate boolean AND '&&' */
                   1244:                                        if (*src == '&' && *(src-1) == '&') {
                   1245:                                                dst--;
                   1246:                                        } else {
                   1247:                                                if (!is_separated) {
                   1248:                                                        *dst++ = ' ';
                   1249:                                                }
                   1250:                                        }
                   1251:                                }
                   1252:                                *dst++ = *src;
                   1253:                                *dst++ = ' ';
                   1254:                                is_separated = true;
                   1255:                                if (i < 4) {
                   1256:                                        has_comparison = true;
                   1257:                                }
                   1258:                                break;
                   1259:                        }
                   1260:                }
                   1261:                /* validate & copy other characters */
                   1262:                if (!sep) {
                   1263:                        /* variable/register/symbol or number prefix? */
1.1.1.6   root     1264:                        if (!(isalnum((unsigned char)*src) || *src == '_' ||
1.1       root     1265:                              *src == '$' || *src == '#' || *src == '%')) {
                   1266:                                pstate->error = "invalid character";
                   1267:                                pstate->arg = src-expression;
                   1268:                                free(normalized);
                   1269:                                return NULL;
                   1270:                        }
                   1271:                        *dst++ = *src;
                   1272:                        is_separated = false;
                   1273:                }
                   1274:        }
                   1275:        if (is_separated) {
                   1276:                dst--;  /* no trailing space */
                   1277:        }
                   1278:        *dst = '\0';
                   1279: 
                   1280:        if (!has_comparison) {
                   1281:                pstate->error = "condition comparison missing";
                   1282:                pstate->arg = strlen(expression)/2;
                   1283:                free(normalized);
                   1284:                return NULL;
                   1285:        }
                   1286: 
                   1287:        /* allocate exact space for tokenized string array + strings */
                   1288:        tokens = 1;
                   1289:        for (dst = normalized; *dst; dst++) {
                   1290:                if (*dst == ' ') {
                   1291:                        tokens++;
                   1292:                }
                   1293:        }
                   1294:        pstate->argv = malloc(tokens*sizeof(char*)+strlen(normalized)+1);
                   1295:        if (!pstate->argv) {
                   1296:                pstate->error = "alloc failed";
                   1297:                free(normalized);
                   1298:                return NULL;
                   1299:        }
                   1300:        /* and copy/tokenize... */
                   1301:        dst = (char*)(pstate->argv) + tokens*sizeof(char*);
                   1302:        strcpy(dst, normalized);
                   1303:        pstate->argv[0] = strtok(dst, " ");
                   1304:        for (i = 1; (dst = strtok(NULL, " ")); i++) {
                   1305:                pstate->argv[i] = dst;
                   1306:        }
                   1307:        assert(i == tokens);
                   1308:        pstate->argc = tokens;
                   1309: #if DEBUG
                   1310:        fprintf(stderr, "args->");
                   1311:        for (i = 0; i < tokens; i++) {
                   1312:                fprintf(stderr, " %d: %s,", i, pstate->argv[i]);
                   1313:        }
                   1314:        fprintf(stderr, "\n");
                   1315: #endif
                   1316:        return normalized;
                   1317: }
                   1318: 
                   1319: 
                   1320: /**
1.1.1.7 ! root     1321:  * Select corrent breakpoints struct and provide name for it.
1.1.1.6   root     1322:  * Make sure there's always space for at least one additional breakpoint.
1.1.1.7 ! root     1323:  * Return pointer to the breakpoints struct
1.1       root     1324:  */
1.1.1.7 ! root     1325: static bc_breakpoints_t* BreakCond_GetListInfo(bool bForDsp)
1.1       root     1326: {
1.1.1.7 ! root     1327:        bc_breakpoints_t *bps;
1.1       root     1328:        if (bForDsp) {
1.1.1.7 ! root     1329:                bps = &DspBreakPoints;
1.1       root     1330:        } else {
1.1.1.7 ! root     1331:                bps = &CpuBreakPoints;
1.1       root     1332:        }
1.1.1.6   root     1333:        /* allocate (more) space for breakpoints when needed */
1.1.1.7 ! root     1334:        if (bps->count + 1 >= bps->allocated) {
        !          1335:                if (!bps->allocated) {
1.1.1.6   root     1336:                        /* initial count of available breakpoints */
1.1.1.7 ! root     1337:                        bps->allocated = 16;
1.1.1.6   root     1338:                } else {
1.1.1.7 ! root     1339:                        bps->allocated *= 2;
1.1.1.6   root     1340:                }
1.1.1.7 ! root     1341:                if (bps->delayed_change) {
        !          1342:                        if(bps->breakpoint2delete) {
        !          1343:                                /* getting second re-alloc within same breakpoint handler is really
        !          1344:                                 * unlikely, this would require adding dozens of new breakpoints.
        !          1345:                                 */
        !          1346:                                fprintf(stderr, "ERROR: too many new breakpoints added within single breakpoint hit!\n");
        !          1347:                                abort();
        !          1348:                        }
        !          1349:                        bps->breakpoint2delete = bps->breakpoint;
        !          1350:                        bps->breakpoint = malloc(bps->allocated * sizeof(bc_breakpoint_t));
1.1.1.6   root     1351:                } else {
1.1.1.7 ! root     1352:                        bps->breakpoint = realloc(bps->breakpoint, bps->allocated * sizeof(bc_breakpoint_t));
1.1.1.6   root     1353:                }
1.1.1.7 ! root     1354:                assert(bps->breakpoint);
1.1.1.6   root     1355:        }
1.1.1.7 ! root     1356:        return bps;
1.1       root     1357: }
                   1358: 
                   1359: 
                   1360: /**
                   1361:  * Check whether any of the breakpoint conditions is such that it's
                   1362:  * intended for tracking given value changes (inequality comparison
                   1363:  * on identical values) or for retrieving the current value to break
                   1364:  * on next value change (other comparisons on identical values).
                   1365:  *
                   1366:  * On former case, mark it for tracking, on other cases, just
                   1367:  * retrieve the value.
                   1368:  */
                   1369: static void BreakCond_CheckTracking(bc_breakpoint_t *bp)
                   1370: {
                   1371:        bc_condition_t *condition;
                   1372:        bool track = false;
                   1373:        Uint32 value;
                   1374:        int i;
                   1375: 
                   1376:        condition = bp->conditions;
                   1377:        for (i = 0; i < bp->ccount; condition++, i++) {
                   1378:                
                   1379:                if (memcmp(&(condition->lvalue), &(condition->rvalue), sizeof(bc_value_t)) == 0) {
                   1380:                        /* set current value to right side */
                   1381:                        value = BreakCond_GetValue(&(condition->rvalue));
                   1382:                        condition->rvalue.value.number = value;
                   1383:                        condition->rvalue.valuetype = VALUE_TYPE_NUMBER;
                   1384:                        condition->rvalue.is_indirect = false;
1.1.1.6   root     1385:                        /* track those changes */
                   1386:                        if (condition->comparison != '=') {
1.1       root     1387:                                condition->track = true;
                   1388:                                track = true;
                   1389:                        } else {
                   1390:                                fprintf(stderr, "\t%d. condition: %c $%x\n",
                   1391:                                        i+1, condition->comparison, value);
                   1392:                        }
                   1393:                }
                   1394:        }
                   1395:        if (track) {
                   1396:                fprintf(stderr, "-> Track value changes, show value(s) when matched.\n");
                   1397:        }
                   1398: }
                   1399: 
                   1400: 
                   1401: /**
                   1402:  * Parse given breakpoint expression and store it.
                   1403:  * Return true for success and false for failure.
                   1404:  */
1.1.1.2   root     1405: static bool BreakCond_Parse(const char *expression, bc_options_t *options, bool bForDsp)
1.1       root     1406: {
                   1407:        parser_state_t pstate;
1.1.1.7 ! root     1408:        bc_breakpoints_t *bps;
1.1       root     1409:        bc_breakpoint_t *bp;
                   1410:        char *normalized;
                   1411:        int ccount;
                   1412: 
1.1.1.7 ! root     1413:        bps = BreakCond_GetListInfo(bForDsp);
1.1.1.6   root     1414: 
1.1.1.7 ! root     1415:        bp = bps->breakpoint + bps->count;
1.1       root     1416:        memset(bp, 0, sizeof(bc_breakpoint_t));
                   1417: 
                   1418:        normalized = BreakCond_TokenizeExpression(expression, &pstate);
                   1419:        if (normalized) {
                   1420:                bp->expression = normalized;
1.1.1.6   root     1421:                ccount = BreakCond_ParseCondition(&pstate, bForDsp, bp, 0);
                   1422:                /* fail? */
                   1423:                if (!ccount) {
                   1424:                        bp->expression = NULL;
                   1425:                        if (bp->conditions) {
                   1426:                                /* free what was allocated by ParseCondition */
                   1427:                                free(bp->conditions);
                   1428:                                bp->conditions = NULL;
                   1429:                        }
                   1430:                }
1.1       root     1431:                bp->ccount = ccount;
                   1432:        } else {
                   1433:                ccount = 0;
                   1434:        }
                   1435:        if (pstate.argv) {
                   1436:                free(pstate.argv);
                   1437:        }
                   1438:        if (ccount > 0) {
1.1.1.7 ! root     1439:                bps->count++;
1.1.1.5   root     1440:                if (!options->quiet) {
                   1441:                        fprintf(stderr, "%s condition breakpoint %d with %d condition(s) added:\n\t%s\n",
1.1.1.7 ! root     1442:                                bps->name, bps->count, ccount, bp->expression);
1.1.1.5   root     1443:                        if (options->skip) {
                   1444:                                fprintf(stderr, "-> Break only on every %d hit.\n", options->skip);
                   1445:                        }
                   1446:                        if (options->once) {
                   1447:                                fprintf(stderr, "-> Once, delete after breaking.\n");
                   1448:                        }
                   1449:                        if (options->trace) {
                   1450:                                fprintf(stderr, "-> Trace instead of breaking, but show still hits.\n");
                   1451:                                if (options->lock) {
                   1452:                                        fprintf(stderr, "-> Show also info selected with lock command.\n");
                   1453:                                }                               
                   1454:                                if (options->noinit) {
                   1455:                                        fprintf(stderr, "-> Skip debugger inits on hit.\n");
                   1456:                                }                               
                   1457:                        }
                   1458:                        if (options->filename) {
                   1459:                                fprintf(stderr, "-> Execute debugger commands from '%s' file on hit.\n", options->filename);
1.1.1.2   root     1460:                        }
                   1461:                }
1.1.1.5   root     1462:                BreakCond_CheckTracking(bp);
                   1463: 
                   1464:                bp->options.quiet = options->quiet;
                   1465:                bp->options.skip = options->skip;
                   1466:                bp->options.once = options->once;
                   1467:                bp->options.trace = options->trace;
                   1468:                bp->options.lock = options->lock;
                   1469:                bp->options.noinit = options->noinit;
1.1.1.2   root     1470:                if (options->filename) {
                   1471:                        bp->options.filename = strdup(options->filename);
1.1       root     1472:                }
                   1473:        } else {
                   1474:                if (normalized) {
                   1475:                        int offset, i = 0;
                   1476:                        char *s = normalized;
                   1477:                        while (*s && i < pstate.arg) {
                   1478:                                if (*s++ == ' ') {
                   1479:                                        i++;
                   1480:                                }
                   1481:                        }
                   1482:                        offset = s - normalized;
                   1483:                        /* show tokenized string and point out
                   1484:                         * the token where the error was encountered
                   1485:                         */
                   1486:                        fprintf(stderr, "ERROR in tokenized string:\n'%s'\n%*c-%s\n",
                   1487:                                normalized, offset+2, '^', pstate.error);
                   1488:                        free(normalized);
                   1489:                } else {
                   1490:                        /* show original string and point out the character
                   1491:                         * where the error was encountered
                   1492:                         */
                   1493:                        fprintf(stderr, "ERROR in parsed string:\n'%s'\n%*c-%s\n",
                   1494:                                expression, pstate.arg+2, '^', pstate.error);
                   1495:                }
                   1496:        }
                   1497:        return (ccount > 0);
                   1498: }
                   1499: 
                   1500: 
                   1501: /**
                   1502:  * print single breakpoint
                   1503:  */
                   1504: static void BreakCond_Print(bc_breakpoint_t *bp)
                   1505: {
                   1506:                fprintf(stderr, "\t%s", bp->expression);
1.1.1.2   root     1507:                if (bp->options.skip) {
                   1508:                        fprintf(stderr, " :%d", bp->options.skip);
1.1       root     1509:                }
1.1.1.2   root     1510:                if (bp->options.once) {
1.1       root     1511:                        fprintf(stderr, " :once");
                   1512:                }
1.1.1.2   root     1513:                if (bp->options.trace) {
                   1514:                        if (bp->options.lock) {
                   1515:                                fprintf(stderr, " :lock");
                   1516:                        } else {
                   1517:                                fprintf(stderr, " :trace");
                   1518:                        }
1.1.1.5   root     1519:                        if (bp->options.noinit) {
                   1520:                                fprintf(stderr, " :noinit");
                   1521:                        }
1.1.1.2   root     1522:                }
                   1523:                if (bp->options.filename) {
                   1524:                        fprintf(stderr, " :file %s", bp->options.filename);
1.1       root     1525:                }
1.1.1.7 ! root     1526:                if (bp->options.deleted) {
        !          1527:                        fprintf(stderr, " (deleted)");
        !          1528:                }
1.1       root     1529:                fprintf(stderr, "\n");
                   1530: }
                   1531: 
                   1532: /**
                   1533:  * List condition breakpoints
                   1534:  */
1.1.1.7 ! root     1535: static void BreakCond_List(bc_breakpoints_t *bps)
1.1       root     1536: {
                   1537:        bc_breakpoint_t *bp;
1.1.1.7 ! root     1538:        int i;
        !          1539: 
        !          1540:        if (!bps->count) {
        !          1541:                fprintf(stderr, "No conditional %s breakpoints.\n", bps->name);
1.1       root     1542:                return;
                   1543:        }
1.1.1.7 ! root     1544:        fprintf(stderr, "%d conditional %s breakpoints:\n", bps->count, bps->name);
        !          1545:        bp = bps->breakpoint;
        !          1546:        for (i = 1; i <= bps->count; bp++, i++) {
1.1       root     1547:                fprintf(stderr, "%4d:", i);
                   1548:                BreakCond_Print(bp);
                   1549:        }
                   1550: }
                   1551: 
                   1552: 
                   1553: /**
                   1554:  * Remove condition breakpoint at given position
                   1555:  */
1.1.1.7 ! root     1556: static bool BreakCond_Remove(bc_breakpoints_t *bps, int position)
1.1       root     1557: {
                   1558:        bc_breakpoint_t *bp;
1.1.1.2   root     1559: 
1.1.1.7 ! root     1560:        if (!bps->count) {
        !          1561:                fprintf(stderr, "No (more) %s breakpoints to remove.\n", bps->name);
1.1       root     1562:                return false;
                   1563:        }
1.1.1.7 ! root     1564:        if (position < 1 || position > bps->count) {
        !          1565:                fprintf(stderr, "ERROR: No such %s breakpoint.\n", bps->name);
1.1       root     1566:                return false;
                   1567:        }
1.1.1.7 ! root     1568:        bp = bps->breakpoint + (position - 1);
        !          1569:        if (bps->delayed_change) {
        !          1570:                bp->options.deleted = true;
        !          1571:                return true;
        !          1572:        }
        !          1573:        if (!bp->options.quiet) {
        !          1574:                fprintf(stderr, "Removed %s breakpoint %d:\n", bps->name, position);
        !          1575:                BreakCond_Print(bp);
1.1.1.5   root     1576:        }
1.1.1.7 ! root     1577:        free(bp->expression);
        !          1578:        free(bp->conditions);
        !          1579:        bp->expression = NULL;
        !          1580:        bp->conditions = NULL;
1.1.1.6   root     1581: 
1.1.1.7 ! root     1582:        if (bp->options.filename) {
        !          1583:                free(bp->options.filename);
1.1.1.2   root     1584:        }
1.1       root     1585: 
1.1.1.7 ! root     1586:        if (position < bps->count) {
        !          1587:                memmove(bp, bp + 1, (bps->count - position) * sizeof(bc_breakpoint_t));
1.1       root     1588:        }
1.1.1.7 ! root     1589:        bps->count--;
1.1       root     1590:        return true;
                   1591: }
                   1592: 
                   1593: 
                   1594: /**
1.1.1.7 ! root     1595:  * Remove all conditional breakpoints
        !          1596:  */
        !          1597: static void BreakCond_RemoveAll(bc_breakpoints_t *bps)
        !          1598: {
        !          1599:        bool removed;
        !          1600:        int i;
        !          1601: 
        !          1602:        for (i = bps->count; i > 0; i--) {
        !          1603:                removed = BreakCond_Remove(bps, i);
        !          1604:                ASSERT_VARIABLE(removed);
        !          1605:        }
        !          1606:        fprintf(stderr, "%s breakpoints: %d\n", bps->name, bps->count);
        !          1607: }
        !          1608: 
        !          1609: /**
        !          1610:  * Do delayed actions (remove breakpoints and old array alloc)
        !          1611:  * 
        !          1612:  * If those removals affect the triggered breakpoint index, update it.
        !          1613:  * 
        !          1614:  * Return updated breakpoint index.
1.1       root     1615:  */
1.1.1.7 ! root     1616: static int BreakCond_DoDelayedActions(bc_breakpoints_t *bps, int triggered)
1.1       root     1617: {
1.1.1.7 ! root     1618:        bc_options_t *options;
        !          1619:        bool removed;
        !          1620:        int i;
        !          1621: 
        !          1622:        assert(!bps->delayed_change);
        !          1623:        if (bps->breakpoint2delete) {
        !          1624:                free(bps->breakpoint2delete);
        !          1625:                bps->breakpoint2delete = NULL;
        !          1626:        }
        !          1627:        for (i = bps->count; i > 0; i--) {
        !          1628:                options = &(bps->breakpoint[i-1].options);
        !          1629:                if (options->deleted) {
        !          1630:                        options->deleted = false;
        !          1631:                        removed = BreakCond_Remove(bps, i);
        !          1632:                        ASSERT_VARIABLE(removed);
        !          1633:                        if (triggered >= i) {
        !          1634:                                triggered--;
        !          1635:                        }
        !          1636:                }
        !          1637:        }
        !          1638:        return triggered;
1.1       root     1639: }
                   1640: 
                   1641: 
                   1642: /**
                   1643:  * Return true if given CPU breakpoint has given CPU expression.
                   1644:  * Used by the test code.
                   1645:  */
                   1646: int BreakCond_MatchCpuExpression(int position, const char *expression)
                   1647: {
1.1.1.7 ! root     1648:        if (position < 1 || position > CpuBreakPoints.count) {
1.1       root     1649:                return false;
                   1650:        }
1.1.1.7 ! root     1651:        if (strcmp(expression, CpuBreakPoints.breakpoint[position-1].expression)) {
1.1       root     1652:                return false;
                   1653:        }
                   1654:        return true;
                   1655: }
                   1656: 
                   1657: 
                   1658: /**
                   1659:  * help
                   1660:  */
                   1661: static void BreakCond_Help(void)
                   1662: {
                   1663:        Uint32 value;
                   1664:        int i;
                   1665:        fputs(
1.1.1.2   root     1666: "  condition = <value>[.mode] [& <mask>] <comparison> <value>[.mode]\n"
1.1       root     1667: "\n"
                   1668: "  where:\n"
                   1669: "      value = [(] <register/symbol/variable name | number> [)]\n"
1.1.1.2   root     1670: "      number/mask = [#|$|%]<digits>\n"
1.1       root     1671: "      comparison = '<' | '>' | '=' | '!'\n"
                   1672: "      addressing mode (width) = 'b' | 'w' | 'l'\n"
                   1673: "      addressing mode (space) = 'p' | 'x' | 'y'\n"
                   1674: "\n"
                   1675: "  If the value is in parenthesis like in '($ff820)' or '(a0)', then\n"
                   1676: "  the used value will be read from the memory address pointed by it.\n"
                   1677: "\n"
1.1.1.2   root     1678: "  If the parsed value expressions on both sides of it are exactly\n"
                   1679: "  the same, right side is replaced with its current value.  For\n"
                   1680: "  inequality ('!') comparison, the breakpoint will additionally track\n"
                   1681: "  all further changes for the given address/register expression value.\n"
                   1682: "  (This is useful for tracking register and memory value changes.)\n"
1.1       root     1683: "\n"
                   1684: "  M68k addresses can have byte (b), word (w) or long (l, default) width.\n"
                   1685: "  DSP addresses belong to different address spaces: P, X or Y. Note that\n"
                   1686: "  on DSP only R0-R7 registers can be used for memory addressing.\n"
                   1687: "\n"
                   1688: "  Valid Hatari variable names (and their current values) are:\n", stderr);
                   1689:        for (i = 0; i < ARRAYSIZE(hatari_vars); i++) {
1.1.1.7 ! root     1690:                const var_addr_t *hvar = hatari_vars + i;
        !          1691:                switch (hvar->vtype) {
1.1       root     1692:                case VALUE_TYPE_FUNCTION32:
1.1.1.7 ! root     1693:                        value = ((Uint32(*)(void))(hvar->addr))();
1.1       root     1694:                        break;
                   1695:                case VALUE_TYPE_VAR32:
1.1.1.7 ! root     1696:                        value = *(hvar->addr);
1.1       root     1697:                        break;
                   1698:                default:
                   1699:                        fprintf(stderr, "ERROR: variable '%s' has unsupported type '%d'\n",
1.1.1.7 ! root     1700:                                hvar->name, hvar->vtype);
1.1       root     1701:                        continue;
                   1702:                }
1.1.1.7 ! root     1703:                fprintf(stderr, "  - %s ($%x)", hvar->name, value);
        !          1704:                if (hvar->constraints) {
        !          1705:                        fprintf(stderr, ", %s\n", hvar->constraints);
1.1       root     1706:                } else {
                   1707:                        fprintf(stderr, "\n");
                   1708:                }
                   1709:        }
                   1710:        fputs(
                   1711: "\n"
                   1712: "  Examples:\n"
                   1713: "      pc = $64543  &&  ($ff820).w & 3 = (a0)  &&  d0 = %1100\n"
                   1714: "       ($ffff9202).w ! ($ffff9202).w :trace\n"
                   1715: "      (r0).x = 1 && (r0).y = 2\n", stderr);
                   1716: }
                   1717: 
                   1718: 
                   1719: /* ------------- breakpoint condition parsing, public API ------------ */
                   1720: 
                   1721: const char BreakCond_Description[] =
1.1.1.2   root     1722:        "<condition> [&& <condition> ...] [:<option>] | <index> | help | all\n"
                   1723:        "\n"
                   1724:        "\tSet breakpoint with given <conditions>, remove breakpoint with\n"
                   1725:        "\tgiven <index>, remove all breakpoints with 'all' or output\n"
                   1726:        "\tbreakpoint condition syntax with 'help'.  Without arguments,\n"
                   1727:        "\tlists currently active breakpoints.\n"
                   1728:        "\n"
                   1729:        "\tMultiple breakpoint action options can be specified after\n"
                   1730:        "\tthe breakpoint condition(s):\n"
                   1731:        "\t- 'trace', print the breakpoint match without stopping\n"
                   1732:        "\t- 'lock', print the debugger entry info without stopping\n"
1.1.1.5   root     1733:        "\t- 'noinit', no debugger inits on hit, useful for stack tracing\n"
1.1.1.2   root     1734:        "\t- 'file <file>', execute debugger commands from given <file>\n"
                   1735:        "\t- 'once', delete the breakpoint after it's hit\n"
1.1.1.5   root     1736:        "\t- 'quiet', no output from setting & hitting breakpoint\n"
1.1.1.2   root     1737:        "\t- '<count>', break only on every <count> hit";
                   1738: 
                   1739: /**
                   1740:  * Parse options for the given breakpoint command.
                   1741:  * Return true for success and false for failure.
                   1742:  */
                   1743: static bool BreakCond_Options(char *str, bc_options_t *options, char marker)
                   1744: {
                   1745:        char *option, *next, *filename;
                   1746:        int skip;
                   1747:        
                   1748:        memset(options, 0, sizeof(*options));
                   1749: 
                   1750:        option = strchr(str, marker);
                   1751:        if (option) {
                   1752:                /* end breakcond command at options */
                   1753:                *option = 0;
                   1754:        }
                   1755:        for (next = option; option; option = next) {
                   1756: 
                   1757:                /* skip marker + end & trim this option */
                   1758:                option = next + 1;
                   1759:                next = strchr(option, marker);
                   1760:                if (next) {
                   1761:                        *next = 0;
                   1762:                }
                   1763:                option = Str_Trim(option);
                   1764: 
                   1765:                if (strcmp(option, "once") == 0) {
                   1766:                        options->once = true;
1.1.1.5   root     1767:                } else if (strcmp(option, "quiet") == 0) {
                   1768:                        options->quiet = true;
1.1.1.2   root     1769:                } else if (strcmp(option, "trace") == 0) {
                   1770:                        options->trace = true;
                   1771:                } else if (strcmp(option, "lock") == 0) {
                   1772:                        options->trace = true;
                   1773:                        options->lock = true;
1.1.1.5   root     1774:                } else if (strcmp(option, "noinit") == 0) {
                   1775:                        options->trace = true;
                   1776:                        options->noinit = true;
1.1.1.2   root     1777:                } else if (strncmp(option, "file ", 5) == 0) {
                   1778:                        filename = Str_Trim(option+4);
                   1779:                        if (!File_Exists(filename)) {
                   1780:                                fprintf(stderr, "ERROR: given file '%s' doesn't exist!\n", filename);
                   1781:                                fprintf(stderr, "(if you use 'cd' command, do it before setting breakpoints)\n");
                   1782:                                return false;
                   1783:                        }
                   1784:                        options->filename = filename;
1.1.1.6   root     1785:                } else if (isdigit((unsigned char)*option)) {
1.1.1.2   root     1786:                        /* :<value> */
                   1787:                        skip = atoi(option);
                   1788:                        if (skip < 2) {
                   1789:                                fprintf(stderr, "ERROR: invalid breakpoint skip count '%s'!\n", option);
                   1790:                                return false;
                   1791:                        }
                   1792:                        options->skip = skip;
                   1793:                } else {
                   1794:                        fprintf(stderr, "ERROR: unrecognized breakpoint option '%s'!\n", option);
                   1795:                        return false;
                   1796:                }
                   1797:        }
                   1798:        return true;
                   1799: }
1.1       root     1800: 
                   1801: /**
                   1802:  * Parse given command expression to set/remove/list
                   1803:  * conditional breakpoints for CPU or DSP.
                   1804:  * Return true for success and false for failure.
                   1805:  */
                   1806: bool BreakCond_Command(const char *args, bool bForDsp)
                   1807: {
1.1.1.7 ! root     1808:        bc_breakpoints_t *bps;
1.1.1.2   root     1809:        char *expression, *argscopy;
1.1       root     1810:        unsigned int position;
1.1.1.2   root     1811:        bc_options_t options;
1.1       root     1812:        const char *end;
1.1.1.2   root     1813:        bool ret = true;
1.1.1.7 ! root     1814: 
        !          1815:        bps = BreakCond_GetListInfo(bForDsp);
1.1       root     1816:        if (!args) {
1.1.1.7 ! root     1817:                BreakCond_List(bps);
1.1       root     1818:                return true;
                   1819:        }
                   1820:        argscopy = strdup(args);
                   1821:        assert(argscopy);
                   1822:        
                   1823:        expression = Str_Trim(argscopy);
                   1824:        
1.1.1.2   root     1825:        /* subcommands? */
1.1       root     1826:        if (strncmp(expression, "help", 4) == 0) {
                   1827:                BreakCond_Help();
                   1828:                goto cleanup;
                   1829:        }
                   1830:        if (strcmp(expression, "all") == 0) {
1.1.1.7 ! root     1831:                BreakCond_RemoveAll(bps);
1.1       root     1832:                goto cleanup;
                   1833:        }
                   1834: 
                   1835:        if (bForDsp && !bDspEnabled) {
                   1836:                ret = false;
                   1837:                fprintf(stderr, "ERROR: DSP not enabled!\n");
                   1838:                goto cleanup;
                   1839:        }
                   1840: 
1.1.1.2   root     1841:        /* postfix options? */
                   1842:        if (!BreakCond_Options(expression, &options, ':')) {
                   1843:                ret = false;
                   1844:                goto cleanup;
1.1       root     1845:        }
                   1846: 
1.1.1.2   root     1847:        /* index (for breakcond removal)? */
1.1       root     1848:        end = expression;
1.1.1.6   root     1849:        while (isdigit((unsigned char)*end)) {
1.1       root     1850:                end++;
                   1851:        }
                   1852:        if (end > expression && *end == '\0' &&
                   1853:            sscanf(expression, "%u", &position) == 1) {
1.1.1.7 ! root     1854:                ret = BreakCond_Remove(bps, position);
1.1       root     1855:        } else {
                   1856:                /* add breakpoint? */
1.1.1.2   root     1857:                ret = BreakCond_Parse(expression, &options, bForDsp);
1.1       root     1858:        }
                   1859: cleanup:
                   1860:        free(argscopy);
                   1861:        return ret;
                   1862: }
                   1863: 
                   1864: 
                   1865: const char BreakAddr_Description[] =
1.1.1.2   root     1866:        "<address> [:<option>]\n"
1.1       root     1867:        "\tCreate conditional breakpoint for given PC <address>.\n"
1.1.1.2   root     1868:        "\n"
                   1869:        "\tBreakpoint action option alternatives:\n"
                   1870:        "\t- 'trace', print the breakpoint match without stopping\n"
                   1871:        "\t- 'lock', print the debugger entry info without stopping\n"
                   1872:        "\t- 'once', delete the breakpoint after it's hit\n"
1.1.1.5   root     1873:        "\t- 'quiet', no output from setting & hitting breakpoint\n"
1.1.1.2   root     1874:        "\t- '<count>', break only on every <count> hit\n"
                   1875:        "\n"
                   1876:        "\tUse conditional breakpoint commands to manage the created\n"
1.1       root     1877:        "\tbreakpoints.";
                   1878: 
                   1879: /**
                   1880:  * Set CPU & DSP program counter address breakpoints by converting
                   1881:  * them to conditional breakpoints.
                   1882:  * Return true for success and false for failure.
                   1883:  */
                   1884: bool BreakAddr_Command(char *args, bool bForDsp)
                   1885: {
                   1886:        const char *errstr, *expression = (const char *)args;
                   1887:        char *cut, command[32];
                   1888:        Uint32 addr;
                   1889:        int offset;
                   1890: 
1.1.1.2   root     1891:        if (!args) {
                   1892:                if (bForDsp) {
                   1893:                        DebugUI_PrintCmdHelp("dspaddress");
                   1894:                } else {
                   1895:                        DebugUI_PrintCmdHelp("address");
                   1896:                }
                   1897:                return true;
                   1898:        }
                   1899: 
1.1       root     1900:        /* split options */
                   1901:        if ((cut = strchr(args, ':'))) {
                   1902:                *cut = '\0';
                   1903:                cut = Str_Trim(cut+1);
1.1.1.2   root     1904:                if (strlen(cut) > 5) {
                   1905:                        cut[5] = '\0';
1.1       root     1906:                }
                   1907:        }
                   1908: 
                   1909:        /* evaluate address expression */
                   1910:        errstr = Eval_Expression(expression, &addr, &offset, bForDsp);
                   1911:        if (errstr) {
                   1912:                fprintf(stderr, "ERROR in the address expression:\n'%s'\n%*c-%s\n",
                   1913:                        expression, offset+2, '^', errstr);
                   1914:                return false;
                   1915:        }
                   1916: 
                   1917:        /* add the address breakpoint with optional option */
                   1918:        sprintf(command, "pc=$%x %c%s", addr, cut?':':' ', cut?cut:"");
                   1919:        if (!BreakCond_Command(command, bForDsp)) {
                   1920:                return false;
                   1921:        }
                   1922: 
                   1923:        /* on success, show on what instruction it was added */
                   1924:        if (bForDsp) {
1.1.1.5   root     1925:                DSP_DisasmAddress(stderr, addr, addr);
1.1       root     1926:        } else {
                   1927:                uaecptr dummy;
1.1.1.5   root     1928:                Disasm(stderr, (uaecptr)addr, &dummy, 1);
1.1       root     1929:        }
                   1930:        return true;
                   1931: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.