GB: Redo double speed emulation (closes #1515)

This commit is contained in:
Vicki Pfau 2020-12-28 03:32:43 -08:00
parent 8361d56683
commit bda4316839
12 changed files with 69 additions and 78 deletions

View File

@ -96,6 +96,7 @@ Misc:
- Core: Improve support for ROM patch cheats, supporting disabling overlapping patches - Core: Improve support for ROM patch cheats, supporting disabling overlapping patches
- GB: Allow pausing event loop while CPU is blocked - GB: Allow pausing event loop while CPU is blocked
- GB: Add support for sleep and shutdown callbacks - GB: Add support for sleep and shutdown callbacks
- GB: Redo double speed emulation (closes mgba.io/i/1515)
- GB Core: Return the current number of banks for ROM/SRAM, not theoretical max - GB Core: Return the current number of banks for ROM/SRAM, not theoretical max
- GB I/O: Implement preliminary support for PCM12/PCM34 (closes mgba.io/i/1468) - GB I/O: Implement preliminary support for PCM12/PCM34 (closes mgba.io/i/1468)
- GBA: Allow pausing event loop while CPU is blocked - GBA: Allow pausing event loop while CPU is blocked

View File

@ -133,6 +133,7 @@ struct SM83Core {
uint16_t index; uint16_t index;
int tMultiplier;
int32_t cycles; int32_t cycles;
int32_t nextEvent; int32_t nextEvent;
enum SM83ExecutionState executionState; enum SM83ExecutionState executionState;

View File

@ -65,7 +65,7 @@ void GBAudioInit(struct GBAudio* audio, size_t samples, uint8_t* nr52, enum GBAu
if (style == GB_AUDIO_GBA) { if (style == GB_AUDIO_GBA) {
audio->timingFactor = 4; audio->timingFactor = 4;
} else { } else {
audio->timingFactor = 1; audio->timingFactor = 2;
} }
audio->frameEvent.context = audio; audio->frameEvent.context = audio;
@ -339,7 +339,7 @@ void GBAudioWriteNR34(struct GBAudio* audio, uint8_t value) {
if (audio->playingCh3) { if (audio->playingCh3) {
audio->ch3.readable = audio->style != GB_AUDIO_DMG; audio->ch3.readable = audio->style != GB_AUDIO_DMG;
// TODO: Where does this cycle delay come from? // TODO: Where does this cycle delay come from?
mTimingSchedule(audio->timing, &audio->ch3Event, audio->timingFactor * 4 + 2 * (2048 - audio->ch3.rate)); mTimingSchedule(audio->timing, &audio->ch3Event, audio->timingFactor * (4 + 2 * (2048 - audio->ch3.rate)));
} }
*audio->nr52 &= ~0x0004; *audio->nr52 &= ~0x0004;
*audio->nr52 |= audio->playingCh3 << 2; *audio->nr52 |= audio->playingCh3 << 2;
@ -477,11 +477,8 @@ void GBAudioWriteNR52(struct GBAudio* audio, uint8_t value) {
audio->skipFrame = false; audio->skipFrame = false;
audio->frame = 7; audio->frame = 7;
if (audio->p) { if (audio->p && audio->p->timer.internalDiv & 0x400) {
unsigned timingFactor = 0x400 >> !audio->p->doubleSpeed; audio->skipFrame = true;
if (audio->p->timer.internalDiv & timingFactor) {
audio->skipFrame = true;
}
} }
} }
} }
@ -914,7 +911,7 @@ static void _updateChannel3(struct mTiming* timing, void* user, uint32_t cyclesL
audio->ch3.readable = true; audio->ch3.readable = true;
if (audio->style == GB_AUDIO_DMG) { if (audio->style == GB_AUDIO_DMG) {
mTimingDeschedule(audio->timing, &audio->ch3Fade); mTimingDeschedule(audio->timing, &audio->ch3Fade);
mTimingSchedule(timing, &audio->ch3Fade, 2 - cyclesLate); mTimingSchedule(timing, &audio->ch3Fade, 4 - cyclesLate);
} }
int cycles = 2 * (2048 - ch->rate); int cycles = 2 * (2048 - ch->rate);
mTimingSchedule(timing, &audio->ch3Event, audio->timingFactor * cycles - cyclesLate); mTimingSchedule(timing, &audio->ch3Event, audio->timingFactor * cycles - cyclesLate);

View File

@ -742,7 +742,7 @@ void GBSetInterrupts(struct SM83Core* cpu, bool enable) {
gb->memory.ime = false; gb->memory.ime = false;
GBUpdateIRQs(gb); GBUpdateIRQs(gb);
} else { } else {
mTimingSchedule(&gb->timing, &gb->eiPending, 4); mTimingSchedule(&gb->timing, &gb->eiPending, 4 * cpu->tMultiplier);
} }
} }
@ -796,7 +796,7 @@ void GBStop(struct SM83Core* cpu) {
struct GB* gb = (struct GB*) cpu->master; struct GB* gb = (struct GB*) cpu->master;
if (gb->model >= GB_MODEL_CGB && gb->memory.io[GB_REG_KEY1] & 1) { if (gb->model >= GB_MODEL_CGB && gb->memory.io[GB_REG_KEY1] & 1) {
gb->doubleSpeed ^= 1; gb->doubleSpeed ^= 1;
gb->audio.timingFactor = gb->doubleSpeed + 1; gb->cpu->tMultiplier = 2 - gb->doubleSpeed;
gb->memory.io[GB_REG_KEY1] = 0; gb->memory.io[GB_REG_KEY1] = 0;
gb->memory.io[GB_REG_KEY1] |= gb->doubleSpeed << 7; gb->memory.io[GB_REG_KEY1] |= gb->doubleSpeed << 7;
} else { } else {

View File

@ -417,15 +417,15 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) {
} }
return; return;
case GB_REG_TIMA: case GB_REG_TIMA:
if (value && mTimingUntil(&gb->timing, &gb->timer.irq) > 1) { if (value && mTimingUntil(&gb->timing, &gb->timer.irq) > 2 - (int) gb->doubleSpeed) {
mTimingDeschedule(&gb->timing, &gb->timer.irq); mTimingDeschedule(&gb->timing, &gb->timer.irq);
} }
if (mTimingUntil(&gb->timing, &gb->timer.irq) == -1) { if (mTimingUntil(&gb->timing, &gb->timer.irq) == (int) gb->doubleSpeed - 2) {
return; return;
} }
break; break;
case GB_REG_TMA: case GB_REG_TMA:
if (mTimingUntil(&gb->timing, &gb->timer.irq) == -1) { if (mTimingUntil(&gb->timing, &gb->timer.irq) == (int) gb->doubleSpeed - 2) {
gb->memory.io[GB_REG_TIMA] = value; gb->memory.io[GB_REG_TIMA] = value;
} }
break; break;

View File

@ -531,10 +531,7 @@ void GBMemoryDMA(struct GB* gb, uint16_t base) {
base &= 0xDFFF; base &= 0xDFFF;
} }
mTimingDeschedule(&gb->timing, &gb->memory.dmaEvent); mTimingDeschedule(&gb->timing, &gb->memory.dmaEvent);
mTimingSchedule(&gb->timing, &gb->memory.dmaEvent, 8); mTimingSchedule(&gb->timing, &gb->memory.dmaEvent, 8 * (2 - gb->doubleSpeed));
if (gb->cpu->cycles + 8 < gb->cpu->nextEvent) {
gb->cpu->nextEvent = gb->cpu->cycles + 8;
}
gb->memory.dmaSource = base; gb->memory.dmaSource = base;
gb->memory.dmaDest = 0; gb->memory.dmaDest = 0;
gb->memory.dmaRemaining = 0xA0; gb->memory.dmaRemaining = 0xA0;
@ -580,7 +577,7 @@ void _GBMemoryDMAService(struct mTiming* timing, void* context, uint32_t cyclesL
++gb->memory.dmaDest; ++gb->memory.dmaDest;
gb->memory.dmaRemaining = dmaRemaining - 1; gb->memory.dmaRemaining = dmaRemaining - 1;
if (gb->memory.dmaRemaining) { if (gb->memory.dmaRemaining) {
mTimingSchedule(timing, &gb->memory.dmaEvent, 4 - cyclesLate); mTimingSchedule(timing, &gb->memory.dmaEvent, 4 * (2 - gb->doubleSpeed) - cyclesLate);
} }
} }
@ -594,7 +591,7 @@ void _GBMemoryHDMAService(struct mTiming* timing, void* context, uint32_t cycles
--gb->memory.hdmaRemaining; --gb->memory.hdmaRemaining;
if (gb->memory.hdmaRemaining) { if (gb->memory.hdmaRemaining) {
mTimingDeschedule(timing, &gb->memory.hdmaEvent); mTimingDeschedule(timing, &gb->memory.hdmaEvent);
mTimingSchedule(timing, &gb->memory.hdmaEvent, 2 - cyclesLate); mTimingSchedule(timing, &gb->memory.hdmaEvent, 4 - cyclesLate);
} else { } else {
gb->cpuBlocked = false; gb->cpuBlocked = false;
gb->memory.io[GB_REG_HDMA1] = gb->memory.hdmaSource >> 8; gb->memory.io[GB_REG_HDMA1] = gb->memory.hdmaSource >> 8;

View File

@ -175,8 +175,6 @@ bool GBDeserialize(struct GB* gb, const struct GBSerializedState* state) {
gb->cpu->halted = GBSerializedCpuFlagsGetHalted(flags); gb->cpu->halted = GBSerializedCpuFlagsGetHalted(flags);
gb->cpuBlocked = GBSerializedCpuFlagsGetBlocked(flags); gb->cpuBlocked = GBSerializedCpuFlagsGetBlocked(flags);
gb->audio.timingFactor = gb->doubleSpeed + 1;
LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles); LOAD_32LE(gb->cpu->cycles, 0, &state->cpu.cycles);
LOAD_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent); LOAD_32LE(gb->cpu->nextEvent, 0, &state->cpu.nextEvent);
gb->timing.root = NULL; gb->timing.root = NULL;

View File

@ -77,7 +77,7 @@ void _GBSIOProcessEvents(struct mTiming* timing, void* context, uint32_t cyclesL
sio->pendingSB = 0xFF; sio->pendingSB = 0xFF;
} }
} else { } else {
mTimingSchedule(timing, &sio->event, sio->period); mTimingSchedule(timing, &sio->event, sio->period * (2 - sio->p->doubleSpeed));
} }
} }
@ -93,7 +93,7 @@ void GBSIOWriteSC(struct GBSIO* sio, uint8_t sc) {
if (GBRegisterSCIsEnable(sc)) { if (GBRegisterSCIsEnable(sc)) {
mTimingDeschedule(&sio->p->timing, &sio->event); mTimingDeschedule(&sio->p->timing, &sio->event);
if (GBRegisterSCIsShiftClock(sc)) { if (GBRegisterSCIsShiftClock(sc)) {
mTimingSchedule(&sio->p->timing, &sio->event, sio->period); mTimingSchedule(&sio->p->timing, &sio->event, sio->period * (2 - sio->p->doubleSpeed));
sio->remainingBits = 8; sio->remainingBits = 8;
} }
} }

View File

@ -128,7 +128,7 @@ static int32_t _masterUpdate(struct GBSIOLockstepNode* node) {
case TRANSFER_FINISHING: case TRANSFER_FINISHING:
// Finish the transfer // Finish the transfer
// We need to make sure the other GBs catch up so they don't get behind // We need to make sure the other GBs catch up so they don't get behind
node->nextEvent += node->d.p->period - 8; // Split the cycles to avoid waiting too long node->nextEvent += node->d.p->period * (2 - node->d.p->p->doubleSpeed) - 8; // Split the cycles to avoid waiting too long
#ifndef NDEBUG #ifndef NDEBUG
ATOMIC_ADD(node->p->d.transferId, 1); ATOMIC_ADD(node->p->d.transferId, 1);
#endif #endif
@ -208,7 +208,7 @@ static void _GBSIOLockstepNodeProcessEvents(struct mTiming* timing, void* user,
struct GBSIOLockstepNode* node = user; struct GBSIOLockstepNode* node = user;
mLockstepLock(&node->p->d); mLockstepLock(&node->p->d);
if (node->p->d.attached < 2) { if (node->p->d.attached < 2) {
mTimingSchedule(timing, &node->event, (GBSIOCyclesPerTransfer[0] >> 1) - cyclesLate); mTimingSchedule(timing, &node->event, (GBSIOCyclesPerTransfer[0] >> 1) * (2 - node->d.p->p->doubleSpeed) - cyclesLate);
mLockstepUnlock(&node->p->d); mLockstepUnlock(&node->p->d);
return; return;
} }

View File

@ -20,17 +20,18 @@ void _GBTimerIRQ(struct mTiming* timing, void* context, uint32_t cyclesLate) {
} }
static void _GBTimerDivIncrement(struct GBTimer* timer, uint32_t cyclesLate) { static void _GBTimerDivIncrement(struct GBTimer* timer, uint32_t cyclesLate) {
while (timer->nextDiv >= GB_DMG_DIV_PERIOD) { int tMultiplier = 2 - timer->p->doubleSpeed;
timer->nextDiv -= GB_DMG_DIV_PERIOD; while (timer->nextDiv >= GB_DMG_DIV_PERIOD * tMultiplier) {
timer->nextDiv -= GB_DMG_DIV_PERIOD * tMultiplier;
// Make sure to trigger when the correct bit is a falling edge // Make sure to trigger when the correct bit is a falling edge
if (timer->timaPeriod > 0 && (timer->internalDiv & (timer->timaPeriod - 1)) == timer->timaPeriod - 1) { if (timer->timaPeriod > 0 && (timer->internalDiv & (timer->timaPeriod - 1)) == timer->timaPeriod - 1) {
++timer->p->memory.io[GB_REG_TIMA]; ++timer->p->memory.io[GB_REG_TIMA];
if (!timer->p->memory.io[GB_REG_TIMA]) { if (!timer->p->memory.io[GB_REG_TIMA]) {
mTimingSchedule(&timer->p->timing, &timer->irq, 7 - ((timer->p->cpu->executionState - cyclesLate) & 3)); mTimingSchedule(&timer->p->timing, &timer->irq, 7 * tMultiplier - ((timer->p->cpu->executionState * tMultiplier - cyclesLate) & (3 * tMultiplier)));
} }
} }
unsigned timingFactor = 0x3FF >> !timer->p->doubleSpeed; unsigned timingFactor = 0x1FF;
if ((timer->internalDiv & timingFactor) == timingFactor) { if ((timer->internalDiv & timingFactor) == timingFactor) {
GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing); GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing);
} }
@ -52,7 +53,7 @@ void _GBTimerUpdate(struct mTiming* timing, void* context, uint32_t cyclesLate)
if (timaToGo < divsToGo) { if (timaToGo < divsToGo) {
divsToGo = timaToGo; divsToGo = timaToGo;
} }
timer->nextDiv = GB_DMG_DIV_PERIOD * divsToGo; timer->nextDiv = GB_DMG_DIV_PERIOD * divsToGo * (2 - timer->p->doubleSpeed);
mTimingSchedule(timing, &timer->event, timer->nextDiv - cyclesLate); mTimingSchedule(timing, &timer->event, timer->nextDiv - cyclesLate);
} }
@ -66,7 +67,7 @@ void GBTimerReset(struct GBTimer* timer) {
timer->irq.callback = _GBTimerIRQ; timer->irq.callback = _GBTimerIRQ;
timer->event.priority = 0x21; timer->event.priority = 0x21;
timer->nextDiv = GB_DMG_DIV_PERIOD; // TODO: GBC differences timer->nextDiv = GB_DMG_DIV_PERIOD * 2;
timer->timaPeriod = 1024 >> 4; timer->timaPeriod = 1024 >> 4;
} }
@ -74,27 +75,27 @@ void GBTimerDivReset(struct GBTimer* timer) {
timer->nextDiv -= mTimingUntil(&timer->p->timing, &timer->event); timer->nextDiv -= mTimingUntil(&timer->p->timing, &timer->event);
mTimingDeschedule(&timer->p->timing, &timer->event); mTimingDeschedule(&timer->p->timing, &timer->event);
_GBTimerDivIncrement(timer, 0); _GBTimerDivIncrement(timer, 0);
if (((timer->internalDiv << 1) | ((timer->nextDiv >> 3) & 1)) & timer->timaPeriod) { int tMultiplier = 2 - timer->p->doubleSpeed;
if (((timer->internalDiv << 1) | ((timer->nextDiv >> (4 - timer->p->doubleSpeed)) & 1)) & timer->timaPeriod) {
++timer->p->memory.io[GB_REG_TIMA]; ++timer->p->memory.io[GB_REG_TIMA];
if (!timer->p->memory.io[GB_REG_TIMA]) { if (!timer->p->memory.io[GB_REG_TIMA]) {
mTimingSchedule(&timer->p->timing, &timer->irq, 7 - (timer->p->cpu->executionState & 3)); mTimingSchedule(&timer->p->timing, &timer->irq, (7 - (timer->p->cpu->executionState & 3)) * tMultiplier);
} }
} }
unsigned timingFactor = 0x400 >> !timer->p->doubleSpeed; if (timer->internalDiv & 0x200) {
if (timer->internalDiv & timingFactor) {
GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing); GBAudioUpdateFrame(&timer->p->audio, &timer->p->timing);
} }
timer->p->memory.io[GB_REG_DIV] = 0; timer->p->memory.io[GB_REG_DIV] = 0;
timer->internalDiv = 0; timer->internalDiv = 0;
timer->nextDiv = GB_DMG_DIV_PERIOD; timer->nextDiv = GB_DMG_DIV_PERIOD * (2 - timer->p->doubleSpeed);
mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv - ((timer->p->cpu->executionState + 1) & 3)); mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv - ((timer->p->cpu->executionState + 1) & 3) * tMultiplier);
} }
uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) { uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {
if (GBRegisterTACIsRun(tac)) { if (GBRegisterTACIsRun(tac)) {
timer->nextDiv -= mTimingUntil(&timer->p->timing, &timer->event); timer->nextDiv -= mTimingUntil(&timer->p->timing, &timer->event);
mTimingDeschedule(&timer->p->timing, &timer->event); mTimingDeschedule(&timer->p->timing, &timer->event);
_GBTimerDivIncrement(timer, (timer->p->cpu->executionState + 2) & 3); _GBTimerDivIncrement(timer, ((timer->p->cpu->executionState + 2) & 3) * (2 - timer->p->doubleSpeed));
switch (GBRegisterTACGetClock(tac)) { switch (GBRegisterTACGetClock(tac)) {
case 0: case 0:
@ -111,7 +112,7 @@ uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {
break; break;
} }
timer->nextDiv += GB_DMG_DIV_PERIOD; timer->nextDiv += GB_DMG_DIV_PERIOD * (2 - timer->p->doubleSpeed);
mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv); mTimingSchedule(&timer->p->timing, &timer->event, timer->nextDiv);
} else { } else {
timer->timaPeriod = 0; timer->timaPeriod = 0;

View File

@ -253,7 +253,7 @@ void GBVideoSkipBIOS(struct GBVideo* video) {
GBUpdateIRQs(video->p); GBUpdateIRQs(video->p);
video->p->memory.io[GB_REG_STAT] = video->stat; video->p->memory.io[GB_REG_STAT] = video->stat;
mTimingDeschedule(&video->p->timing, &video->modeEvent); mTimingDeschedule(&video->p->timing, &video->modeEvent);
mTimingSchedule(&video->p->timing, &video->modeEvent, next); mTimingSchedule(&video->p->timing, &video->modeEvent, next << 1);
} }
void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) { void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
@ -297,7 +297,7 @@ void _endMode0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
GBUpdateIRQs(video->p); GBUpdateIRQs(video->p);
video->p->memory.io[GB_REG_STAT] = video->stat; video->p->memory.io[GB_REG_STAT] = video->stat;
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate); mTimingSchedule(timing, &video->modeEvent, (next << 1) - cyclesLate);
} }
void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) { void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
@ -334,14 +334,14 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
GBUpdateIRQs(video->p); GBUpdateIRQs(video->p);
} }
video->p->memory.io[GB_REG_STAT] = video->stat; video->p->memory.io[GB_REG_STAT] = video->stat;
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate); mTimingSchedule(timing, &video->modeEvent, (next << 1) - cyclesLate);
} }
void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) { void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct GBVideo* video = context; struct GBVideo* video = context;
_cleanOAM(video, video->ly); _cleanOAM(video, video->ly);
video->x = -(video->p->memory.io[GB_REG_SCX] & 7); video->x = -(video->p->memory.io[GB_REG_SCX] & 7);
video->dotClock = mTimingCurrentTime(timing) - cyclesLate + 5 - (video->x << video->p->doubleSpeed); video->dotClock = mTimingCurrentTime(timing) - cyclesLate + 10 - (video->x << 1);
int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 6 - video->x; int32_t next = GB_VIDEO_MODE_3_LENGTH_BASE + video->objMax * 6 - video->x;
video->mode = 3; video->mode = 3;
video->modeEvent.callback = _endMode3; video->modeEvent.callback = _endMode3;
@ -352,7 +352,7 @@ void _endMode2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
GBUpdateIRQs(video->p); GBUpdateIRQs(video->p);
} }
video->p->memory.io[GB_REG_STAT] = video->stat; video->p->memory.io[GB_REG_STAT] = video->stat;
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate); mTimingSchedule(timing, &video->modeEvent, (next << 1) - cyclesLate);
} }
void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) { void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
@ -375,18 +375,18 @@ void _endMode3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
video->p->memory.io[GB_REG_STAT] = video->stat; video->p->memory.io[GB_REG_STAT] = video->stat;
// TODO: Cache SCX & 7 in case it changes // TODO: Cache SCX & 7 in case it changes
int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 6 - (video->p->memory.io[GB_REG_SCX] & 7); int32_t next = GB_VIDEO_MODE_0_LENGTH_BASE - video->objMax * 6 - (video->p->memory.io[GB_REG_SCX] & 7);
mTimingSchedule(timing, &video->modeEvent, (next << video->p->doubleSpeed) - cyclesLate); mTimingSchedule(timing, &video->modeEvent, (next << 1) - cyclesLate);
} }
void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLate) { void _updateFrameCount(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(cyclesLate); UNUSED(cyclesLate);
struct GBVideo* video = context; struct GBVideo* video = context;
if (video->p->cpu->executionState != SM83_CORE_FETCH) { if (video->p->cpu->executionState != SM83_CORE_FETCH) {
mTimingSchedule(timing, &video->frameEvent, 4 - ((video->p->cpu->executionState + 1) & 3)); mTimingSchedule(timing, &video->frameEvent, (4 - ((video->p->cpu->executionState + 1) & 3)) * (2 - video->p->doubleSpeed));
return; return;
} }
if (!GBRegisterLCDCIsEnable(video->p->memory.io[GB_REG_LCDC])) { if (!GBRegisterLCDCIsEnable(video->p->memory.io[GB_REG_LCDC])) {
mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH); mTimingSchedule(timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH << 1);
} }
--video->frameskipCounter; --video->frameskipCounter;
@ -424,7 +424,7 @@ void GBVideoProcessDots(struct GBVideo* video, uint32_t cyclesLate) {
return; return;
} }
int oldX = video->x; int oldX = video->x;
video->x = (int32_t) (mTimingCurrentTime(&video->p->timing) - cyclesLate - video->dotClock) >> video->p->doubleSpeed; video->x = ((int32_t) (mTimingCurrentTime(&video->p->timing) - cyclesLate - video->dotClock)) >> 1;
if (video->x > GB_VIDEO_HORIZONTAL_PIXELS) { if (video->x > GB_VIDEO_HORIZONTAL_PIXELS) {
video->x = GB_VIDEO_HORIZONTAL_PIXELS; video->x = GB_VIDEO_HORIZONTAL_PIXELS;
} else if (video->x < 0) { } else if (video->x < 0) {
@ -444,7 +444,7 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
video->modeEvent.callback = _endMode2; video->modeEvent.callback = _endMode2;
int32_t next = GB_VIDEO_MODE_2_LENGTH - 5; // TODO: Why is this fudge factor needed? Might be related to T-cycles for load/store differing int32_t next = GB_VIDEO_MODE_2_LENGTH - 5; // TODO: Why is this fudge factor needed? Might be related to T-cycles for load/store differing
mTimingDeschedule(&video->p->timing, &video->modeEvent); mTimingDeschedule(&video->p->timing, &video->modeEvent);
mTimingSchedule(&video->p->timing, &video->modeEvent, next << video->p->doubleSpeed); mTimingSchedule(&video->p->timing, &video->modeEvent, next << 1);
video->ly = 0; video->ly = 0;
video->p->memory.io[GB_REG_LY] = 0; video->p->memory.io[GB_REG_LY] = 0;
@ -471,7 +471,7 @@ void GBVideoWriteLCDC(struct GBVideo* video, GBRegisterLCDC value) {
mTimingDeschedule(&video->p->timing, &video->modeEvent); mTimingDeschedule(&video->p->timing, &video->modeEvent);
mTimingDeschedule(&video->p->timing, &video->frameEvent); mTimingDeschedule(&video->p->timing, &video->frameEvent);
mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH); mTimingSchedule(&video->p->timing, &video->frameEvent, GB_VIDEO_TOTAL_LENGTH << 1);
} }
video->p->memory.io[GB_REG_STAT] = video->stat; video->p->memory.io[GB_REG_STAT] = video->stat;
} }

