GBA: Support printing debug strings from inside a game
Jeffrey Pfau jeffrey@endrift.com
Wed, 28 Sep 2016 14:37:48 -0700
5 files changed,
60 insertions(+),
1 deletions(-)
M
src/gba/gba.c
→
src/gba/gba.c
@@ -27,6 +27,7 @@ #include "util/patch.h"
#include "util/vfs.h" mLOG_DEFINE_CATEGORY(GBA, "GBA"); +mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug"); const uint32_t GBA_COMPONENT_MAGIC = 0x1000000;@@ -199,6 +200,9 @@ gba->lastJump = 0;
gba->haltPending = false; gba->idleDetectionStep = 0; gba->idleDetectionFailures = 0; + + gba->debug = false; + memset(gba->debugString, 0, sizeof(gba->debugString)); } void GBASkipBIOS(struct GBA* gba) {@@ -679,6 +683,19 @@ return;
} gba->cpu->nextEvent = gba->cpu->cycles; gba->stopCallback->stop(gba->stopCallback); +} + +void GBADebug(struct GBA* gba, uint16_t flags) { + gba->debugFlags = flags; + if (GBADebugFlagsIsSend(gba->debugFlags)) { + int level = 1 << GBADebugFlagsGetLevel(gba->debugFlags); + level &= 0x1F; + char oolBuf[0x101]; + strncpy(oolBuf, gba->debugString, sizeof(gba->debugString)); + oolBuf[0x100] = '\0'; + mLog(_mLOG_CAT_GBA_DEBUG(), level, "%s", oolBuf); + } + gba->debugFlags = GBADebugFlagsClearSend(gba->debugFlags); } bool GBAIsROM(struct VFile* vf) {
M
src/gba/gba.h
→
src/gba/gba.h
@@ -53,12 +53,17 @@ struct Patch;
struct VFile; mLOG_DECLARE_CATEGORY(GBA); +mLOG_DECLARE_CATEGORY(GBA_DEBUG); DECL_BITFIELD(GBATimerFlags, uint32_t); DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4); DECL_BIT(GBATimerFlags, CountUp, 4); DECL_BIT(GBATimerFlags, DoIrq, 5); DECL_BIT(GBATimerFlags, Enable, 6); + +DECL_BITFIELD(GBADebugFlags, uint16_t); +DECL_BITS(GBADebugFlags, Level, 0, 3); +DECL_BIT(GBADebugFlags, Send, 8); struct GBATimer { uint16_t reload;@@ -120,6 +125,10 @@
bool realisticTiming; bool hardCrash; bool allowOpposingDirections; + + bool debug; + char debugString[0x100]; + GBADebugFlags debugFlags; }; struct GBACartridge {@@ -153,6 +162,7 @@ void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq);
void GBATestIRQ(struct ARMCore* cpu); void GBAHalt(struct GBA* gba); void GBAStop(struct GBA* gba); +void GBADebug(struct GBA* gba, uint16_t value); void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger); void GBADetachDebugger(struct GBA* gba);
M
src/gba/io.c
→
src/gba/io.c
@@ -540,7 +540,20 @@ break;
case REG_MAX: // Some bad interrupt libraries will write to this break; + case REG_DEBUG_ENABLE: + gba->debug = value == 0xC0DE; + return; + case REG_DEBUG_FLAGS: + if (gba->debug) { + GBADebug(gba, value); + return; + } + // Fall through default: + if (address >= REG_DEBUG_STRING && address - REG_DEBUG_STRING < sizeof(gba->debugString)) { + STORE_16LE(value, address - REG_DEBUG_STRING, gba->debugString); + return; + } mLOG(GBA_IO, STUB, "Stub I/O register write: %03X", address); if (address >= REG_MAX) { mLOG(GBA_IO, GAME_ERROR, "Write to unused I/O register: %03X", address);@@ -560,6 +573,10 @@ GBAHalt(gba);
} else { GBAStop(gba); } + return; + } + if (address >= REG_DEBUG_STRING && address - REG_DEBUG_STRING < sizeof(gba->debugString)) { + gba->debugString[address - REG_DEBUG_STRING] = value; return; } if (address > SIZE_IO) {@@ -613,6 +630,10 @@ case REG_DMA3DAD_LO:
value = GBAMemoryWriteDMADAD(gba, 3, value); break; default: + if (address >= REG_DEBUG_STRING && address - REG_DEBUG_STRING < sizeof(gba->debugString)) { + STORE_32LE(value, address - REG_DEBUG_STRING, gba->debugString); + return; + } GBAIOWrite(gba, address, value & 0xFFFF); GBAIOWrite(gba, address | 2, value >> 16); return;@@ -848,6 +869,11 @@ case 0x86:
case 0x8A: mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address); return 0; + case REG_DEBUG_ENABLE: + if (gba->debug) { + return 0x1DEA; + } + // Fall through default: mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address); return GBALoadBad(gba->cpu);
M
src/gba/io.h
→
src/gba/io.h
@@ -150,7 +150,11 @@
REG_MAX = 0x20A, REG_POSTFLG = 0x300, - REG_HALTCNT = 0x301 + REG_HALTCNT = 0x301, + + REG_DEBUG_STRING = 0xFFF600, + REG_DEBUG_FLAGS = 0xFFF700, + REG_DEBUG_ENABLE = 0xFFF780, }; mLOG_DECLARE_CATEGORY(GBA_IO);