Annotation of hatari/src/breakcond.c, revision 1.1

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

unix.superglobalmegacorp.com

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