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

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

unix.superglobalmegacorp.com

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