Annotation of previous/src/debug/breakcond.c, revision 1.1.1.4

1.1       root        1: /*
                      2:   Hatari - breakcond.c
                      3: 
                      4:   Copyright (c) 2009-2010 by Eero Tamminen
                      5: 
                      6:   This file is distributed under the GNU Public License, version 2 or at
                      7:   your option any later version. Read the file gpl.txt for details.
                      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 "str.h"
1.1.1.2   root       23: #include "screen.h" /* for defines needed by video.h */
1.1       root       24: #include "video.h"     /* for Hatari video variable addresses */
                     25: 
                     26: #include "debug_priv.h"
                     27: #include "breakcond.h"
                     28: #include "debugcpu.h"
1.1.1.2   root       29: #include "debugInfo.h"
                     30: #include "debugui.h"
1.1       root       31: #include "evaluate.h"
                     32: #include "symbols.h"
1.1.1.2   root       33: #include "68kDisass.h"
1.1       root       34: 
                     35: /* set to 1 to enable parsing function tracing / debug output */
                     36: #define DEBUG 0
                     37: 
                     38: /* needs to go through long long to handle x=32 */
                     39: #define BITMASK(x)      ((Uint32)(((unsigned long long)1<<(x))-1))
                     40: 
                     41: #define BC_MAX_CONDITION_BREAKPOINTS 16
                     42: #define BC_MAX_CONDITIONS_PER_BREAKPOINT 4
                     43: 
                     44: #define BC_DEFAULT_DSP_SPACE 'P'
                     45: 
1.1.1.2   root       46: typedef enum { 
1.1       root       47:        /* plain number */
                     48:        VALUE_TYPE_NUMBER     = 0,
                     49: 
                     50:        /* functions to call to get value */
                     51:        VALUE_TYPE_FUNCTION32 = 2,
                     52: 
                     53:        /* internal Hatari value variables */
                     54:        VALUE_TYPE_VAR32      = 4,
                     55: 
                     56:        /* size must match register size used in BreakCond_ParseRegister() */
                     57:        VALUE_TYPE_REG16      = 16,
                     58:        VALUE_TYPE_REG32      = 32
1.1.1.2   root       59: } value_t;
1.1       root       60: 
                     61: typedef struct {
                     62:        bool is_indirect;
                     63:        char dsp_space; /* DSP has P, X, Y address spaces, zero if not DSP */
                     64:        value_t valuetype;      /* Hatari value variable type */
                     65:        union {
                     66:                Uint32 number;
                     67:                Uint16 (*func16)(void);
                     68:                Uint32 (*func32)(void);
                     69:                Uint16 *reg16;
                     70:                Uint32 *reg32;
                     71:        } value;
                     72:        Uint32 bits;    /* CPU has 8/16/32 bit address widths */
                     73:        Uint32 mask;    /* <width mask> && <value mask> */
                     74: } bc_value_t;
                     75: 
                     76: typedef struct {
                     77:        bc_value_t lvalue;
                     78:        bc_value_t rvalue;
                     79:        char comparison;
                     80:        bool track;     /* track value changes */
                     81: } bc_condition_t;
                     82: 
                     83: typedef struct {
1.1.1.2   root       84:     char *filename;    /* file where to read commands to do on hit */
                     85:     int skip;  /* how many times to hit before breaking */
                     86:     bool once; /* remove after hit&break */
                     87:     bool trace;        /* trace mode, don't break */
                     88:     bool lock; /* tracing + show locked info */
                     89: } bc_options_t;
                     90: 
                     91: typedef struct {
1.1       root       92:        char *expression;
1.1.1.2   root       93:     bc_options_t options;
1.1       root       94:        bc_condition_t conditions[BC_MAX_CONDITIONS_PER_BREAKPOINT];
                     95:        int ccount;     /* condition count */
                     96:        int hits;       /* how many times breakpoint hit */
                     97: } bc_breakpoint_t;
                     98: 
                     99: static bc_breakpoint_t BreakPointsCpu[BC_MAX_CONDITION_BREAKPOINTS];
                    100: static bc_breakpoint_t BreakPointsDsp[BC_MAX_CONDITION_BREAKPOINTS];
                    101: static int BreakPointCpuCount;
                    102: static int BreakPointDspCount;
                    103: 
                    104: 
                    105: /* forward declarations */
                    106: static bool BreakCond_Remove(int position, bool bForDsp);
                    107: static void BreakCond_Print(bc_breakpoint_t *bp);
                    108: 
                    109: 
                    110: /**
                    111:  * Save breakpoints as debugger input file
                    112:  * return true for success, false for failure
                    113:  */
                    114: bool BreakCond_Save(const char *filename)
                    115: {
                    116:        FILE *fp;
                    117:        int i;
                    118: 
                    119:        if (!(BreakPointCpuCount || BreakPointDspCount)) {
1.1.1.2   root      120:         if (File_Exists(filename)) {
                    121:             if (remove(filename)) {
                    122:                 perror("ERROR");
                    123:                 return false;
                    124:             }
1.1       root      125:                }
                    126:                return true;
                    127:        }
                    128: 
                    129:        fprintf(stderr, "Saving breakpoints to '%s'...\n", filename);
                    130:        fp = fopen(filename, "w");
                    131:        if (!fp) {
                    132:                perror("ERROR");
                    133:                return false;
                    134:        }
                    135:        /* save conditional breakpoints as debugger input file */
                    136:        for (i = 0; i < BreakPointCpuCount; i++) {
                    137:                fprintf(fp, "b %s\n", BreakPointsCpu[i].expression);
                    138:        }
                    139:        for (i = 0; i < BreakPointDspCount; i++) {
                    140:                fprintf(fp, "db %s\n", BreakPointsDsp[i].expression);
                    141:        }
                    142:        fclose(fp);
                    143:        return true;
                    144: }
                    145: 
                    146: 
                    147: /* --------------------- debugging code ------------------- */
                    148: 
                    149: #if DEBUG
                    150: /* see parsing code for usage examples */
                    151: static int _traceIndent;
                    152: static void _spaces(void)
                    153: {
                    154:        int spaces = _traceIndent;
                    155:        while(spaces-- > 0) {
                    156:                putchar(' ');   /* fputc(' ',stdout); */
                    157:        }
                    158: }
                    159: #define ENTERFUNC(args) { _traceIndent += 2; _spaces(); printf args ; fflush(stdout); }
                    160: #define EXITFUNC(args) { _spaces(); printf args ; fflush(stdout); _traceIndent -= 2; }
                    161: #else
                    162: #define ENTERFUNC(args)
                    163: #define EXITFUNC(args)
                    164: #endif
                    165: 
                    166: 
                    167: /* ------------- breakpoint condition checking, internals ------------- */
                    168: 
                    169: /**
                    170:  * Return value of given size read from given ST memory address
                    171:  */
