From e79963b007a0feee6c241774609808061c288f1b Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Wed, 28 Sep 2016 14:37:48 -0700 Subject: [PATCH] GBA: Support printing debug strings from inside a game --- CHANGES | 2 ++ src/gba/gba.c | 17 +++++++++++++++++ src/gba/gba.h | 10 ++++++++++ src/gba/io.c | 26 ++++++++++++++++++++++++++ src/gba/io.h | 6 +++++- 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 93595c376..970cb9b74 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,6 @@ 0.6.0: (Future) +Features: + - GBA: Support printing debug strings from inside a game Bugfixes: - GB MBC: Fix MBC7 when size is incorrectly specified - GB Video: Setting LYC=LY during mode 2 should trigger an IRQ diff --git a/src/gba/gba.c b/src/gba/gba.c index 66cd13ca4..bcbd732fb 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -27,6 +27,7 @@ #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 @@ void GBAReset(struct ARMCore* cpu) { gba->haltPending = false; gba->idleDetectionStep = 0; gba->idleDetectionFailures = 0; + + gba->debug = false; + memset(gba->debugString, 0, sizeof(gba->debugString)); } void GBASkipBIOS(struct GBA* gba) { @@ -681,6 +685,19 @@ void GBAStop(struct GBA* gba) { 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) { if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET, SEEK_SET) < 0) { return false; diff --git a/src/gba/gba.h b/src/gba/gba.h index 38dff3f99..ce50234b2 100644 --- a/src/gba/gba.h +++ b/src/gba/gba.h @@ -53,6 +53,7 @@ struct Patch; struct VFile; mLOG_DECLARE_CATEGORY(GBA); +mLOG_DECLARE_CATEGORY(GBA_DEBUG); DECL_BITFIELD(GBATimerFlags, uint32_t); DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4); @@ -60,6 +61,10 @@ 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; uint16_t oldReload; @@ -120,6 +125,10 @@ struct GBA { 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); diff --git a/src/gba/io.c b/src/gba/io.c index b73b08e40..8eb2c4926 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -540,7 +540,20 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) { 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); @@ -562,6 +575,10 @@ void GBAIOWrite8(struct GBA* gba, uint32_t address, uint8_t value) { } 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) { return; } @@ -613,6 +630,10 @@ void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) { 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 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) { 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); diff --git a/src/gba/io.h b/src/gba/io.h index a47662a64..5a73bce34 100644 --- a/src/gba/io.h +++ b/src/gba/io.h @@ -150,7 +150,11 @@ enum GBAIORegisters { 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);