Annotation of hatari/src/debug/breakcond.c, revision 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.