Annotation of hatari/src/debug/debugui.c, revision 1.1.1.6

1.1       root        1: /*
                      2:   Hatari - debugui.c
                      3: 
1.1.1.6 ! root        4:   This file is distributed under the GNU General Public License, version 2
        !             5:   or at your option any later version. Read the file gpl.txt for details.
1.1       root        6: 
                      7:   debugui.c - this is the code for the mini-debugger. When the pause button is
                      8:   pressed, the emulator is (hopefully) halted and this little CLI can be used
                      9:   (in the terminal box) for debugging tasks like memory and register dumps.
                     10: */
                     11: const char DebugUI_fileid[] = "Hatari debugui.c : " __DATE__ " " __TIME__;
                     12: 
                     13: #include <ctype.h>
                     14: #include <stdio.h>
                     15: #include <unistd.h>
                     16: 
                     17: #include "config.h"
                     18: 
                     19: #if HAVE_LIBREADLINE
                     20: #include <readline/readline.h>
                     21: #include <readline/history.h>
                     22: #endif
                     23: 
                     24: #include "main.h"
                     25: #include "change.h"
                     26: #include "configuration.h"
                     27: #include "file.h"
                     28: #include "log.h"
                     29: #include "m68000.h"
                     30: #include "memorySnapShot.h"
                     31: #include "options.h"
                     32: #include "screen.h"
                     33: #include "statusbar.h"
                     34: #include "str.h"
                     35: 
                     36: #include "debug_priv.h"
                     37: #include "breakcond.h"
                     38: #include "debugcpu.h"
                     39: #include "debugdsp.h"
1.1.1.6 ! root       40: #include "68kDisass.h"
1.1       root       41: #include "debugInfo.h"
                     42: #include "debugui.h"
                     43: #include "evaluate.h"
1.1.1.3   root       44: #include "history.h"
1.1       root       45: #include "symbols.h"
                     46: 
1.1.1.5   root       47: int bExceptionDebugging;
1.1       root       48: 
1.1.1.5   root       49: FILE *debugOutput;
1.1       root       50: 
                     51: static dbgcommand_t *debugCommand;
                     52: static int debugCommands;
                     53: 
                     54: /* stores last 'e' command result as hex, used for TAB-completion */
                     55: static char lastResult[10];
                     56: 