1.1.1.4 ! root      172: static Uint32 BreakCond_ReadNeXTMemory(Uint32 addr, const bc_value_t *bc_value)
1.1       root      173: {
                    174:        switch (bc_value->bits) {
                    175:        case 32:
1.1.1.4 ! root      176:                return DBGMemory_ReadLong(addr);
1.1       root      177:        case 16:
1.1.1.4 ! root      178:                return DBGMemory_ReadWord(addr);
1.1       root      179:        case 8:
1.1.1.4 ! root      180:                return DBGMemory_ReadByte(addr);
1.1       root      181:        default:
1.1.1.4 ! root      182:                fprintf(stderr, "ERROR: unknown address size %d!\n", bc_value->bits);
1.1       root      183:                abort();
                    184:        }
                    185: }
                    186: 
                    187: 
                    188: /**
                    189:  * Return Uint32 value according to given bc_value_t specification
                    190:  */
                    191: static Uint32 BreakCond_GetValue(const bc_value_t *bc_value)
                    192: {
                    193:        Uint32 value;
                    194: 
                    195:        switch (bc_value->valuetype) {
                    196:        case VALUE_TYPE_NUMBER:
                    197:                value = bc_value->value.number;
                    198:                break;
                    199:        case VALUE_TYPE_FUNCTION32:
                    200:                value = bc_value->value.func32();
                    201:                break;
                    202:        case VALUE_TYPE_REG16:
                    203:                value = *(bc_value->value.reg16);
                    204:                break;
                    205:        case VALUE_TYPE_VAR32:
                    206:        case VALUE_TYPE_REG32:
                    207:                value = *(bc_value->value.reg32);
                    208:                break;
                    209:        default:
                    210:                fprintf(stderr, "ERROR: unknown condition value size/type %d!\n", bc_value->valuetype);
                    211:                abort();
                    212:        }
                    213:        if (bc_value->is_indirect) {
1.1.1.4 ! root      214:                        value = BreakCond_ReadNeXTMemory(value, bc_value);
1.1       root      215:        }
                    216:        return (value & bc_value->mask);
                    217: }
                    218: 
                    219: 
                    220: /**
                    221:  * Return true if all of the given breakpoint's conditions match
                    222:  */
                    223: static bool BreakCond_MatchConditions(const bc_condition_t *condition, int count)
                    224: {
                    225:        Uint32 lvalue, rvalue;
                    226:        bool hit = false;
                    227:        int i;
                    228:        
                    229:        for (i = 0; i < count; condition++, i++) {
                    230: 
                    231:                lvalue = BreakCond_GetValue(&(condition->lvalue));
                    232:                rvalue = BreakCond_GetValue(&(condition->rvalue));
                    233: 
                    234:                switch (condition->comparison) {
                    235:                case '<':
                    236:                        hit = (lvalue < rvalue);
                    237:                        break;
                    238:                case '>':
                    239:                        hit = (lvalue > rvalue);
                    240:                        break;
                    241:                case '=':
                    242:                        hit = (lvalue == rvalue);
                    243:                        break;
                    244:                case '!':
                    245:                        hit = (lvalue != rvalue);
                    246:                        break;
                    247:                default:
                    248:                        fprintf(stderr, "ERROR: Unknown breakpoint value comparison operator '%c'!\n",
                    249:                                condition->comparison);
                    250:                        abort();
                    251:                }
                    252:                if (!hit) {
                    253:                        return false;
                    254:                }
                    255:        }
                    256:        /* all conditions matched */
                    257:        return true;
                    258: }
                    259: 
                    260: 
                    261: /**
                    262:  * Show values for the tracked breakpoint conditions
                    263:  */
                    264: static void BreakCond_ShowTracked(bc_condition_t *condition, int count)
                    265: {
                    266:        Uint32 addr, value;
                    267:        char sep;
                    268:        int i;
                    269:        
                    270:        sep = ' ';
                    271:        for (i = 0; i < count; condition++, i++) {
                    272:                if (!condition->track) {
                    273:                        continue;
                    274:                }
                    275: 
                    276:                /* get the new value in address */
                    277:                value = BreakCond_GetValue(&(condition->lvalue));
                    278:                /* next monitor changes to this new value */
                    279:                condition->rvalue.value.number = value;
                    280:                
                    281:                if (condition->lvalue.is_indirect &&
                    282:                    condition->lvalue.valuetype == VALUE_TYPE_NUMBER) {
                    283:                        /* simple memory address */
                    284:                        addr = condition->lvalue.value.number;
                    285:                        fprintf(stderr, "%c $%x = $%x", sep, addr, value);
                    286:                } else {
                    287:                        /* register tms. */
                    288:                        fprintf(stderr, "%c $%x", sep, value);
                    289:                }
                    290:                sep = ',';
                    291:        }
                    292:        fprintf(stderr, "\n");
                    293: }
                    294: 
                    295: 
                    296: /**
                    297:  * Return which of the given condition breakpoints match
                    298:  * or zero if none matched
                    299:  */
                    300: static int BreakCond_MatchBreakPoints(bc_breakpoint_t *bp, int count, const char *name)
                    301: {
                    302:        int i;
                    303:        
                    304:        for (i = 0; i < count; bp++, i++) {
                    305:                if (BreakCond_MatchConditions(bp->conditions, bp->ccount)) {
                    306:                        BreakCond_ShowTracked(bp->conditions, bp->ccount);
                    307:                        bp->hits++;
1.1.1.2   root      308:                        if (bp->options.skip &&
                    309:                 (bp->hits % bp->options.skip) == 0) {
                    310:                 return 0;
1.1       root      311:                        }
                    312:                        fprintf(stderr, "%d. %s breakpoint condition(s) matched %d times.\n",
                    313:                                i+1, name, bp->hits);
1.1.1.2   root      314:             
                    315:             BreakCond_Print(bp);
                    316:                        if (bp->options.once) {
1.1       root      317:                                BreakCond_Remove(i+1, (bp-i == BreakPointsDsp));
                    318:                        }
1.1.1.2   root      319:                        if (bp->options.lock) {
                    320:                 DebugCpu_InitSession();
                    321:                 DebugDsp_InitSession();
                    322:                 DebugInfo_ShowSessionInfo();
                    323:             }
                    324:             if (bp->options.filename) {
                    325:                 DebugUI_ParseFile(bp->options.filename);
                    326:             }
                    327:             if (bp->options.trace) {
                    328:                 return 0;
                    329:             } else {
                    330:                 /* indexes for BreakCond_Remove() start from 1 */
                    331:                 return i + 1;
                    332:             }
1.1       root      333:                }
                    334:        }
                    335:        return 0;
                    336: }
                    337: 
                    338: /* ------------- breakpoint condition checking, public API ------------- */
                    339: 
                    340: /**
                    341:  * Return matched CPU breakpoint index or zero for an error.
                    342:  */
                    343: int BreakCond_MatchCpu(void)
                    344: {
                    345:        return BreakCond_MatchBreakPoints(BreakPointsCpu, BreakPointCpuCount, "CPU");
                    346: }
                    347: 
                    348: /**
                    349:  * Return matched DSP breakpoint index or zero for an error.
                    350:  */
                    351: int BreakCond_MatchDsp(void)
                    352: {
                    353:        return BreakCond_MatchBreakPoints(BreakPointsDsp, BreakPointDspCount, "DSP");
                    354: }
                    355: 
                    356: /**
                    357:  * Return number of condition breakpoints
                    358:  */
                    359: int BreakCond_BreakPointCount(bool bForDsp)
                    360: {
                    361:        if (bForDsp) {
                    362:                return BreakPointDspCount;
                    363:        } else {
                    364:                return BreakPointCpuCount;
                    365:        }
                    366: }
                    367: 
                    368: 
                    369: /* -------------- breakpoint condition parsing, internals ------------- */
                    370: 
                    371: /* struct for passing around breakpoint conditions parsing state */
                    372: typedef struct {
                    373:        int arg;                /* current arg */
                    374:        int argc;               /* arg count */
                    375:        const char **argv;      /* arg pointer array (+ strings) */
                    376:        const char *error;      /* error from parsing args */
                    377: } parser_state_t;
                    378: 
                    379: 
                    380: /* Hatari variable name & address array items */
                    381: typedef struct {
                    382:        const char *name;
                    383:        Uint32 *addr;
                    384:        value_t vtype;
                    385:        size_t bits;
                    386:        const char *constraints;
                    387: } var_addr_t;
                    388: 
                    389: /**
                    390:  * Readline match callback for CPU variable/symbol name completion.
                    391:  * STATE = 0 -> different text from previous one.
                    392:  * Return next match or NULL if no matches.
                    393:  */
                    394: char *BreakCond_MatchCpuVariable(const char *text, int state)
                    395: {
1.1.1.4 ! root      396:        /*
1.1       root      397:        static int i, len;
                    398:        
                    399:        if (!state) {
1.1.1.4 ! root      400:                // first match
1.1       root      401:                len = strlen(text);
                    402:                i = 0;
1.1.1.4 ! root      403:        }*/
1.1       root      404:        /* next match */
1.1.1.2   root      405: //     while (i < ARRAYSIZE(hatari_vars)) {
                    406: //             name = hatari_vars[i++].name;
                    407: //             if (strncasecmp(name, text, len) == 0)
                    408: //                     return (strdup(name));
                    409: //     }
1.1       root      410:        /* no variable match, check all CPU symbols */
                    411:        return Symbols_MatchCpuAddress(text, state);
                    412: }
                    413: 
                    414: /**
                    415:  * Readline match callback for DSP variable/symbol name completion.
                    416:  * STATE = 0 -> different text from previous one.
                    417:  * Return next match or NULL if no matches.
                    418:  */
                    419: char *BreakCond_MatchDspVariable(const char *text, int state)
                    420: {
                    421:        /* currently no DSP variables, check all DSP symbols */
                    422:        return Symbols_MatchDspAddress(text, state);
                    423: }
                    424: 
                    425: 
                    426: /**
                    427:  * If given string is a Hatari variable name, set bc_value
                    428:  * fields accordingly and return true, otherwise return false.
                    429:  */
1.1.1.2   root      430: //static bool BreakCond_ParseVariable(const char *name, bc_value_t *bc_value)
                    431: //{
1.1       root      432:        /* left, right, middle, direction */
                    433:         int l, r, m, dir;
                    434: 
