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

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>
1.1.1.11! root       16: #include <SDL.h>
1.1       root       17: 
                     18: #include "config.h"
                     19: 
                     20: #if HAVE_LIBREADLINE
                     21: #include <readline/readline.h>
                     22: #include <readline/history.h>
                     23: #endif
                     24: 
                     25: #include "main.h"
                     26: #include "change.h"
                     27: #include "configuration.h"
                     28: #include "file.h"
                     29: #include "log.h"
                     30: #include "m68000.h"
                     31: #include "memorySnapShot.h"
                     32: #include "options.h"
1.1.1.8   root       33: #include "reset.h"
1.1       root       34: #include "screen.h"
                     35: #include "statusbar.h"
                     36: #include "str.h"
                     37: 
                     38: #include "debug_priv.h"
                     39: #include "breakcond.h"
                     40: #include "debugcpu.h"
                     41: #include "debugdsp.h"
1.1.1.6   root       42: #include "68kDisass.h"
1.1       root       43: #include "debugInfo.h"
                     44: #include "debugui.h"
                     45: #include "evaluate.h"
1.1.1.3   root       46: #include "history.h"
1.1       root       47: #include "symbols.h"
1.1.1.9   root       48: #include "vars.h"
1.1       root       49: 
1.1.1.5   root       50: FILE *debugOutput;
1.1       root       51: 
                     52: static dbgcommand_t *debugCommand;
                     53: static int debugCommands;
                     54: 
                     55: /* stores last 'e' command result as hex, used for TAB-completion */
                     56: static char lastResult[10];
                     57: 
1.1.1.6   root       58: /* parse debugger commands from here on init */
1.1       root       59: static const char *parseFileName;
                     60: 
1.1.1.10  root       61: /* to which directory to change after (potentially recursed) scripts parsing finishes */
                     62: static char *finalDir;
                     63: 