1.1.1.6 ! root       57: /* parse debugger commands from here on init */
1.1       root       58: static const char *parseFileName;
                     59: 
                     60: 
                     61: /**
                     62:  * Save/Restore snapshot of debugging session variables
                     63:  */
                     64: void DebugUI_MemorySnapShot_Capture(const char *path, bool bSave)
                     65: {
                     66:        char *filename;
                     67: 
                     68:        filename = malloc(strlen(path) + strlen(".debug") + 1);
                     69:        assert(filename);
                     70:        strcpy(filename, path);
                     71:        strcat(filename, ".debug");
                     72:        
                     73:        if (bSave)
                     74:        {
                     75:                /* save breakpoints as debugger input file */
                     76:                BreakCond_Save(filename);
                     77:        }
                     78:        else
                     79:        {
                     80:                /* remove current CPU and DSP breakpoints */
                     81:                BreakCond_Command("all", false);
                     82:                BreakCond_Command("all", true);
                     83: 
                     84:                if (File_Exists(filename))
                     85:                {
                     86:                        /* and parse back the saved breakpoints */
1.1.1.6 ! root       87:                        DebugUI_ParseFile(filename, true);
1.1       root       88:                }
                     89:        }
                     90:        free(filename);
                     91: }
                     92: 
                     93: 
                     94: /**
                     95:  * Close a log file if open, and set it to default stream.
                     96:  */
                     97: static void DebugUI_SetLogDefault(void)
                     98: {
                     99:        if (debugOutput != stderr)
                    100:        {
                    101:                if (debugOutput)
                    102:                {
                    103:                        File_Close(debugOutput);
                    104:                        fprintf(stderr, "Debug log closed.\n");
                    105:                }
                    106:                debugOutput = stderr;
                    107:        }
                    108: }
                    109: 
                    110: 
                    111: /**
                    112:  * Open (or close) given log file.
                    113:  */
                    114: static int DebugUI_SetLogFile(int nArgc, char *psArgs[])
                    115: {
                    116:        File_Close(debugOutput);
                    117:        debugOutput = NULL;
                    118: 
                    119:        if (nArgc > 1)
                    120:                debugOutput = File_Open(psArgs[1], "w");
                    121: 
                    122:        if (debugOutput)
                    123:                fprintf(stderr, "Debug log '%s' opened.\n", psArgs[1]);
                    124:        else
                    125:                debugOutput = stderr;
                    126: 
                    127:        return DEBUGGER_CMDDONE;
                    128: }
                    129: 
                    130: 
                    131: /**
                    132:  * Helper to print given value in all supported number bases
                    133:  */
                    134: static void DebugUI_PrintValue(Uint32 value)
                    135: {
                    136:        bool one, ones;
                    137:        int bit;
                    138: 
                    139:        fputs("= %", stderr);
                    140:        ones = false;
                    141:        for (bit = 31; bit >= 0; bit--)
                    142:        {
                    143:                one = value & (1<<bit);
                    144:                if (one || ones)
                    145:                {
                    146:                        fputc(one ? '1':'0', stderr);
                    147:                        ones = true;
                    148:                }
                    149:        }
                    150:        if (!ones)
                    151:                fputc('0', stderr);
                    152:        if (value & 0x80000000)
                    153:                fprintf(stderr, " (bin), #%u/%d (dec), $%x (hex)\n", value, (int)value, value);
                    154:        else
                    155:                fprintf(stderr, " (bin), #%u (dec), $%x (hex)\n", value, value);
                    156: 
                    157:        sprintf(lastResult, "%x", value);
                    158: }
                    159: 
                    160: 
                    161: /**
                    162:  * Commmand: Evaluate an expression with CPU reg and symbol parsing.
                    163:  */
                    164: static int DebugUI_Evaluate(int nArgc, char *psArgs[])
                    165: {
                    166:        const char *errstr, *expression = (const char *)psArgs[1];
                    167:        Uint32 result;
                    168:        int offset;
                    169: 
                    170:        if (nArgc < 2)
                    171:        {
                    172:                DebugUI_PrintCmdHelp(psArgs[0]);
                    173:                return DEBUGGER_CMDDONE;
                    174:        }
                    175: 
                    176:        errstr = Eval_Expression(expression, &result, &offset, false);
                    177:        if (errstr)
                    178:                fprintf(stderr, "ERROR in the expression:\n'%s'\n%*c-%s\n",
                    179:                        expression, offset+3, '^', errstr);
                    180:        else
                    181:                DebugUI_PrintValue(result);
                    182:        return DEBUGGER_CMDDONE;
                    183: }
                    184: 
                    185: 
                    186: /**
                    187:  * Check whether given string is a two letter command starting with 'd'
                    188:  * or a long command starting with "dsp". String should be trimmed.
                    189:  * Return true if given string is command for DSP, false otherwise.
                    190:  */
                    191: static bool DebugUI_IsForDsp(const char *cmd)
                    192: {
1.1.1.6 ! root      193:        return ((cmd[0] == 'd' && isalpha(cmd[1]) && !isalpha(cmd[2]))
1.1       root      194:                || strncmp(cmd, "dsp", 3) == 0);
                    195: }
                    196: 
                    197: /**
1.1.1.6 ! root      198:  * Evaluate everything include within single or double quotes ("" or '')
        !           199:  * and replace them with the result.
        !           200:  * Caller needs to free the returned string separately.
1.1       root      201:  * 
1.1.1.6 ! root      202:  * Return new string with expressions (potentially) expanded, or
1.1.1.2   root      203:  * NULL when there's an error in the expression.
1.1       root      204:  */
1.1.1.6 ! root      205: static char *DebugUI_EvaluateExpressions(const char *initial)
1.1       root      206: {
                    207:        int offset, count, diff, inputlen;
1.1.1.6 ! root      208:        char *end, *start, *input;
1.1       root      209:        const char *errstr;
                    210:        char valuestr[12];
                    211:        Uint32 value;
                    212:        bool fordsp;
                    213: 
                    214:        /* input is split later on, need to save len here */
1.1.1.6 ! root      215:        input = strdup(initial);
        !           216:        if (!input)
        !           217:        {
        !           218:                perror("ERROR: Input string alloc failed\n");
        !           219:                return NULL;
        !           220:        }
1.1       root      221:        fordsp = DebugUI_IsForDsp(input);
                    222:        inputlen = strlen(input);
1.1.1.6 ! root      223:        start = input;
        !           224: 
        !           225:        while ((count = strcspn(start, "\"'")) && *(start+count))
1.1       root      226:        {
1.1.1.6 ! root      227:                start += count;
        !           228:                end = strchr(start+1, *start);
1.1       root      229:                if (!end)
                    230:                {
1.1.1.6 ! root      231:                        fprintf(stderr, "ERROR: matching '%c' missing from '%s'!\n", *start, start);
1.1       root      232:                        return NULL;
                    233:                }
                    234:                
                    235:                if (end == start+1)
                    236:                {
                    237:                        /* empty expression */
                    238:                        memmove(start, start+2, strlen(start+2)+1);
                    239:                        continue;
                    240:                }
                    241: 
                    242:                *end = '\0';
                    243:                errstr = Eval_Expression(start+1, &value, &offset, fordsp);
                    244:                if (errstr) {
1.1.1.6 ! root      245:                        *end = *start; /* restore expression mark */
1.1       root      246:                        fprintf(stderr, "Expression ERROR:\n'%s'\n%*c-%s\n",
                    247:                                input, (int)(start-input)+offset+3, '^', errstr);
                    248:                        return NULL;
                    249:                }
                    250:                end++;
1.1.1.6 ! root      251: 
1.1       root      252:                count = sprintf(valuestr, "$%x", value);
1.1.1.6 ! root      253:                fprintf(stderr, "- '%s' -> %s\n", start+1, valuestr);
1.1       root      254: 
                    255:                diff = end-start;
                    256:                if (count < diff)
                    257:                {
                    258:                        memcpy(start, valuestr, count);
                    259:                        start += count;
                    260:                        memmove(start, end, strlen(end) + 1);
                    261:                } else {
                    262:                        /* value won't fit to expression, expand string */
                    263:                        char *tmp;
                    264:                        inputlen += count-diff+1;
                    265:                        tmp = malloc(inputlen+1);
                    266:                        if (!tmp)
                    267:                        {
                    268:                                perror("ERROR: Input string alloc failed\n");
                    269:                                return NULL;
                    270:                        }
                    271: 
                    272:                        memcpy(tmp, input, start-input);
                    273:                        start = tmp+(start-input);
                    274:                        memcpy(start, valuestr, count);
                    275:                        start += count;
                    276:                        memcpy(start, end, strlen(end) + 1);
                    277: 
1.1.1.6 ! root      278:                        free(input);
1.1       root      279:                        input = tmp;
                    280:                }
                    281:        }
                    282:        /* no (more) expressions to evaluate */
                    283:        return input;
                    284: }
                    285: 
                    286: 
                    287: /**
                    288:  * Command: Store and restore emulation state
                    289:  */
                    290: static int DebugUI_DoMemorySnap(int argc, char *argv[])
                    291: {
                    292:        const char *file;
                    293: 
                    294:        if (argc > 1)
                    295:                file = argv[1];
                    296:        else
                    297:                file = ConfigureParams.Memory.szMemoryCaptureFileName;
                    298: 
                    299:        if (strcmp(argv[0], "stateload") == 0)
                    300:                MemorySnapShot_Restore(file, true);
                    301:        else
                    302:                MemorySnapShot_Capture(file, true);
                    303: 
                    304:        return DEBUGGER_CMDDONE;
                    305: }
                    306: 
                    307: 
                    308: /**
                    309:  * Command: Set command line and debugger options
                    310:  */
                    311: static int DebugUI_SetOptions(int argc, char *argv[])
                    312: {
                    313:        CNF_PARAMS current;
                    314:        static const struct {
                    315:                const char name[4];
                    316:                int base;
                    317:        } bases[] = {
                    318:                { "bin", 2 },
                    319:                { "dec", 10 },
                    320:                { "hex", 16 }
                    321:        };
                    322:        const char *arg;
                    323:        int i;
                    324:        
                    325:        if (argc < 2)
                    326:        {
                    327:                DebugUI_PrintCmdHelp(argv[0]);
                    328:                return DEBUGGER_CMDDONE;
                    329:        }
                    330:        arg = argv[1];
1.1.1.6 ! root      331: 
1.1       root      332:        for (i = 0; i < ARRAYSIZE(bases); i++)
                    333:        {
                    334:                if (strcasecmp(bases[i].name, arg) == 0)
                    335:                {
                    336:                        if (ConfigureParams.Debugger.nNumberBase != bases[i].base)
                    337:                        {
                    338:                                fprintf(stderr, "Switched default number base from %d to %d-based (%s) values.\n",
                    339:                                        ConfigureParams.Debugger.nNumberBase,
                    340:                                        bases[i].base, bases[i].name);
                    341:                                ConfigureParams.Debugger.nNumberBase = bases[i].base;
                    342:                        } else {
                    343:                                fprintf(stderr, "Already in '%s' mode.\n", bases[i].name);
                    344:                        }
                    345:                        return DEBUGGER_CMDDONE;
                    346:                }
                    347:        }
                    348: 
                    349:        /* get configuration changes */
                    350:        current = ConfigureParams;
                    351: 
                    352:        /* Parse and apply options */
1.1.1.2   root      353:        if (Opt_ParseParameters(argc, (const char * const *)argv))
1.1       root      354:        {
                    355:                ConfigureParams.Screen.bFullScreen = false;
                    356:                Change_CopyChangedParamsToConfiguration(&current, &ConfigureParams, false);
                    357:        }
                    358:        else
                    359:        {
                    360:                ConfigureParams = current;
                    361:        }
                    362: 
                    363:        return DEBUGGER_CMDDONE;
                    364: }
                    365: 
                    366: 
                    367: /**
                    368:  * Command: Set tracing
                    369:  */
                    370: static int DebugUI_SetTracing(int argc, char *argv[])
                    371: {
                    372:        const char *errstr;
                    373:        if (argc != 2)
                    374:        {
                    375:                DebugUI_PrintCmdHelp(argv[0]);
                    376:                return DEBUGGER_CMDDONE;
                    377:        }
                    378:        errstr = Log_SetTraceOptions(argv[1]);
                    379:        if (errstr && errstr[0])
                    380:                fprintf(stderr, "ERROR: %s\n", errstr);
                    381: 
                    382:        return DEBUGGER_CMDDONE;
                    383: }
                    384: 
                    385: 
                    386: /**
                    387:  * Command: Change Hatari work directory
                    388:  */
                    389: static int DebugUI_ChangeDir(int argc, char *argv[])
                    390: {
                    391:        if (argc == 2)
                    392:        {
                    393:                if (chdir(argv[1]) == 0)
                    394:                        return DEBUGGER_CMDDONE;
                    395:                perror("ERROR");
                    396:        }
                    397:        DebugUI_PrintCmdHelp(argv[0]);
                    398:        return DEBUGGER_CMDDONE;
                    399: }
                    400: 
                    401: 
                    402: /**
                    403:  * Command: Read debugger commands from a file
                    404:  */
                    405: static int DebugUI_CommandsFromFile(int argc, char *argv[])
                    406: {
                    407:        if (argc == 2)
1.1.1.6 ! root      408:                DebugUI_ParseFile(argv[1], true);
1.1       root      409:        else
                    410:                DebugUI_PrintCmdHelp(argv[0]);
                    411:        return DEBUGGER_CMDDONE;
                    412: }
                    413: 
                    414: 
                    415: /**
                    416:  * Command: Quit emulator
                    417:  */
                    418: static int DebugUI_QuitEmu(int nArgc, char *psArgv[])
                    419: {
                    420:        bQuitProgram = true;
                    421:        M68000_SetSpecial(SPCFLAG_BRK);   /* Assure that CPU core shuts down */
                    422:        return DEBUGGER_END;
                    423: }
                    424: 
                    425: 
                    426: /**
                    427:  * Print help text for one command
                    428:  */
                    429: void DebugUI_PrintCmdHelp(const char *psCmd)
                    430: {
                    431:        dbgcommand_t *cmd;
                    432:        int i;
                    433: 
                    434:        /* Search the command ... */
                    435:        for (cmd = debugCommand, i = 0; i < debugCommands; i++, cmd++)
                    436:        {
                    437:                if (!debugCommand[i].pFunction)
                    438:                        continue;
                    439:                if ((*(cmd->sShortName) && !strcmp(psCmd, cmd->sShortName))
                    440:                    || !strcmp(psCmd, cmd->sLongName))
                    441:                {
                    442:                        bool bShort = *(cmd->sShortName);
                    443:                        /* ... and print help text */
                    444:                        if (bShort)
                    445:                        {
                    446:                                fprintf(stderr, "'%s' or '%s' - %s\n",
                    447:                                        cmd->sLongName,
                    448:                                        cmd->sShortName,
                    449:                                        cmd->sShortDesc);
                    450:                        }
                    451:                        else
                    452:                        {
                    453:                                fprintf(stderr, "'%s' - %s\n",
                    454:                                        cmd->sLongName,
                    455:                                        cmd->sShortDesc);
                    456:                        }
                    457:                        fprintf(stderr, "Usage:  %s %s\n",
                    458:                                bShort ? cmd->sShortName : cmd->sLongName,
                    459:                                cmd->sUsage);
                    460:                        return;
                    461:                }
                    462:        }
                    463: 
                    464:        fprintf(stderr, "Unknown command '%s'\n", psCmd);
                    465: }
                    466: 
                    467: 
                    468: /**
                    469:  * Command: Print debugger help screen.
                    470:  */
                    471: static int DebugUI_Help(int nArgc, char *psArgs[])
                    472: {
                    473:        int i;
                    474: 
                    475:        if (nArgc > 1)
                    476:        {
                    477:                DebugUI_PrintCmdHelp(psArgs[1]);
                    478:                return DEBUGGER_CMDDONE;
                    479:        }
                    480: 
                    481:        for (i = 0; i < debugCommands; i++)
                    482:        {
                    483:                if (!debugCommand[i].pFunction)
                    484:                {
                    485:                        fprintf(stderr, "\n%s:\n", debugCommand[i].sLongName);
                    486:                        continue;
                    487:                }
                    488:                fprintf(stderr, " %12s (%2s) : %s\n", debugCommand[i].sLongName,
                    489:                        debugCommand[i].sShortName, debugCommand[i].sShortDesc);
                    490:        }
                    491: 
                    492:        fprintf(stderr,
                    493:                "\n"
                    494:                "If value is prefixed with '$', it's a hexadecimal, if with '#', it's\n"
                    495:                "a normal decimal, if with '%%', it's a binary decimal. Prefix can\n"
                    496:                "be skipped for numbers in the default number base (currently %d).\n"
                    497:                "\n"
                    498:                "Any expression given in quotes (within \"\"), will be evaluated\n"
                    499:                "before given to the debugger command.  Any register and symbol\n"
                    500:                "names in the expression are replaced by their values.\n"
                    501:                "\n"
                    502:                "Note that address ranges like '$fc0000-$fc0100' should have no\n"
                    503:                "spaces between the range numbers.\n"
                    504:                "\n"
                    505:                "'help <command>' gives more help.\n", ConfigureParams.Debugger.nNumberBase);
                    506:        return DEBUGGER_CMDDONE;
                    507: }
                    508: 
                    509: 
                    510: /**
                    511:  * Parse debug command and execute it.
                    512:  */
