/** * Command: Step CPU, but proceed through subroutines * Does this by temporary conditional breakpoint */ static int DebugCpu_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], "exception") == 0) optype = CALL_EXCEPTION; 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, "CpuOpcodeType & $%x > 0 :once :quiet\n", optype); } else { Uint32 optype, nextpc; optype = DebugCpu_OpcodeType(); /* can this instruction be stepped normally? */ if (optype != CALL_SUBROUTINE && optype != CALL_EXCEPTION) { nCpuSteps = 1; return DEBUGGER_END; } nextpc = Disasm_GetNextPC(M68000_GetPC()); sprintf(command, "pc=$%x :once :quiet\n", nextpc); } /* use breakpoint, not steps */ if (BreakCond_Command(command, false)) { nCpuSteps = 0; return DEBUGGER_END; } return DEBUGGER_CMDDONE; }
/** * 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); } }