1.1       root       64: 
                     65: /**
                     66:  * Save/Restore snapshot of debugging session variables
                     67:  */
                     68: void DebugUI_MemorySnapShot_Capture(const char *path, bool bSave)
                     69: {
                     70:        char *filename;
                     71: 
                     72:        filename = malloc(strlen(path) + strlen(".debug") + 1);
                     73:        assert(filename);
                     74:        strcpy(filename, path);
                     75:        strcat(filename, ".debug");
                     76:        
                     77:        if (bSave)
                     78:        {
                     79:                /* save breakpoints as debugger input file */
                     80:                BreakCond_Save(filename);
                     81:        }
                     82:        else
                     83:        {
                     84:                /* remove current CPU and DSP breakpoints */
                     85:                BreakCond_Command("all", false);
                     86:                BreakCond_Command("all", true);
                     87: 
                     88:                if (File_Exists(filename))
                     89:                {
                     90:                        /* and parse back the saved breakpoints */
1.1.1.6   root       91:                        DebugUI_ParseFile(filename, true);
1.1       root       92:                }
                     93:        }
                     94:        free(filename);
                     95: }
                     96: 
                     97: 
                     98: /**
                     99:  * Close a log file if open, and set it to default stream.
                    100:  */
                    101: static void DebugUI_SetLogDefault(void)
                    102: {
                    103:        if (debugOutput != stderr)
                    104:        {
                    105:                if (debugOutput)
                    106:                {
                    107:                        File_Close(debugOutput);
                    108:                        fprintf(stderr, "Debug log closed.\n");
                    109:                }
                    110:                debugOutput = stderr;
                    111:        }
                    112: }
                    113: 
                    114: 
                    115: /**
                    116:  * Open (or close) given log file.
                    117:  */
                    118: static int DebugUI_SetLogFile(int nArgc, char *psArgs[])
                    119: {
1.1.1.9   root      120:        if (debugOutput != stderr)
                    121:        {
                    122:                fprintf(stderr, "Debug log closed.\n");
                    123:                File_Close(debugOutput);
                    124:        }
                    125:        debugOutput = stderr;
1.1       root      126: 
                    127:        if (nArgc > 1)
1.1.1.9   root      128:        {
                    129:                if ((debugOutput = File_Open(psArgs[1], "w")))
                    130:                {
                    131:                        fprintf(stderr, "Debug log '%s' opened.\n", psArgs[1]);
                    132:                }
                    133:                else
                    134:                {
                    135:                        fprintf(stderr, "Debug log '%s' opening FAILED.\n", psArgs[1]);
                    136:                        debugOutput = stderr;
                    137:                }
                    138:        }
1.1       root      139:        return DEBUGGER_CMDDONE;
                    140: }
                    141: 
                    142: 
                    143: /**
                    144:  * Helper to print given value in all supported number bases
                    145:  */
                    146: static void DebugUI_PrintValue(Uint32 value)
                    147: {
                    148:        bool one, ones;
                    149:        int bit;
                    150: 
                    151:        fputs("= %", stderr);
                    152:        ones = false;
                    153:        for (bit = 31; bit >= 0; bit--)
                    154:        {
1.1.1.11! root      155:                one = value & (1U << bit);
1.1       root      156:                if (one || ones)
                    157:                {
                    158:                        fputc(one ? '1':'0', stderr);
                    159:                        ones = true;
                    160:                }
                    161:        }
                    162:        if (!ones)
                    163:                fputc('0', stderr);
                    164:        if (value & 0x80000000)
                    165:                fprintf(stderr, " (bin), #%u/%d (dec), $%x (hex)\n", value, (int)value, value);
                    166:        else
                    167:                fprintf(stderr, " (bin), #%u (dec), $%x (hex)\n", value, value);
                    168: 
                    169:        sprintf(lastResult, "%x", value);
                    170: }
                    171: 
                    172: 
                    173: /**
                    174:  * Commmand: Evaluate an expression with CPU reg and symbol parsing.
                    175:  */
                    176: static int DebugUI_Evaluate(int nArgc, char *psArgs[])
                    177: {
                    178:        const char *errstr, *expression = (const char *)psArgs[1];
                    179:        Uint32 result;
                    180:        int offset;
                    181: 
                    182:        if (nArgc < 2)
                    183:        {
1.1.1.7   root      184:                return DebugUI_PrintCmdHelp(psArgs[0]);
1.1       root      185:        }
                    186: 
                    187:        errstr = Eval_Expression(expression, &result, &offset, false);
                    188:        if (errstr)
                    189:                fprintf(stderr, "ERROR in the expression:\n'%s'\n%*c-%s\n",
                    190:                        expression, offset+3, '^', errstr);
                    191:        else
                    192:                DebugUI_PrintValue(result);
                    193:        return DEBUGGER_CMDDONE;
                    194: }
                    195: 
                    196: 
                    197: /**
                    198:  * Check whether given string is a two letter command starting with 'd'
                    199:  * or a long command starting with "dsp". String should be trimmed.
                    200:  * Return true if given string is command for DSP, false otherwise.
                    201:  */
                    202: static bool DebugUI_IsForDsp(const char *cmd)
                    203: {
1.1.1.7   root      204:        return ((cmd[0] == 'd' && isalpha((unsigned char)cmd[1])
                    205:                               && !isalpha((unsigned char)cmd[2]))
                    206:                || strncmp(cmd, "dsp", 3) == 0);
1.1       root      207: }
                    208: 
                    209: /**
1.1.1.6   root      210:  * Evaluate everything include within single or double quotes ("" or '')
                    211:  * and replace them with the result.
                    212:  * Caller needs to free the returned string separately.
1.1       root      213:  * 
1.1.1.6   root      214:  * Return new string with expressions (potentially) expanded, or
1.1.1.2   root      215:  * NULL when there's an error in the expression.
1.1       root      216:  */
1.1.1.6   root      217: static char *DebugUI_EvaluateExpressions(const char *initial)
1.1       root      218: {
                    219:        int offset, count, diff, inputlen;
1.1.1.6   root      220:        char *end, *start, *input;
1.1       root      221:        const char *errstr;
                    222:        char valuestr[12];
                    223:        Uint32 value;
                    224:        bool fordsp;
                    225: 
                    226:        /* input is split later on, need to save len here */
1.1.1.6   root      227:        input = strdup(initial);
                    228:        if (!input)
                    229:        {
                    230:                perror("ERROR: Input string alloc failed\n");
                    231:                return NULL;
                    232:        }
1.1       root      233:        fordsp = DebugUI_IsForDsp(input);
                    234:        inputlen = strlen(input);
1.1.1.6   root      235:        start = input;
                    236: 
                    237:        while ((count = strcspn(start, "\"'")) && *(start+count))
1.1       root      238:        {
1.1.1.6   root      239:                start += count;
                    240:                end = strchr(start+1, *start);
1.1       root      241:                if (!end)
                    242:                {
1.1.1.6   root      243:                        fprintf(stderr, "ERROR: matching '%c' missing from '%s'!\n", *start, start);
1.1.1.7   root      244:                        free(input);
1.1       root      245:                        return NULL;
                    246:                }
                    247:                
                    248:                if (end == start+1)
                    249:                {
                    250:                        /* empty expression */
                    251:                        memmove(start, start+2, strlen(start+2)+1);
                    252:                        continue;
                    253:                }
                    254: 
                    255:                *end = '\0';
                    256:                errstr = Eval_Expression(start+1, &value, &offset, fordsp);
                    257:                if (errstr) {
1.1.1.6   root      258:                        *end = *start; /* restore expression mark */
1.1       root      259:                        fprintf(stderr, "Expression ERROR:\n'%s'\n%*c-%s\n",
                    260:                                input, (int)(start-input)+offset+3, '^', errstr);
1.1.1.7   root      261:                        free(input);
1.1       root      262:                        return NULL;
                    263:                }
                    264:                end++;
1.1.1.6   root      265: 
1.1       root      266:                count = sprintf(valuestr, "$%x", value);
1.1.1.6   root      267:                fprintf(stderr, "- '%s' -> %s\n", start+1, valuestr);
1.1       root      268: 
                    269:                diff = end-start;
                    270:                if (count < diff)
                    271:                {
                    272:                        memcpy(start, valuestr, count);
                    273:                        start += count;
                    274:                        memmove(start, end, strlen(end) + 1);
                    275:                } else {
                    276:                        /* value won't fit to expression, expand string */
                    277:                        char *tmp;
                    278:                        inputlen += count-diff+1;
                    279:                        tmp = malloc(inputlen+1);
                    280:                        if (!tmp)
                    281:                        {
                    282:                                perror("ERROR: Input string alloc failed\n");
1.1.1.7   root      283:                                free(input);
1.1       root      284:                                return NULL;
                    285:                        }
                    286: 
                    287:                        memcpy(tmp, input, start-input);
                    288:                        start = tmp+(start-input);
                    289:                        memcpy(start, valuestr, count);
                    290:                        start += count;
                    291:                        memcpy(start, end, strlen(end) + 1);
                    292: 
1.1.1.6   root      293:                        free(input);
1.1       root      294:                        input = tmp;
                    295:                }
                    296:        }
                    297:        /* no (more) expressions to evaluate */
                    298:        return input;
                    299: }
                    300: 
                    301: 
                    302: /**
                    303:  * Command: Store and restore emulation state
                    304:  */
                    305: static int DebugUI_DoMemorySnap(int argc, char *argv[])
                    306: {
                    307:        const char *file;
                    308: 
                    309:        if (argc > 1)
                    310:                file = argv[1];
                    311:        else
                    312:                file = ConfigureParams.Memory.szMemoryCaptureFileName;
                    313: 
1.1.1.11! root      314:        /* [NP] TODO : we need to restart emulation to complete restore, */
        !           315:        /* it can't be done immediately. Try to call m68k_go() and go back automatically to debugger ? */
1.1       root      316:        if (strcmp(argv[0], "stateload") == 0)
                    317:                MemorySnapShot_Restore(file, true);
                    318:        else
1.1.1.11! root      319:                MemorySnapShot_Capture_Immediate(file, true);
1.1       root      320: 
                    321:        return DEBUGGER_CMDDONE;
                    322: }
                    323: 
                    324: 
                    325: /**
                    326:  * Command: Set command line and debugger options
                    327:  */
                    328: static int DebugUI_SetOptions(int argc, char *argv[])
                    329: {
                    330:        CNF_PARAMS current;
                    331:        static const struct {
                    332:                const char name[4];
                    333:                int base;
                    334:        } bases[] = {
                    335:                { "bin", 2 },
                    336:                { "dec", 10 },
                    337:                { "hex", 16 }
                    338:        };
                    339:        const char *arg;
                    340:        int i;
                    341:        
                    342:        if (argc < 2)
                    343:        {
1.1.1.7   root      344:                return DebugUI_PrintCmdHelp(argv[0]);
1.1       root      345:        }
                    346:        arg = argv[1];
1.1.1.6   root      347: 
1.1.1.9   root      348:        for (i = 0; i < ARRAY_SIZE(bases); i++)
1.1       root      349:        {
                    350:                if (strcasecmp(bases[i].name, arg) == 0)
                    351:                {
                    352:                        if (ConfigureParams.Debugger.nNumberBase != bases[i].base)
                    353:                        {
                    354:                                fprintf(stderr, "Switched default number base from %d to %d-based (%s) values.\n",
                    355:                                        ConfigureParams.Debugger.nNumberBase,
                    356:                                        bases[i].base, bases[i].name);
                    357:                                ConfigureParams.Debugger.nNumberBase = bases[i].base;
                    358:                        } else {
                    359:                                fprintf(stderr, "Already in '%s' mode.\n", bases[i].name);
                    360:                        }
                    361:                        return DEBUGGER_CMDDONE;
                    362:                }
                    363:        }
                    364: 
                    365:        /* get configuration changes */
                    366:        current = ConfigureParams;
                    367: 
                    368:        /* Parse and apply options */
1.1.1.2   root      369:        if (Opt_ParseParameters(argc, (const char * const *)argv))
1.1       root      370:        {
                    371:                ConfigureParams.Screen.bFullScreen = false;
                    372:                Change_CopyChangedParamsToConfiguration(&current, &ConfigureParams, false);
                    373:        }
                    374:        else
                    375:        {
                    376:                ConfigureParams = current;
                    377:        }
                    378: 
                    379:        return DEBUGGER_CMDDONE;
                    380: }
                    381: 
                    382: 
                    383: /**
                    384:  * Command: Set tracing
                    385:  */
                    386: static int DebugUI_SetTracing(int argc, char *argv[])
                    387: {
                    388:        const char *errstr;
                    389:        if (argc != 2)
                    390:        {
1.1.1.7   root      391:                return DebugUI_PrintCmdHelp(argv[0]);
1.1       root      392:        }
                    393:        errstr = Log_SetTraceOptions(argv[1]);
                    394:        if (errstr && errstr[0])
                    395:                fprintf(stderr, "ERROR: %s\n", errstr);
                    396: 
                    397:        return DEBUGGER_CMDDONE;
                    398: }
                    399: 
                    400: 
                    401: /**
                    402:  * Command: Change Hatari work directory
                    403:  */
                    404: static int DebugUI_ChangeDir(int argc, char *argv[])
                    405: {
1.1.1.10  root      406:        if (argc == 3 && strcmp("-f", argv[2]) == 0)
                    407:        {
                    408:                if (finalDir)
                    409:                        free(finalDir);
                    410:                finalDir = strdup(argv[1]);
                    411:                fprintf(stderr, "Will switch to '%s' dir after all scripts have finished.\n", argv[1]);
                    412:                return DEBUGGER_CMDDONE;
                    413:        }
1.1       root      414:        if (argc == 2)
                    415:        {
                    416:                if (chdir(argv[1]) == 0)
                    417:                        return DEBUGGER_CMDDONE;
                    418:                perror("ERROR");
                    419:        }
1.1.1.7   root      420:        return DebugUI_PrintCmdHelp(argv[0]);
                    421: }
                    422: 
                    423: /**
                    424:  * Command: Rename file
                    425:  */
                    426: static int DebugUI_Rename(int argc, char *argv[])
                    427: {
                    428:        if (argc == 3)
                    429:        {
                    430:                if (rename(argv[1], argv[2]) == 0)
                    431:                        return DEBUGGER_CMDDONE;
                    432:                perror("ERROR");
                    433:        }
                    434:        return DebugUI_PrintCmdHelp(argv[0]);
1.1       root      435: }
                    436: 
                    437: 
                    438: /**
1.1.1.8   root      439:  * Command: Reset emulation
                    440:  */
                    441: static char *DebugUI_MatchReset(const char *text, int state)
                    442: {
                    443:        static const char* types[] = {  "cold", "hard", "soft", "warm" };
1.1.1.9   root      444:        return DebugUI_MatchHelper(types, ARRAY_SIZE(types), text, state);
1.1.1.8   root      445: }
                    446: static int DebugUI_Reset(int argc, char *argv[])
                    447: {
                    448:        if (argc != 2)
                    449:                return DebugUI_PrintCmdHelp(argv[0]);
                    450: 
                    451:        if (strcmp(argv[1], "soft") == 0 || strcmp(argv[1], "warm") == 0)
                    452:                Reset_Warm();
                    453:        else if (strcmp(argv[1], "cold") == 0 || strcmp(argv[1], "hard") == 0)
                    454:                Reset_Cold();
                    455:        else
                    456:                return DebugUI_PrintCmdHelp(argv[0]);
                    457:        return DEBUGGER_END;
                    458: }
                    459: 
                    460: 
                    461: /**
1.1       root      462:  * Command: Read debugger commands from a file
                    463:  */
                    464: static int DebugUI_CommandsFromFile(int argc, char *argv[])
                    465: {
                    466:        if (argc == 2)
1.1.1.6   root      467:                DebugUI_ParseFile(argv[1], true);
1.1       root      468:        else
                    469:                DebugUI_PrintCmdHelp(argv[0]);
                    470:        return DEBUGGER_CMDDONE;
                    471: }
                    472: 
                    473: 
                    474: /**
                    475:  * Command: Quit emulator
                    476:  */
                    477: static int DebugUI_QuitEmu(int nArgc, char *psArgv[])
                    478: {
1.1.1.7   root      479:        int exitval;
                    480: 
                    481:        if (nArgc > 2)
                    482:                return DebugUI_PrintCmdHelp(psArgv[0]);
                    483: 
                    484:        if (nArgc == 2)
                    485:                exitval = atoi(psArgv[1]);
                    486:        else
                    487:                exitval = 0;
                    488: 
                    489:        ConfigureParams.Log.bConfirmQuit = false;
                    490:        Main_RequestQuit(exitval);
1.1       root      491:        return DEBUGGER_END;
                    492: }
                    493: 
                    494: 
                    495: /**
                    496:  * Print help text for one command
                    497:  */