1.1.1.2   root      513: static int DebugUI_ParseCommand(const char *input_orig)
1.1       root      514: {
1.1.1.2   root      515:        char *psArgs[64], *input;
1.1       root      516:        const char *delim;
                    517:        static char sLastCmd[80] = { '\0' };
                    518:        int nArgc, cmd = -1;
                    519:        int i, retval;
                    520: 
1.1.1.2   root      521:        input = strdup(input_orig);
1.1       root      522:        psArgs[0] = strtok(input, " \t");
                    523: 
                    524:        if (psArgs[0] == NULL)
                    525:        {
                    526:                if (strlen(sLastCmd) > 0)
                    527:                        psArgs[0] = sLastCmd;
                    528:                else
1.1.1.2   root      529:                {
                    530:                        free(input);
1.1       root      531:                        return DEBUGGER_CMDDONE;
1.1.1.2   root      532:                }
1.1       root      533:        }
                    534: 
                    535:        /* Search the command ... */
                    536:        for (i = 0; i < debugCommands; i++)
                    537:        {
                    538:                if (!debugCommand[i].pFunction)
                    539:                        continue;
                    540:                if (!strcmp(psArgs[0], debugCommand[i].sShortName) ||
                    541:                    !strcmp(psArgs[0], debugCommand[i].sLongName))
                    542:                {
                    543:                        cmd = i;
                    544:                        break;
                    545:                }
                    546:        }
                    547:        if (cmd == -1)
                    548:        {
                    549:                fprintf(stderr, "Command '%s' not found.\n"
                    550:                        "Use 'help' to view a list of available commands.\n",
                    551:                        psArgs[0]);
1.1.1.2   root      552:                free(input);
1.1       root      553:                return DEBUGGER_CMDDONE;
                    554:        }
                    555: 
                    556:        if (debugCommand[cmd].bNoParsing)
                    557:                delim = "";
                    558:        else
                    559:                delim = " \t";
                    560: 
                    561:        /* Separate arguments and put the pointers into psArgs */
                    562:        for (nArgc = 1; nArgc < ARRAYSIZE(psArgs); nArgc++)
                    563:        {
                    564:                psArgs[nArgc] = strtok(NULL, delim);
                    565:                if (psArgs[nArgc] == NULL)
                    566:                        break;
                    567:        }
                    568: 
                    569:        if (!debugOutput) {
                    570:                /* make sure also calls from control.c work */
                    571:                DebugUI_SetLogDefault();
                    572:        }
                    573: 
                    574:        /* ... and execute the function */
                    575:        retval = debugCommand[i].pFunction(nArgc, psArgs);
                    576:        /* Save commando string if it can be repeated */
                    577:        if (retval == DEBUGGER_CMDCONT)
                    578:                strncpy(sLastCmd, psArgs[0], sizeof(sLastCmd));
                    579:        else
                    580:                sLastCmd[0] = '\0';
1.1.1.2   root      581:        free(input);
1.1       root      582:        return retval;
                    583: }
                    584: 
                    585: 
                    586: /* See "info:readline" e.g. in Konqueror for readline usage. */
                    587: 
                    588: /**
                    589:  * Readline match callback for long command name completion.
                    590:  * STATE = 0 -> different text from previous one.
                    591:  * Return next match or NULL if no matches.
                    592:  */
                    593: static char *DebugUI_MatchCommand(const char *text, int state)
                    594: {
                    595:        static int i, len;
                    596:        const char *name;
                    597:        
                    598:        if (!state)
                    599:        {
                    600:                /* first match */
                    601:                len = strlen(text);
                    602:                i = 0;
                    603:        }
                    604:        /* next match */
                    605:        while (i < debugCommands)
                    606:        {
                    607:                name = debugCommand[i].sLongName;
                    608:                if (debugCommand[i++].pFunction &&
                    609:                    strncmp(name, text, len) == 0)
                    610:                        return (strdup(name));
                    611:        }
                    612:        return NULL;
                    613: }
                    614: 
                    615: 
                    616: #if HAVE_LIBREADLINE
                    617: /**
                    618:  * Readline match callback returning last result.
                    619:  */
                    620: static char *DebugUI_MatchLast(const char *text, int state)
                    621: {
                    622:        if (state)
                    623:                return NULL;
                    624:        return strdup(lastResult);
                    625: }
                    626: 
                    627: /**
                    628:  * Readline completion callback. Returns matches.
                    629:  */
                    630: static char **DebugUI_Completion(const char *text, int a, int b)
                    631: {
                    632:        int i, cmd, quotes, end, start = 0;
                    633:        char *str, buf[32];
                    634:        size_t len;
                    635: 
                    636:        /* check where's the first word (ignore white space) */
                    637:        while (start < rl_point && isspace(rl_line_buffer[start]))
                    638:                start++;
                    639:        end = start;
                    640:        while (end < rl_point && !isspace(rl_line_buffer[end]))
                    641:                end++;
                    642: 
                    643:        if (end >= rl_point)
                    644:                /* first word on line */
                    645:                return rl_completion_matches(text, DebugUI_MatchCommand);
                    646:        
                    647:        /* complete '$' with last result? */
                    648:        if (lastResult[0] && rl_line_buffer[rl_point-1] == '$')
                    649:                return rl_completion_matches(text, DebugUI_MatchLast);
                    650: 
                    651:        /* check which command args are to be completed */
                    652:        len = end - start;
                    653:        if (len >= sizeof(buf))
                    654:                len = sizeof(buf)-1;
                    655:        memcpy(buf, &(rl_line_buffer[start]), len);
                    656:        buf[len] = '\0';
                    657: 
                    658:        /* expression completion needed (= open quote)? */
                    659:        str = strchr(&(rl_line_buffer[end]), '"');
                    660:        quotes = 0;
                    661:        while (str)
                    662:        {
                    663:                quotes++;
                    664:                str = strchr(str+1, '"');
                    665:        }
                    666:        if (quotes & 1)
                    667:        {
                    668:                if (DebugUI_IsForDsp(buf))
                    669:                        return rl_completion_matches(text, Symbols_MatchDspAddress);
                    670:                return rl_completion_matches(text, Symbols_MatchCpuAddress);
                    671:        }
                    672: 
                    673:        /* do command argument completion */
                    674:        cmd = -1;
                    675:        for (i = 0; i < debugCommands; i++)
                    676:        {
                    677:                if (!debugCommand[i].pFunction)
                    678:                        continue;
                    679:                if (!strcmp(buf, debugCommand[i].sShortName) ||
                    680:                    !strcmp(buf, debugCommand[i].sLongName))
                    681:                {
                    682:                        cmd = i;
                    683:                        break;
                    684:                }
                    685:        }
                    686:        if (cmd < 0)
                    687:        {
                    688:                rl_attempted_completion_over = true;
                    689:                return NULL;
                    690:        }
                    691:        if (debugCommand[cmd].pMatch)
                    692:                return rl_completion_matches(text, debugCommand[cmd].pMatch);
                    693:        else
                    694:                return rl_completion_matches(text, rl_filename_completion_function);
                    695: }
                    696: 