1.1.1.2   root      435: //     ENTERFUNC(("BreakCond_ParseVariable('%s')\n", name));
1.1       root      436:        /* bisect */
1.1.1.2   root      437: //     l = 0;
                    438: //     r = ARRAYSIZE(hatari_vars) - 1;
                    439: //     do {
                    440: //             m = (l+r) >> 1;
                    441: //             dir = strcasecmp(name, hatari_vars[m].name);
                    442: //             if (dir == 0) {
                    443: //                     bc_value->value.reg32 = hatari_vars[m].addr;
                    444: //                     bc_value->valuetype = hatari_vars[m].vtype;
                    445: //                     bc_value->bits = hatari_vars[m].bits;
                    446: //                     assert(bc_value->bits == 32 || bc_value->valuetype !=  VALUE_TYPE_VAR32);
                    447: //                     EXITFUNC(("-> true\n"));
                    448: //                     return true;
                    449: //             }
                    450: //             if (dir < 0) {
                    451: //                     r = m-1;
                    452: //             } else {
                    453: //                     l = m+1;
                    454: //             }
                    455: //     } while (l <= r);
                    456: //     EXITFUNC(("-> false\n"));
                    457: //     return false;
                    458: //}
                    459: 
                    460: 
                    461: /**
                    462:  * If given string is a Hatari variable name, set value to given
                    463:  * variable value and return true, otherwise return false.
                    464:  */
                    465: bool BreakCond_GetHatariVariable(const char *name, Uint32 *value)
                    466: {
                    467:     bc_value_t bc_value;
                    468: //    if (!BreakCond_ParseVariable(name, &bc_value)) {
                    469: //        return false;
                    470: //    }
1.1.1.4 ! root      471:     bc_value.mask         = 0xffffffff;
        !           472:     bc_value.is_indirect  = false;
        !           473:        bc_value.valuetype    = VALUE_TYPE_NUMBER;
        !           474:        bc_value.value.number = 0;
1.1.1.2   root      475:     *value = BreakCond_GetValue(&bc_value);
                    476:     return true;
1.1       root      477: }
                    478: 
                    479: 
                    480: /**
                    481:  * If given string matches a suitable symbol, set bc_value
                    482:  * fields accordingly and return true, otherwise return false.
                    483:  */
                    484: static bool BreakCond_ParseSymbol(const char *name, bc_value_t *bc_value)
                    485: {
                    486:        symtype_t symtype;
                    487:        Uint32 addr;
                    488: 
                    489:        ENTERFUNC(("BreakCond_ParseSymbol('%s')\n", name));
                    490:        if (bc_value->is_indirect) {
                    491:                /* indirect use of address makes sense only for data */
                    492:                symtype = SYMTYPE_DATA|SYMTYPE_BSS;
                    493:        } else {
                    494:                /* direct value can be compared for anything */
                    495:                symtype = SYMTYPE_ALL;
                    496:        }
                    497:        
                    498:        if (bc_value->dsp_space) {
                    499:                if (!Symbols_GetDspAddress(symtype, name, &addr)) {
                    500:                        EXITFUNC(("-> false (DSP)\n"));
                    501:                        return false;
                    502:                }
                    503:                /* all DSP memory values are 24-bits */
                    504:                bc_value->bits = 24;
                    505:                bc_value->value.number = addr;
                    506:                bc_value->valuetype = VALUE_TYPE_NUMBER;
                    507:                EXITFUNC(("-> true (DSP)\n"));
                    508:                return true;
                    509:        }
                    510:        
                    511:        if (!Symbols_GetCpuAddress(symtype, name, &addr)) {
                    512:                EXITFUNC(("-> false (CPU)\n"));
                    513:                return false;
                    514:        }
                    515:        if (addr & 1) {
                    516:                /* only bytes can be at odd addresses */
                    517:                bc_value->bits = 8;
                    518:        } else {
                    519:                bc_value->bits = 32;
                    520:        }
                    521:        bc_value->value.number = addr;
                    522:        bc_value->valuetype = VALUE_TYPE_NUMBER;
                    523:        EXITFUNC(("-> true (CPU)\n"));
                    524:        return true;
                    525: }
                    526: 
                    527: 
                    528: /**
                    529:  * Helper function to get CPU PC register value with static inline as Uint32
                    530:  */
                    531: static Uint32 GetCpuPC(void)
                    532: {
                    533:        return M68000_GetPC();
                    534: }
                    535: /**
                    536:  * Helper function to get CPU SR register value with static inline as Uint32
                    537:  */
                    538: static Uint32 GetCpuSR(void)
                    539: {
                    540:        return M68000_GetSR();
                    541: }
                    542: 
                    543: /**
                    544:  * If given string is register name (for DSP or CPU), set bc_value
                    545:  * fields accordingly and return true, otherwise return false.
                    546:  */
                    547: static bool BreakCond_ParseRegister(const char *regname, bc_value_t *bc_value)
                    548: {
                    549:        int regsize;
                    550:        ENTERFUNC(("BreakCond_ParseRegister('%s')\n", regname));
                    551:        regsize = DebugCpu_GetRegisterAddress(regname, &(bc_value->value.reg32));
                    552:        if (regsize) {
                    553:                bc_value->bits = regsize;
                    554:                /* valuetypes for registers are 16 & 32 */
                    555:                bc_value->valuetype = regsize;
                    556:                EXITFUNC(("-> true (CPU)\n"));
                    557:                return true;
                    558:        }
                    559:        /* Exact UAE core 32-bit PC & 16-bit SR register values
                    560:         * can be gotten only through AUE accessors, not directly
                    561:         */
                    562:        if (strcasecmp(regname, "PC") == 0) {
                    563:                bc_value->bits = 32;
                    564:                bc_value->value.func32 = GetCpuPC;
                    565:                bc_value->valuetype = VALUE_TYPE_FUNCTION32;
                    566:                EXITFUNC(("-> true (CPU)\n"));
                    567:                return true;
                    568:        }
                    569:        if (strcasecmp(regname, "SR") == 0) {
                    570:                bc_value->bits = 16;
                    571:                bc_value->value.func32 = GetCpuSR;
                    572:                bc_value->valuetype = VALUE_TYPE_FUNCTION32;
                    573:                EXITFUNC(("-> true (CPU)\n"));
                    574:                return true;
                    575:        }
                    576:        EXITFUNC(("-> false (CPU)\n"));
                    577:        return false;
                    578: }
                    579: 
                    580: /**
                    581:  * If given address is valid (for DSP or CPU), return true.
                    582:  */
                    583: static bool BreakCond_CheckAddress(bc_value_t *bc_value)
                    584: {
                    585:        Uint32 highbyte, bit23, addr = bc_value->value.number;
                    586: 
                    587:        ENTERFUNC(("BreakCond_CheckAddress(%x)\n", addr));
                    588: 
                    589:        bit23 = (addr >> 23) & 1;
                    590:        highbyte = (addr >> 24) & 0xff;
                    591:        if ((bit23 == 0 && highbyte != 0) ||
                    592:            (bit23 == 1 && highbyte != 0xff)) {
                    593:                fprintf(stderr, "WARNING: address 0x%x 23th bit isn't extended to bits 24-31.\n", addr);
                    594:        }
                    595:        EXITFUNC(("-> true (CPU)\n"));
                    596:        return true;
                    597: }
                    598: 
                    599: 
                    600: /**
                    601:  * Check for and parse a condition value address space/width modifier.
                    602:  * Modify pstate according to parsing (arg index and error string).
                    603:  * Return false for error and true for no or successfully parsed modifier.
                    604:  */
                    605: static bool BreakCond_ParseAddressModifier(parser_state_t *pstate, bc_value_t *bc_value)
                    606: {
                    607:        char mode;
                    608: 
                    609:        ENTERFUNC(("BreakCond_ParseAddressModifier()\n"));
                    610:        if (pstate->arg+2 > pstate->argc ||
                    611:            strcmp(pstate->argv[pstate->arg], ".") != 0) {
                    612:                if (bc_value->dsp_space && bc_value->is_indirect) {
                    613:                        pstate->error = "DSP memory addresses need to specify address space";
                    614:                        EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    615:                        return false;
                    616:                }
                    617:                EXITFUNC(("arg:%d -> true (missing)\n", pstate->arg));
                    618:                return true;
                    619:        }
                    620:        if (!bc_value->is_indirect) {
                    621:                pstate->error = "space/width modifier makes sense only for an address (register)";
                    622:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    623:                return false;
                    624:        }
                    625:        pstate->arg++;
                    626:        if (bc_value->dsp_space) {
                    627:                switch (pstate->argv[pstate->arg][0]) {
                    628:                case 'p':
                    629:                case 'x':
                    630:                case 'y':
                    631:                        mode = toupper(pstate->argv[pstate->arg][0]);
                    632:                        break;
                    633:                default:
                    634:                        pstate->error = "invalid address space modifier";
                    635:                        EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    636:                        return false;
                    637:                }
                    638:        } else {
                    639:                switch (pstate->argv[pstate->arg][0]) {
                    640:                case 'l':
                    641:                        mode = 32;
                    642:                        break;
                    643:                case 'w':
                    644:                        mode = 16;
                    645:                        break;
                    646:                case 'b':
                    647:                        mode = 8;
                    648:                        break;
                    649:                default:
                    650:                        pstate->error = "invalid address width modifier";
                    651:                        EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    652:                        return false;
                    653:                }
                    654:        }
                    655:        if (pstate->argv[pstate->arg][1]) {
                    656:                pstate->error = "invalid address space/width modifier";
                    657:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    658:                return false;
                    659:        }
                    660:        if (bc_value->dsp_space) {
                    661:                bc_value->dsp_space = mode;
                    662:                EXITFUNC(("arg:%d -> space:%c, true\n", pstate->arg, mode));
                    663:        } else {
                    664:                bc_value->bits = mode;
                    665:                EXITFUNC(("arg:%d -> width:%d, true\n", pstate->arg, mode));
                    666:        }
                    667:        pstate->arg++;
                    668:        return true;
                    669: }
                    670: 
                    671: 
                    672: /**
                    673:  * Check for and parse a condition value mask.
                    674:  * Modify pstate according to parsing (arg index and error string).
                    675:  * Return false for error and true for no or successfully parsed modifier.
                    676:  */
                    677: static bool BreakCond_ParseMaskModifier(parser_state_t *pstate, bc_value_t *bc_value)
                    678: {
                    679:        ENTERFUNC(("BreakCond_ParseMaskModifier()\n"));
                    680:        if (pstate->arg+2 > pstate->argc ||
                    681:            strcmp(pstate->argv[pstate->arg], "&") != 0) {
                    682:                EXITFUNC(("arg:%d -> true (missing)\n", pstate->arg));
                    683:                return true;
                    684:        }
                    685:        if (bc_value->valuetype == VALUE_TYPE_NUMBER &&
                    686:            !bc_value->is_indirect) {
                    687:                fprintf(stderr, "WARNING: plain numbers shouldn't need masks.\n");
                    688:        }
                    689:        pstate->arg++;
                    690:        if (!Eval_Number(pstate->argv[pstate->arg], &(bc_value->mask))) {
                    691:                pstate->error = "invalid dec/hex/bin value";
                    692:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    693:                return false;
                    694:        }
                    695:        if (bc_value->mask == 0 ||
                    696:            (bc_value->valuetype == VALUE_TYPE_NUMBER && !bc_value->is_indirect &&
                    697:             bc_value->value.number && !(bc_value->value.number & bc_value->mask))) {
                    698:                pstate->error = "mask zeroes value";
                    699:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    700:                return false;
                    701:        }
                    702:        EXITFUNC(("arg:%d -> true (%x)\n", pstate->arg, bc_value->mask));
                    703:        pstate->arg++;
                    704:        return true;
                    705: }
                    706: 
                    707: 
                    708: /**
                    709:  * Parse a breakpoint condition value.
                    710:  * Modify pstate according to parsing (arg index and error string).
                    711:  * Return true for success and false for error.
                    712:  */
                    713: static bool BreakCond_ParseValue(parser_state_t *pstate, bc_value_t *bc_value)
                    714: {
                    715:        const char *str;
                    716:        int skip = 1;
                    717: 
                    718:        ENTERFUNC(("BreakCond_Value()\n"));
                    719:        if (pstate->arg >= pstate->argc) {
                    720:                pstate->error = "value missing";
                    721:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    722:                return false;
                    723:        }
                    724:        /* parse indirection */
                    725:        if (pstate->arg+3 <= pstate->argc) {
                    726:                if (strcmp(pstate->argv[pstate->arg+0], "(") == 0 &&
                    727:                    strcmp(pstate->argv[pstate->arg+2], ")") == 0) {
                    728:                        bc_value->is_indirect = true;
                    729:                        pstate->arg++;
                    730:                        skip = 2;
                    731:                }
                    732:        }
                    733:        
                    734:        str = pstate->argv[pstate->arg];
                    735:        if (isalpha(*str) || *str == '_') {
                    736:                /* parse direct or indirect variable/register/symbol name */
                    737:                if (bc_value->is_indirect) {
                    738:                        /* a valid register or data symbol name? */
                    739:                        if (!BreakCond_ParseRegister(str, bc_value) &&
                    740:                            !BreakCond_ParseSymbol(str, bc_value)) {
                    741:                                pstate->error = "invalid register/symbol name for indirection";
                    742:                                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    743:                                return false;
                    744:                        }
                    745:                } else {
                    746:                        /* a valid Hatari variable or register name?
                    747:                         * variables cannot be used for ST memory indirection.
                    748:                         */
1.1.1.2   root      749: //                     if (!BreakCond_ParseVariable(str, bc_value) &&
                    750: //                         !BreakCond_ParseRegister(str, bc_value) &&
                    751: //                         !BreakCond_ParseSymbol(str, bc_value)) {
                    752: //                             pstate->error = "invalid variable/register/symbol name";
                    753: //                             EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    754: //                             return false;
                    755: //                     }
1.1       root      756:                }
                    757:        } else {
                    758:                /* a number */
                    759:                if (!Eval_Number(str, &(bc_value->value.number))) {
                    760:                        pstate->error = "invalid dec/hex/bin value";
                    761:                        EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    762:                        return false;
                    763:                }
                    764:        }
                    765:        /* memory address (indirect value) -> OK as address? */
                    766:        if (bc_value->is_indirect &&
                    767:            bc_value->valuetype == VALUE_TYPE_NUMBER &&
                    768:            !BreakCond_CheckAddress(bc_value)) {
                    769:                pstate->error = "invalid address";
                    770:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    771:                return false;
                    772:        }
                    773:        pstate->arg += skip;
                    774: 
                    775:        /* parse modifiers */
                    776:        if (!BreakCond_ParseAddressModifier(pstate, bc_value)) {
                    777:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    778:                return false;
                    779:        }
                    780:        if (!BreakCond_ParseMaskModifier(pstate, bc_value)) {
                    781:                EXITFUNC(("arg:%d -> false\n", pstate->arg));
                    782:                return false;
                    783:        }
                    784:        EXITFUNC(("arg:%d -> true (%s value)\n", pstate->arg,
                    785:                  (bc_value->is_indirect ? "indirect" : "direct")));
                    786:        return true;
                    787: }
                    788: 
                    789: 
                    790: /**
                    791:  * Parse a breakpoint comparison character.
                    792:  * Modify pstate according to parsing (arg index and error string).
                    793:  * Return the character or nil for an error.
                    794:  */
                    795: static char BreakCond_ParseComparison(parser_state_t *pstate)
                    796: {
                    797:        const char *comparison;
                    798:        
                    799:        ENTERFUNC(("BreakCond_ParseComparison(), arg:%d\n", pstate->arg));
                    800:        if (pstate->arg >= pstate->argc) {
                    801:                pstate->error = "breakpoint comparison missing";
                    802:                EXITFUNC(("-> false\n"));
                    803:                return false;
                    804:        }
                    805:        comparison = pstate->argv[pstate->arg];
                    806:        switch (comparison[0]) {
                    807:        case '<':
                    808:        case '>':
                    809:        case '=':
                    810:        case '!':
                    811:                break;
                    812:        default:
                    813:                pstate->error = "invalid comparison character";
                    814:                EXITFUNC(("-> false\n"));
                    815:                return false;
                    816:        }
                    817:        if (comparison[1]) {
                    818:                pstate->error = "trailing comparison character(s)";
                    819:                EXITFUNC(("-> false\n"));
                    820:                return false;
                    821:        }
                    822: 
                    823:        pstate->arg++;
                    824:        if (pstate->arg >= pstate->argc) {
                    825:                pstate->error = "right side missing";
                    826:                EXITFUNC(("-> false\n"));
                    827:                return false;
                    828:        }
                    829:        EXITFUNC(("-> '%c'\n", *comparison));
                    830:        return *comparison;
                    831: }
                    832: 
                    833: 
                    834: /**
                    835:  * If no value, use the other value, if that also missing, use default
                    836:  */
                    837: static void BreakCond_InheritDefault(Uint32 *value1, Uint32 value2, Uint32 defvalue)
                    838: {
                    839:        if (!*value1) {
                    840:                if (value2) {
                    841:                        *value1 = value2;
                    842:                } else {
                    843:                        *value1 = defvalue;
                    844:                }
                    845:        }
                    846: }
                    847: 
                    848: /**
                    849:  * Check & ensure that the masks and address sizes are sane
                    850:  * and allow comparison with the other side.
                    851:  * If yes, return true, otherwise false.
                    852:  */
                    853: static bool BreakCond_CrossCheckValues(parser_state_t *pstate,
                    854:                                       bc_value_t *bc_value1,
                    855:                                       bc_value_t *bc_value2)
                    856: {
                    857:        Uint32 mask1, mask2, defbits;
                    858:        ENTERFUNC(("BreakCond_CrossCheckValues()\n"));
                    859: 
                    860:        /* make sure there're valid bit widths and that masks have some value */
                    861:        if (bc_value1->dsp_space) {
                    862:                defbits = 24;
                    863:        } else {
                    864:                defbits = 32;
                    865:        }
                    866:        BreakCond_InheritDefault(&(bc_value1->bits), bc_value2->bits, defbits);
                    867:        BreakCond_InheritDefault(&(bc_value2->bits), bc_value1->bits, defbits);
                    868:        BreakCond_InheritDefault(&(bc_value1->mask), bc_value2->mask, BITMASK(bc_value1->bits));
                    869:        BreakCond_InheritDefault(&(bc_value2->mask), bc_value1->mask, BITMASK(bc_value2->bits));
                    870: 
                    871:        /* check first value mask & bit width */
                    872:        mask1 = BITMASK(bc_value1->bits) & bc_value1->mask;
                    873:        
                    874:        if (mask1 != bc_value1->mask) {
                    875:                fprintf(stderr, "WARNING: mask 0x%x doesn't fit into %d address/register bits.\n",
                    876:                        bc_value1->mask, bc_value1->bits);
                    877:        }
                    878:        if (!bc_value1->dsp_space &&
                    879:            bc_value1->is_indirect &&
                    880:            (bc_value1->value.number & 1) && bc_value1->bits > 8) {
                    881:                fprintf(stderr, "WARNING: odd CPU address 0x%x given without using byte (.b) width.\n",
                    882:                        bc_value1->value.number);
                    883:        }
                    884:        
                    885:        /* cross-check both values masks */
                    886:        mask2 = BITMASK(bc_value2->bits) & bc_value2->mask;
                    887: 
                    888:        if ((mask1 & mask2) == 0) {
                    889:                pstate->error = "values masks cancel each other";
                    890:                EXITFUNC(("-> false\n"));
                    891:                return false;
                    892:        }
                    893:        if (bc_value2->is_indirect ||
                    894:            bc_value2->value.number == 0 ||
                    895:            bc_value2->valuetype != VALUE_TYPE_NUMBER) {
                    896:                EXITFUNC(("-> true (no problematic direct types)\n"));
                    897:                return true;
                    898:        }
                    899:        if ((bc_value2->value.number & mask1) != bc_value2->value.number) {
                    900:                pstate->error = "number doesn't fit the other side address width&mask";
                    901:                EXITFUNC(("-> false\n"));
                    902:                return false;
                    903:        }
                    904:        EXITFUNC(("-> true\n"));
                    905:        return true;
                    906: }
                    907: 
                    908: 
                    909: /**
                    910:  * Parse given breakpoint conditions and append them to breakpoints.
                    911:  * Modify pstate according to parsing (arg index and error string).
                    912:  * Return number of added conditions or zero for failure.
                    913:  */
                    914: static int BreakCond_ParseCondition(parser_state_t *pstate, bool bForDsp,
                    915:                                    bc_condition_t *conditions, int ccount)
                    916: {
                    917:        bc_condition_t condition;
                    918: 
                    919:        ENTERFUNC(("BreakCond_ParseCondition(...)\n"));
                    920:        if (ccount >= BC_MAX_CONDITIONS_PER_BREAKPOINT) {
                    921:                pstate->error = "max number of conditions exceeded";
                    922:                EXITFUNC(("-> 0 (no conditions free)\n"));
                    923:                return 0;
                    924:        }
                    925: 
                    926:        /* setup condition */
                    927:        memset(&condition, 0, sizeof(bc_condition_t));
                    928:        if (bForDsp) {
                    929:                /* used also for checking whether value is for DSP */
                    930:                condition.lvalue.dsp_space = BC_DEFAULT_DSP_SPACE;
                    931:                condition.rvalue.dsp_space = BC_DEFAULT_DSP_SPACE;
                    932:        }
                    933: 
                    934:        /* parse condition */
                    935:        if (!BreakCond_ParseValue(pstate, &(condition.lvalue))) {
                    936:                EXITFUNC(("-> 0\n"));
                    937:                return 0;
                    938:        }
                    939:        condition.comparison = BreakCond_ParseComparison(pstate);
                    940:        if (!condition.comparison) {
                    941:                EXITFUNC(("-> 0\n"));
                    942:                return 0;
                    943:        }
                    944:        if (!BreakCond_ParseValue(pstate, &(condition.rvalue))) {
                    945:                EXITFUNC(("-> 0\n"));
                    946:                return 0;
                    947:        }
                    948:        if (!(BreakCond_CrossCheckValues(pstate, &(condition.lvalue), &(condition.rvalue)) &&
                    949:              BreakCond_CrossCheckValues(pstate, &(condition.rvalue), &(condition.lvalue)))) {
                    950:                EXITFUNC(("-> 0\n"));
                    951:                return 0;
                    952:        }
                    953:        /* new condition */
                    954:        conditions[ccount++] = condition;
                    955: 
                    956:        /* continue with next condition? */
                    957:        if (pstate->arg == pstate->argc) {
                    958:                EXITFUNC(("-> %d (conditions)\n", ccount-1));
                    959:                return ccount;
                    960:        }
                    961:        if (strcmp(pstate->argv[pstate->arg], "&&") != 0) {
                    962:                pstate->error = "trailing content for breakpoint condition";
                    963:                EXITFUNC(("-> 0\n"));
                    964:                return 0;
                    965:        }
                    966:        pstate->arg++;
                    967: 
                    968:        /* recurse conditions parsing */
                    969:        ccount = BreakCond_ParseCondition(pstate, bForDsp, conditions, ccount);
                    970:        if (!ccount) {
                    971:                EXITFUNC(("-> 0\n"));
                    972:                return 0;
                    973:        }
                    974:        EXITFUNC(("-> %d (conditions)\n", ccount-1));
                    975:        return ccount;
                    976: }
                    977: 
                    978: 
                    979: /**
                    980:  * Tokenize given breakpoint expression to given parser struct.
                    981:  * Return normalized expression string that corresponds to tokenization
                    982:  * or NULL on error. On error, pstate->error contains the error message
                    983:  * and pstate->arg index to invalid character (instead of to token like
                    984:  * after parsing).
                    985:  */
                    986: static char *BreakCond_TokenizeExpression(const char *expression,
                    987:                                          parser_state_t *pstate)
                    988: {
                    989:        char separator[] = {
                    990:                '=', '!', '<', '>',  /* comparison operators */
                    991:                '(', ')', '.', '&',  /* other separators */
                    992:                '\0'                 /* terminator */
                    993:        };
                    994:        bool is_separated, has_comparison;
                    995:        char sep, *dst, *normalized;
                    996:        const char *src;
                    997:        int i, tokens;
                    998: 
                    999:        memset(pstate, 0, sizeof(parser_state_t));
                   1000: 
                   1001:        /* _minimum_ safe size for normalized expression is 2x+1 */
                   1002:        normalized = malloc(2*strlen(expression)+1);
                   1003:        if (!normalized) {
                   1004:                pstate->error = "alloc failed";
                   1005:                return NULL;
                   1006:        }
                   1007: 
                   1008:        /* check characters & normalize string */
                   1009:        dst = normalized;
                   1010:        is_separated = false;
                   1011:        has_comparison = false;
                   1012:        for (src = expression; *src; src++) {
                   1013:                /* discard white space in source */
                   1014:                if (isspace(*src)) {
                   1015:                        continue;
                   1016:                }
                   1017:                /* separate tokens with single space in destination */
                   1018:                for (i = 0; (sep = separator[i]); i++) {
                   1019:                        if (*src == sep) {
                   1020:                                if (dst > normalized) {
                   1021:                                        /* don't separate boolean AND '&&' */
                   1022:                                        if (*src == '&' && *(src-1) == '&') {
                   1023:                                                dst--;
                   1024:                                        } else {
                   1025:                                                if (!is_separated) {
                   1026:                                                        *dst++ = ' ';
                   1027:                                                }
                   1028:                                        }
                   1029:                                }
                   1030:                                *dst++ = *src;
                   1031:                                *dst++ = ' ';
                   1032:                                is_separated = true;
                   1033:                                if (i < 4) {
                   1034:                                        has_comparison = true;
                   1035:                                }
                   1036:                                break;
                   1037:                        }
                   1038:                }
                   1039:                /* validate & copy other characters */
                   1040:                if (!sep) {
                   1041:                        /* variable/register/symbol or number prefix? */
                   1042:                        if (!(isalnum(*src) || *src == '_' ||
                   1043:                              *src == '$' || *src == '#' || *src == '%')) {
                   1044:                                pstate->error = "invalid character";
                   1045:                                pstate->arg = src-expression;
                   1046:                                free(normalized);
                   1047:                                return NULL;
                   1048:                        }
                   1049:                        *dst++ = *src;
                   1050:                        is_separated = false;
                   1051:                }
                   1052:        }
                   1053:        if (is_separated) {
                   1054:                dst--;  /* no trailing space */
                   1055:        }
                   1056:        *dst = '\0';
                   1057: 
                   1058:        if (!has_comparison) {
                   1059:                pstate->error = "condition comparison missing";
                   1060:                pstate->arg = strlen(expression)/2;
                   1061:                free(normalized);
                   1062:                return NULL;
                   1063:        }
                   1064: 
                   1065:        /* allocate exact space for tokenized string array + strings */
                   1066:        tokens = 1;
                   1067:        for (dst = normalized; *dst; dst++) {
                   1068:                if (*dst == ' ') {
                   1069:                        tokens++;
                   1070:                }
                   1071:        }
                   1072:        pstate->argv = malloc(tokens*sizeof(char*)+strlen(normalized)+1);
                   1073:        if (!pstate->argv) {
                   1074:                pstate->error = "alloc failed";
                   1075:                free(normalized);
                   1076:                return NULL;
                   1077:        }
                   1078:        /* and copy/tokenize... */
                   1079:        dst = (char*)(pstate->argv) + tokens*sizeof(char*);
                   1080:        strcpy(dst, normalized);
                   1081:        pstate->argv[0] = strtok(dst, " ");
                   1082:        for (i = 1; (dst = strtok(NULL, " ")); i++) {
                   1083:                pstate->argv[i] = dst;
                   1084:        }
                   1085:        assert(i == tokens);
                   1086:        pstate->argc = tokens;
                   1087: #if DEBUG
                   1088:        fprintf(stderr, "args->");
                   1089:        for (i = 0; i < tokens; i++) {
                   1090:                fprintf(stderr, " %d: %s,", i, pstate->argv[i]);
                   1091:        }
                   1092:        fprintf(stderr, "\n");
                   1093: #endif
                   1094:        return normalized;
                   1095: }
                   1096: 
                   1097: 
                   1098: /**
                   1099:  * Helper to set corrent breakpoint list and type name to given variables.
                   1100:  * Return pointer to breakpoint list count
                   1101:  */
                   1102: static int* BreakCond_GetListInfo(bc_breakpoint_t **bp,
                   1103:                                  const char **name, bool bForDsp)
                   1104: {
                   1105:        int *bcount;
                   1106:        if (bForDsp) {
                   1107:                bcount = &BreakPointDspCount;
                   1108:                *bp = BreakPointsDsp;
                   1109:                *name = "DSP";
                   1110:        } else {
                   1111:                bcount = &BreakPointCpuCount;
                   1112:                *bp = BreakPointsCpu;
                   1113:                *name = "CPU";
                   1114:        }
                   1115:        return bcount;
                   1116: }
                   1117: 
                   1118: 
                   1119: /**
                   1120:  * Check whether any of the breakpoint conditions is such that it's
                   1121:  * intended for tracking given value changes (inequality comparison
                   1122:  * on identical values) or for retrieving the current value to break
                   1123:  * on next value change (other comparisons on identical values).
                   1124:  *
                   1125:  * On former case, mark it for tracking, on other cases, just
                   1126:  * retrieve the value.
                   1127:  */
                   1128: static void BreakCond_CheckTracking(bc_breakpoint_t *bp)
                   1129: {
                   1130:        bc_condition_t *condition;
                   1131:        bool track = false;
                   1132:        Uint32 value;
                   1133:        int i;
                   1134: 
                   1135:        condition = bp->conditions;
                   1136:        for (i = 0; i < bp->ccount; condition++, i++) {
                   1137:                
                   1138:                if (memcmp(&(condition->lvalue), &(condition->rvalue), sizeof(bc_value_t)) == 0) {
                   1139:                        /* set current value to right side */
                   1140:                        value = BreakCond_GetValue(&(condition->rvalue));
                   1141:                        condition->rvalue.value.number = value;
                   1142:                        condition->rvalue.valuetype = VALUE_TYPE_NUMBER;
                   1143:                        condition->rvalue.is_indirect = false;
                   1144:                        if (condition->comparison == '!') {
                   1145:                                /* which changes will be traced */
                   1146:                                condition->track = true;
                   1147:                                track = true;
                   1148:                        } else {
                   1149:                                fprintf(stderr, "\t%d. condition: %c $%x\n",
                   1150:                                        i+1, condition->comparison, value);
                   1151:                        }
                   1152:                }
                   1153:        }
                   1154:        if (track) {
                   1155:                fprintf(stderr, "-> Track value changes, show value(s) when matched.\n");
                   1156:        }
                   1157: }
                   1158: 
                   1159: 
                   1160: /**
                   1161:  * Parse given breakpoint expression and store it.
                   1162:  * Return true for success and false for failure.
                   1163:  */
