mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'master' into medusa
This commit is contained in:
commit
206ba1129d
4
CHANGES
4
CHANGES
|
@ -32,6 +32,7 @@ Features:
|
|||
- e-Reader card scanning
|
||||
- Add WebP and APNG recording
|
||||
- Support for unlicensed Pokemon Jade/Diamond Game Boy mapper
|
||||
- Stack tracing tools in ARM debugger (by ahigerd)
|
||||
Emulation fixes:
|
||||
- ARM: Fix ALU reading PC after shifting
|
||||
- ARM: Fix STR storing PC after address calculation
|
||||
|
@ -52,12 +53,14 @@ Emulation fixes:
|
|||
- GBA DMA: Linger last DMA on bus (fixes mgba.io/i/301 and mgba.io/i/1320)
|
||||
- GBA Memory: Improve gamepak prefetch timing
|
||||
- GBA Memory: Stall on VRAM access in mode 2 (fixes mgba.io/i/190)
|
||||
- GBA Savedata: Fix potential corruption when loading a 1Mbit flash save
|
||||
- GBA SIO: Fix copying Normal mode transfer values
|
||||
- GBA Video: Latch scanline at end of Hblank (fixes mgba.io/i/1319)
|
||||
- GBA Video: Fix Hblank timing
|
||||
- GBA Video: Fix invalid read in mode 4 mosaic
|
||||
- GBA Video: Fix color of disabled screen
|
||||
- SM83: Emulate HALT bug
|
||||
- SM83: Fix flags on little endian PowerPC
|
||||
Other fixes:
|
||||
- All: Improve export headers (fixes mgba.io/i/1738)
|
||||
- All: Correct format strings for some numbers on Windows (fixes mgba.io/i/1794)
|
||||
|
@ -77,6 +80,7 @@ Other fixes:
|
|||
- Qt: Fix Italian RTC translation (fixes mgba.io/i/1798)
|
||||
- Qt: Add missing option for Wisdom Tree in overrides list
|
||||
- Util: Fix crash if PNG header fails to write
|
||||
- SM83: Simplify register pair access on big endian
|
||||
Misc:
|
||||
- Debugger: Keep track of global cycle count
|
||||
- FFmpeg: Add looping option for GIF/APNG
|
||||
|
|
|
@ -25,7 +25,7 @@ if(NOT MSVC)
|
|||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-format")
|
||||
endif()
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /wd4003 /wd4244 /wd4146 /wd4267")
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED LIBMGBA_ONLY)
|
||||
|
|
|
@ -13,6 +13,7 @@ CXX_GUARD_START
|
|||
#include <mgba/core/cpu.h>
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba-util/vector.h>
|
||||
#include <mgba/internal/debugger/stack-trace.h>
|
||||
|
||||
mLOG_DECLARE_CATEGORY(DEBUGGER);
|
||||
|
||||
|
@ -50,7 +51,8 @@ enum mDebuggerEntryReason {
|
|||
DEBUGGER_ENTER_ATTACHED,
|
||||
DEBUGGER_ENTER_BREAKPOINT,
|
||||
DEBUGGER_ENTER_WATCHPOINT,
|
||||
DEBUGGER_ENTER_ILLEGAL_OP
|
||||
DEBUGGER_ENTER_ILLEGAL_OP,
|
||||
DEBUGGER_ENTER_STACK
|
||||
};
|
||||
|
||||
struct mDebuggerEntryInfo {
|
||||
|
@ -67,6 +69,10 @@ struct mDebuggerEntryInfo {
|
|||
uint32_t opcode;
|
||||
enum mBreakpointType breakType;
|
||||
} bp;
|
||||
|
||||
struct {
|
||||
enum mStackTraceMode traceType;
|
||||
} st;
|
||||
} type;
|
||||
ssize_t pointId;
|
||||
};
|
||||
|
@ -114,6 +120,10 @@ struct mDebuggerPlatform {
|
|||
bool (*getRegister)(struct mDebuggerPlatform*, const char* name, int32_t* value);
|
||||
bool (*setRegister)(struct mDebuggerPlatform*, const char* name, int32_t value);
|
||||
bool (*lookupIdentifier)(struct mDebuggerPlatform*, const char* name, int32_t* value, int* segment);
|
||||
|
||||
uint32_t (*getStackTraceMode)(struct mDebuggerPlatform*);
|
||||
void (*setStackTraceMode)(struct mDebuggerPlatform*, uint32_t mode);
|
||||
bool (*updateStackTrace)(struct mDebuggerPlatform* d);
|
||||
};
|
||||
|
||||
struct mDebugger {
|
||||
|
@ -123,6 +133,7 @@ struct mDebugger {
|
|||
enum mDebuggerType type;
|
||||
struct mCore* core;
|
||||
struct mScriptBridge* bridge;
|
||||
struct mStackTrace stackTrace;
|
||||
|
||||
void (*init)(struct mDebugger*);
|
||||
void (*deinit)(struct mDebugger*);
|
||||
|
|
|
@ -70,7 +70,7 @@ struct ARMCore;
|
|||
|
||||
union PSR {
|
||||
struct {
|
||||
#if defined(__BIG_ENDIAN__)
|
||||
#ifdef __BIG_ENDIAN__
|
||||
unsigned n : 1;
|
||||
unsigned z : 1;
|
||||
unsigned c : 1;
|
||||
|
@ -96,7 +96,7 @@ union PSR {
|
|||
};
|
||||
|
||||
struct {
|
||||
#if defined(__BIG_ENDIAN__)
|
||||
#ifdef __BIG_ENDIAN__
|
||||
uint8_t flags;
|
||||
uint8_t status;
|
||||
uint8_t extension;
|
||||
|
@ -231,10 +231,21 @@ struct ARMCP15 {
|
|||
} r9;
|
||||
};
|
||||
|
||||
#define ARM_REGISTER_FILE struct { \
|
||||
int32_t gprs[16]; \
|
||||
union PSR cpsr; \
|
||||
union PSR spsr; \
|
||||
}
|
||||
|
||||
struct ARMRegisterFile {
|
||||
ARM_REGISTER_FILE;
|
||||
};
|
||||
|
||||
struct ARMCore {
|
||||
int32_t gprs[16];
|
||||
union PSR cpsr;
|
||||
union PSR spsr;
|
||||
union {
|
||||
struct ARMRegisterFile regs;
|
||||
ARM_REGISTER_FILE;
|
||||
};
|
||||
|
||||
int32_t cycles;
|
||||
int32_t nextEvent;
|
||||
|
@ -259,6 +270,7 @@ struct ARMCore {
|
|||
size_t numComponents;
|
||||
struct mCPUComponent** components;
|
||||
};
|
||||
#undef ARM_REGISTER_FILE
|
||||
|
||||
void ARMInit(struct ARMCore* cpu);
|
||||
void ARMDeinit(struct ARMCore* cpu);
|
||||
|
|
|
@ -36,6 +36,7 @@ struct ARMDebugger {
|
|||
struct ARMMemory originalMemory;
|
||||
|
||||
ssize_t nextId;
|
||||
uint32_t stackTraceMode;
|
||||
|
||||
void (*entered)(struct mDebugger*, enum mDebuggerEntryReason, struct mDebuggerEntryInfo*);
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ CXX_GUARD_START
|
|||
|
||||
// Bit 0: a register is involved with this operand
|
||||
// Bit 1: an immediate is invovled with this operand
|
||||
// Bit 2: a memory access is invovled with this operand
|
||||
// Bit 2: a memory access is involved with this operand
|
||||
// Bit 3: the destination of this operand is affected by this opcode
|
||||
// Bit 4: this operand is shifted by a register
|
||||
// Bit 5: this operand is shifted by an immediate
|
||||
|
|
|
@ -99,15 +99,12 @@ static inline void _ARMReadCPSR(struct ARMCore* cpu) {
|
|||
cpu->irqh.readCPSR(cpu);
|
||||
}
|
||||
|
||||
static inline uint32_t _ARMInstructionLength(struct ARMCore* cpu) {
|
||||
return cpu->cpsr.t == MODE_ARM ? WORD_SIZE_ARM : WORD_SIZE_THUMB;
|
||||
}
|
||||
|
||||
static inline uint32_t _ARMPCAddress(struct ARMCore* cpu) {
|
||||
int instructionLength;
|
||||
enum ExecutionMode mode = cpu->cpsr.t;
|
||||
if (mode == MODE_ARM) {
|
||||
instructionLength = WORD_SIZE_ARM;
|
||||
} else {
|
||||
instructionLength = WORD_SIZE_THUMB;
|
||||
}
|
||||
return cpu->gprs[ARM_PC] - instructionLength * 2;
|
||||
return cpu->gprs[ARM_PC] - _ARMInstructionLength(cpu) * 2;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
/* Copyright (c) 2013-2020 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef STACK_TRACE_H
|
||||
#define STACK_TRACE_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/cpu.h>
|
||||
#include <mgba/core/log.h>
|
||||
#include <mgba-util/vector.h>
|
||||
|
||||
enum mStackTraceMode {
|
||||
STACK_TRACE_DISABLED = 0,
|
||||
STACK_TRACE_ENABLED = 1,
|
||||
STACK_TRACE_BREAK_ON_RETURN = 2,
|
||||
STACK_TRACE_BREAK_ON_CALL = 4,
|
||||
STACK_TRACE_BREAK_ON_BOTH = STACK_TRACE_BREAK_ON_RETURN | STACK_TRACE_BREAK_ON_CALL
|
||||
};
|
||||
|
||||
struct mStackFrame {
|
||||
uint32_t callAddress;
|
||||
uint32_t entryAddress;
|
||||
uint32_t frameBaseAddress;
|
||||
void* regs;
|
||||
bool finished;
|
||||
bool breakWhenFinished;
|
||||
bool interrupt;
|
||||
};
|
||||
|
||||
DECLARE_VECTOR(mStackFrames, struct mStackFrame);
|
||||
|
||||
struct mStackTrace {
|
||||
struct mStackFrames stack;
|
||||
size_t registersSize;
|
||||
|
||||
void (*formatRegisters)(struct mStackFrame* frame, char* out, size_t* length);
|
||||
};
|
||||
|
||||
void mStackTraceInit(struct mStackTrace* stack, size_t registersSize);
|
||||
void mStackTraceDeinit(struct mStackTrace* stack);
|
||||
|
||||
void mStackTraceClear(struct mStackTrace* stack);
|
||||
size_t mStackTraceGetDepth(struct mStackTrace* stack);
|
||||
struct mStackFrame* mStackTracePush(struct mStackTrace* stack, uint32_t pc, uint32_t destAddress, uint32_t sp, void* regs);
|
||||
struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, uint32_t frame);
|
||||
void mStackTraceFormatFrame(struct mStackTrace* stack, uint32_t frame, char* out, size_t* length);
|
||||
void mStackTracePop(struct mStackTrace* stack);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -18,7 +18,7 @@ struct SM83Core;
|
|||
#pragma pack(push, 1)
|
||||
union FlagRegister {
|
||||
struct {
|
||||
#if defined(__POWERPC__) || defined(__PPC__)
|
||||
#ifdef __BIG_ENDIAN__
|
||||
unsigned z : 1;
|
||||
unsigned n : 1;
|
||||
unsigned h : 1;
|
||||
|
@ -74,39 +74,62 @@ struct SM83InterruptHandler {
|
|||
void (*hitIllegal)(struct SM83Core* cpu);
|
||||
};
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
#define SM83_REGISTER_PAIR(HIGH, LOW) union { \
|
||||
struct { \
|
||||
uint8_t HIGH; \
|
||||
uint8_t LOW; \
|
||||
}; \
|
||||
uint16_t HIGH ## LOW; \
|
||||
}
|
||||
|
||||
#define SM83_AF_REGISTER union { \
|
||||
struct { \
|
||||
uint8_t a; \
|
||||
union FlagRegister f; \
|
||||
}; \
|
||||
uint16_t af; \
|
||||
}
|
||||
#else
|
||||
#define SM83_REGISTER_PAIR(HIGH, LOW) union { \
|
||||
struct { \
|
||||
uint8_t LOW; \
|
||||
uint8_t HIGH; \
|
||||
}; \
|
||||
uint16_t HIGH ## LOW; \
|
||||
}
|
||||
|
||||
#define SM83_AF_REGISTER union { \
|
||||
struct { \
|
||||
union FlagRegister f; \
|
||||
uint8_t a; \
|
||||
}; \
|
||||
uint16_t af; \
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SM83_REGISTER_FILE struct { \
|
||||
SM83_AF_REGISTER; \
|
||||
SM83_REGISTER_PAIR(b, c); \
|
||||
SM83_REGISTER_PAIR(d, e); \
|
||||
SM83_REGISTER_PAIR(h, l); \
|
||||
uint16_t sp; \
|
||||
uint16_t pc; \
|
||||
}
|
||||
|
||||
struct SM83RegisterFile {
|
||||
#pragma pack(push, 1)
|
||||
SM83_REGISTER_FILE;
|
||||
#pragma pack(pop)
|
||||
};
|
||||
|
||||
struct SM83Core {
|
||||
#pragma pack(push, 1)
|
||||
union {
|
||||
struct {
|
||||
union FlagRegister f;
|
||||
uint8_t a;
|
||||
};
|
||||
uint16_t af;
|
||||
struct SM83RegisterFile regs;
|
||||
SM83_REGISTER_FILE;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
union {
|
||||
struct {
|
||||
uint8_t c;
|
||||
uint8_t b;
|
||||
};
|
||||
uint16_t bc;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
uint8_t e;
|
||||
uint8_t d;
|
||||
};
|
||||
uint16_t de;
|
||||
};
|
||||
union {
|
||||
struct {
|
||||
uint8_t l;
|
||||
uint8_t h;
|
||||
};
|
||||
uint16_t hl;
|
||||
};
|
||||
uint16_t sp;
|
||||
uint16_t pc;
|
||||
|
||||
uint16_t index;
|
||||
|
||||
|
@ -129,6 +152,7 @@ struct SM83Core {
|
|||
size_t numComponents;
|
||||
struct mCPUComponent** components;
|
||||
};
|
||||
#undef SM83_REGISTER_FILE
|
||||
|
||||
void SM83Init(struct SM83Core* cpu);
|
||||
void SM83Deinit(struct SM83Core* cpu);
|
||||
|
|
|
@ -11,9 +11,102 @@
|
|||
#include <mgba/internal/arm/isa-inlines.h>
|
||||
#include <mgba/internal/arm/debugger/memory-debugger.h>
|
||||
#include <mgba/internal/debugger/parser.h>
|
||||
#include <mgba/internal/debugger/stack-trace.h>
|
||||
#include <mgba-util/math.h>
|
||||
|
||||
DEFINE_VECTOR(ARMDebugBreakpointList, struct ARMDebugBreakpoint);
|
||||
|
||||
static bool ARMDebuggerUpdateStackTraceInternal(struct mDebuggerPlatform* d, uint32_t pc) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
struct ARMCore* cpu = debugger->cpu;
|
||||
struct ARMInstructionInfo info;
|
||||
uint32_t instruction = cpu->prefetch[0];
|
||||
struct mStackTrace* stack = &d->p->stackTrace;
|
||||
bool interrupt = false;
|
||||
ARMDecodeARM(instruction, &info);
|
||||
|
||||
if (_ARMModeHasSPSR(cpu->cpsr.priv)) {
|
||||
struct mStackFrame* irqFrame = mStackTraceGetFrame(stack, 0);
|
||||
uint32_t ivtBase = ARMControlRegIsVE(cpu->cp15.r1.c0) ? 0xFFFF0000 : 0x00000000;
|
||||
if (ivtBase <= pc && pc < ivtBase + 0x20 && !(irqFrame && _ARMModeHasSPSR(((struct ARMRegisterFile*) irqFrame->regs)->cpsr.priv))) {
|
||||
// TODO: Potential enhancement opportunity: add break-on-exception mode
|
||||
irqFrame = mStackTracePush(stack, pc, pc, cpu->gprs[ARM_SP], &cpu->regs);
|
||||
irqFrame->interrupt = true;
|
||||
interrupt = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (info.branchType == ARM_BRANCH_NONE && !interrupt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct mStackFrame* frame = mStackTraceGetFrame(stack, 0);
|
||||
bool isCall = (info.branchType & ARM_BRANCH_LINKED);
|
||||
uint32_t destAddress;
|
||||
|
||||
if (frame && frame->finished) {
|
||||
mStackTracePop(stack);
|
||||
frame = NULL;
|
||||
}
|
||||
|
||||
if (interrupt && info.branchType == ARM_BRANCH_NONE) {
|
||||
// The stack frame was already pushed up above, so there's no
|
||||
// action necessary here, but we still want to check for a
|
||||
// breakpoint down below.
|
||||
//
|
||||
// The first instruction could possibly be a call, which would
|
||||
// need ANOTHER stack frame, so only skip if it's not.
|
||||
} else if (info.operandFormat & ARM_OPERAND_MEMORY_1) {
|
||||
// This is most likely ldmia ..., {..., pc}, which is a function return.
|
||||
// To find which stack slot holds the return address, count the number of set bits.
|
||||
int regCount = popcount32(info.op1.immediate);
|
||||
uint32_t baseAddress = cpu->gprs[info.memory.baseReg] + ((regCount - 1) << 2);
|
||||
destAddress = cpu->memory.load32(cpu, baseAddress, NULL);
|
||||
} else if (info.operandFormat & ARM_OPERAND_IMMEDIATE_1) {
|
||||
if (!isCall) {
|
||||
return false;
|
||||
}
|
||||
destAddress = info.op1.immediate + cpu->gprs[ARM_PC];
|
||||
} else if (info.operandFormat & ARM_OPERAND_REGISTER_1) {
|
||||
if (!isCall && info.op1.reg != ARM_LR && !(_ARMModeHasSPSR(cpu->cpsr.priv) && info.op1.reg == ARM_PC)) {
|
||||
return false;
|
||||
}
|
||||
destAddress = cpu->gprs[info.op1.reg];
|
||||
} else {
|
||||
mLOG(DEBUGGER, ERROR, "Unknown branch operand in stack trace");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.branchType & ARM_BRANCH_INDIRECT) {
|
||||
destAddress = cpu->memory.load32(cpu, destAddress, NULL);
|
||||
}
|
||||
|
||||
if (isCall) {
|
||||
int instructionLength = _ARMInstructionLength(debugger->cpu);
|
||||
frame = mStackTracePush(stack, pc, destAddress + instructionLength, cpu->gprs[ARM_SP], &cpu->regs);
|
||||
if (!(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_CALL)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
frame = mStackTraceGetFrame(stack, 0);
|
||||
if (!frame) {
|
||||
return false;
|
||||
}
|
||||
if (!frame->breakWhenFinished && !(debugger->stackTraceMode & STACK_TRACE_BREAK_ON_RETURN)) {
|
||||
mStackTracePop(stack);
|
||||
return false;
|
||||
}
|
||||
frame->finished = true;
|
||||
}
|
||||
struct mDebuggerEntryInfo debuggerInfo = {
|
||||
.address = pc,
|
||||
.type.st.traceType = isCall ? STACK_TRACE_BREAK_ON_CALL : STACK_TRACE_BREAK_ON_RETURN,
|
||||
.pointId = 0
|
||||
};
|
||||
mDebuggerEnter(d->p, DEBUGGER_ENTER_STACK, &debuggerInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct ARMDebugBreakpoint* _lookupBreakpoint(struct ARMDebugBreakpointList* breakpoints, uint32_t address) {
|
||||
size_t i;
|
||||
for (i = 0; i < ARMDebugBreakpointListSize(breakpoints); ++i) {
|
||||
|
@ -40,14 +133,12 @@ static void _destroyWatchpoint(struct mWatchpoint* watchpoint) {
|
|||
|
||||
static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform* d) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
int instructionLength;
|
||||
enum ExecutionMode mode = debugger->cpu->cpsr.t;
|
||||
if (mode == MODE_ARM) {
|
||||
instructionLength = WORD_SIZE_ARM;
|
||||
} else {
|
||||
instructionLength = WORD_SIZE_THUMB;
|
||||
int instructionLength = _ARMInstructionLength(debugger->cpu);
|
||||
uint32_t pc = debugger->cpu->gprs[ARM_PC] - instructionLength;
|
||||
if (debugger->stackTraceMode != STACK_TRACE_DISABLED && ARMDebuggerUpdateStackTraceInternal(d, pc)) {
|
||||
return;
|
||||
}
|
||||
struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, debugger->cpu->gprs[ARM_PC] - instructionLength);
|
||||
struct ARMDebugBreakpoint* breakpoint = _lookupBreakpoint(&debugger->breakpoints, pc);
|
||||
if (!breakpoint) {
|
||||
return;
|
||||
}
|
||||
|
@ -79,8 +170,13 @@ static void ARMDebuggerListWatchpoints(struct mDebuggerPlatform*, struct mWatchp
|
|||
static void ARMDebuggerCheckBreakpoints(struct mDebuggerPlatform*);
|
||||
static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform*);
|
||||
static void ARMDebuggerTrace(struct mDebuggerPlatform*, char* out, size_t* length);
|
||||
static void ARMDebuggerFormatRegisters(struct ARMRegisterFile* regs, char* out, size_t* length);
|
||||
static void ARMDebuggerFrameFormatRegisters(struct mStackFrame* frame, char* out, size_t* length);
|
||||
static bool ARMDebuggerGetRegister(struct mDebuggerPlatform*, const char* name, int32_t* value);
|
||||
static bool ARMDebuggerSetRegister(struct mDebuggerPlatform*, const char* name, int32_t value);
|
||||
static uint32_t ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform*);
|
||||
static void ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform*, uint32_t);
|
||||
static bool ARMDebuggerUpdateStackTrace(struct mDebuggerPlatform* d);
|
||||
|
||||
struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
|
||||
struct mDebuggerPlatform* platform = (struct mDebuggerPlatform*) malloc(sizeof(struct ARMDebugger));
|
||||
|
@ -97,6 +193,9 @@ struct mDebuggerPlatform* ARMDebuggerPlatformCreate(void) {
|
|||
platform->trace = ARMDebuggerTrace;
|
||||
platform->getRegister = ARMDebuggerGetRegister;
|
||||
platform->setRegister = ARMDebuggerSetRegister;
|
||||
platform->getStackTraceMode = ARMDebuggerGetStackTraceMode;
|
||||
platform->setStackTraceMode = ARMDebuggerSetStackTraceMode;
|
||||
platform->updateStackTrace = ARMDebuggerUpdateStackTrace;
|
||||
return platform;
|
||||
}
|
||||
|
||||
|
@ -105,9 +204,13 @@ void ARMDebuggerInit(void* cpu, struct mDebuggerPlatform* platform) {
|
|||
debugger->cpu = cpu;
|
||||
debugger->originalMemory = debugger->cpu->memory;
|
||||
debugger->nextId = 1;
|
||||
debugger->stackTraceMode = STACK_TRACE_DISABLED;
|
||||
ARMDebugBreakpointListInit(&debugger->breakpoints, 0);
|
||||
ARMDebugBreakpointListInit(&debugger->swBreakpoints, 0);
|
||||
mWatchpointListInit(&debugger->watchpoints, 0);
|
||||
struct mStackTrace* stack = &platform->p->stackTrace;
|
||||
mStackTraceInit(stack, sizeof(struct ARMRegisterFile));
|
||||
stack->formatRegisters = ARMDebuggerFrameFormatRegisters;
|
||||
}
|
||||
|
||||
void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
|
||||
|
@ -133,6 +236,7 @@ void ARMDebuggerDeinit(struct mDebuggerPlatform* platform) {
|
|||
}
|
||||
ARMDebugBreakpointListDeinit(&debugger->swBreakpoints);
|
||||
mWatchpointListDeinit(&debugger->watchpoints);
|
||||
mStackTraceDeinit(&platform->p->stackTrace);
|
||||
}
|
||||
|
||||
static void ARMDebuggerEnter(struct mDebuggerPlatform* platform, enum mDebuggerEntryReason reason, struct mDebuggerEntryInfo* info) {
|
||||
|
@ -261,7 +365,7 @@ static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBrea
|
|||
++i;
|
||||
} else if (sw) {
|
||||
*b = sw->d;
|
||||
++s;
|
||||
++s;
|
||||
} else {
|
||||
abort(); // Should be unreachable
|
||||
}
|
||||
|
@ -270,7 +374,7 @@ static void ARMDebuggerListBreakpoints(struct mDebuggerPlatform* d, struct mBrea
|
|||
|
||||
static bool ARMDebuggerHasBreakpoints(struct mDebuggerPlatform* d) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
return ARMDebugBreakpointListSize(&debugger->breakpoints) || mWatchpointListSize(&debugger->watchpoints);
|
||||
return ARMDebugBreakpointListSize(&debugger->breakpoints) || mWatchpointListSize(&debugger->watchpoints) || debugger->stackTraceMode != STACK_TRACE_DISABLED;
|
||||
}
|
||||
|
||||
static ssize_t ARMDebuggerSetWatchpoint(struct mDebuggerPlatform* d, const struct mWatchpoint* info) {
|
||||
|
@ -320,12 +424,23 @@ static void ARMDebuggerTrace(struct mDebuggerPlatform* d, char* out, size_t* len
|
|||
}
|
||||
}
|
||||
|
||||
*length = snprintf(out, *length, "%08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X cpsr: %08X | %s",
|
||||
cpu->gprs[0], cpu->gprs[1], cpu->gprs[2], cpu->gprs[3],
|
||||
cpu->gprs[4], cpu->gprs[5], cpu->gprs[6], cpu->gprs[7],
|
||||
cpu->gprs[8], cpu->gprs[9], cpu->gprs[10], cpu->gprs[11],
|
||||
cpu->gprs[12], cpu->gprs[13], cpu->gprs[14], cpu->gprs[15],
|
||||
cpu->cpsr.packed, disassembly);
|
||||
size_t regStringLen = *length;
|
||||
ARMDebuggerFormatRegisters(&cpu->regs, out, ®StringLen);
|
||||
regStringLen += snprintf(out + regStringLen, *length - regStringLen, " | %s", disassembly);
|
||||
*length = regStringLen;
|
||||
}
|
||||
|
||||
static void ARMDebuggerFormatRegisters(struct ARMRegisterFile* regs, char* out, size_t* length) {
|
||||
*length = snprintf(out, *length, "%08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X %08X cpsr: %08X",
|
||||
regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3],
|
||||
regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7],
|
||||
regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11],
|
||||
regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15],
|
||||
regs->cpsr.packed);
|
||||
}
|
||||
|
||||
static void ARMDebuggerFrameFormatRegisters(struct mStackFrame* frame, char* out, size_t* length) {
|
||||
ARMDebuggerFormatRegisters(frame->regs, out, length);
|
||||
}
|
||||
|
||||
bool ARMDebuggerGetRegister(struct mDebuggerPlatform* d, const char* name, int32_t* value) {
|
||||
|
@ -403,3 +518,28 @@ bool ARMDebuggerSetRegister(struct mDebuggerPlatform* d, const char* name, int32
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static uint32_t ARMDebuggerGetStackTraceMode(struct mDebuggerPlatform* d) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
return debugger->stackTraceMode;
|
||||
}
|
||||
|
||||
static void ARMDebuggerSetStackTraceMode(struct mDebuggerPlatform* d, uint32_t mode) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
struct mStackTrace* stack = &d->p->stackTrace;
|
||||
if (mode == STACK_TRACE_DISABLED && debugger->stackTraceMode != STACK_TRACE_DISABLED) {
|
||||
mStackTraceClear(stack);
|
||||
}
|
||||
debugger->stackTraceMode = mode;
|
||||
}
|
||||
|
||||
static bool ARMDebuggerUpdateStackTrace(struct mDebuggerPlatform* d) {
|
||||
struct ARMDebugger* debugger = (struct ARMDebugger*) d;
|
||||
int instructionLength = _ARMInstructionLength(debugger->cpu);
|
||||
uint32_t pc = debugger->cpu->gprs[ARM_PC] - instructionLength;
|
||||
if (debugger->stackTraceMode != STACK_TRACE_DISABLED) {
|
||||
return ARMDebuggerUpdateStackTraceInternal(d, pc);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
cpu->cpsr.v = ARM_V_SUBTRACTION(M, N, D);
|
||||
|
||||
#define THUMB_SUBTRACTION_CARRY_S(M, N, D, C) \
|
||||
cpu->cpsr.flags = 0; \
|
||||
cpu->cpsr.n = ARM_SIGN(D); \
|
||||
cpu->cpsr.z = !(D); \
|
||||
cpu->cpsr.c = ARM_BORROW_FROM_CARRY(M, N, D, C); \
|
||||
|
|
|
@ -3,7 +3,8 @@ set(SOURCE_FILES
|
|||
cli-debugger.c
|
||||
debugger.c
|
||||
parser.c
|
||||
symbols.c)
|
||||
symbols.c
|
||||
stack-trace.c)
|
||||
|
||||
set(TEST_FILES
|
||||
test/lexer.c
|
||||
|
@ -13,4 +14,4 @@ source_group("Debugger" FILES ${SOURCE_FILES})
|
|||
source_group("Debugger tests" FILES ${TEST_FILES})
|
||||
|
||||
export_directory(DEBUGGER SOURCE_FILES)
|
||||
export_directory(DEBUGGER_TEST TEST_FILES)
|
||||
export_directory(DEBUGGER_TEST TEST_FILES)
|
||||
|
|
|
@ -69,12 +69,17 @@ static void _dumpWord(struct CLIDebugger*, struct CLIDebugVector*);
|
|||
#ifdef ENABLE_SCRIPTING
|
||||
static void _source(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
#endif
|
||||
static void _backtrace(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _finish(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
static void _setStackTraceMode(struct CLIDebugger*, struct CLIDebugVector*);
|
||||
|
||||
static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
||||
{ "backtrace", _backtrace, "i", "Print backtrace of all or specified frames" },
|
||||
{ "break", _setBreakpoint, "Is", "Set a breakpoint" },
|
||||
{ "continue", _continue, "", "Continue execution" },
|
||||
{ "delete", _clearBreakpoint, "I", "Delete a breakpoint or watchpoint" },
|
||||
{ "disassemble", _disassemble, "Ii", "Disassemble instructions" },
|
||||
{ "finish", _finish, "", "Execute until current stack frame returns" },
|
||||
{ "help", _printHelp, "S", "Print help" },
|
||||
{ "listb", _listBreakpoints, "", "List breakpoints" },
|
||||
{ "listw", _listWatchpoints, "", "List watchpoints" },
|
||||
|
@ -87,6 +92,7 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
|||
{ "r/1", _readByte, "I", "Read a byte from a specified offset" },
|
||||
{ "r/2", _readHalfword, "I", "Read a halfword from a specified offset" },
|
||||
{ "r/4", _readWord, "I", "Read a word from a specified offset" },
|
||||
{ "stack", _setStackTraceMode, "S", "Changes the stack tracing mode" },
|
||||
{ "status", _printStatus, "", "Print the current status" },
|
||||
{ "trace", _trace, "Is", "Trace a number of instructions" },
|
||||
{ "w/1", _writeByte, "II", "Write a byte at a specified offset" },
|
||||
|
@ -111,6 +117,7 @@ static struct CLIDebuggerCommandSummary _debuggerCommands[] = {
|
|||
|
||||
static struct CLIDebuggerCommandAlias _debuggerCommandAliases[] = {
|
||||
{ "b", "break" },
|
||||
{ "bt", "backtrace" },
|
||||
{ "c", "continue" },
|
||||
{ "d", "delete" },
|
||||
{ "dis", "disassemble" },
|
||||
|
@ -153,6 +160,18 @@ static void _breakInto(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
|
|||
}
|
||||
#endif
|
||||
|
||||
static bool CLIDebuggerCheckTraceMode(struct CLIDebugger* debugger, bool requireEnabled) {
|
||||
struct mDebuggerPlatform* platform = debugger->d.platform;
|
||||
if (!platform->getStackTraceMode) {
|
||||
debugger->backend->printf(debugger->backend, "Stack tracing is not supported by this platform.\n");
|
||||
return false;
|
||||
} else if (requireEnabled && platform->getStackTraceMode(platform) == STACK_TRACE_DISABLED) {
|
||||
debugger->backend->printf(debugger->backend, "Stack tracing is not enabled.\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _continue(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
debugger->d.state = debugger->traceRemaining != 0 ? DEBUGGER_CALLBACK : DEBUGGER_RUNNING;
|
||||
|
@ -160,7 +179,11 @@ static void _continue(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
|
||||
static void _next(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
struct mDebuggerPlatform* platform = debugger->d.platform;
|
||||
debugger->d.core->step(debugger->d.core);
|
||||
if (platform->getStackTraceMode && platform->getStackTraceMode(platform) != STACK_TRACE_DISABLED) {
|
||||
platform->updateStackTrace(platform);
|
||||
}
|
||||
_printStatus(debugger, 0);
|
||||
}
|
||||
|
||||
|
@ -327,6 +350,7 @@ static void _readByte(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
|||
|
||||
static void _reset(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
mStackTraceClear(&debugger->d.stackTrace);
|
||||
debugger->d.core->reset(debugger->d.core);
|
||||
_printStatus(debugger, 0);
|
||||
}
|
||||
|
@ -979,7 +1003,7 @@ static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason r
|
|||
if (info->pointId > 0) {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit breakpoint %" PRIz "i at 0x%08X\n", info->pointId, info->address);
|
||||
} else {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit unknown breakpoint at 0x%08X\n", info->address);
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit unknown breakpoint at 0x%08X\n", info->address);
|
||||
}
|
||||
} else {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit breakpoint\n");
|
||||
|
@ -1003,6 +1027,18 @@ static void _reportEntry(struct mDebugger* debugger, enum mDebuggerEntryReason r
|
|||
cliDebugger->backend->printf(cliDebugger->backend, "Hit illegal opcode\n");
|
||||
}
|
||||
break;
|
||||
case DEBUGGER_ENTER_STACK:
|
||||
if (info) {
|
||||
if (info->type.st.traceType == STACK_TRACE_BREAK_ON_CALL) {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit function call at at 0x%08X\n", info->address);
|
||||
} else {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit function return at at 0x%08X\n", info->address);
|
||||
}
|
||||
} else {
|
||||
cliDebugger->backend->printf(cliDebugger->backend, "Hit function call or return\n");
|
||||
}
|
||||
_backtrace(cliDebugger, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1131,3 +1167,68 @@ bool CLIDebuggerTabComplete(struct CLIDebugger* debugger, const char* token, boo
|
|||
debugger->backend->lineAppend(debugger->backend, " ");
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _backtrace(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
if (!CLIDebuggerCheckTraceMode(debugger, true)) {
|
||||
return;
|
||||
}
|
||||
struct mStackTrace* stack = &debugger->d.stackTrace;
|
||||
ssize_t frames = mStackTraceGetDepth(stack);
|
||||
if (dv && dv->type == CLIDV_INT_TYPE && dv->intValue < frames) {
|
||||
frames = dv->intValue;
|
||||
}
|
||||
ssize_t i;
|
||||
for (i = 0; i < frames; ++i) {
|
||||
char trace[1024];
|
||||
size_t traceSize = sizeof(trace) - 2;
|
||||
mStackTraceFormatFrame(stack, i, trace, &traceSize);
|
||||
debugger->backend->printf(debugger->backend, "%s", trace);
|
||||
}
|
||||
}
|
||||
|
||||
static void _finish(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
UNUSED(dv);
|
||||
if (!CLIDebuggerCheckTraceMode(debugger, true)) {
|
||||
return;
|
||||
}
|
||||
struct mStackTrace* stack = &debugger->d.stackTrace;
|
||||
struct mStackFrame* frame = mStackTraceGetFrame(stack, 0);
|
||||
if (!frame) {
|
||||
debugger->backend->printf(debugger->backend, "No current stack frame.\n");
|
||||
return;
|
||||
}
|
||||
frame->breakWhenFinished = true;
|
||||
_continue(debugger, dv);
|
||||
}
|
||||
|
||||
static void _setStackTraceMode(struct CLIDebugger* debugger, struct CLIDebugVector* dv) {
|
||||
if (!CLIDebuggerCheckTraceMode(debugger, false)) {
|
||||
return;
|
||||
}
|
||||
if (!dv) {
|
||||
debugger->backend->printf(debugger->backend, "off disable stack tracing (default)\n");
|
||||
debugger->backend->printf(debugger->backend, "trace-only enable stack tracing\n");
|
||||
debugger->backend->printf(debugger->backend, "break-call break on function calls\n");
|
||||
debugger->backend->printf(debugger->backend, "break-return break on function returns\n");
|
||||
debugger->backend->printf(debugger->backend, "break-all break on function calls and returns\n");
|
||||
return;
|
||||
}
|
||||
if (dv->type != CLIDV_CHAR_TYPE) {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
return;
|
||||
}
|
||||
struct mDebuggerPlatform* platform = debugger->d.platform;
|
||||
if (strcmp(dv->charValue, "off") == 0) {
|
||||
platform->setStackTraceMode(platform, STACK_TRACE_DISABLED);
|
||||
} else if (strcmp(dv->charValue, "trace-only") == 0) {
|
||||
platform->setStackTraceMode(platform, STACK_TRACE_ENABLED);
|
||||
} else if (strcmp(dv->charValue, "break-call") == 0) {
|
||||
platform->setStackTraceMode(platform, STACK_TRACE_BREAK_ON_CALL);
|
||||
} else if (strcmp(dv->charValue, "break-return") == 0) {
|
||||
platform->setStackTraceMode(platform, STACK_TRACE_BREAK_ON_RETURN);
|
||||
} else if (strcmp(dv->charValue, "break-all") == 0) {
|
||||
platform->setStackTraceMode(platform, STACK_TRACE_BREAK_ON_BOTH);
|
||||
} else {
|
||||
debugger->backend->printf(debugger->backend, "%s\n", ERROR_INVALID_ARGS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/* Copyright (c) 2013-2020 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/debugger/stack-trace.h>
|
||||
|
||||
#include <mgba/core/core.h>
|
||||
|
||||
#define CHECK_LENGTH() \
|
||||
if (written >= *length) { \
|
||||
*length = written; \
|
||||
return; \
|
||||
}
|
||||
|
||||
DEFINE_VECTOR(mStackFrames, struct mStackFrame);
|
||||
|
||||
void mStackTraceInit(struct mStackTrace* stack, size_t registersSize) {
|
||||
mStackFramesInit(&stack->stack, 0);
|
||||
stack->registersSize = registersSize;
|
||||
}
|
||||
|
||||
void mStackTraceDeinit(struct mStackTrace* stack) {
|
||||
mStackTraceClear(stack);
|
||||
mStackFramesDeinit(&stack->stack);
|
||||
}
|
||||
|
||||
void mStackTraceClear(struct mStackTrace* stack) {
|
||||
ssize_t i = mStackTraceGetDepth(stack) - 1;
|
||||
while (i >= 0) {
|
||||
free(mStackTraceGetFrame(stack, i)->regs);
|
||||
--i;
|
||||
}
|
||||
mStackFramesClear(&stack->stack);
|
||||
}
|
||||
|
||||
size_t mStackTraceGetDepth(struct mStackTrace* stack) {
|
||||
return mStackFramesSize(&stack->stack);
|
||||
}
|
||||
|
||||
struct mStackFrame* mStackTracePush(struct mStackTrace* stack, uint32_t pc, uint32_t destAddress, uint32_t sp, void* regs) {
|
||||
struct mStackFrame* frame = mStackFramesAppend(&stack->stack);
|
||||
frame->callAddress = pc;
|
||||
frame->entryAddress = destAddress;
|
||||
frame->frameBaseAddress = sp;
|
||||
frame->regs = malloc(stack->registersSize);
|
||||
frame->finished = false;
|
||||
frame->breakWhenFinished = false;
|
||||
frame->interrupt = false;
|
||||
memcpy(frame->regs, regs, stack->registersSize);
|
||||
return frame;
|
||||
}
|
||||
|
||||
struct mStackFrame* mStackTraceGetFrame(struct mStackTrace* stack, uint32_t frame) {
|
||||
size_t depth = mStackTraceGetDepth(stack);
|
||||
if (frame >= depth) {
|
||||
return NULL;
|
||||
}
|
||||
return mStackFramesGetPointer(&stack->stack, depth - frame - 1);
|
||||
}
|
||||
|
||||
void mStackTraceFormatFrame(struct mStackTrace* stack, uint32_t frame, char* out, size_t* length) {
|
||||
struct mStackFrame* stackFrame = mStackTraceGetFrame(stack, frame);
|
||||
struct mStackFrame* prevFrame = mStackTraceGetFrame(stack, frame + 1);
|
||||
size_t written = snprintf(out, *length, "#%d ", frame);
|
||||
CHECK_LENGTH();
|
||||
if (prevFrame) {
|
||||
written += snprintf(out + written, *length - written, "%08X ", prevFrame->entryAddress);
|
||||
CHECK_LENGTH();
|
||||
}
|
||||
if (!stackFrame) {
|
||||
written += snprintf(out + written, *length - written, "no stack frame available)\n");
|
||||
*length = written;
|
||||
return;
|
||||
} else if (stack->formatRegisters) {
|
||||
written += snprintf(out + written, *length - written, "(");
|
||||
CHECK_LENGTH();
|
||||
char buffer[1024];
|
||||
size_t formattedSize = sizeof(buffer) - 2;
|
||||
stack->formatRegisters(stackFrame, buffer, &formattedSize);
|
||||
written += snprintf(out + written, *length - written, "%s)\n ", buffer);
|
||||
CHECK_LENGTH();
|
||||
}
|
||||
if (prevFrame) {
|
||||
int32_t offset = stackFrame->callAddress - prevFrame->entryAddress;
|
||||
written += snprintf(out + written, *length - written, "at %08X [%08X+%d]\n", stackFrame->callAddress, prevFrame->entryAddress, offset);
|
||||
} else {
|
||||
written += snprintf(out + written, *length - written, "at %08X\n", stackFrame->callAddress);
|
||||
}
|
||||
*length = written;
|
||||
}
|
||||
|
||||
void mStackTracePop(struct mStackTrace* stack) {
|
||||
size_t depth = mStackTraceGetDepth(stack);
|
||||
if (depth > 0) {
|
||||
struct mStackFrame* frame = mStackFramesGetPointer(&stack->stack, depth - 1);
|
||||
free(frame->regs);
|
||||
mStackFramesResize(&stack->stack, -1);
|
||||
}
|
||||
}
|
|
@ -599,7 +599,6 @@ void GBASavedataDeserialize(struct GBASavedata* savedata, const struct GBASerial
|
|||
|
||||
void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
|
||||
mLOG(GBA_SAVE, DEBUG, "Performing flash bank switch to bank %i", bank);
|
||||
savedata->currentBank = &savedata->data[bank << 16];
|
||||
if (bank > 0 && savedata->type == SAVEDATA_FLASH512) {
|
||||
mLOG(GBA_SAVE, INFO, "Updating flash chip from 512kb to 1Mb");
|
||||
savedata->type = SAVEDATA_FLASH1M;
|
||||
|
@ -614,6 +613,7 @@ void _flashSwitchBank(struct GBASavedata* savedata, int bank) {
|
|||
}
|
||||
}
|
||||
}
|
||||
savedata->currentBank = &savedata->data[bank << 16];
|
||||
}
|
||||
|
||||
void _flashErase(struct GBASavedata* savedata) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -90,6 +90,9 @@ struct mDebuggerPlatform* SM83DebuggerPlatformCreate(void) {
|
|||
platform->d.trace = SM83DebuggerTrace;
|
||||
platform->d.getRegister = SM83DebuggerGetRegister;
|
||||
platform->d.setRegister = SM83DebuggerSetRegister;
|
||||
platform->d.getStackTraceMode = NULL;
|
||||
platform->d.setStackTraceMode = NULL;
|
||||
platform->d.updateStackTrace = NULL;
|
||||
platform->printStatus = NULL;
|
||||
return &platform->d;
|
||||
}
|
||||
|
|
|
@ -9,33 +9,27 @@
|
|||
#include <mgba/internal/sm83/sm83.h>
|
||||
|
||||
static inline uint16_t SM83ReadHL(struct SM83Core* cpu) {
|
||||
uint16_t hl;
|
||||
LOAD_16LE(hl, 0, &cpu->hl);
|
||||
return hl;
|
||||
return cpu->hl;
|
||||
}
|
||||
|
||||
static inline void SM83WriteHL(struct SM83Core* cpu, uint16_t hl) {
|
||||
STORE_16LE(hl, 0, &cpu->hl);
|
||||
cpu->hl = hl;
|
||||
}
|
||||
|
||||
static inline uint16_t SM83ReadBC(struct SM83Core* cpu) {
|
||||
uint16_t bc;
|
||||
LOAD_16LE(bc, 0, &cpu->bc);
|
||||
return bc;
|
||||
return cpu->bc;
|
||||
}
|
||||
|
||||
static inline void SM83WriteBC(struct SM83Core* cpu, uint16_t bc) {
|
||||
STORE_16LE(bc, 0, &cpu->bc);
|
||||
cpu->bc = bc;
|
||||
}
|
||||
|
||||
static inline uint16_t SM83ReadDE(struct SM83Core* cpu) {
|
||||
uint16_t de;
|
||||
LOAD_16LE(de, 0, &cpu->de);
|
||||
return de;
|
||||
return cpu->de;
|
||||
}
|
||||
|
||||
static inline void SM83WriteDE(struct SM83Core* cpu, uint16_t de) {
|
||||
STORE_16LE(de, 0, &cpu->de);
|
||||
cpu->de = de;
|
||||
}
|
||||
|
||||
#define DEFINE_INSTRUCTION_SM83(NAME, BODY) \
|
||||
|
@ -77,7 +71,7 @@ DEFINE_INSTRUCTION_SM83(JPDelay,
|
|||
DEFINE_CONDITIONAL_INSTRUCTION_SM83(JP);
|
||||
|
||||
DEFINE_INSTRUCTION_SM83(JPHL,
|
||||
cpu->pc = SM83ReadHL(cpu);
|
||||
cpu->pc = cpu->hl;
|
||||
cpu->memory.setActiveRegion(cpu, cpu->pc);)
|
||||
|
||||
DEFINE_INSTRUCTION_SM83(JRFinish,
|
||||
|
@ -226,7 +220,7 @@ DEFINE_CONDITIONAL_ONLY_INSTRUCTION_SM83(RET)
|
|||
#define DEFINE_LDHL__INSTRUCTION_SM83(NAME, OPERAND) \
|
||||
DEFINE_INSTRUCTION_SM83(LDHL_ ## NAME, \
|
||||
cpu->bus = OPERAND; \
|
||||
cpu->index = SM83ReadHL(cpu); \
|
||||
cpu->index = cpu->hl; \
|
||||
cpu->executionState = SM83_CORE_MEMORY_STORE; \
|
||||
cpu->instruction = _SM83InstructionNOP;)
|
||||
|
||||
|
@ -244,7 +238,7 @@ DEFINE_CONDITIONAL_ONLY_INSTRUCTION_SM83(RET)
|
|||
DEFINE_ ## NAME ## _INSTRUCTION_SM83(L, cpu->l);
|
||||
|
||||
DEFINE_INSTRUCTION_SM83(LDHL_Bus, \
|
||||
cpu->index = SM83ReadHL(cpu); \
|
||||
cpu->index = cpu->hl; \
|
||||
cpu->executionState = SM83_CORE_MEMORY_STORE; \
|
||||
cpu->instruction = _SM83InstructionNOP;)
|
||||
|
||||
|
@ -267,7 +261,7 @@ DEFINE_INSTRUCTION_SM83(LDHL_SP,
|
|||
cpu->instruction = _SM83InstructionLDHL_SPDelay;)
|
||||
|
||||
DEFINE_INSTRUCTION_SM83(LDSP_HL,
|
||||
cpu->sp = SM83ReadHL(cpu);
|
||||
cpu->sp = cpu->hl;
|
||||
cpu->executionState = SM83_CORE_STALL;)
|
||||
|
||||
#define DEFINE_ALU_INSTRUCTION_SM83_MEM(NAME, REG) \
|
||||
|
@ -378,7 +372,7 @@ DEFINE_INSTRUCTION_SM83(LDBC, \
|
|||
cpu->instruction = _SM83InstructionLDBCDelay;)
|
||||
|
||||
DEFINE_INSTRUCTION_SM83(LDBC_A, \
|
||||
cpu->index = SM83ReadBC(cpu); \
|
||||
cpu->index = cpu->bc; \
|
||||
cpu->bus = cpu->a; \
|
||||
cpu->executionState = SM83_CORE_MEMORY_STORE; \
|
||||
cpu->instruction = _SM83InstructionNOP;)
|
||||
|
@ -393,7 +387,7 @@ DEFINE_INSTRUCTION_SM83(LDDE, \
|
|||
cpu->instruction = _SM83InstructionLDDEDelay;)
|
||||
|
||||
DEFINE_INSTRUCTION_SM83(LDDE_A, \
|
||||
cpu->index = SM83ReadDE(cpu); \
|
||||
cpu->index = cpu->de; \
|
||||
cpu->bus = cpu->a; \
|
||||
cpu->executionState = SM83_CORE_MEMORY_STORE; \
|
||||
cpu->instruction = _SM83InstructionNOP;)
|
||||
|
@ -419,27 +413,27 @@ DEFINE_INSTRUCTION_SM83(LDSP, \
|
|||
cpu->instruction = _SM83InstructionLDSPDelay;)
|
||||
|
||||
DEFINE_INSTRUCTION_SM83(LDIHLA, \
|
||||
cpu->index = SM83ReadHL(cpu); \
|
||||
cpu->index = cpu->hl; \
|
||||
SM83WriteHL(cpu, cpu->index + 1); \
|
||||
cpu->bus = cpu->a; \
|
||||
cpu->executionState = SM83_CORE_MEMORY_STORE; \
|
||||
cpu->instruction = _SM83InstructionNOP;)
|
||||
|
||||
DEFINE_INSTRUCTION_SM83(LDDHLA, \
|
||||
cpu->index = SM83ReadHL(cpu); \
|
||||
cpu->index = cpu->hl; \
|
||||
SM83WriteHL(cpu, cpu->index - 1); \
|
||||
cpu->bus = cpu->a; \
|
||||
cpu->executionState = SM83_CORE_MEMORY_STORE; \
|
||||
cpu->instruction = _SM83InstructionNOP;)
|
||||
|
||||
DEFINE_INSTRUCTION_SM83(LDA_IHL, \
|
||||
cpu->index = SM83ReadHL(cpu); \
|
||||
cpu->index = cpu->hl; \
|
||||
SM83WriteHL(cpu, cpu->index + 1); \
|
||||
cpu->executionState = SM83_CORE_MEMORY_LOAD; \
|
||||
cpu->instruction = _SM83InstructionLDA_Bus;)
|
||||
|
||||
DEFINE_INSTRUCTION_SM83(LDA_DHL, \
|
||||
cpu->index = SM83ReadHL(cpu); \
|
||||
cpu->index = cpu->hl; \
|
||||
SM83WriteHL(cpu, cpu->index - 1); \
|
||||
cpu->executionState = SM83_CORE_MEMORY_LOAD; \
|
||||
cpu->instruction = _SM83InstructionLDA_Bus;)
|
||||
|
@ -587,7 +581,7 @@ DEFINE_INSTRUCTION_SM83(INC_HLDelay,
|
|||
cpu->executionState = SM83_CORE_MEMORY_STORE;)
|
||||
|
||||
DEFINE_INSTRUCTION_SM83(INC_HL,
|
||||
cpu->index = SM83ReadHL(cpu);
|
||||
cpu->index = cpu->hl;
|
||||
cpu->instruction = _SM83InstructionINC_HLDelay;
|
||||
cpu->executionState = SM83_CORE_MEMORY_LOAD;)
|
||||
|
||||
|
@ -601,7 +595,7 @@ DEFINE_INSTRUCTION_SM83(DEC_HLDelay,
|
|||
cpu->executionState = SM83_CORE_MEMORY_STORE;)
|
||||
|
||||
DEFINE_INSTRUCTION_SM83(DEC_HL,
|
||||
cpu->index = SM83ReadHL(cpu);
|
||||
cpu->index = cpu->hl;
|
||||
cpu->instruction = _SM83InstructionDEC_HLDelay;
|
||||
cpu->executionState = SM83_CORE_MEMORY_LOAD;)
|
||||
|
||||
|
@ -699,7 +693,7 @@ DEFINE_POPPUSH_INSTRUCTION_SM83(AF, A, a, f.packed);
|
|||
cpu->executionState = WB; \
|
||||
cpu->instruction = _SM83InstructionNOP;) \
|
||||
DEFINE_INSTRUCTION_SM83(NAME ## HL, \
|
||||
cpu->index = SM83ReadHL(cpu); \
|
||||
cpu->index = cpu->hl; \
|
||||
cpu->executionState = SM83_CORE_MEMORY_LOAD; \
|
||||
cpu->instruction = _SM83Instruction ## NAME ## HLDelay;) \
|
||||
DEFINE_INSTRUCTION_SM83(NAME ## A, uint8_t reg = cpu->a; BODY; cpu->a = reg)
|
||||
|
|
Loading…
Reference in New Issue