1.1.1.6 ! root      697: /**
        !           698:  * Add non-repeated command to readline history
        !           699:  * and free the given string
        !           700:  *  @return    its new value
        !           701:  */
        !           702: static char *DebugUI_FreeCommand(char *input)
        !           703: {
        !           704:        if (input && *input)
        !           705:        {
        !           706:                HIST_ENTRY *hist = history_get(history_length);
        !           707:                /* don't store duplicate successive entries */
        !           708:                if (!hist || !hist->line || strcmp(hist->line, input) != 0)
        !           709:                {
        !           710:                        add_history(input);
        !           711:                }
        !           712:                free(input);
        !           713:        }
        !           714:        return NULL;
        !           715: }
1.1       root      716: 
                    717: /**
                    718:  * Read a command line from the keyboard and return a pointer to the string.
1.1.1.2   root      719:  * Only string returned by this function can be given for it as argument!
                    720:  * The string will be stored into command history buffer.
1.1.1.6 ! root      721:  * @return     Pointer to the string which should be given back to this
        !           722:  *              function or DebugUI_FreeCommand() for re-use/history.
        !           723:  *              Returns NULL when error occurred.
1.1       root      724:  */
1.1.1.2   root      725: static char *DebugUI_GetCommand(char *input)
1.1       root      726: {
                    727:        /* Allow conditional parsing of the ~/.inputrc file. */
                    728:        rl_readline_name = "Hatari";
                    729:        
                    730:        /* Tell the completer that we want a crack first. */
                    731:        rl_attempted_completion_function = DebugUI_Completion;
1.1.1.6 ! root      732:        input = DebugUI_FreeCommand(input);
1.1.1.2   root      733:        return Str_Trim(readline("> "));
1.1       root      734: }
                    735: 
                    736: #else /* !HAVE_LIBREADLINE */
                    737: 
                    738: /**
1.1.1.6 ! root      739:  * Free Command input string
        !           740:  *  @return    its new value
        !           741:  */
        !           742: static char *DebugUI_FreeCommand(char *input)
        !           743: {
        !           744:        if (input)
        !           745:                free(input);
        !           746:        return NULL;
        !           747: }
        !           748: 
        !           749: /**
1.1       root      750:  * Read a command line from the keyboard and return a pointer to the string.
1.1.1.2   root      751:  * Only string returned by this function can be given for it as argument!
1.1.1.6 ! root      752:  * @return     Pointer to the string which should be given back to this
        !           753:  *              function or DebugUI_FreeCommand() for re-use/freeing.
        !           754:  *              Returns NULL when error occurred.
1.1       root      755:  */
1.1.1.2   root      756: static char *DebugUI_GetCommand(char *input)
1.1       root      757: {
                    758:        fprintf(stderr, "> ");
                    759:        if (!input)
1.1.1.2   root      760:        {
                    761:                input = malloc(256);
                    762:                assert(input);
                    763:        }
1.1       root      764:        input[0] = '\0';
                    765:        if (fgets(input, 256, stdin) == NULL)
                    766:        {
                    767:                free(input);
                    768:                return NULL;
                    769:        }
                    770:        return Str_Trim(input);
                    771: }
                    772: 
                    773: #endif /* !HAVE_LIBREADLINE */
                    774: 
                    775: 
                    776: static const dbgcommand_t uicommand[] =
                    777: {
                    778:        { NULL, NULL, "Generic commands", NULL, NULL, NULL, false },
                    779:        /* NULL as match function will complete file names */
                    780:        { DebugUI_ChangeDir, NULL,
                    781:          "cd", "",
                    782:          "change directory",
                    783:          "<directory>\n"
                    784:          "\tChange Hatari work directory.",
                    785:          false },
                    786:        { DebugUI_Evaluate, Symbols_MatchCpuAddress,
                    787:          "evaluate", "e",
                    788:          "evaluate an expression",
                    789:          "<expression>\n"
                    790:          "\tEvaluate an expression and show the result.  Expression can\n"
                    791:          "\tinclude CPU register and symbol names, those are replaced\n"
                    792:          "\tby their values. Supported operators in expressions are,\n"
                    793:          "\tin the decending order of precedence:\n"
                    794:          "\t\t(), +, -, ~, *, /, +, -, >>, <<, ^, &, |\n"
1.1.1.2   root      795:          "\tParenthesis will fetch a _long_ value from the address\n"
                    796:          "\tto what the value inside it evaluates to. Prefixes can be\n"
                    797:          "\tused only in start of line or parenthesis.\n"
1.1       root      798:          "\tFor example:\n"
1.1.1.2   root      799:          "\t\t~%101 & $f0f0f ^ (d0 + 0x21)\n"
1.1       root      800:          "\tResult value is shown as binary, decimal and hexadecimal.\n"
                    801:          "\tAfter this, '$' will TAB-complete to last result value.",
                    802:          true },
                    803:        { DebugUI_Help, DebugUI_MatchCommand,
                    804:          "help", "h",
                    805:          "print help",
                    806:          "[command]\n"
                    807:          "\tPrint help text for available commands.",
                    808:          false },
1.1.1.3   root      809:        { History_Parse, NULL,
                    810:          "history", "hi",
1.1.1.6 ! root      811:          "show last CPU/DSP PC values & executed instructions",
        !           812:          "cpu|dsp|on|off|<count>\n"
        !           813:          "\t'cpu' and 'dsp' tracks instruction history for just given\n"
        !           814:          "\t processor, 'on' tracks them both, 'off' will disable history.\n"
        !           815:          "\tCount will show (at max) given number of last saved PC values\n"
1.1.1.3   root      816:          "\tand instructions at corresponding RAM addresses.",
                    817:          false },
1.1       root      818:        { DebugInfo_Command, DebugInfo_MatchInfo,
                    819:          "info", "i",
                    820:          "show machine/OS information",
                    821:          "[subject [arg]]\n"
                    822:          "\tPrint information on requested subject or list them if\n"
                    823:          "\tno subject given.",
                    824:          false },
                    825:        { DebugInfo_Command, DebugInfo_MatchLock,
                    826:          "lock", "",
1.1.1.2   root      827:          "specify information to show on entering the debugger",
1.1       root      828:          "[subject [args]]\n"
1.1.1.2   root      829:          "\tLock what information should be shown every time debugger\n"
                    830:          "\tis entered, or list available options if no subject's given.",
1.1       root      831:          false },
                    832:        { DebugUI_SetLogFile, NULL,
                    833:          "logfile", "f",
                    834:          "open or close log file",
                    835:          "[filename]\n"
                    836:          "\tOpen log file, no argument closes the log file. Output of\n"
                    837:          "\tregister & memory dumps and disassembly will be written to it.",
                    838:          false },
                    839:        { DebugUI_CommandsFromFile, NULL,
                    840:          "parse", "p",
                    841:          "get debugger commands from file",
                    842:          "[filename]\n"
                    843:          "\tRead debugger commands from given file and do them.",
                    844:          false },
                    845:        { DebugUI_SetOptions, Opt_MatchOption,
                    846:          "setopt", "o",
                    847:          "set Hatari command line and debugger options",
1.1.1.6 ! root      848:          "[bin|dec|hex|<command line options>]\n"
1.1       root      849:          "\tSet Hatari options. For example to enable exception catching,\n"
                    850:          "\tuse following command line option: 'setopt --debug'. Special\n"
                    851:          "\t'bin', 'dec' and 'hex' arguments change the default number base\n"
                    852:          "\tused in debugger.",
                    853:          false },
                    854:        { DebugUI_DoMemorySnap, NULL,
                    855:          "stateload", "",
                    856:          "restore emulation state",
                    857:          "[filename]\n"
                    858:          "\tRestore emulation snapshot from default or given file",
                    859:          false },
                    860:        { DebugUI_DoMemorySnap, NULL,
                    861:          "statesave", "",
                    862:          "save emulation state",
                    863:          "[filename]\n"
                    864:          "\tSave emulation snapshot to default or given file",
                    865:          false },
                    866:        { DebugUI_SetTracing, Log_MatchTrace,
                    867:          "trace", "t",
                    868:          "select Hatari tracing settings",
                    869:          "[set1,set2...]\n"
1.1.1.2   root      870:          "\tSelect Hatari tracing settings. 'help' shows all the available\n"
                    871:          "\tsettings.  For example, to enable CPU disassembly and VBL\n"
                    872:          "\ttracing, use:\n\t\ttrace cpu_disasm,video_hbl",
1.1       root      873:          false },
                    874:        { DebugUI_QuitEmu, NULL,
                    875:          "quit", "q",
                    876:          "quit emulator",
                    877:          "\n"
                    878:          "\tLeave debugger and quit emulator.",
                    879:          false }
                    880: };
                    881: 
                    882: 
                    883: /**
                    884:  * Debugger user interface initialization.
                    885:  */
                    886: void DebugUI_Init(void)
                    887: {
                    888:        const dbgcommand_t *cpucmd, *dspcmd;
                    889:        int cpucmds, dspcmds;
                    890: 
                    891:        /* already intialized? */
                    892:        if (debugCommands)
                    893:                return;
                    894: 
                    895:        /* if you want disassembly or memdumping to start/continue from
                    896:         * specific address, you can set them in these functions.
                    897:         */
                    898:        dspcmds = DebugDsp_Init(&dspcmd);
                    899:        cpucmds = DebugCpu_Init(&cpucmd);
                    900: 
                    901:        /* on first time copy the command structures to a single table */
                    902:        debugCommands = ARRAYSIZE(uicommand);
                    903:        debugCommand = malloc(sizeof(dbgcommand_t) * (dspcmds + cpucmds + debugCommands));
                    904:        assert(debugCommand);
                    905:        
                    906:        memcpy(debugCommand, uicommand, sizeof(dbgcommand_t) * debugCommands);
                    907:        memcpy(&debugCommand[debugCommands], cpucmd, sizeof(dbgcommand_t) * cpucmds);
                    908:        debugCommands += cpucmds;
                    909:        memcpy(&debugCommand[debugCommands], dspcmd, sizeof(dbgcommand_t) * dspcmds);
                    910:        debugCommands += dspcmds;
                    911: 
                    912:        if (parseFileName)
1.1.1.6 ! root      913:                DebugUI_ParseFile(parseFileName, true);
1.1       root      914: }
                    915: 
                    916: 
                    917: /**
1.1.1.2   root      918:  * Set debugger commands file during Hatari startup before things
                    919:  * needed by the debugger are initialized so that it can be parsed
                    920:  * when debugger itself gets initialized.
1.1       root      921:  * Return true if file exists, false otherwise.
                    922:  */
                    923: bool DebugUI_SetParseFile(const char *path)
                    924: {
                    925:        if (File_Exists(path))
                    926:        {
                    927:                parseFileName = path;
                    928:                return true;
                    929:        }
                    930:        fprintf(stderr, "ERROR: debugger input file '%s' missing.\n", path);
                    931:        return false;
                    932: }
                    933: 
                    934: 
                    935: /**
                    936:  * Debugger user interface main function.
                    937:  */