1.1.1.7   root      498: int DebugUI_PrintCmdHelp(const char *psCmd)
1.1       root      499: {
                    500:        dbgcommand_t *cmd;
                    501:        int i;
                    502: 
                    503:        /* Search the command ... */
                    504:        for (cmd = debugCommand, i = 0; i < debugCommands; i++, cmd++)
                    505:        {
                    506:                if (!debugCommand[i].pFunction)
                    507:                        continue;
                    508:                if ((*(cmd->sShortName) && !strcmp(psCmd, cmd->sShortName))
                    509:                    || !strcmp(psCmd, cmd->sLongName))
                    510:                {
                    511:                        bool bShort = *(cmd->sShortName);
                    512:                        /* ... and print help text */
                    513:                        if (bShort)
                    514:                        {
                    515:                                fprintf(stderr, "'%s' or '%s' - %s\n",
                    516:                                        cmd->sLongName,
                    517:                                        cmd->sShortName,
                    518:                                        cmd->sShortDesc);
                    519:                        }
                    520:                        else
                    521:                        {
                    522:                                fprintf(stderr, "'%s' - %s\n",
                    523:                                        cmd->sLongName,
                    524:                                        cmd->sShortDesc);
                    525:                        }
                    526:                        fprintf(stderr, "Usage:  %s %s\n",
                    527:                                bShort ? cmd->sShortName : cmd->sLongName,
                    528:                                cmd->sUsage);
1.1.1.7   root      529:                        return DEBUGGER_CMDDONE;
1.1       root      530:                }
                    531:        }
                    532: 
                    533:        fprintf(stderr, "Unknown command '%s'\n", psCmd);
1.1.1.7   root      534:        return DEBUGGER_CMDDONE;
1.1       root      535: }
                    536: 
                    537: 
                    538: /**
                    539:  * Command: Print debugger help screen.
                    540:  */
                    541: static int DebugUI_Help(int nArgc, char *psArgs[])
                    542: {
                    543:        int i;
                    544: 
                    545:        if (nArgc > 1)
                    546:        {
1.1.1.7   root      547:                return DebugUI_PrintCmdHelp(psArgs[1]);
1.1       root      548:        }
                    549: 
                    550:        for (i = 0; i < debugCommands; i++)
                    551:        {
                    552:                if (!debugCommand[i].pFunction)
                    553:                {
                    554:                        fprintf(stderr, "\n%s:\n", debugCommand[i].sLongName);
                    555:                        continue;
                    556:                }
                    557:                fprintf(stderr, " %12s (%2s) : %s\n", debugCommand[i].sLongName,
                    558:                        debugCommand[i].sShortName, debugCommand[i].sShortDesc);
                    559:        }
                    560: 
                    561:        fprintf(stderr,
                    562:                "\n"
                    563:                "If value is prefixed with '$', it's a hexadecimal, if with '#', it's\n"
                    564:                "a normal decimal, if with '%%', it's a binary decimal. Prefix can\n"
                    565:                "be skipped for numbers in the default number base (currently %d).\n"
                    566:                "\n"
                    567:                "Any expression given in quotes (within \"\"), will be evaluated\n"
                    568:                "before given to the debugger command.  Any register and symbol\n"
                    569:                "names in the expression are replaced by their values.\n"
                    570:                "\n"
                    571:                "Note that address ranges like '$fc0000-$fc0100' should have no\n"
                    572:                "spaces between the range numbers.\n"
                    573:                "\n"
                    574:                "'help <command>' gives more help.\n", ConfigureParams.Debugger.nNumberBase);
                    575:        return DEBUGGER_CMDDONE;
                    576: }
                    577: 
                    578: 
                    579: /**
                    580:  * Parse debug command and execute it.
                    581:  */
