mirror of https://github.com/mgba-emu/mgba.git
GBA: Support printing debug strings from inside a game
This commit is contained in:
parent
278ac5f35b
commit
e79963b007
2
CHANGES
2
CHANGES
|
@ -1,4 +1,6 @@
|
||||||
0.6.0: (Future)
|
0.6.0: (Future)
|
||||||
|
Features:
|
||||||
|
- GBA: Support printing debug strings from inside a game
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
- GB MBC: Fix MBC7 when size is incorrectly specified
|
- GB MBC: Fix MBC7 when size is incorrectly specified
|
||||||
- GB Video: Setting LYC=LY during mode 2 should trigger an IRQ
|
- GB Video: Setting LYC=LY during mode 2 should trigger an IRQ
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "util/vfs.h"
|
#include "util/vfs.h"
|
||||||
|
|
||||||
mLOG_DEFINE_CATEGORY(GBA, "GBA");
|
mLOG_DEFINE_CATEGORY(GBA, "GBA");
|
||||||
|
mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug");
|
||||||
|
|
||||||
const uint32_t GBA_COMPONENT_MAGIC = 0x1000000;
|
const uint32_t GBA_COMPONENT_MAGIC = 0x1000000;
|
||||||
|
|
||||||
|
@ -199,6 +200,9 @@ void GBAReset(struct ARMCore* cpu) {
|
||||||
gba->haltPending = false;
|
gba->haltPending = false;
|
||||||
gba->idleDetectionStep = 0;
|
gba->idleDetectionStep = 0;
|
||||||
gba->idleDetectionFailures = 0;
|
gba->idleDetectionFailures = 0;
|
||||||
|
|
||||||
|
gba->debug = false;
|
||||||
|
memset(gba->debugString, 0, sizeof(gba->debugString));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBASkipBIOS(struct GBA* gba) {
|
void GBASkipBIOS(struct GBA* gba) {
|
||||||
|
@ -681,6 +685,19 @@ void GBAStop(struct GBA* gba) {
|
||||||
gba->stopCallback->stop(gba->stopCallback);
|
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) {
|
bool GBAIsROM(struct VFile* vf) {
|
||||||
if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET, SEEK_SET) < 0) {
|
if (vf->seek(vf, GBA_ROM_MAGIC_OFFSET, SEEK_SET) < 0) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -53,6 +53,7 @@ struct Patch;
|
||||||
struct VFile;
|
struct VFile;
|
||||||
|
|
||||||
mLOG_DECLARE_CATEGORY(GBA);
|
mLOG_DECLARE_CATEGORY(GBA);
|
||||||
|
mLOG_DECLARE_CATEGORY(GBA_DEBUG);
|
||||||
|
|
||||||
DECL_BITFIELD(GBATimerFlags, uint32_t);
|
DECL_BITFIELD(GBATimerFlags, uint32_t);
|
||||||
DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4);
|
DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4);
|
||||||
|
@ -60,6 +61,10 @@ DECL_BIT(GBATimerFlags, CountUp, 4);
|
||||||
DECL_BIT(GBATimerFlags, DoIrq, 5);
|
DECL_BIT(GBATimerFlags, DoIrq, 5);
|
||||||
DECL_BIT(GBATimerFlags, Enable, 6);
|
DECL_BIT(GBATimerFlags, Enable, 6);
|
||||||
|
|
||||||
|
DECL_BITFIELD(GBADebugFlags, uint16_t);
|
||||||
|
DECL_BITS(GBADebugFlags, Level, 0, 3);
|
||||||
|
DECL_BIT(GBADebugFlags, Send, 8);
|
||||||
|
|
||||||
struct GBATimer {
|
struct GBATimer {
|
||||||
uint16_t reload;
|
uint16_t reload;
|
||||||
uint16_t oldReload;
|
uint16_t oldReload;
|
||||||
|
@ -120,6 +125,10 @@ struct GBA {
|
||||||
bool realisticTiming;
|
bool realisticTiming;
|
||||||
bool hardCrash;
|
bool hardCrash;
|
||||||
bool allowOpposingDirections;
|
bool allowOpposingDirections;
|
||||||
|
|
||||||
|
bool debug;
|
||||||
|
char debugString[0x100];
|
||||||
|
GBADebugFlags debugFlags;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GBACartridge {
|
struct GBACartridge {
|
||||||
|
@ -153,6 +162,7 @@ void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq);
|
||||||
void GBATestIRQ(struct ARMCore* cpu);
|
void GBATestIRQ(struct ARMCore* cpu);
|
||||||
void GBAHalt(struct GBA* gba);
|
void GBAHalt(struct GBA* gba);
|
||||||
void GBAStop(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 GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger);
|
||||||
void GBADetachDebugger(struct GBA* gba);
|
void GBADetachDebugger(struct GBA* gba);
|
||||||
|
|
26
src/gba/io.c
26
src/gba/io.c
|
@ -540,7 +540,20 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
|
||||||
case REG_MAX:
|
case REG_MAX:
|
||||||
// Some bad interrupt libraries will write to this
|
// Some bad interrupt libraries will write to this
|
||||||
break;
|
break;
|
||||||
|
case REG_DEBUG_ENABLE:
|
||||||
|
gba->debug = value == 0xC0DE;
|
||||||
|
return;
|
||||||
|
case REG_DEBUG_FLAGS:
|
||||||
|
if (gba->debug) {
|
||||||
|
GBADebug(gba, value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Fall through
|
||||||
default:
|
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);
|
mLOG(GBA_IO, STUB, "Stub I/O register write: %03X", address);
|
||||||
if (address >= REG_MAX) {
|
if (address >= REG_MAX) {
|
||||||
mLOG(GBA_IO, GAME_ERROR, "Write to unused I/O register: %03X", address);
|
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;
|
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) {
|
if (address > SIZE_IO) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -613,6 +630,10 @@ void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) {
|
||||||
value = GBAMemoryWriteDMADAD(gba, 3, value);
|
value = GBAMemoryWriteDMADAD(gba, 3, value);
|
||||||
break;
|
break;
|
||||||
default:
|
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, value & 0xFFFF);
|
||||||
GBAIOWrite(gba, address | 2, value >> 16);
|
GBAIOWrite(gba, address | 2, value >> 16);
|
||||||
return;
|
return;
|
||||||
|
@ -848,6 +869,11 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
||||||
case 0x8A:
|
case 0x8A:
|
||||||
mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address);
|
mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address);
|
||||||
return 0;
|
return 0;
|
||||||
|
case REG_DEBUG_ENABLE:
|
||||||
|
if (gba->debug) {
|
||||||
|
return 0x1DEA;
|
||||||
|
}
|
||||||
|
// Fall through
|
||||||
default:
|
default:
|
||||||
mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address);
|
mLOG(GBA_IO, GAME_ERROR, "Read from unused I/O register: %03X", address);
|
||||||
return GBALoadBad(gba->cpu);
|
return GBALoadBad(gba->cpu);
|
||||||
|
|
|
@ -150,7 +150,11 @@ enum GBAIORegisters {
|
||||||
REG_MAX = 0x20A,
|
REG_MAX = 0x20A,
|
||||||
|
|
||||||
REG_POSTFLG = 0x300,
|
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);
|
mLOG_DECLARE_CATEGORY(GBA_IO);
|
||||||
|
|
Loading…
Reference in New Issue