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

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

unix.superglobalmegacorp.com

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