1.1.1.2   root      582: static int DebugUI_ParseCommand(const char *input_orig)
1.1       root      583: {
1.1.1.2   root      584:        char *psArgs[64], *input;
1.1       root      585:        const char *delim;
                    586:        static char sLastCmd[80] = { '\0' };
                    587:        int nArgc, cmd = -1;
                    588:        int i, retval;
                    589: 
1.1.1.2   root      590:        input = strdup(input_orig);
1.1       root      591:        psArgs[0] = strtok(input, " \t");
                    592: 
                    593:        if (psArgs[0] == NULL)
                    594:        {
                    595:                if (strlen(sLastCmd) > 0)
                    596:                        psArgs[0] = sLastCmd;
                    597:                else
1.1.1.2   root      598:                {
                    599:                        free(input);
1.1       root      600:                        return DEBUGGER_CMDDONE;
1.1.1.2   root      601:                }
1.1       root      602:        }
                    603: 
                    604:        /* Search the command ... */
                    605:        for (i = 0; i < debugCommands; i++)
                    606:        {
                    607:                if (!debugCommand[i].pFunction)
                    608:                        continue;
                    609:                if (!strcmp(psArgs[0], debugCommand[i].sShortName) ||
                    610:                    !strcmp(psArgs[0], debugCommand[i].sLongName))
                    611:                {
                    612:                        cmd = i;
                    613:                        break;
                    614:                }
                    615:        }
                    616:        if (cmd == -1)
                    617:        {
                    618:                fprintf(stderr, "Command '%s' not found.\n"
                    619:                        "Use 'help' to view a list of available commands.\n",
                    620:                        psArgs[0]);
1.1.1.2   root      621:                free(input);
1.1       root      622:                return DEBUGGER_CMDDONE;
                    623:        }
                    624: 
                    625:        if (debugCommand[cmd].bNoParsing)
                    626:                delim = "";
                    627:        else
                    628:                delim = " \t";
                    629: 
                    630:        /* Separate arguments and put the pointers into psArgs */
1.1.1.9   root      631:        for (nArgc = 1; nArgc < ARRAY_SIZE(psArgs); nArgc++)
1.1       root      632:        {
                    633:                psArgs[nArgc] = strtok(NULL, delim);
                    634:                if (psArgs[nArgc] == NULL)
                    635:                        break;
                    636:        }
1.1.1.11! root      637:        if (nArgc >= ARRAY_SIZE(psArgs))
        !           638:        {
        !           639:                fprintf(stderr, "Error: too many arguments (currently up to %d supported)\n",
        !           640:                        ARRAY_SIZE(psArgs));
        !           641:                retval = DEBUGGER_CMDCONT;
        !           642:        }
        !           643:        else
        !           644:        {
        !           645:                /* ... and execute the function */
        !           646:                retval = debugCommand[i].pFunction(nArgc, psArgs);
        !           647:        }
1.1       root      648:        /* Save commando string if it can be repeated */
                    649:        if (retval == DEBUGGER_CMDCONT)
1.1.1.7   root      650:        {
                    651:                if (psArgs[0] != sLastCmd)
1.1.1.8   root      652:                        strlcpy(sLastCmd, psArgs[0], sizeof(sLastCmd));
1.1.1.7   root      653:        }
1.1       root      654:        else
                    655:                sLastCmd[0] = '\0';
1.1.1.2   root      656:        free(input);
1.1       root      657:        return retval;
                    658: }
                    659: 
                    660: 
                    661: /* See "info:readline" e.g. in Konqueror for readline usage. */
                    662: 
                    663: /**
1.1.1.7   root      664:  * Generic readline match callback helper.
                    665:  * STATE = 0 -> different text from previous one.
                    666:  * Return next match or NULL if no matches.
                    667:  */
                    668: char *DebugUI_MatchHelper(const char **strings, int items, const char *text, int state)
                    669: {
                    670:        static int i, len;
                    671:        
                    672:        if (!state)
                    673:        {
                    674:                /* first match */
                    675:                len = strlen(text);
                    676:                i = 0;
                    677:        }
                    678:        /* next match */
                    679:        while (i < items) {
                    680:                if (strncasecmp(strings[i++], text, len) == 0)
                    681:                        return (strdup(strings[i-1]));
                    682:        }
                    683:        return NULL;
                    684: }
                    685: 
                    686: /**
1.1       root      687:  * Readline match callback for long command name completion.
                    688:  * STATE = 0 -> different text from previous one.
                    689:  * Return next match or NULL if no matches.
                    690:  */
                    691: static char *DebugUI_MatchCommand(const char *text, int state)
                    692: {
                    693:        static int i, len;
                    694:        const char *name;
                    695:        
                    696:        if (!state)
                    697:        {
                    698:                /* first match */
                    699:                len = strlen(text);
                    700:                i = 0;
                    701:        }
                    702:        /* next match */
                    703:        while (i < debugCommands)
                    704:        {
                    705:                name = debugCommand[i].sLongName;
                    706:                if (debugCommand[i++].pFunction &&
                    707:                    strncmp(name, text, len) == 0)
                    708:                        return (strdup(name));
                    709:        }
                    710:        return NULL;
                    711: }
                    712: 
                    713: 
                    714: #if HAVE_LIBREADLINE
                    715: /**
                    716:  * Readline match callback returning last result.
                    717:  */
                    718: static char *DebugUI_MatchLast(const char *text, int state)
                    719: {
                    720:        if (state)
                    721:                return NULL;
                    722:        return strdup(lastResult);
                    723: }
                    724: 
                    725: /**
                    726:  * Readline completion callback. Returns matches.
                    727:  */
                    728: static char **DebugUI_Completion(const char *text, int a, int b)
                    729: {
                    730:        int i, cmd, quotes, end, start = 0;
                    731:        char *str, buf[32];
                    732:        size_t len;
                    733: 
                    734:        /* check where's the first word (ignore white space) */
1.1.1.7   root      735:        while (start < rl_point && isspace((unsigned char)rl_line_buffer[start]))
1.1       root      736:                start++;
                    737:        end = start;
1.1.1.7   root      738:        while (end < rl_point && !isspace((unsigned char)rl_line_buffer[end]))
1.1       root      739:                end++;
                    740: 
                    741:        if (end >= rl_point)
                    742:                /* first word on line */
                    743:                return rl_completion_matches(text, DebugUI_MatchCommand);
                    744:        
                    745:        /* complete '$' with last result? */
                    746:        if (lastResult[0] && rl_line_buffer[rl_point-1] == '$')
                    747:                return rl_completion_matches(text, DebugUI_MatchLast);
                    748: 
                    749:        /* check which command args are to be completed */
                    750:        len = end - start;
                    751:        if (len >= sizeof(buf))
                    752:                len = sizeof(buf)-1;
                    753:        memcpy(buf, &(rl_line_buffer[start]), len);
                    754:        buf[len] = '\0';
                    755: 
                    756:        /* expression completion needed (= open quote)? */
                    757:        str = strchr(&(rl_line_buffer[end]), '"');
                    758:        quotes = 0;
                    759:        while (str)
                    760:        {
                    761:                quotes++;
                    762:                str = strchr(str+1, '"');
                    763:        }
                    764:        if (quotes & 1)
                    765:        {
                    766:                if (DebugUI_IsForDsp(buf))
                    767:                        return rl_completion_matches(text, Symbols_MatchDspAddress);
                    768:                return rl_completion_matches(text, Symbols_MatchCpuAddress);
                    769:        }
                    770: 
                    771:        /* do command argument completion */
                    772:        cmd = -1;
                    773:        for (i = 0; i < debugCommands; i++)
                    774:        {
                    775:                if (!debugCommand[i].pFunction)
                    776:                        continue;
                    777:                if (!strcmp(buf, debugCommand[i].sShortName) ||
                    778:                    !strcmp(buf, debugCommand[i].sLongName))
                    779:                {
                    780:                        cmd = i;
                    781:                        break;
                    782:                }
                    783:        }
                    784:        if (cmd < 0)
                    785:        {
                    786:                rl_attempted_completion_over = true;
                    787:                return NULL;
                    788:        }
                    789:        if (debugCommand[cmd].pMatch)
                    790:                return rl_completion_matches(text, debugCommand[cmd].pMatch);
                    791:        else
                    792:                return rl_completion_matches(text, rl_filename_completion_function);
                    793: }
                    794: 