1.1.1.2   root     1164: static bool BreakCond_Parse(const char *expression, bc_options_t *options, bool bForDsp)
1.1       root     1165: {
                   1166:        parser_state_t pstate;
                   1167:        bc_breakpoint_t *bp;
                   1168:        const char *name;
                   1169:        char *normalized;
                   1170:        int *bcount;
                   1171:        int ccount;
                   1172: 
                   1173:        bcount = BreakCond_GetListInfo(&bp, &name, bForDsp);
                   1174:        if (*bcount >= BC_MAX_CONDITION_BREAKPOINTS) {
                   1175:                fprintf(stderr, "ERROR: no free %s condition breakpoints left.\n", name);
                   1176:                return false;
                   1177:        }
                   1178:        bp += *bcount;
                   1179:        memset(bp, 0, sizeof(bc_breakpoint_t));
                   1180: 
                   1181:        normalized = BreakCond_TokenizeExpression(expression, &pstate);
                   1182:        if (normalized) {
                   1183:                bp->expression = normalized;
                   1184:                ccount = BreakCond_ParseCondition(&pstate, bForDsp,
                   1185:                                                  bp->conditions, 0);
                   1186:                bp->ccount = ccount;
                   1187:        } else {
                   1188:                ccount = 0;
                   1189:        }
                   1190:        if (pstate.argv) {
                   1191:                free(pstate.argv);
                   1192:        }
                   1193:        if (ccount > 0) {
                   1194:                (*bcount)++;
                   1195:                fprintf(stderr, "%s condition breakpoint %d with %d condition(s) added:\n\t%s\n",
                   1196:                        name, *bcount, ccount, bp->expression);
                   1197:                BreakCond_CheckTracking(bp);
1.1.1.2   root     1198:                if (options->skip) {
                   1199:             fprintf(stderr, "-> Break only on every %d hit.\n", options->skip);
                   1200:             bp->options.skip = options->skip;
                   1201:                }
                   1202:                if (options->once) {
                   1203:                        fprintf(stderr, "-> Once, delete after breaking.\n");
                   1204:             bp->options.once = true;
1.1       root     1205:                }
1.1.1.2   root     1206:                if (options->trace) {
1.1       root     1207:                        fprintf(stderr, "-> Trace instead of breaking, but show still hits.\n");
1.1.1.2   root     1208:             if (options->lock) {
                   1209:                 fprintf(stderr, "-> Show also info selected with lock command.\n");
                   1210:                 bp->options.lock = true;
                   1211:             }
                   1212:             bp->options.trace = true;
                   1213:         }
                   1214:         if (options->filename) {
                   1215:             fprintf(stderr, "-> Execute debugger commands from '%s' file on hit.\n", options->filename);
                   1216:             bp->options.filename = strdup(options->filename);
                   1217:         }
1.1       root     1218:        } else {
                   1219:                if (normalized) {
                   1220:                        int offset, i = 0;
                   1221:                        char *s = normalized;
                   1222:                        while (*s && i < pstate.arg) {
                   1223:                                if (*s++ == ' ') {
                   1224:                                        i++;
                   1225:                                }
                   1226:                        }
                   1227:                        offset = s - normalized;
                   1228:                        /* show tokenized string and point out
                   1229:                         * the token where the error was encountered
                   1230:                         */
                   1231:                        fprintf(stderr, "ERROR in tokenized string:\n'%s'\n%*c-%s\n",
                   1232:                                normalized, offset+2, '^', pstate.error);
                   1233:                        free(normalized);
                   1234:                        bp->expression = NULL;
                   1235:                } else {
                   1236:                        /* show original string and point out the character
                   1237:                         * where the error was encountered
                   1238:                         */
                   1239:                        fprintf(stderr, "ERROR in parsed string:\n'%s'\n%*c-%s\n",
                   1240:                                expression, pstate.arg+2, '^', pstate.error);
                   1241:                }
                   1242:        }
                   1243:        return (ccount > 0);
                   1244: }
                   1245: 
                   1246: 
                   1247: /**
                   1248:  * print single breakpoint
                   1249:  */
                   1250: static void BreakCond_Print(bc_breakpoint_t *bp)
                   1251: {
                   1252:                fprintf(stderr, "\t%s", bp->expression);
1.1.1.2   root     1253:     if (bp->options.skip) {
                   1254:         fprintf(stderr, " :%d", bp->options.skip);
1.1       root     1255:                }
1.1.1.2   root     1256:                if (bp->options.once) {
1.1       root     1257:                        fprintf(stderr, " :once");
                   1258:                }
1.1.1.2   root     1259:     if (bp->options.trace) {
                   1260:         if (bp->options.lock) {
                   1261:             fprintf(stderr, " :lock");
                   1262:         } else {
                   1263:             fprintf(stderr, " :trace");
                   1264:         }
                   1265:     }
                   1266:     if (bp->options.filename) {
                   1267:         fprintf(stderr, " :file %s", bp->options.filename);
1.1       root     1268:                }
                   1269:                fprintf(stderr, "\n");
                   1270: }
                   1271: 
                   1272: /**
                   1273:  * List condition breakpoints
                   1274:  */
                   1275: static void BreakCond_List(bool bForDsp)
                   1276: {
                   1277:        const char *name;
                   1278:        bc_breakpoint_t *bp;
                   1279:        int i, bcount;
                   1280:        
                   1281:        bcount = *BreakCond_GetListInfo(&bp, &name, bForDsp);
                   1282:        if (!bcount) {
                   1283:                fprintf(stderr, "No conditional %s breakpoints.\n", name);
                   1284:                return;
                   1285:        }
                   1286: 
                   1287:        fprintf(stderr, "%d conditional %s breakpoints:\n", bcount, name);
                   1288:        for (i = 1; i <= bcount; bp++, i++) {
                   1289:                fprintf(stderr, "%4d:", i);
                   1290:                BreakCond_Print(bp);
                   1291:        }
                   1292: }
                   1293: 
                   1294: 
                   1295: /**
                   1296:  * Remove condition breakpoint at given position
                   1297:  */
                   1298: static bool BreakCond_Remove(int position, bool bForDsp)
                   1299: {
                   1300:        const char *name;
                   1301:        bc_breakpoint_t *bp;
                   1302:        int *bcount, offset;
                   1303:        
                   1304:        bcount = BreakCond_GetListInfo(&bp, &name, bForDsp);
                   1305:        if (!*bcount) {
1.1.1.2   root     1306:                fprintf(stderr, "No (more) %s breakpoints to remove.\n", name);
1.1       root     1307:                return false;
                   1308:        }
                   1309:        if (position < 1 || position > *bcount) {
                   1310:                fprintf(stderr, "ERROR: No such %s breakpoint.\n", name);
                   1311:                return false;
                   1312:        }
                   1313:        offset = position - 1;
                   1314:        fprintf(stderr, "Removed %s breakpoint %d:\n", name, position);
                   1315:        BreakCond_Print(&(bp[offset]));
                   1316:        free(bp[offset].expression);
1.1.1.2   root     1317:     if (bp[offset].options.filename) {
                   1318:         free(bp[offset].options.filename);
                   1319:     }
1.1       root     1320:        bp[offset].expression = NULL;
                   1321: 
                   1322:        if (position < *bcount) {
                   1323:                memmove(bp+offset, bp+position,
                   1324:                        (*bcount-position)*sizeof(bc_breakpoint_t));
                   1325:        }
                   1326:        (*bcount)--;
                   1327:        return true;
                   1328: }
                   1329: 
                   1330: 
                   1331: /**
                   1332:  * Remove all condition breakpoints
                   1333:  */
                   1334: static void BreakCond_RemoveAll(bool bForDsp)
                   1335: {
                   1336:        while (BreakCond_Remove(1, bForDsp))
                   1337:                ;
                   1338: }
                   1339: 
                   1340: 
                   1341: /**
                   1342:  * Return true if given CPU breakpoint has given CPU expression.
                   1343:  * Used by the test code.
                   1344:  */
                   1345: int BreakCond_MatchCpuExpression(int position, const char *expression)
                   1346: {
                   1347:        if (position < 1 || position > BreakPointCpuCount) {
                   1348:                return false;
                   1349:        }
                   1350:        if (strcmp(expression, BreakPointsCpu[position-1].expression)) {
                   1351:                return false;
                   1352:        }
                   1353:        return true;
                   1354: }
                   1355: 
                   1356: 
                   1357: /**
                   1358:  * help
                   1359:  */
                   1360: static void BreakCond_Help(void)
                   1361: {
                   1362:        fputs(
1.1.1.2   root     1363: "  condition = <value>[.mode] [& <mask>] <comparison> <value>[.mode]\n"
1.1       root     1364: "\n"
                   1365: "  where:\n"
                   1366: "      value = [(] <register/symbol/variable name | number> [)]\n"
1.1.1.2   root     1367: "      number/mask = [#|$|%]<digits>\n"
1.1       root     1368: "      comparison = '<' | '>' | '=' | '!'\n"
                   1369: "      addressing mode (width) = 'b' | 'w' | 'l'\n"
                   1370: "      addressing mode (space) = 'p' | 'x' | 'y'\n"
                   1371: "\n"
                   1372: "  If the value is in parenthesis like in '($ff820)' or '(a0)', then\n"
                   1373: "  the used value will be read from the memory address pointed by it.\n"
                   1374: "\n"
1.1.1.2   root     1375: "  If the parsed value expressions on both sides of it are exactly\n"
                   1376: "  the same, right side is replaced with its current value.  For\n"
                   1377: "  inequality ('!') comparison, the breakpoint will additionally track\n"
                   1378: "  all further changes for the given address/register expression value.\n"
                   1379: "  (This is useful for tracking register and memory value changes.)\n"
1.1       root     1380: "\n"
                   1381: "  M68k addresses can have byte (b), word (w) or long (l, default) width.\n"
                   1382: "  DSP addresses belong to different address spaces: P, X or Y. Note that\n"
                   1383: "  on DSP only R0-R7 registers can be used for memory addressing.\n"
                   1384: "\n"
                   1385: "  Valid Hatari variable names (and their current values) are:\n", stderr);
1.1.1.2   root     1386: //     for (i = 0; i < ARRAYSIZE(hatari_vars); i++) {
                   1387: //             switch (hatari_vars[i].vtype) {
                   1388: //             case VALUE_TYPE_FUNCTION32:
                   1389: //                     value = ((Uint32(*)(void))(hatari_vars[i].addr))();
                   1390: //                     break;
                   1391: //             case VALUE_TYPE_VAR32:
                   1392: //                     value = *(hatari_vars[i].addr);
                   1393: //                     break;
                   1394: //             default:
                   1395: //                     fprintf(stderr, "ERROR: variable '%s' has unsupported type '%d'\n",
                   1396: //                             hatari_vars[i].name, hatari_vars[i].vtype);
                   1397: //                     continue;
                   1398: //             }
                   1399: //             fprintf(stderr, "  - %s ($%x)", hatari_vars[i].name, value);
                   1400: //             if (hatari_vars[i].constraints) {
                   1401: //                     fprintf(stderr, ", %s\n", hatari_vars[i].constraints);
                   1402: //             } else {
                   1403: //                     fprintf(stderr, "\n");
                   1404: //             }
                   1405: //     }
1.1       root     1406:        fputs(
                   1407: "\n"
                   1408: "  Examples:\n"
                   1409: "      pc = $64543  &&  ($ff820).w & 3 = (a0)  &&  d0 = %1100\n"
                   1410: "       ($ffff9202).w ! ($ffff9202).w :trace\n"
                   1411: "      (r0).x = 1 && (r0).y = 2\n", stderr);
                   1412: }
                   1413: 
                   1414: 
                   1415: /* ------------- breakpoint condition parsing, public API ------------ */
                   1416: 
                   1417: const char BreakCond_Description[] =
