/** * This function is called after each DSP instruction when debugging is enabled. */ void DebugDsp_Check(void) { if (bDspProfiling) { Profile_DspUpdate(); } if (LOG_TRACE_LEVEL((TRACE_DSP_DISASM|TRACE_DSP_SYMBOLS))) { DebugDsp_ShowAddressInfo(DSP_GetPC()); } if (nDspActiveCBs) { if (BreakCond_MatchDsp()) DebugUI(REASON_DSP_BREAKPOINT); } if (nDspSteps) { nDspSteps -= 1; if (nDspSteps == 0) DebugUI(REASON_DSP_STEPS); } if (bHistoryEnabled) { History_AddDsp(); } }
/** * Shorcut to debug interface */ static void ShortCut_Debug(void) { int running; /* Call the debugger */ running = Main_PauseEmulation(true); DebugUI(); if (running) Main_UnPauseEmulation(); }
/** * This function is called after each CPU instruction when debugging is enabled. */ void DebugCpu_Check(void) { if (bCpuProfiling) { Profile_CpuUpdate(); } if (LOG_TRACE_LEVEL(TRACE_CPU_DISASM)) { DebugCpu_ShowAddressInfo(M68000_GetPC()); } if (nCpuActiveCBs) { if (BreakCond_MatchCpu()) DebugUI(); } if (nCpuSteps) { nCpuSteps -= 1; if (nCpuSteps == 0) DebugUI(); } }
/** * convert Atari memory address to sorting array profile data index. */ static inline Uint32 address2index(Uint32 pc) { if (unlikely(pc & 1)) { fprintf(stderr, "WARNING: odd CPU profile instruction address 0x%x!\n", pc); #if DEBUG skip_assert = true; DebugUI(REASON_CPU_EXCEPTION); #endif } if (pc < STRamEnd) { /* most likely case, use RAM address as-is */ } else if (pc >= TosAddress && pc < TosAddress + TosSize) { /* TOS, put it after RAM data */ pc = pc - TosAddress + STRamEnd; if (TosAddress >= CART_END) { /* and after cartridge data as it's higher */ pc += CART_SIZE; } } else if (pc >= CART_START && pc < CART_END) { /* ROM, put it after RAM data */ pc = pc - CART_START + STRamEnd; if (TosAddress < CART_START) { /* and after TOS as it's higher */ pc += TosSize; } } else { fprintf(stderr, "WARNING: 'invalid' CPU PC profile instruction address 0x%x!\n", pc); /* extra entry at end is reserved for invalid PC values */ pc = STRamEnd + TosSize + 0x20000; #if DEBUG skip_assert = true; DebugUI(REASON_CPU_EXCEPTION); #endif } /* CPU instructions are at even addresses, save space by halving */ return (pc >> 1); }
/** * This function is called after each CPU instruction when debugging is enabled. */ void DebugCpu_Check(void) { nCpuInstructions++; if (bCpuProfiling) { Profile_CpuUpdate(); } if (LOG_TRACE_LEVEL((TRACE_CPU_DISASM|TRACE_CPU_SYMBOLS))) { DebugCpu_ShowAddressInfo(M68000_GetPC()); } if (nCpuActiveCBs) { if (BreakCond_MatchCpu()) { DebugUI(REASON_CPU_BREAKPOINT); /* make sure we don't decrease step count * below, before even even getting out of here */ if (nCpuSteps) nCpuSteps++; } } if (nCpuSteps) { nCpuSteps--; if (nCpuSteps == 0) DebugUI(REASON_CPU_STEPS); } if (History_TrackCpu()) { History_AddCpu(); } if (ConOutDevice != CONOUT_DEVICE_NONE) { Console_Check(); } }
/** * Debugger invocation based on exception */ void DebugUI_Exceptions(int nr, long pc) { static struct { int flag; const char *name; } ex[] = { { EXCEPT_BUS, "Bus error" }, /* 2 */ { EXCEPT_ADDRESS, "Address error" }, /* 3 */ { EXCEPT_ILLEGAL, "Illegal instruction" }, /* 4 */ { EXCEPT_ZERODIV, "Div by zero" }, /* 5 */ { EXCEPT_CHK, "CHK" }, /* 6 */ { EXCEPT_TRAPV, "TRAPV" }, /* 7 */ { EXCEPT_PRIVILEGE, "Privilege violation" } /* 8 */ }; nr -= 2; if (nr < 0 || nr >= ARRAYSIZE(ex)) return; if (!(ExceptionDebugMask & ex[nr].flag)) return; fprintf(stderr,"%s exception at 0x%lx!\n", ex[nr].name, pc); DebugUI(REASON_CPU_EXCEPTION); }
/** * If call tracking is enabled (there are symbols), collect * information about subroutine and other calls, and their costs. * * Like with profile data, caller info checks need to be for previous * instruction, that's why "pc" argument for this function actually * needs to be previous PC. */ static void collect_calls(Uint32 pc, counters_t *counters) { calltype_t flag; int idx, family; Uint32 prev_pc, caller_pc; family = cpu_profile.prev_family; cpu_profile.prev_family = OpcodeFamily; prev_pc = cpu_callinfo.prev_pc; cpu_callinfo.prev_pc = pc; caller_pc = PC_UNDEFINED; /* address is return address for last subroutine call? */ if (unlikely(pc == cpu_callinfo.return_pc) && likely(cpu_callinfo.depth)) { flag = cpu_opcode_type(family, prev_pc, pc); /* previous address can be exception return (e.g. RTE) instead of RTS, * if exception occurred right after returning from subroutine call. */ if (likely(flag == CALL_SUBRETURN || flag == CALL_EXCRETURN)) { caller_pc = Profile_CallEnd(&cpu_callinfo, counters); } else { #if DEBUG /* although at return address, it didn't return yet, * e.g. because there was a jsr or jump to return address */ Uint32 nextpc; fprintf(stderr, "WARNING: subroutine call returned 0x%x -> 0x%x, not through RTS!\n", prev_pc, pc); Disasm(stderr, prev_pc, &nextpc, 1); #endif } /* next address might be another symbol, so need to fall through */ } /* address is one which we're tracking? */ idx = Symbols_GetCpuAddressIndex(pc); if (unlikely(idx >= 0)) { flag = cpu_opcode_type(family, prev_pc, pc); if (flag == CALL_SUBROUTINE || flag == CALL_EXCEPTION) { /* special HACK for for EmuTOS AES switcher which * changes stack content to remove itself from call * stack and uses RTS for subroutine *calls*, not * for returning from them. * * It wouldn't be reliable to detect calls from it, * so I'm making call *to* it show up as branch, to * keep callstack depth correct. */ if (unlikely(pc == etos_switcher)) { flag = CALL_BRANCH; } else if (unlikely(prev_pc == PC_UNDEFINED)) { /* if first profiled instruction * is subroutine call, it doesn't have * valid prev_pc value stored */ cpu_callinfo.return_pc = PC_UNDEFINED; fprintf(stderr, "WARNING: previous PC from callinfo for 0x%d is undefined!\n", pc); #if DEBUG skip_assert = true; DebugUI(REASON_CPU_EXCEPTION); #endif } else { /* slow! */ cpu_callinfo.return_pc = Disasm_GetNextPC(prev_pc); } } else if (caller_pc != PC_UNDEFINED) { /* returned from function to first instruction of another symbol: * 0xf384 jsr some_function * other_symbol: * 0f3x8a some_instruction * -> change return instruction address to * address of what did the returned call. */ prev_pc = caller_pc; assert(is_prev_instr(prev_pc, pc)); flag = CALL_NEXT; } Profile_CallStart(idx, &cpu_callinfo, prev_pc, flag, pc, counters); } }