1.1.1.6   root      795: /**
                    796:  * Add non-repeated command to readline history
                    797:  * and free the given string
                    798:  */
1.1.1.7   root      799: static void DebugUI_FreeCommand(char *input)
1.1.1.6   root      800: {
                    801:        if (input && *input)
                    802:        {
                    803:                HIST_ENTRY *hist = history_get(history_length);
                    804:                /* don't store duplicate successive entries */
                    805:                if (!hist || !hist->line || strcmp(hist->line, input) != 0)
                    806:                {
                    807:                        add_history(input);
                    808:                }
                    809:                free(input);
                    810:        }
                    811: }
1.1       root      812: 
                    813: /**
                    814:  * Read a command line from the keyboard and return a pointer to the string.
1.1.1.2   root      815:  * Only string returned by this function can be given for it as argument!
                    816:  * The string will be stored into command history buffer.
1.1.1.6   root      817:  * @return     Pointer to the string which should be given back to this
                    818:  *              function or DebugUI_FreeCommand() for re-use/history.
                    819:  *              Returns NULL when error occurred.
1.1       root      820:  */
1.1.1.2   root      821: static char *DebugUI_GetCommand(char *input)
1.1       root      822: {
1.1.1.11! root      823:        /* We need this indirection for libedit's rl_readline_name which is
        !           824:         * not declared as "const char *" (i.e. this is necessary for macOS) */
        !           825:        static char hatari_readline_name[] = "Hatari";
        !           826: 
1.1       root      827:        /* Allow conditional parsing of the ~/.inputrc file. */
1.1.1.11! root      828:        rl_readline_name = hatari_readline_name;
1.1       root      829:        
                    830:        /* Tell the completer that we want a crack first. */
                    831:        rl_attempted_completion_function = DebugUI_Completion;
1.1.1.7   root      832:        DebugUI_FreeCommand(input);
1.1.1.2   root      833:        return Str_Trim(readline("> "));
1.1       root      834: }
                    835: 
1.1.1.10  root      836: /**
                    837:  * Get readlines idea of the terminal size
                    838:  */
                    839: static void DebugUI_GetScreenSize(int *rows, int *cols)
                    840: {
                    841:        rl_get_screen_size(rows, cols);
                    842: }
                    843: 
1.1       root      844: #else /* !HAVE_LIBREADLINE */
                    845: 
                    846: /**
1.1.1.6   root      847:  * Free Command input string
                    848:  */
1.1.1.7   root      849: static void DebugUI_FreeCommand(char *input)
1.1.1.6   root      850: {
1.1.1.9   root      851:        free(input);
1.1.1.6   root      852: }
                    853: 
                    854: /**
1.1.1.10  root      855:  * Get number of lines/columns for terminal output
                    856:  */
                    857: static void DebugUI_GetScreenSize(int *rows, int *cols)
                    858: {
                    859:        const char *p;
                    860: 
                    861:        *rows = 24;
                    862:        *cols = 80;
                    863:        if ((p = getenv("LINES")) != NULL)
                    864:                *rows = (int)strtol(p, NULL, 0);
                    865:        if ((p = getenv("COLUMS")) != NULL)
                    866:                *cols = (int)strtol(p, NULL, 0);
                    867: }
                    868: 
                    869: /**
1.1       root      870:  * Read a command line from the keyboard and return a pointer to the string.
1.1.1.2   root      871:  * Only string returned by this function can be given for it as argument!
1.1.1.6   root      872:  * @return     Pointer to the string which should be given back to this
                    873:  *              function or DebugUI_FreeCommand() for re-use/freeing.
                    874:  *              Returns NULL when error occurred.
1.1       root      875:  */
1.1.1.2   root      876: static char *DebugUI_GetCommand(char *input)
1.1       root      877: {
                    878:        fprintf(stderr, "> ");
                    879:        if (!input)
1.1.1.2   root      880:        {
                    881:                input = malloc(256);
                    882:                assert(input);
                    883:        }
1.1       root      884:        input[0] = '\0';
                    885:        if (fgets(input, 256, stdin) == NULL)
                    886:        {
                    887:                free(input);
                    888:                return NULL;
                    889:        }
                    890:        return Str_Trim(input);
                    891: }
                    892: 
                    893: #endif /* !HAVE_LIBREADLINE */
                    894: 
1.1.1.10  root      895: /**
                    896:  * How many lines to "page" when user invokes calling command.
                    897:  *
                    898:  * If config value is >=0, use that.  If it's negative, get number of lines
                    899:  * from screensize. If even that's not defined, fall back to default value.
                    900:  *
                    901:  * @return Number of lines to output at the time.
                    902:  */
                    903: int DebugUI_GetPageLines(int config, int defvalue)
                    904: {
                    905:        int rows, cols;
                    906: 
                    907:        if (config >= 0) {
                    908:                return config;
                    909:        }
                    910:        DebugUI_GetScreenSize(&rows, &cols);
                    911:        /* leave 1 line for pager prompt */
                    912:        if (--rows > 0) {
                    913:                return rows;
                    914:        }
                    915:        return defvalue;
                    916: }
                    917: 
