mirror of https://github.com/mgba-emu/mgba.git
GB: Redo double speed emulation (closes #1515)
This commit is contained in:
parent
8361d56683
commit
bda4316839
1
CHANGES
1
CHANGES
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue