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

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

unix.superglobalmegacorp.com

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