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

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

unix.superglobalmegacorp.com

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