1.1.1.3   root      938: void DebugUI(debug_reason_t reason)
1.1       root      939: {
                    940:        int cmdret, alertLevel;
1.1.1.2   root      941:        char *expCmd, *psCmd = NULL;
1.1       root      942:        static const char *welcome =
                    943:                "\n----------------------------------------------------------------------"
                    944:                "\nYou have entered debug mode. Type c to continue emulation, h for help.\n";
1.1.1.3   root      945: 
                    946:        History_Mark(reason);
                    947: 
1.1       root      948:        if (bInFullScreen)
                    949:                Screen_ReturnFromFullScreen();
                    950: 
                    951:        DebugUI_Init();
                    952: 
                    953:        if (welcome)
                    954:        {
                    955:                fputs(welcome, stderr);
                    956:                welcome = NULL;
                    957:        }
                    958:        DebugCpu_InitSession();
                    959:        DebugDsp_InitSession();
                    960:        DebugInfo_ShowSessionInfo();
                    961: 
                    962:        /* override paused message so that user knows to look into console
                    963:         * on how to continue in case he invoked the debugger by accident.
                    964:         */
                    965:        Statusbar_AddMessage("Console Debugger", 100);
                    966:        Statusbar_Update(sdlscrn);
                    967: 
                    968:        /* disable normal GUI alerts while on console */
                    969:        alertLevel = Log_SetAlertLevel(LOG_FATAL);
                    970: 
                    971:        cmdret = DEBUGGER_CMDDONE;
                    972:        do
                    973:        {
1.1.1.2   root      974:                /* Read command from the keyboard and give previous
                    975:                 * command for freeing / adding to history
                    976:                 */
                    977:                psCmd = DebugUI_GetCommand(psCmd);
1.1       root      978:                if (!psCmd)
                    979:                        break;
                    980: 
1.1.1.6 ! root      981:                /* returns new expression expanded string */
1.1.1.2   root      982:                if (!(expCmd = DebugUI_EvaluateExpressions(psCmd)))
1.1       root      983:                        continue;
                    984: 
                    985:                /* Parse and execute the command string */
1.1.1.2   root      986:                cmdret = DebugUI_ParseCommand(expCmd);
1.1.1.6 ! root      987:                free(expCmd);
1.1       root      988:        }
                    989:        while (cmdret != DEBUGGER_END);
                    990: 
1.1.1.6 ! root      991:        /* free exit command */
        !           992:        psCmd = DebugUI_FreeCommand(psCmd);
1.1.1.2   root      993: 
1.1       root      994:        Log_SetAlertLevel(alertLevel);
                    995:        DebugUI_SetLogDefault();
                    996: 
                    997:        DebugCpu_SetDebugging();
                    998:        DebugDsp_SetDebugging();
                    999: }
                   1000: 
                   1001: 
                   1002: /**
1.1.1.6 ! root     1003:  * Read debugger commands from a file.  If 'reinit' is set
        !          1004:  * (as it normally should), reinitialize breakpoints etc.
        !          1005:  * afterwards. return false for error, true for success.
1.1       root     1006:  */
1.1.1.6 ! root     1007: bool DebugUI_ParseFile(const char *path, bool reinit)
1.1       root     1008: {
1.1.1.2   root     1009:        char *olddir, *dir, *cmd, *input, *expanded, *slash;
1.1       root     1010:        FILE *fp;
                   1011: 
                   1012:        fprintf(stderr, "Reading debugger commands from '%s'...\n", path);
                   1013:        if (!(fp = fopen(path, "r")))
                   1014:        {
                   1015:                perror("ERROR");
                   1016:                return false;
                   1017:        }
                   1018: 
                   1019:        /* change to directory where the debugger file resides */
1.1.1.2   root     1020:        olddir = NULL;
1.1       root     1021:        dir = strdup(path);
                   1022:        slash = strrchr(dir, PATHSEP);
                   1023:        if (slash)
                   1024:        {
1.1.1.2   root     1025:                olddir = malloc(FILENAME_MAX);
                   1026:                if (olddir)
                   1027:                {
                   1028:                        if (!getcwd(olddir, FILENAME_MAX))
                   1029:                                strcpy(olddir, ".");
                   1030:                }
1.1       root     1031:                *slash = '\0';
1.1.1.2   root     1032:                if (chdir(dir) != 0)
1.1       root     1033:                {
                   1034:                        perror("ERROR");
1.1.1.2   root     1035:                        if (olddir)
                   1036:                                free(olddir);
1.1       root     1037:                        free(dir);
1.1.1.6 ! root     1038:                        fclose(fp);
1.1       root     1039:                        return false;
                   1040:                }
1.1.1.2   root     1041:                fprintf(stderr, "Changed to input file dir '%s'.\n", dir);
1.1       root     1042:        }
                   1043:        free(dir);
                   1044: 
                   1045:        input = NULL;
                   1046:        for (;;)
                   1047:        {
                   1048:                if (!input)
                   1049:                {
                   1050:                        input = malloc(256);
                   1051:                        assert(input);
                   1052:                }
                   1053:                if (!fgets(input, 256, fp))
                   1054:                        break;
                   1055: 
1.1.1.2   root     1056:                /* ignore empty and comment lines */
                   1057:                cmd = Str_Trim(input);
                   1058:                if (!*cmd || *cmd == '#')
1.1       root     1059:                        continue;
                   1060: 
1.1.1.2   root     1061:                /* returns new string if input needed expanding! */
                   1062:                expanded = DebugUI_EvaluateExpressions(input);
                   1063:                if (!expanded)
                   1064:                        continue;
                   1065: 
                   1066:                cmd = Str_Trim(expanded);
                   1067:                fprintf(stderr, "> %s\n", cmd);
                   1068:                DebugUI_ParseCommand(cmd);
1.1.1.6 ! root     1069:                free(expanded);
1.1       root     1070:        }
                   1071: 
                   1072:        free(input);
1.1.1.6 ! root     1073:        fclose(fp);
        !          1074: 
1.1.1.2   root     1075:        if (olddir)
                   1076:        {
                   1077:                if (chdir(olddir) != 0)
                   1078:                        perror("ERROR");
                   1079:                else
                   1080:                        fprintf(stderr, "Changed back to '%s' dir.\n", olddir);
                   1081:                free(olddir);
                   1082:        }
1.1       root     1083: 
1.1.1.6 ! root     1084:        if (reinit)
        !          1085:        {
        !          1086:                DebugCpu_SetDebugging();
        !          1087:                DebugDsp_SetDebugging();
        !          1088:        }
1.1       root     1089:        return true;
                   1090: }
                   1091: 
                   1092: 
                   1093: /**
1.1.1.6 ! root     1094:  * Remote/parallel debugger line usage API.
1.1       root     1095:  * Return false for failed command, true for success.
                   1096:  */
1.1.1.6 ! root     1097: bool DebugUI_ParseLine(const char *input)
1.1       root     1098: {
1.1.1.6 ! root     1099:        char *expanded;
        !          1100:        int ret = 0;
1.1       root     1101: 
                   1102:        DebugUI_Init();
                   1103: 
1.1.1.6 ! root     1104:        /* returns new string if input needed expanding! */
        !          1105:        expanded = DebugUI_EvaluateExpressions(input);
        !          1106:        if (expanded)
        !          1107:        {
        !          1108:                fprintf(stderr, "> %s\n", expanded);
        !          1109:                ret = DebugUI_ParseCommand(expanded);
        !          1110:                free(expanded);
1.1       root     1111: 
1.1.1.6 ! root     1112:                DebugCpu_SetDebugging();
        !          1113:                DebugDsp_SetDebugging();
        !          1114:        }
1.1       root     1115:        return (ret == DEBUGGER_CMDDONE);
                   1116: }

unix.superglobalmegacorp.com

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