1.1.1.2   root     1418: "<condition> [&& <condition> ...] [:<option>] | <index> | help | all\n"
                   1419: "\n"
                   1420: "\tSet breakpoint with given <conditions>, remove breakpoint with\n"
                   1421: "\tgiven <index>, remove all breakpoints with 'all' or output\n"
                   1422: "\tbreakpoint condition syntax with 'help'.  Without arguments,\n"
                   1423: "\tlists currently active breakpoints.\n"
                   1424: "\n"
                   1425: "\tMultiple breakpoint action options can be specified after\n"
                   1426: "\tthe breakpoint condition(s):\n"
                   1427: "\t- 'trace', print the breakpoint match without stopping\n"
                   1428: "\t- 'lock', print the debugger entry info without stopping\n"
                   1429: "\t- 'file <file>', execute debugger commands from given <file>\n"
                   1430: "\t- 'once', delete the breakpoint after it's hit\n"
                   1431: "\t- '<count>', break only on every <count> hit";
                   1432: 
                   1433: /**
                   1434:  * Parse options for the given breakpoint command.
                   1435:  * Return true for success and false for failure.
                   1436:  */
                   1437: static bool BreakCond_Options(char *str, bc_options_t *options, char marker)
                   1438: {
                   1439:     char *option, *next, *filename;
                   1440:     int skip;
                   1441: 
                   1442:     memset(options, 0, sizeof(*options));
                   1443: 
                   1444:     option = strchr(str, marker);
                   1445:     if (option) {
                   1446:         /* end breakcond command at options */
                   1447:         *option = 0;
                   1448:     }
                   1449:     for (next = option; option; option = next) {
                   1450: 
                   1451:         /* skip marker + end & trim this option */
                   1452:         option = next + 1;
                   1453:         next = strchr(option, marker);
                   1454:         if (next) {
                   1455:             *next = 0;
                   1456:         }
                   1457:         option = Str_Trim(option);
                   1458: 
                   1459:         if (strcmp(option, "once") == 0) {
                   1460:             options->once = true;
                   1461:         } else if (strcmp(option, "trace") == 0) {
                   1462:             options->trace = true;
                   1463:         } else if (strcmp(option, "lock") == 0) {
                   1464:             options->trace = true;
                   1465:             options->lock = true;
                   1466:         } else if (strncmp(option, "file ", 5) == 0) {
                   1467:             filename = Str_Trim(option+4);
                   1468:             if (!File_Exists(filename)) {
                   1469:                 fprintf(stderr, "ERROR: given file '%s' doesn't exist!\n", filename);
                   1470:                 fprintf(stderr, "(if you use 'cd' command, do it before setting breakpoints)\n");
                   1471:                 return false;
                   1472:             }
                   1473:             options->filename = filename;
                   1474:         } else if (isdigit(*option)) {
                   1475:             /* :<value> */
                   1476:             skip = atoi(option);
                   1477:             if (skip < 2) {
                   1478:                 fprintf(stderr, "ERROR: invalid breakpoint skip count '%s'!\n", option);
                   1479:                 return false;
                   1480:             }
                   1481:             options->skip = skip;
                   1482:         } else {
                   1483:             fprintf(stderr, "ERROR: unrecognized breakpoint option '%s'!\n", option);
                   1484:             return false;
                   1485:         }
                   1486:     }
                   1487:     return true;
                   1488: }
