GB: Fix HALT breaking M-cycle alignment (fixes #250)

This commit is contained in:
Vicki Pfau 2022-09-03 06:48:09 -07:00
parent c6b25f14a3
commit e0b07a6446
2 changed files with 54 additions and 24 deletions

View File

@ -19,6 +19,7 @@ Features:
Emulation fixes: Emulation fixes:
- ARM7: Fix unsigned multiply timing - ARM7: Fix unsigned multiply timing
- GB: Copy logo from ROM if not running the BIOS intro (fixes mgba.io/i/2378) - GB: Copy logo from ROM if not running the BIOS intro (fixes mgba.io/i/2378)
- GB: Fix HALT breaking M-cycle alignment (fixes mgba.io/i/250)
- GB Audio: Fix channel 1/2 reseting edge cases (fixes mgba.io/i/1925) - GB Audio: Fix channel 1/2 reseting edge cases (fixes mgba.io/i/1925)
- GB Audio: Properly apply per-model audio differences - GB Audio: Properly apply per-model audio differences
- GB Audio: Revamp channel rendering - GB Audio: Revamp channel rendering

View File

@ -844,39 +844,67 @@ void GBUpdateIRQs(struct GB* gb) {
SM83RaiseIRQ(gb->cpu); SM83RaiseIRQ(gb->cpu);
} }
static void _GBAdvanceCycles(struct GB* gb) {
struct SM83Core* cpu = gb->cpu;
int stateMask = (4 * (2 - gb->doubleSpeed)) - 1;
int stateOffset = ((cpu->nextEvent - cpu->cycles) & stateMask) >> !gb->doubleSpeed;
cpu->cycles = cpu->nextEvent;
cpu->executionState = (cpu->executionState + stateOffset) & 3;
}
void GBProcessEvents(struct SM83Core* cpu) { void GBProcessEvents(struct SM83Core* cpu) {
struct GB* gb = (struct GB*) cpu->master; struct GB* gb = (struct GB*) cpu->master;
do { #ifndef NDEBUG
int32_t cycles = cpu->cycles; int stateMask = (4 * (2 - gb->doubleSpeed)) - 1;
int32_t nextEvent; int state = (mTimingGlobalTime(&gb->timing) & stateMask) >> !gb->doubleSpeed;
if (((state + 3) & 3) != (cpu->executionState & 3)) {
cpu->cycles = 0; mLOG(GB, ERROR, "T-states and M-cycles became misaligned");
cpu->nextEvent = INT_MAX; }
nextEvent = cycles;
do {
#ifdef USE_DEBUGGERS
gb->timing.globalCycles += nextEvent;
#endif #endif
nextEvent = mTimingTick(&gb->timing, nextEvent); bool wasHalted = cpu->halted;
} while (gb->cpuBlocked); while (true) {
// This loop cannot early exit until the SM83 run loop properly handles mid-M-cycle-exits do {
cpu->nextEvent = nextEvent; int32_t cycles = cpu->cycles;
int32_t nextEvent;
if (cpu->halted) { cpu->cycles = 0;
cpu->cycles = cpu->nextEvent; cpu->nextEvent = INT_MAX;
if (!gb->memory.ie || !gb->memory.ime) {
nextEvent = cycles;
do {
#ifdef USE_DEBUGGERS
gb->timing.globalCycles += nextEvent;
#endif
nextEvent = mTimingTick(&gb->timing, nextEvent);
} while (gb->cpuBlocked);
// This loop cannot early exit until the SM83 run loop properly handles mid-M-cycle-exits
cpu->nextEvent = nextEvent;
if (cpu->halted) {
_GBAdvanceCycles(gb);
if (!gb->memory.ie || !gb->memory.ime) {
break;
}
}
if (gb->earlyExit) {
break; break;
} }
} while (cpu->cycles >= cpu->nextEvent);
if (gb->cpuBlocked) {
_GBAdvanceCycles(gb);
} }
if (gb->earlyExit) { if (!wasHalted || (cpu->executionState & 3) == SM83_CORE_FETCH) {
break; break;
} }
} while (cpu->cycles >= cpu->nextEvent); int nextFetch = (SM83_CORE_FETCH - cpu->executionState) * cpu->tMultiplier;
gb->earlyExit = false; if (nextFetch < cpu->nextEvent) {
if (gb->cpuBlocked) { cpu->cycles += nextFetch;
cpu->cycles = cpu->nextEvent; cpu->executionState = SM83_CORE_FETCH;
break;
}
_GBAdvanceCycles(gb);
} }
gb->earlyExit = false;
} }
void GBSetInterrupts(struct SM83Core* cpu, bool enable) { void GBSetInterrupts(struct SM83Core* cpu, bool enable) {
@ -928,7 +956,8 @@ static void _enableInterrupts(struct mTiming* timing, void* user, uint32_t cycle
void GBHalt(struct SM83Core* cpu) { void GBHalt(struct SM83Core* cpu) {
struct GB* gb = (struct GB*) cpu->master; struct GB* gb = (struct GB*) cpu->master;
if (!(gb->memory.ie & gb->memory.io[GB_REG_IF] & 0x1F)) { if (!(gb->memory.ie & gb->memory.io[GB_REG_IF] & 0x1F)) {
cpu->cycles = cpu->nextEvent; _GBAdvanceCycles(gb);
cpu->executionState = (cpu->executionState - 1) & 3;
cpu->halted = true; cpu->halted = true;
} else if (!gb->memory.ime) { } else if (!gb->memory.ime) {
mLOG(GB, GAME_ERROR, "HALT bug"); mLOG(GB, GAME_ERROR, "HALT bug");