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

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

unix.superglobalmegacorp.com

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