1.1       root     1489: 
                   1490: /**
                   1491:  * Parse given command expression to set/remove/list
                   1492:  * conditional breakpoints for CPU or DSP.
                   1493:  * Return true for success and false for failure.
                   1494:  */
                   1495: bool BreakCond_Command(const char *args, bool bForDsp)
                   1496: {
1.1.1.2   root     1497:        char *expression, *argscopy;
1.1       root     1498:        unsigned int position;
1.1.1.2   root     1499:     bc_options_t options;
1.1       root     1500:        const char *end;
1.1.1.2   root     1501:        bool ret = true;
1.1       root     1502:        
                   1503:        if (!args) {
                   1504:                BreakCond_List(bForDsp);
                   1505:                return true;
                   1506:        }
                   1507:        argscopy = strdup(args);
                   1508:        assert(argscopy);
                   1509:        
                   1510:        expression = Str_Trim(argscopy);
                   1511:        
1.1.1.2   root     1512:        /* subcommands? */
1.1       root     1513:        if (strncmp(expression, "help", 4) == 0) {
                   1514:                BreakCond_Help();
                   1515:                goto cleanup;
                   1516:        }
                   1517:        if (strcmp(expression, "all") == 0) {
                   1518:                BreakCond_RemoveAll(bForDsp);
                   1519:                goto cleanup;
                   1520:        }
                   1521: 
1.1.1.2   root     1522:     /* postfix options? */
                   1523:     if (!BreakCond_Options(expression, &options, ':')) {
                   1524:         ret = false;
                   1525:         goto cleanup;
                   1526:     }
                   1527:     
                   1528:     /* index (for breakcond removal)? */
1.1       root     1529:        end = expression;
                   1530:        while (isdigit(*end)) {
                   1531:                end++;
                   1532:        }
                   1533:        if (end > expression && *end == '\0' &&
                   1534:            sscanf(expression, "%u", &position) == 1) {
                   1535:                ret = BreakCond_Remove(position, bForDsp);
                   1536:        } else {
                   1537:                /* add breakpoint? */
1.1.1.2   root     1538:                ret = BreakCond_Parse(expression, &options, bForDsp);
1.1       root     1539:        }
                   1540: cleanup:
                   1541:        free(argscopy);
                   1542:        return ret;
                   1543: }
                   1544: 
                   1545: 
                   1546: const char BreakAddr_Description[] =
