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:
- ARM7: Fix unsigned multiply timing
- 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: Properly apply per-model audio differences
- GB Audio: Revamp channel rendering

View File

@ -844,39 +844,67 @@ void GBUpdateIRQs(struct GB* gb) {
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) {
struct GB* gb = (struct GB*) cpu->master;
do {
int32_t cycles = cpu->cycles;
int32_t nextEvent;
cpu->cycles = 0;
cpu->nextEvent = INT_MAX;
nextEvent = cycles;
do {
#ifdef USE_DEBUGGERS
gb->timing.globalCycles += nextEvent;
#ifndef NDEBUG
int stateMask = (4 * (2 - gb->doubleSpeed)) - 1;
int state = (mTimingGlobalTime(&gb->timing) & stateMask) >> !gb->doubleSpeed;
if (((state + 3) & 3) != (cpu->executionState & 3)) {
mLOG(GB, ERROR, "T-states and M-cycles became misaligned");
}
#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;
bool wasHalted = cpu->halted;
while (true) {
do {
int32_t cycles = cpu->cycles;
int32_t nextEvent;
if (cpu->halted) {
cpu->cycles = cpu->nextEvent;
if (!gb->memory.ie || !gb->memory.ime) {
cpu->cycles = 0;
cpu->nextEvent = INT_MAX;
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;
}
} while (cpu->cycles >= cpu->nextEvent);
if (gb->cpuBlocked) {
_GBAdvanceCycles(gb);
}
if (gb->earlyExit) {
if (!wasHalted || (cpu->executionState & 3) == SM83_CORE_FETCH) {
break;
}
} while (cpu->cycles >= cpu->nextEvent);
gb->earlyExit = false;
if (gb->cpuBlocked) {
cpu->cycles = cpu->nextEvent;
int nextFetch = (SM83_CORE_FETCH - cpu->executionState) * cpu->tMultiplier;
if (nextFetch < cpu->nextEvent) {
cpu->cycles += nextFetch;
cpu->executionState = SM83_CORE_FETCH;
break;
}
_GBAdvanceCycles(gb);
}
gb->earlyExit = false;
}
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) {
struct GB* gb = (struct GB*) cpu->master;
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;
} else if (!gb->memory.ime) {
mLOG(GB, GAME_ERROR, "HALT bug");