diff --git a/include/mgba/internal/arm/decoder.h b/include/mgba/internal/arm/decoder.h index 9d6481d99..dffa7042a 100644 --- a/include/mgba/internal/arm/decoder.h +++ b/include/mgba/internal/arm/decoder.h @@ -217,6 +217,7 @@ void ARMDecodeThumb(uint16_t opcode, struct ARMInstructionInfo* info); bool ARMDecodeThumbCombine(struct ARMInstructionInfo* info1, struct ARMInstructionInfo* info2, struct ARMInstructionInfo* out); int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, int blen); +uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegisterFile* regs, uint32_t pc); CXX_GUARD_END diff --git a/src/arm/debugger/debugger.c b/src/arm/debugger/debugger.c index 7b75bdc92..b7f345b3d 100644 --- a/src/arm/debugger/debugger.c +++ b/src/arm/debugger/debugger.c @@ -15,6 +15,8 @@ #include #include +#define FRAME_PRIV(FRAME) ((struct ARMRegisterFile*) FRAME->regs)->cpsr.priv + DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint); static bool ARMDecodeCombined(struct ARMCore* cpu, struct ARMInstructionInfo* info) { @@ -29,16 +31,6 @@ static bool ARMDecodeCombined(struct ARMCore* cpu, struct ARMInstructionInfo* in } } -static inline int ARMGetStack(struct ARMRegisterFile* regs) { - if (!regs) { - return MODE_USER; - } - if (regs->cpsr.priv == MODE_SYSTEM) { - return MODE_USER; - } - return regs->cpsr.priv; -} - static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uint32_t pc) { struct ARMDebugger* debugger = (struct ARMDebugger*) d; struct ARMCore* cpu = debugger->cpu; @@ -46,8 +38,8 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin struct mStackTrace* stack = &d->p->stackTrace; struct mStackFrame* frame = mStackTraceGetFrame(stack, 0); - int currentStack = ARMGetStack(&cpu->regs); - if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMGetStack(frame->regs)) { + enum RegisterBank currentStack = ARMSelectBank(cpu->cpsr.priv); + if (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMSelectBank(FRAME_PRIV(frame))) { // The stack frame has been popped off the stack. This means the function // has been returned from, or that the stack pointer has been otherwise // manipulated. Either way, the function is done executing. @@ -56,7 +48,7 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin shouldBreak = shouldBreak || frame->breakWhenFinished; mStackTracePop(stack); frame = mStackTraceGetFrame(stack, 0); - } while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMGetStack(frame->regs)); + } while (frame && frame->frameBaseAddress < (uint32_t) cpu->gprs[ARM_SP] && currentStack == ARMSelectBank(FRAME_PRIV(frame))); if (shouldBreak) { struct mDebuggerEntryInfo debuggerInfo = { .address = pc, @@ -95,7 +87,7 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin return false; } - bool isCall = (info.branchType & ARM_BRANCH_LINKED); + bool isCall = info.branchType & ARM_BRANCH_LINKED; uint32_t destAddress; if (interrupt && !isCall) { @@ -126,8 +118,9 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin bool isBranch = ARMInstructionIsBranch(info.mnemonic); int reg = (isBranch ? info.op1.reg : info.op2.reg); destAddress = cpu->gprs[reg]; - if ((info.operandFormat & ARM_OPERAND_MEMORY_2) && (info.branchType & ARM_BRANCH_INDIRECT)) { - destAddress = cpu->memory.load32(cpu, destAddress, NULL); + if (!isBranch && (info.branchType & ARM_BRANCH_INDIRECT) && info.op1.reg == ARM_PC && info.operandFormat & ARM_OPERAND_MEMORY_2) { + uint32_t ptrAddress = ARMResolveMemoryAccess(&info, &cpu->regs, pc); + destAddress = cpu->memory.load32(cpu, ptrAddress, NULL); } if (isBranch || (info.op1.reg == ARM_PC && !isMovPcLr)) { // ARMv4 doesn't have the BLX opcode, so it uses an assignment to LR before a BX for that purpose. @@ -166,7 +159,7 @@ static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uin return false; } } else if (!interrupt) { - if (frame && currentStack == ARMGetStack(frame->regs)) { + if (frame && currentStack == ARMSelectBank(FRAME_PRIV(frame))) { mStackTracePop(stack); } if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) { diff --git a/src/arm/decoder.c b/src/arm/decoder.c index 8ebdbcaa0..3cb87d77c 100644 --- a/src/arm/decoder.c +++ b/src/arm/decoder.c @@ -488,3 +488,46 @@ int ARMDisassemble(struct ARMInstructionInfo* info, uint32_t pc, char* buffer, i buffer[blen - 1] = '\0'; return total; } + +uint32_t ARMResolveMemoryAccess(struct ARMInstructionInfo* info, struct ARMRegisterFile* regs, uint32_t pc) { + uint32_t address = 0; + int32_t offset = 0; + if (info->memory.format & ARM_MEMORY_REGISTER_BASE) { + if (info->memory.baseReg == ARM_PC && info->memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) { + address = pc; + } else { + address = regs->gprs[info->memory.baseReg]; + } + } + if (info->memory.format & ARM_MEMORY_POST_INCREMENT) { + return address; + } + if (info->memory.format & ARM_MEMORY_IMMEDIATE_OFFSET) { + offset = info->memory.offset.immediate; + } else if (info->memory.format & ARM_MEMORY_REGISTER_OFFSET) { + offset = info->memory.offset.reg == ARM_PC ? pc : regs->gprs[info->memory.offset.reg]; + } + if (info->memory.format & ARM_MEMORY_SHIFTED_OFFSET) { + uint8_t shiftSize = info->memory.offset.shifterImm; + switch (info->memory.offset.shifterOp) { + case ARM_SHIFT_LSL: + offset <<= shiftSize; + break; + case ARM_SHIFT_LSR: + offset = ((uint32_t) offset) >> shiftSize; + break; + case ARM_SHIFT_ASR: + offset >>= shiftSize; + break; + case ARM_SHIFT_ROR: + offset = ROR(offset, shiftSize); + break; + case ARM_SHIFT_RRX: + offset = (regs->cpsr.c << 31) | ((uint32_t) offset >> 1); + break; + default: + break; + }; + } + return address + (info->memory.format & ARM_MEMORY_OFFSET_SUBTRACT ? -offset : offset); +}