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