1.1       root      918: 
                    919: static const dbgcommand_t uicommand[] =
                    920: {
                    921:        { NULL, NULL, "Generic commands", NULL, NULL, NULL, false },
                    922:        /* NULL as match function will complete file names */
                    923:        { DebugUI_ChangeDir, NULL,
                    924:          "cd", "",
                    925:          "change directory",
1.1.1.10  root      926:          "<directory> [-f]\n"
                    927:          "\tChange Hatari work directory. With '-f', directory is\n"
                    928:          "\tchanged only after all script files have been parsed.",
1.1       root      929:          false },
1.1.1.9   root      930:        { DebugUI_Evaluate, Vars_MatchCpuVariable,
1.1       root      931:          "evaluate", "e",
                    932:          "evaluate an expression",
                    933:          "<expression>\n"
                    934:          "\tEvaluate an expression and show the result.  Expression can\n"
1.1.1.9   root      935:          "\tinclude CPU register & symbol and Hatari variable names.\n"
                    936:          "\tThose are replaced by their values. Supported operators in\n"
                    937:          "\texpressions are, in the decending order of precedence:\n"
1.1       root      938:          "\t\t(), +, -, ~, *, /, +, -, >>, <<, ^, &, |\n"
1.1.1.2   root      939:          "\tParenthesis will fetch a _long_ value from the address\n"
                    940:          "\tto what the value inside it evaluates to. Prefixes can be\n"
                    941:          "\tused only in start of line or parenthesis.\n"
1.1       root      942:          "\tFor example:\n"
1.1.1.2   root      943:          "\t\t~%101 & $f0f0f ^ (d0 + 0x21)\n"
1.1       root      944:          "\tResult value is shown as binary, decimal and hexadecimal.\n"
                    945:          "\tAfter this, '$' will TAB-complete to last result value.",
                    946:          true },
                    947:        { DebugUI_Help, DebugUI_MatchCommand,
                    948:          "help", "h",
                    949:          "print help",
                    950:          "[command]\n"
                    951:          "\tPrint help text for available commands.",
                    952:          false },
1.1.1.7   root      953:        { History_Parse, History_Match,
1.1.1.3   root      954:          "history", "hi",
1.1.1.6   root      955:          "show last CPU/DSP PC values & executed instructions",
1.1.1.8   root      956:          "cpu|dsp|on|off|<count> [limit]|save <file>\n"
1.1.1.7   root      957:          "\t'cpu' and 'dsp' enable instruction history tracking for just given\n"
                    958:          "\tprocessor, 'on' tracks them both, 'off' will disable history.\n"
                    959:          "\tOptional 'limit' will set how many past instructions are tracked.\n"
                    960:          "\tGiving just count will show (at max) given number of last saved PC\n"
                    961:          "\tvalues and instructions currently at corresponding RAM addresses.",
1.1.1.3   root      962:          false },
1.1       root      963:        { DebugInfo_Command, DebugInfo_MatchInfo,
                    964:          "info", "i",
                    965:          "show machine/OS information",
                    966:          "[subject [arg]]\n"
                    967:          "\tPrint information on requested subject or list them if\n"
                    968:          "\tno subject given.",
                    969:          false },
                    970:        { DebugInfo_Command, DebugInfo_MatchLock,
                    971:          "lock", "",
1.1.1.2   root      972:          "specify information to show on entering the debugger",
1.1       root      973:          "[subject [args]]\n"
1.1.1.2   root      974:          "\tLock what information should be shown every time debugger\n"
                    975:          "\tis entered, or list available options if no subject's given.",
1.1       root      976:          false },
                    977:        { DebugUI_SetLogFile, NULL,
                    978:          "logfile", "f",
                    979:          "open or close log file",
                    980:          "[filename]\n"
                    981:          "\tOpen log file, no argument closes the log file. Output of\n"
                    982:          "\tregister & memory dumps and disassembly will be written to it.",
                    983:          false },
                    984:        { DebugUI_CommandsFromFile, NULL,
                    985:          "parse", "p",
                    986:          "get debugger commands from file",
                    987:          "[filename]\n"
1.1.1.10  root      988:          "\tRead debugger commands from given file and do them.\n"
                    989:          "\tCurrent directory is script directory during this.\n"
                    990:          "\tTo specify directory to be used also for breakpoint\n"
                    991:          "\tscripts execution, use '-f' option for 'cd' command.",
1.1       root      992:          false },
1.1.1.7   root      993:        { DebugUI_Rename, NULL,
                    994:          "rename", "",
                    995:          "rename given file",
                    996:          "old new\n"
                    997:          "\tRenames file with 'old' name to 'new'.",
                    998:          false },
1.1.1.8   root      999:        { DebugUI_Reset, DebugUI_MatchReset,
                   1000:          "reset", "",
                   1001:          "reset emulation",
                   1002:          "<soft|hard>\n",
                   1003:          false },
1.1       root     1004:        { DebugUI_SetOptions, Opt_MatchOption,
                   1005:          "setopt", "o",
                   1006:          "set Hatari command line and debugger options",
1.1.1.6   root     1007:          "[bin|dec|hex|<command line options>]\n"
1.1.1.8   root     1008:          "\tSpecial 'bin', 'dec' and 'hex' arguments change the default\n"
                   1009:          "\tnumber base used in debugger.  <TAB> lists available command\n"
                   1010:          "\tline options, 'setopt --help' their descriptions.",
1.1       root     1011:          false },
                   1012:        { DebugUI_DoMemorySnap, NULL,
                   1013:          "stateload", "",
                   1014:          "restore emulation state",
                   1015:          "[filename]\n"
                   1016:          "\tRestore emulation snapshot from default or given file",
                   1017:          false },
                   1018:        { DebugUI_DoMemorySnap, NULL,
                   1019:          "statesave", "",
                   1020:          "save emulation state",
                   1021:          "[filename]\n"
                   1022:          "\tSave emulation snapshot to default or given file",
                   1023:          false },
                   1024:        { DebugUI_SetTracing, Log_MatchTrace,
                   1025:          "trace", "t",
                   1026:          "select Hatari tracing settings",
                   1027:          "[set1,set2...]\n"
1.1.1.2   root     1028:          "\tSelect Hatari tracing settings. 'help' shows all the available\n"
                   1029:          "\tsettings.  For example, to enable CPU disassembly and VBL\n"
                   1030:          "\ttracing, use:\n\t\ttrace cpu_disasm,video_hbl",
1.1       root     1031:          false },
1.1.1.9   root     1032:        { Vars_List, NULL,
                   1033:          "variables", "v",
                   1034:          "List builtin symbols / variables",
                   1035:          "\n"
                   1036:          "\tList Hatari debugger builtin symbols / variables and their values.\n"
                   1037:          "\tThey're accepted by breakpoints and evaluate command.",
                   1038:          false },
1.1       root     1039:        { DebugUI_QuitEmu, NULL,
                   1040:          "quit", "q",
                   1041:          "quit emulator",
1.1.1.7   root     1042:          "[exit value]\n"
                   1043:          "\tLeave debugger and quit emulator with given exit value.",
1.1       root     1044:          false }
                   1045: };
                   1046: 
                   1047: 
                   1048: /**
                   1049:  * Debugger user interface initialization.
                   1050:  */
                   1051: void DebugUI_Init(void)
                   1052: {
                   1053:        const dbgcommand_t *cpucmd, *dspcmd;
                   1054:        int cpucmds, dspcmds;
                   1055: 
                   1056:        /* already intialized? */
                   1057:        if (debugCommands)
                   1058:                return;
                   1059: 
1.1.1.9   root     1060:        if (!debugOutput)
                   1061:                DebugUI_SetLogDefault();
                   1062: 
1.1       root     1063:        /* if you want disassembly or memdumping to start/continue from
                   1064:         * specific address, you can set them in these functions.
                   1065:         */
                   1066:        dspcmds = DebugDsp_Init(&dspcmd);
                   1067:        cpucmds = DebugCpu_Init(&cpucmd);
                   1068: 
                   1069:        /* on first time copy the command structures to a single table */
1.1.1.9   root     1070:        debugCommands = ARRAY_SIZE(uicommand);
1.1       root     1071:        debugCommand = malloc(sizeof(dbgcommand_t) * (dspcmds + cpucmds + debugCommands));
                   1072:        assert(debugCommand);
                   1073:        
                   1074:        memcpy(debugCommand, uicommand, sizeof(dbgcommand_t) * debugCommands);
                   1075:        memcpy(&debugCommand[debugCommands], cpucmd, sizeof(dbgcommand_t) * cpucmds);
                   1076:        debugCommands += cpucmds;
                   1077:        memcpy(&debugCommand[debugCommands], dspcmd, sizeof(dbgcommand_t) * dspcmds);
                   1078:        debugCommands += dspcmds;
                   1079: 
                   1080:        if (parseFileName)
1.1.1.6   root     1081:                DebugUI_ParseFile(parseFileName, true);
1.1       root     1082: }
                   1083: 
                   1084: 
                   1085: /**
1.1.1.2   root     1086:  * Set debugger commands file during Hatari startup before things
                   1087:  * needed by the debugger are initialized so that it can be parsed
                   1088:  * when debugger itself gets initialized.
1.1       root     1089:  * Return true if file exists, false otherwise.
                   1090:  */
                   1091: bool DebugUI_SetParseFile(const char *path)
                   1092: {
                   1093:        if (File_Exists(path))
                   1094:        {
                   1095:                parseFileName = path;
                   1096:                return true;
                   1097:        }
                   1098:        fprintf(stderr, "ERROR: debugger input file '%s' missing.\n", path);
                   1099:        return false;
                   1100: }
                   1101: 
                   1102: 
                   1103: /**
                   1104:  * Debugger user interface main function.
                   1105:  */
