--- hatari/src/debug/debugdsp.c 2019/04/09 08:48:37 1.1 +++ hatari/src/debug/debugdsp.c 2019/04/09 08:56:49 1.1.1.8 @@ -1,8 +1,8 @@ /* Hatari - debugdsp.c - This file is distributed under the GNU Public License, version 2 or at - your option any later version. Read the file gpl.txt for details. + This file is distributed under the GNU General Public License, version 2 + or at your option any later version. Read the file gpl.txt for details. debugdsp.c - function needed for the DSP debugging tasks like memory and register dumps. @@ -10,6 +10,7 @@ const char DebugDsp_fileid[] = "Hatari debugdsp.c : " __DATE__ " " __TIME__; #include +#include #include "config.h" @@ -21,7 +22,10 @@ const char DebugDsp_fileid[] = "Hatari d #include "debugdsp.h" #include "dsp.h" #include "evaluate.h" +#include "history.h" +#include "log.h" #include "memorySnapShot.h" +#include "profile.h" #include "str.h" #include "symbols.h" @@ -29,6 +33,7 @@ static Uint16 dsp_disasm_addr; /* DSP static Uint16 dsp_memdump_addr; /* DSP memdump address */ static char dsp_mem_space = 'P'; /* X, Y, P */ +static bool bDspProfiling; /* Whether profiling is enabled */ static int nDspActiveCBs = 0; /* Amount of active conditional breakpoints */ static int nDspSteps = 0; /* Amount of steps for DSP single-stepping */ @@ -40,7 +45,7 @@ static int nDspSteps = 0; /* Amo */ static char *DebugDsp_MatchRegister(const char *text, int state) { - static const char regs[][4] = { + static const char* regs[] = { "a0", "a1", "a2", "b0", "b1", "b2", "la", "lc", "m0", "m1", "m2", "m3", "m4", "m5", "m6", "m7", "n0", "n1", "n2", "n3", "n4", "n5", "n6", "n7", @@ -48,22 +53,7 @@ static char *DebugDsp_MatchRegister(cons "omr", "pc", "sp", "sr", "ssh", "ssl", "x0", "x1", "y0", "y1", }; - static int i, len; - - if (!state) - { - /* first match */ - i = 0; - len = strlen(text); - if (len > 2) - return NULL; - } - /* next match */ - while (i < ARRAYSIZE(regs)) { - if (strncasecmp(regs[i++], text, len) == 0) - return (strdup(regs[i-1])); - } - return NULL; + return DebugUI_MatchHelper(regs, ARRAY_SIZE(regs), text, state); } /** @@ -84,7 +74,8 @@ int DebugDsp_Register(int nArgc, char *p if (nArgc == 1) { /* No parameter - dump all registers */ - DSP_DisasmRegisters(); + DSP_DisasmRegisters(debugOutput); + fflush(debugOutput); return DEBUGGER_CMDDONE; } arg = psArgs[1]; @@ -110,14 +101,14 @@ error_msg: /** - * Check whether given address matches any DSP symbol, if yes, - * show the matching symbol information. + * Check whether given address matches any DSP symbol and whether + * there's profiling information available for it. If yes, show it. */ -static void DebugDsp_ShowMatchedSymbol(Uint32 addr) +static void DebugDsp_ShowAddressInfo(Uint16 addr, FILE *fp) { const char *symbol = Symbols_GetByDspAddress(addr); if (symbol) - fprintf(debugOutput, "%s:\n", symbol); + fprintf(fp, "%s:\n", symbol); } @@ -137,7 +128,7 @@ int DebugDsp_DisAsm(int nArgc, char *psA if (nArgc > 1) { - switch (Eval_Range(psArgs[1], &lower, &upper)) + switch (Eval_Range(psArgs[1], &lower, &upper, true)) { case -1: /* invalid value(s) */ @@ -179,11 +170,12 @@ int DebugDsp_DisAsm(int nArgc, char *psA else dsp_disasm_upper = 0xFFFF; } - printf("DSP disasm 0x%hx-0x%hx:\n", dsp_disasm_addr, dsp_disasm_upper); + fprintf(debugOutput, "DSP disasm 0x%hx-0x%hx:\n", dsp_disasm_addr, dsp_disasm_upper); while (dsp_disasm_addr < dsp_disasm_upper) { - DebugDsp_ShowMatchedSymbol(dsp_disasm_addr); - dsp_disasm_addr = DSP_DisasmAddress(dsp_disasm_addr, dsp_disasm_addr); + DebugDsp_ShowAddressInfo(dsp_disasm_addr, debugOutput); + dsp_disasm_addr = DSP_DisasmAddress(debugOutput, dsp_disasm_addr, dsp_disasm_addr); } + fflush(debugOutput); return DEBUGGER_CMDCONT; } @@ -198,22 +190,37 @@ int DebugDsp_MemDump(int nArgc, char *ps { Uint32 lower, upper; Uint16 dsp_memdump_upper = 0; - char space; + char *range, space; if (!bDspEnabled) { fprintf(stderr, "DSP isn't present or initialized.\n"); return DEBUGGER_CMDDONE; } - if (nArgc != 1 && nArgc != 3) + + switch (nArgc) { - DebugUI_PrintCmdHelp(psArgs[0]); - return DEBUGGER_CMDDONE; + case 1: + break; + case 3: /* "x $200" */ + space = psArgs[1][0]; + range = psArgs[2]; + break; + case 2: /* "x:$200" */ + if (psArgs[1][1] == ':') + { + space = psArgs[1][0]; + range = psArgs[1] + 2; + break; + } + /* pass-through */ + default: + return DebugUI_PrintCmdHelp(psArgs[0]); } - if (nArgc == 3) + if (nArgc > 1) { - space = toupper(psArgs[1][0]); + space = toupper((unsigned char)space); switch (space) { case 'X': @@ -224,7 +231,7 @@ int DebugDsp_MemDump(int nArgc, char *ps fprintf(stderr,"Invalid DSP address space '%c'!\n", space); return DEBUGGER_CMDDONE; } - switch (Eval_Range(psArgs[2], &lower, &upper)) + switch (Eval_Range(range, &lower, &upper, true)) { case -1: /* invalid value(s) */ @@ -249,7 +256,7 @@ int DebugDsp_MemDump(int nArgc, char *ps } dsp_memdump_addr = lower; dsp_mem_space = space; - } /* continue */ + } if (!dsp_memdump_upper) { @@ -260,8 +267,9 @@ int DebugDsp_MemDump(int nArgc, char *ps dsp_memdump_upper = 0xFFFF; } - printf("DSP memdump from 0x%hx in '%c' address space:\n", dsp_memdump_addr, dsp_mem_space); - dsp_memdump_addr = DSP_DisasmMemory(dsp_memdump_addr, dsp_memdump_upper, dsp_mem_space); + fprintf(debugOutput, "DSP memdump from 0x%hx in '%c' address space:\n", dsp_memdump_addr, dsp_mem_space); + dsp_memdump_addr = DSP_DisasmMemory(debugOutput, dsp_memdump_addr, dsp_memdump_upper, dsp_mem_space); + fflush(debugOutput); return DEBUGGER_CMDCONT; } @@ -289,9 +297,145 @@ static int DebugDsp_Continue(int nArgc, return DEBUGGER_END; } +/** + * Command: Single-step DSP + */ +static int DebugDsp_Step(int nArgc, char *psArgv[]) +{ + nDspSteps = 1; + return DEBUGGER_END; +} + + +/** + * Readline match callback to list next command opcode types. + * STATE = 0 -> different text from previous one. + * Return next match or NULL if no matches. + */ +static char *DebugDsp_MatchNext(const char *text, int state) +{ + static const char* ntypes[] = { + "branch", "exreturn", "return", "subcall", "subreturn" + }; + return DebugUI_MatchHelper(ntypes, ARRAY_SIZE(ntypes), text, state); +} + +/** + * Command: Step DSP, but proceed through subroutines + * Does this by temporary conditional breakpoint + */ +static int DebugDsp_Next(int nArgc, char *psArgv[]) +{ + char command[40]; + if (nArgc > 1) + { + int optype; + if(strcmp(psArgv[1], "branch") == 0) + optype = CALL_BRANCH; + else if(strcmp(psArgv[1], "exreturn") == 0) + optype = CALL_EXCRETURN; + else if(strcmp(psArgv[1], "subcall") == 0) + optype = CALL_SUBROUTINE; + else if (strcmp(psArgv[1], "subreturn") == 0) + optype = CALL_SUBRETURN; + else if (strcmp(psArgv[1], "return") == 0) + optype = CALL_SUBRETURN | CALL_EXCRETURN; + else + { + fprintf(stderr, "Unrecognized opcode type given!\n"); + return DEBUGGER_CMDDONE; + } + sprintf(command, "DspOpcodeType & $%x > 0 :once :quiet\n", optype); + } + else + { + Uint32 optype; + Uint16 nextpc; + + optype = DebugDsp_OpcodeType(); + /* can this instruction be stepped normally? */ + if (optype != CALL_SUBROUTINE && optype != CALL_EXCEPTION) + { + nDspSteps = 1; + return DEBUGGER_END; + } + + nextpc = DSP_GetNextPC(DSP_GetPC()); + sprintf(command, "pc=$%x :once :quiet\n", nextpc); + } + /* use breakpoint, not steps */ + if (BreakCond_Command(command, true)) { + nDspSteps = 0; + return DEBUGGER_END; + } + return DEBUGGER_CMDDONE; +} + +/* helper to get instruction type, slightly simpler + * version from one in profiledsp.c + */ +Uint32 DebugDsp_OpcodeType(void) +{ + const char *dummy; + Uint32 opcode; + + /* 24-bit instruction opcode */ + opcode = DSP_ReadMemory(DSP_GetPC(), 'P', &dummy) & 0xFFFFFF; + + /* subroutine returns */ + if (opcode == 0xC) { /* (just) RTS */ + return CALL_SUBRETURN; + } + if ( + /* unconditional subroutine calls */ + (opcode & 0xFFF000) == 0xD0000 || /* JSR 00001101 0000aaaa aaaaaaaa */ + (opcode & 0xFFC0FF) == 0xBC080 || /* JSR 00001011 11MMMRRR 10000000 */ + /* conditional subroutine calls */ + (opcode & 0xFF0000) == 0xF0000 || /* JSCC 00001111 CCCCaaaa aaaaaaaa */ + (opcode & 0xFFC0F0) == 0xBC0A0 || /* JSCC 00001011 11MMMRRR 1010CCCC */ + (opcode & 0xFFC0A0) == 0xB4080 || /* JSCLR 00001011 01MMMRRR 1S0bbbbb */ + (opcode & 0xFFC0A0) == 0xB0080 || /* JSCLR 00001011 00aaaaaa 1S0bbbbb */ + (opcode & 0xFFC0A0) == 0xB8080 || /* JSCLR 00001011 10pppppp 1S0bbbbb */ + (opcode & 0xFFC0E0) == 0xBC000 || /* JSCLR 00001011 11DDDDDD 000bbbbb */ + (opcode & 0xFFC0A0) == 0xB40A0 || /* JSSET 00001011 01MMMRRR 1S1bbbbb */ + (opcode & 0xFFC0A0) == 0xB00A0 || /* JSSET 00001011 00aaaaaa 1S1bbbbb */ + (opcode & 0xFFC0A0) == 0xB80A0 || /* JSSET 00001011 10pppppp 1S1bbbbb */ + (opcode & 0xFFC0E0) == 0xBC020) { /* JSSET 00001011 11DDDDDD 001bbbbb */ + return CALL_SUBROUTINE; + } + /* exception handler returns */ + if (opcode == 0x4) { /* (just) RTI */ + return CALL_EXCRETURN; + } + /* branches */ + if ((opcode & 0xFFF000) == 0xC0000 || /* JMP 00001100 0000aaaa aaaaaaaa */ + (opcode & 0xFFC0FF) == 0xAC080 || /* JMP 00001010 11MMMRRR 10000000 */ + (opcode & 0xFF0000) == 0xE0000 || /* JCC 00001110 CCCCaaaa aaaaaaaa */ + (opcode & 0xFFC0F0) == 0xAC0A0 || /* JCC 00001010 11MMMRRR 1010CCCC */ + (opcode & 0xFFC0A0) == 0xA8080 || /* JCLR 00001010 10pppppp 1S0bbbbb */ + (opcode & 0xFFC0A0) == 0xA4080 || /* JCLR 00001010 01MMMRRR 1S0bbbbb */ + (opcode & 0xFFC0A0) == 0xA0080 || /* JCLR 00001010 00aaaaaa 1S0bbbbb */ + (opcode & 0xFFC0E0) == 0xAC000 || /* JCLR 00001010 11dddddd 000bbbbb */ + (opcode & 0xFFC0A0) == 0xA80A0 || /* JSET 00001010 10pppppp 1S1bbbbb */ + (opcode & 0xFFC0A0) == 0xA40A0 || /* JSET 00001010 01MMMRRR 1S1bbbbb */ + (opcode & 0xFFC0A0) == 0xA00A0 || /* JSET 00001010 00aaaaaa 1S1bbbbb */ + (opcode & 0xFFC0E0) == 0xAC020 || /* JSET 00001010 11dddddd 001bbbbb */ + (opcode & 0xFF00F0) == 0x600A0 || /* REP 00000110 iiiiiiii 1010hhhh */ + (opcode & 0xFFC0FF) == 0x6C020 || /* REP 00000110 11dddddd 00100000 */ + (opcode & 0xFFC0BF) == 0x64020 || /* REP 00000110 01MMMRRR 0s100000 */ + (opcode & 0xFFC0BF) == 0x60020 || /* REP 00000110 00aaaaaa 0s100000 */ + (opcode & 0xFF00F0) == 0x60080 || /* DO/ENDO 00000110 iiiiiiii 1000hhhh */ + (opcode & 0xFFC0FF) == 0x6C000 || /* DO/ENDO 00000110 11DDDDDD 00000000 */ + (opcode & 0xFFC0BF) == 0x64000 || /* DO/ENDO 00000110 01MMMRRR 0S000000 */ + (opcode & 0xFFC0BF) == 0x60000) { /* DO/ENDO 00000110 00aaaaaa 0S000000 */ + return CALL_BRANCH; + } + return CALL_UNKNOWN; +} + /** - * DSP wrapper for BreakAddr_Command/BreakPointCount, returns DEBUGGER_END + * DSP wrapper for BreakAddr_Command(). */ static int DebugDsp_BreakAddr(int nArgc, char *psArgs[]) { @@ -300,7 +444,7 @@ static int DebugDsp_BreakAddr(int nArgc, } /** - * DSP wrapper for BreakCond_Command/BreakPointCount, returns DEBUGGER_END + * DSP wrapper for BreakCond_Command(). */ static int DebugDsp_BreakCond(int nArgc, char *psArgs[]) { @@ -308,23 +452,59 @@ static int DebugDsp_BreakCond(int nArgc, return DEBUGGER_CMDDONE; } +/** + * DSP wrapper for Profile_Command(). + */ +static int DebugDsp_Profile(int nArgc, char *psArgs[]) +{ + return Profile_Command(nArgc, psArgs, true); +} + + +/** + * DSP instructions since continuing emulation + */ +static Uint32 nDspInstructions; +Uint32 DebugDsp_InstrCount(void) +{ + return nDspInstructions; +} /** * This function is called after each DSP instruction when debugging is enabled. */ void DebugDsp_Check(void) { - /* TODO: show symbols while disassembling DSP instructions */ + nDspInstructions++; + if (bDspProfiling) + { + Profile_DspUpdate(); + } + if (LOG_TRACE_LEVEL((TRACE_DSP_DISASM|TRACE_DSP_SYMBOLS))) + { + DebugDsp_ShowAddressInfo(DSP_GetPC(), TraceFile); + } if (nDspActiveCBs) { if (BreakCond_MatchDsp()) - DebugUI(); + { + DebugUI(REASON_DSP_BREAKPOINT); + /* make sure we don't decrease step count + * below, before even getting out of here + */ + if (nDspSteps) + nDspSteps++; + } } if (nDspSteps) { - nDspSteps -= 1; + nDspSteps--; if (nDspSteps == 0) - DebugUI(); + DebugUI(REASON_DSP_STEPS); + } + if (History_TrackDsp()) + { + History_AddDsp(); } } @@ -336,9 +516,15 @@ void DebugDsp_Check(void) */ void DebugDsp_SetDebugging(void) { - nDspActiveCBs = BreakCond_BreakPointCount(true); - if (nDspActiveCBs || nDspSteps) + bDspProfiling = Profile_DspStart(); + nDspActiveCBs = BreakCond_DspBreakPointCount(); + + if (nDspActiveCBs || nDspSteps || bDspProfiling || History_TrackDsp() + || LOG_TRACE_LEVEL((TRACE_DSP_DISASM|TRACE_DSP_SYMBOLS))) + { DSP_SetDebugging(true); + nDspInstructions = 0; + } else DSP_SetDebugging(false); } @@ -352,7 +538,8 @@ static const dbgcommand_t dspcommands[] "set DSP PC address breakpoints", BreakAddr_Description, true }, - { DebugDsp_BreakCond, BreakCond_MatchDspVariable, + /* currently no DSP variables, so checks that DSP symbol addresses */ + { DebugDsp_BreakCond, Symbols_MatchDspAddress, "dspbreak", "db", "set/remove/list conditional DSP breakpoints", BreakCond_Description, @@ -375,12 +562,32 @@ static const dbgcommand_t dspcommands[] "load DSP symbols & their addresses", Symbols_Description, false }, + { DebugDsp_Profile, Profile_Match, + "dspprofile", "dp", + "profile DSP code", + Profile_Description, + false }, { DebugDsp_Register, DebugDsp_MatchRegister, "dspreg", "dr", "read/write DSP registers", "[REG=value]" "\tSet or dump contents of DSP registers.", true }, + { DebugDsp_Step, NULL, + "dspstep", "ds", + "single-step DSP", + "\n" + "\tExecute next DSP instruction (equals 'dc 1')", + false }, + { DebugDsp_Next, DebugDsp_MatchNext, + "dspnext", "dn", + "step DSP through subroutine calls / to given instruction type", + "[instruction type]\n" + "\tSame as 'dspstep' command if there are no subroutine calls.\n" + "\tWhen there are, those calls are treated as one instruction.\n" + "\tIf argument is given, continues until instruction of given\n" + "\ttype is encountered.", + false }, { DebugDsp_Continue, NULL, "dspcont", "dc", "continue emulation / DSP single-stepping", @@ -408,7 +615,7 @@ int DebugDsp_Init(const dbgcommand_t **t dsp_mem_space = 'P'; *table = dspcommands; - return ARRAYSIZE(dspcommands); + return ARRAY_SIZE(dspcommands); } /** @@ -418,4 +625,5 @@ int DebugDsp_Init(const dbgcommand_t **t void DebugDsp_InitSession(void) { dsp_disasm_addr = DSP_GetPC(); + Profile_DspStop(); }