View File

@ -61,6 +61,7 @@ void SM83Reset(struct SM83Core* cpu) {
cpu->instruction = 0; cpu->instruction = 0;
cpu->tMultiplier = 2;
cpu->cycles = 0; cpu->cycles = 0;
cpu->nextEvent = 0; cpu->nextEvent = 0;
cpu->executionState = SM83_CORE_FETCH; cpu->executionState = SM83_CORE_FETCH;
@ -102,7 +103,7 @@ static void _SM83InstructionIRQ(struct SM83Core* cpu) {
} }
static void _SM83Step(struct SM83Core* cpu) { static void _SM83Step(struct SM83Core* cpu) {
++cpu->cycles; cpu->cycles += cpu->tMultiplier;
enum SM83ExecutionState state = cpu->executionState; enum SM83ExecutionState state = cpu->executionState;
cpu->executionState = SM83_CORE_IDLE_0; cpu->executionState = SM83_CORE_IDLE_0;
switch (state) { switch (state) {
@ -147,23 +148,31 @@ static void _SM83Step(struct SM83Core* cpu) {
} }
} }
static inline bool _SM83TickInternal(struct SM83Core* cpu) {
bool running = true;
_SM83Step(cpu);
int t = cpu->tMultiplier;
if (cpu->cycles + t * 2 >= cpu->nextEvent) {
int32_t diff = cpu->nextEvent - cpu->cycles;
cpu->cycles = cpu->nextEvent;
cpu->executionState += diff >> (t - 1); // NB: This assumes tMultiplier is either 1 or 2
cpu->irqh.processEvents(cpu);
cpu->cycles += (SM83_CORE_EXECUTE - cpu->executionState) * t;
running = false;
} else {
cpu->cycles += t * 2;
}
cpu->executionState = SM83_CORE_FETCH;
cpu->instruction(cpu);
cpu->cycles += t;
return running;
}
void SM83Tick(struct SM83Core* cpu) { void SM83Tick(struct SM83Core* cpu) {
while (cpu->cycles >= cpu->nextEvent) { while (cpu->cycles >= cpu->nextEvent) {
cpu->irqh.processEvents(cpu); cpu->irqh.processEvents(cpu);
} }
_SM83Step(cpu); _SM83TickInternal(cpu);
if (cpu->cycles + 2 >= cpu->nextEvent) {
int32_t diff = cpu->nextEvent - cpu->cycles;
cpu->cycles = cpu->nextEvent;
cpu->executionState += diff;
cpu->irqh.processEvents(cpu);
cpu->cycles += SM83_CORE_EXECUTE - cpu->executionState;
} else {
cpu->cycles += 2;
}
cpu->executionState = SM83_CORE_FETCH;
cpu->instruction(cpu);
++cpu->cycles;
} }
void SM83Run(struct SM83Core* cpu) { void SM83Run(struct SM83Core* cpu) {
@ -173,19 +182,6 @@ void SM83Run(struct SM83Core* cpu) {
cpu->irqh.processEvents(cpu); cpu->irqh.processEvents(cpu);
break; break;
} }
_SM83Step(cpu); running = _SM83TickInternal(cpu) && running;
if (cpu->cycles + 2 >= cpu->nextEvent) {
int32_t diff = cpu->nextEvent - cpu->cycles;
cpu->cycles = cpu->nextEvent;
cpu->executionState += diff;
cpu->irqh.processEvents(cpu);
cpu->cycles += SM83_CORE_EXECUTE - cpu->executionState;
running = false;
} else {
cpu->cycles += 2;
}
cpu->executionState = SM83_CORE_FETCH;
cpu->instruction(cpu);
++cpu->cycles;
} }
} }