1.1.1.3   root     1106: void DebugUI(debug_reason_t reason)
1.1       root     1107: {
                   1108:        int cmdret, alertLevel;
1.1.1.2   root     1109:        char *expCmd, *psCmd = NULL;
1.1       root     1110:        static const char *welcome =
                   1111:                "\n----------------------------------------------------------------------"
                   1112:                "\nYou have entered debug mode. Type c to continue emulation, h for help.\n";
1.1.1.8   root     1113:        static bool recursing;
                   1114: 
                   1115:        if (recursing)
                   1116:        {
                   1117:                fprintf(stderr, "WARNING: recursive call to DebugUI (through profiler debug option?)!\n");
                   1118:                recursing = false;
                   1119:                return;
                   1120:        }
                   1121:        recursing = true;
1.1.1.3   root     1122: 
                   1123:        History_Mark(reason);
                   1124: 
1.1       root     1125:        if (bInFullScreen)
                   1126:                Screen_ReturnFromFullScreen();
                   1127: 
1.1.1.7   root     1128:        /* Make sure mouse isn't grabbed regardless of where
                   1129:         * this is invoked from.  E.g. returning from fullscreen
                   1130:         * enables grab if that was enabled on windowed mode.
                   1131:         */
                   1132:        SDL_WM_GrabInput(SDL_GRAB_OFF);
                   1133: 
1.1       root     1134:        DebugUI_Init();
                   1135: 
                   1136:        if (welcome)
                   1137:        {
                   1138:                fputs(welcome, stderr);
                   1139:                welcome = NULL;
                   1140:        }
                   1141:        DebugCpu_InitSession();
                   1142:        DebugDsp_InitSession();
1.1.1.7   root     1143:        Symbols_LoadCurrentProgram();
1.1       root     1144:        DebugInfo_ShowSessionInfo();
                   1145: 
                   1146:        /* override paused message so that user knows to look into console
                   1147:         * on how to continue in case he invoked the debugger by accident.
                   1148:         */
                   1149:        Statusbar_AddMessage("Console Debugger", 100);
1.1.1.7   root     1150:        Statusbar_Update(sdlscrn, true);
1.1       root     1151: 
                   1152:        /* disable normal GUI alerts while on console */
                   1153:        alertLevel = Log_SetAlertLevel(LOG_FATAL);
                   1154: 
                   1155:        cmdret = DEBUGGER_CMDDONE;
                   1156:        do
                   1157:        {
1.1.1.2   root     1158:                /* Read command from the keyboard and give previous
                   1159:                 * command for freeing / adding to history
                   1160:                 */
                   1161:                psCmd = DebugUI_GetCommand(psCmd);
1.1       root     1162:                if (!psCmd)
                   1163:                        break;
                   1164: 
1.1.1.6   root     1165:                /* returns new expression expanded string */
1.1.1.2   root     1166:                if (!(expCmd = DebugUI_EvaluateExpressions(psCmd)))
1.1       root     1167:                        continue;
                   1168: 
                   1169:                /* Parse and execute the command string */
1.1.1.2   root     1170:                cmdret = DebugUI_ParseCommand(expCmd);
1.1.1.6   root     1171:                free(expCmd);
1.1       root     1172:        }
                   1173:        while (cmdret != DEBUGGER_END);
                   1174: 
1.1.1.6   root     1175:        /* free exit command */
1.1.1.7   root     1176:        DebugUI_FreeCommand(psCmd);
1.1.1.2   root     1177: 
1.1       root     1178:        Log_SetAlertLevel(alertLevel);
                   1179: 
                   1180:        DebugCpu_SetDebugging();
                   1181:        DebugDsp_SetDebugging();
1.1.1.8   root     1182: 
                   1183:        recursing = false;
1.1       root     1184: }
                   1185: 
                   1186: 
                   1187: /**
1.1.1.6   root     1188:  * Read debugger commands from a file.  If 'reinit' is set
                   1189:  * (as it normally should), reinitialize breakpoints etc.
                   1190:  * afterwards. return false for error, true for success.
1.1       root     1191:  */
1.1.1.6   root     1192: bool DebugUI_ParseFile(const char *path, bool reinit)
1.1       root     1193: {
1.1.1.10  root     1194:        int recurse;
                   1195:        static int recursing;
1.1.1.2   root     1196:        char *olddir, *dir, *cmd, *input, *expanded, *slash;
1.1       root     1197:        FILE *fp;
                   1198: 
                   1199:        fprintf(stderr, "Reading debugger commands from '%s'...\n", path);
                   1200:        if (!(fp = fopen(path, "r")))
                   1201:        {
                   1202:                perror("ERROR");
                   1203:                return false;
                   1204:        }
                   1205: 
                   1206:        /* change to directory where the debugger file resides */
1.1.1.2   root     1207:        olddir = NULL;
1.1       root     1208:        dir = strdup(path);
                   1209:        slash = strrchr(dir, PATHSEP);
                   1210:        if (slash)
                   1211:        {
1.1.1.2   root     1212:                olddir = malloc(FILENAME_MAX);
                   1213:                if (olddir)
                   1214:                {
                   1215:                        if (!getcwd(olddir, FILENAME_MAX))
                   1216:                                strcpy(olddir, ".");
                   1217:                }
1.1       root     1218:                *slash = '\0';
1.1.1.2   root     1219:                if (chdir(dir) != 0)
1.1       root     1220:                {
                   1221:                        perror("ERROR");
1.1.1.9   root     1222:                        free(olddir);
1.1       root     1223:                        free(dir);
1.1.1.6   root     1224:                        fclose(fp);
1.1       root     1225:                        return false;
                   1226:                }
1.1.1.2   root     1227:                fprintf(stderr, "Changed to input file dir '%s'.\n", dir);
1.1       root     1228:        }
                   1229:        free(dir);
                   1230: 
1.1.1.10  root     1231:        recurse = recursing;
                   1232:        recursing = true;
                   1233: 
1.1       root     1234:        input = NULL;
                   1235:        for (;;)
                   1236:        {
                   1237:                if (!input)
                   1238:                {
                   1239:                        input = malloc(256);
                   1240:                        assert(input);
                   1241:                }
                   1242:                if (!fgets(input, 256, fp))
                   1243:                        break;
                   1244: 
1.1.1.2   root     1245:                /* ignore empty and comment lines */
                   1246:                cmd = Str_Trim(input);
                   1247:                if (!*cmd || *cmd == '#')
1.1       root     1248:                        continue;
                   1249: 
1.1.1.2   root     1250:                /* returns new string if input needed expanding! */
                   1251:                expanded = DebugUI_EvaluateExpressions(input);
                   1252:                if (!expanded)
                   1253:                        continue;
                   1254: 
                   1255:                cmd = Str_Trim(expanded);
                   1256:                fprintf(stderr, "> %s\n", cmd);
                   1257:                DebugUI_ParseCommand(cmd);
1.1.1.6   root     1258:                free(expanded);
1.1       root     1259:        }
1.1.1.10  root     1260:        recursing = false;
1.1       root     1261: 
                   1262:        free(input);
1.1.1.6   root     1263:        fclose(fp);
                   1264: 
1.1.1.2   root     1265:        if (olddir)
                   1266:        {
                   1267:                if (chdir(olddir) != 0)
                   1268:                        perror("ERROR");
                   1269:                else
                   1270:                        fprintf(stderr, "Changed back to '%s' dir.\n", olddir);
                   1271:                free(olddir);
                   1272:        }
1.1       root     1273: 
1.1.1.10  root     1274:        if (!recurse)
1.1.1.6   root     1275:        {
1.1.1.10  root     1276:                /* current script (or something called by it) specified final dir */
                   1277:                if (finalDir)
                   1278:                {
                   1279:                        if (chdir(finalDir) != 0)
                   1280:                                perror("ERROR");
                   1281:                        else
                   1282:                                fprintf(stderr, "Delayed change to '%s' dir.\n", finalDir);
                   1283:                        free(finalDir);
                   1284:                        finalDir = NULL;
                   1285:                }
                   1286:                /* only top-level (non-recursed) call has valid re-init info,
                   1287:                 * as that's the only one that can get directly called from
                   1288:                 * breakpoints
                   1289:                 */
                   1290:                if (reinit)
                   1291:                {
                   1292:                        DebugCpu_SetDebugging();
                   1293:                        DebugDsp_SetDebugging();
                   1294:                }
1.1.1.6   root     1295:        }
1.1       root     1296:        return true;
                   1297: }
                   1298: 
                   1299: 
                   1300: /**
1.1.1.6   root     1301:  * Remote/parallel debugger line usage API.
1.1       root     1302:  * Return false for failed command, true for success.
                   1303:  */
