diff --git a/CHANGES b/CHANGES index cedefd7bc..c20f2ba29 100644 --- a/CHANGES +++ b/CHANGES @@ -40,6 +40,7 @@ Other fixes: - Qt: Force OpenGL paint engine creation thread (fixes mgba.io/i/1642) - Qt: Fix OpenGL 2.1 support (fixes mgba.io/i/1678) Misc: + - Debugger: Keep track of global cycle count - FFmpeg: Add looping option for GIF/APNG - Qt: Renderer can be changed while a game is running - Qt: Add hex index to palette view diff --git a/include/mgba/core/timing.h b/include/mgba/core/timing.h index 16154ba9c..10a64171e 100644 --- a/include/mgba/core/timing.h +++ b/include/mgba/core/timing.h @@ -25,6 +25,7 @@ struct mTiming { struct mTimingEvent* root; struct mTimingEvent* reroot; + uint64_t globalCycles; uint32_t masterCycles; int32_t* relativeCycles; int32_t* nextEvent; @@ -38,6 +39,7 @@ void mTimingDeschedule(struct mTiming* timing, struct mTimingEvent*); bool mTimingIsScheduled(const struct mTiming* timing, const struct mTimingEvent*); int32_t mTimingTick(struct mTiming* timing, int32_t cycles); int32_t mTimingCurrentTime(const struct mTiming* timing); +uint64_t mTimingGlobalTime(const struct mTiming* timing); int32_t mTimingNextEvent(struct mTiming* timing); int32_t mTimingUntil(const struct mTiming* timing, const struct mTimingEvent*); diff --git a/include/mgba/internal/gb/serialize.h b/include/mgba/internal/gb/serialize.h index ef2d5c4bc..68b7d8533 100644 --- a/include/mgba/internal/gb/serialize.h +++ b/include/mgba/internal/gb/serialize.h @@ -155,7 +155,8 @@ mLOG_DECLARE_CATEGORY(GB_STATE); * | bit 4: Is HDMA active? * | bits 5 - 7: Active RTC register * | 0x00196 - 0x00197: Reserved (leave zero) - * 0x00198 - 0x0025F: Reserved (leave zero) + * 0x00198 - 0x0019F: Global cycle counter + * 0x001A0 - 0x0025F: Reserved (leave zero) * 0x00260 - 0x002FF: OAM * 0x00300 - 0x0037F: I/O memory * 0x00380 - 0x003FE: HRAM @@ -388,7 +389,9 @@ struct GBSerializedState { uint16_t reserved; } memory; - uint32_t reserved[50]; + uint64_t globalCycles; + + uint32_t reserved[48]; uint8_t oam[GB_SIZE_OAM]; diff --git a/include/mgba/internal/gba/serialize.h b/include/mgba/internal/gba/serialize.h index 672d182cf..a5dabae2c 100644 --- a/include/mgba/internal/gba/serialize.h +++ b/include/mgba/internal/gba/serialize.h @@ -193,7 +193,8 @@ mLOG_DECLARE_CATEGORY(GBA_STATE); * | 0x002F4 - 0x002F7: GBA BIOS bus prefetch * | 0x002F8 - 0x002FB: CPU prefecth (decode slot) * | 0x002FC - 0x002FF: CPU prefetch (fetch slot) - * 0x00300 - 0x00317: Reserved (leave zero) + * 0x00300 - 0x0030F: Reserved (leave zero) + * 0x00310 - 0x00317: Global cycle counter * 0x00318 - 0x0031B: Last prefetched program counter * 0x0031C - 0x0031F: Miscellaneous flags * | bit 0: Is CPU halted? @@ -326,8 +327,9 @@ struct GBASerializedState { uint32_t biosPrefetch; uint32_t cpuPrefetch[2]; - uint32_t reservedCpu[6]; + uint32_t reservedCpu[4]; + uint64_t globalCycles; uint32_t lastPrefetchedPc; GBASerializedMiscFlags miscFlags; uint32_t nextIrq; diff --git a/src/arm/debugger/cli-debugger.c b/src/arm/debugger/cli-debugger.c index c393f8c1e..d5ee883be 100644 --- a/src/arm/debugger/cli-debugger.c +++ b/src/arm/debugger/cli-debugger.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -138,6 +139,7 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) { } be->printf(be, "cpsr: "); _printPSR(be, cpu->cpsr); + be->printf(be, "Cycle: %" PRIu64 "\n", mTimingGlobalTime(debugger->p->d.core->timing)); int instructionLength; enum ExecutionMode mode = cpu->cpsr.t; if (mode == MODE_ARM) { diff --git a/src/core/timing.c b/src/core/timing.c index 88bcf2743..f9854c6ac 100644 --- a/src/core/timing.c +++ b/src/core/timing.c @@ -8,6 +8,7 @@ void mTimingInit(struct mTiming* timing, int32_t* relativeCycles, int32_t* nextEvent) { timing->root = NULL; timing->reroot = NULL; + timing->globalCycles = 0; timing->masterCycles = 0; timing->relativeCycles = relativeCycles; timing->nextEvent = nextEvent; @@ -20,6 +21,7 @@ void mTimingDeinit(struct mTiming* timing) { void mTimingClear(struct mTiming* timing) { timing->root = NULL; timing->reroot = NULL; + timing->globalCycles = 0; timing->masterCycles = 0; } @@ -103,6 +105,10 @@ int32_t mTimingCurrentTime(const struct mTiming* timing) { return timing->masterCycles + *timing->relativeCycles; } +uint64_t mTimingGlobalTime(const struct mTiming* timing) { + return timing->globalCycles + *timing->relativeCycles; +} + int32_t mTimingNextEvent(struct mTiming* timing) { struct mTimingEvent* next = timing->root; if (!next) { diff --git a/src/gb/gb.c b/src/gb/gb.c index 94de90b99..0aaa6ae54 100644 --- a/src/gb/gb.c +++ b/src/gb/gb.c @@ -674,6 +674,9 @@ void GBProcessEvents(struct SM83Core* cpu) { int32_t nextEvent; cpu->cycles = 0; +#ifdef USE_DEBUGGERS + gb->timing.globalCycles += cycles; +#endif cpu->nextEvent = INT_MAX; nextEvent = cycles; diff --git a/src/gb/serialize.c b/src/gb/serialize.c index e8eaeefc7..539f87c2a 100644 --- a/src/gb/serialize.c +++ b/src/gb/serialize.c @@ -23,6 +23,7 @@ void GBSerialize(struct GB* gb, struct GBSerializedState* state) { STORE_32LE(GB_SAVESTATE_MAGIC + GB_SAVESTATE_VERSION, 0, &state->versionMagic); STORE_32LE(gb->romCrc32, 0, &state->romCrc32); STORE_32LE(gb->timing.masterCycles, 0, &state->masterCycles); + STORE_64LE(gb->timing.globalCycles, 0, &state->globalCycles); if (gb->memory.rom) { memcpy(state->title, ((struct GBCartridge*) &gb->memory.rom[0x100])->titleLong, sizeof(state->title)); @@ -150,6 +151,7 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) { } mTimingClear(&gb->timing); LOAD_32LE(gb->timing.masterCycles, 0, &state->masterCycles); + LOAD_64LE(gb->timing.globalCycles, 0, &state->globalCycles); gb->cpu->a = state->cpu.a; gb->cpu->f.packed = state->cpu.f; diff --git a/src/gba/gba.c b/src/gba/gba.c index 7a4bcea0d..d306005a7 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -277,6 +277,9 @@ static void GBAProcessEvents(struct ARMCore* cpu) { do { int32_t cycles = cpu->cycles; cpu->cycles = 0; +#ifdef USE_DEBUGGERS + gba->timing.globalCycles += cycles; +#endif #ifndef NDEBUG if (cycles < 0) { mLOG(GBA, FATAL, "Negative cycles passed: %i", cycles); diff --git a/src/gba/serialize.c b/src/gba/serialize.c index bd543482a..a07587a14 100644 --- a/src/gba/serialize.c +++ b/src/gba/serialize.c @@ -28,6 +28,7 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) { STORE_32(gba->biosChecksum, 0, &state->biosChecksum); STORE_32(gba->romCrc32, 0, &state->romCrc32); STORE_32(gba->timing.masterCycles, 0, &state->masterCycles); + STORE_64LE(gba->timing.globalCycles, 0, &state->globalCycles); if (gba->memory.rom) { state->id = ((struct GBACartridge*) gba->memory.rom)->id; @@ -128,6 +129,7 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) { } mTimingClear(&gba->timing); LOAD_32(gba->timing.masterCycles, 0, &state->masterCycles); + LOAD_64LE(gba->timing.globalCycles, 0, &state->globalCycles); size_t i; for (i = 0; i < 16; ++i) { diff --git a/src/sm83/debugger/cli-debugger.c b/src/sm83/debugger/cli-debugger.c index c227d845e..1c3d2a473 100644 --- a/src/sm83/debugger/cli-debugger.c +++ b/src/sm83/debugger/cli-debugger.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -88,6 +89,7 @@ static void _printStatus(struct CLIDebuggerSystem* debugger) { be->printf(be, "H: %02X L: %02X (HL: %04X)\n", cpu->h, cpu->l, cpu->hl); be->printf(be, "PC: %04X SP: %04X\n", cpu->pc, cpu->sp); _printFlags(be, cpu->f); + be->printf(be, "T-cycle: %" PRIu64 "\n", mTimingGlobalTime(debugger->p->d.core->timing)); struct SM83Debugger* platDebugger = (struct SM83Debugger*) debugger->p->d.platform; size_t i;