1.1.1.2   root     1547:        "<address> [:<option>]\n"
1.1       root     1548:        "\tCreate conditional breakpoint for given PC <address>.\n"
1.1.1.2   root     1549:     "\n"
                   1550:     "\tBreakpoint action option alternatives:\n"
                   1551:     "\t- 'trace', print the breakpoint match without stopping\n"
                   1552:     "\t- 'lock', print the debugger entry info without stopping\n"
                   1553:     "\t- 'once', delete the breakpoint after it's hit\n"
                   1554:     "\t- '<count>', break only on every <count> hit\n"
                   1555:     "\n"
                   1556:     "\tUse conditional breakpoint commands to manage the created\n"
1.1       root     1557:        "\tbreakpoints.";
                   1558: 
                   1559: /**
                   1560:  * Set CPU & DSP program counter address breakpoints by converting
                   1561:  * them to conditional breakpoints.
                   1562:  * Return true for success and false for failure.
                   1563:  */
                   1564: bool BreakAddr_Command(char *args, bool bForDsp)
                   1565: {
                   1566:        const char *errstr, *expression = (const char *)args;
                   1567:        char *cut, command[32];
                   1568:        Uint32 addr;
                   1569:        int offset;
                   1570: 
1.1.1.2   root     1571:     if (!args) {
                   1572:         if (bForDsp) {
                   1573:             DebugUI_PrintCmdHelp("dspaddress");
                   1574:         } else {
                   1575:             DebugUI_PrintCmdHelp("address");
                   1576:         }
                   1577:         return true;
                   1578:     }
                   1579: 
1.1       root     1580:        /* split options */
                   1581:        if ((cut = strchr(args, ':'))) {
                   1582:                *cut = '\0';
                   1583:                cut = Str_Trim(cut+1);
1.1.1.2   root     1584:                if (strlen(cut) > 5) {
                   1585:                        cut[5] = '\0';
1.1       root     1586:                }
                   1587:        }
                   1588: 
                   1589:        /* evaluate address expression */
                   1590:        errstr = Eval_Expression(expression, &addr, &offset, bForDsp);
                   1591:        if (errstr) {
                   1592:                fprintf(stderr, "ERROR in the address expression:\n'%s'\n%*c-%s\n",
                   1593:                        expression, offset+2, '^', errstr);
                   1594:                return false;
                   1595:        }
                   1596: 
                   1597:        /* add the address breakpoint with optional option */
                   1598:        sprintf(command, "pc=$%x %c%s", addr, cut?':':' ', cut?cut:"");
                   1599:        if (!BreakCond_Command(command, bForDsp)) {
                   1600:                return false;
                   1601:        }
                   1602: 
                   1603:        /* on success, show on what instruction it was added */
                   1604:                uaecptr dummy;
1.1.1.4 ! root     1605:                m68k_disasm((uaecptr)addr, &dummy, 1);
1.1       root     1606:        return true;
                   1607: }

unix.superglobalmegacorp.com

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