1.1.1.6   root     1304: bool DebugUI_ParseLine(const char *input)
1.1       root     1305: {
1.1.1.6   root     1306:        char *expanded;
                   1307:        int ret = 0;
1.1       root     1308: 
                   1309:        DebugUI_Init();
                   1310: 
1.1.1.6   root     1311:        /* returns new string if input needed expanding! */
                   1312:        expanded = DebugUI_EvaluateExpressions(input);
                   1313:        if (expanded)
                   1314:        {
                   1315:                fprintf(stderr, "> %s\n", expanded);
                   1316:                ret = DebugUI_ParseCommand(expanded);
                   1317:                free(expanded);
1.1       root     1318: 
1.1.1.6   root     1319:                DebugCpu_SetDebugging();
                   1320:                DebugDsp_SetDebugging();
                   1321:        }
1.1       root     1322:        return (ret == DEBUGGER_CMDDONE);
                   1323: }
1.1.1.7   root     1324: 
                   1325: /**
                   1326:  * Debugger invocation based on exception
                   1327:  */
                   1328: void DebugUI_Exceptions(int nr, long pc)
                   1329: {
                   1330:        static struct {
                   1331:                int flag;
                   1332:                const char *name;
                   1333:        } ex[] = {
                   1334:                { EXCEPT_BUS,       "Bus error" },              /* 2 */
                   1335:                { EXCEPT_ADDRESS,   "Address error" },          /* 3 */
                   1336:                { EXCEPT_ILLEGAL,   "Illegal instruction" },    /* 4 */
                   1337:                { EXCEPT_ZERODIV,   "Div by zero" },            /* 5 */
                   1338:                { EXCEPT_CHK,       "CHK" },                    /* 6 */
                   1339:                { EXCEPT_TRAPV,     "TRAPV" },                  /* 7 */
1.1.1.9   root     1340:                { EXCEPT_PRIVILEGE, "Privilege violation" },    /* 8 */
                   1341:                { EXCEPT_TRACE,     "Trace" }                   /* 9 */
1.1.1.7   root     1342:        };
                   1343:        nr -= 2;
1.1.1.9   root     1344:        if (nr < 0  || nr >= ARRAY_SIZE(ex))
1.1.1.7   root     1345:                return;
                   1346:        if (!(ExceptionDebugMask & ex[nr].flag))
                   1347:                return;
                   1348:        fprintf(stderr,"%s exception at 0x%lx!\n", ex[nr].name, pc);
                   1349:        DebugUI(REASON_CPU_EXCEPTION);
                   1350: }

unix.superglobalmegacorp.com

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