GB Timer: Make timers behave accurately

This commit is contained in:
Jeffrey Pfau 2016-08-26 11:51:24 -07:00
parent a43c465f9f
commit 2347bc4a52
3 changed files with 48 additions and 58 deletions

View File

@ -121,8 +121,11 @@ mLOG_DECLARE_CATEGORY(GB_STATE);
* | 0x00154 - 0x00157: Next event * | 0x00154 - 0x00157: Next event
* | 0x00158 - 0x0015B: Event diff * | 0x00158 - 0x0015B: Event diff
* | 0x0015C - 0x0015F: Next DIV * | 0x0015C - 0x0015F: Next DIV
* | 0x00160 - 0x00163: Next TIMA * | 0x00160 - 0x00163: Inernal DIV
* | 0x00164 - 0x00167: TIMA period * | 0x00164: TIMA period
* | 0x00165: Flags
* | bit 0: Is IRQ pending?
* | 0x00166 - 0x00167: Reserved
* 0x000168 - 0x000197: Memory state * 0x000168 - 0x000197: Memory state
* | 0x00168 - 0x00169: Current ROM bank * | 0x00168 - 0x00169: Current ROM bank
* | 0x0016A: Current WRAM bank * | 0x0016A: Current WRAM bank
@ -205,6 +208,8 @@ DECL_BIT(GBSerializedCpuFlags, Condition, 0);
DECL_BIT(GBSerializedCpuFlags, IrqPending, 1); DECL_BIT(GBSerializedCpuFlags, IrqPending, 1);
DECL_BIT(GBSerializedCpuFlags, DoubleSpeed, 2); DECL_BIT(GBSerializedCpuFlags, DoubleSpeed, 2);
DECL_BITFIELD(GBSerializedTimerFlags, uint8_t);
DECL_BIT(GBSerializedTimerFlags, IrqPending, 0);
DECL_BITFIELD(GBSerializedVideoFlags, uint8_t); DECL_BITFIELD(GBSerializedVideoFlags, uint8_t);
DECL_BIT(GBSerializedVideoFlags, BcpIncrement, 0); DECL_BIT(GBSerializedVideoFlags, BcpIncrement, 0);
@ -290,8 +295,10 @@ struct GBSerializedState {
int32_t eventDiff; int32_t eventDiff;
int32_t nextDiv; int32_t nextDiv;
int32_t nextTima; uint32_t internalDiv;
int32_t timaPeriod; uint8_t timaPeriod;
GBSerializedTimerFlags flags;
uint16_t reserved;
} timer; } timer;
struct { struct {

View File

@ -11,10 +11,10 @@
void GBTimerReset(struct GBTimer* timer) { void GBTimerReset(struct GBTimer* timer) {
timer->nextDiv = GB_DMG_DIV_PERIOD; // TODO: GBC differences timer->nextDiv = GB_DMG_DIV_PERIOD; // TODO: GBC differences
timer->nextTima = INT_MAX;
timer->nextEvent = GB_DMG_DIV_PERIOD; timer->nextEvent = GB_DMG_DIV_PERIOD;
timer->eventDiff = 0; timer->eventDiff = 0;
timer->timaPeriod = 1024; timer->timaPeriod = 1024 >> 4;
timer->internalDiv = 0;
} }
int32_t GBTimerProcessEvents(struct GBTimer* timer, int32_t cycles) { int32_t GBTimerProcessEvents(struct GBTimer* timer, int32_t cycles) {
@ -22,34 +22,28 @@ int32_t GBTimerProcessEvents(struct GBTimer* timer, int32_t cycles) {
timer->nextEvent -= cycles; timer->nextEvent -= cycles;
if (timer->nextEvent <= 0) { if (timer->nextEvent <= 0) {
timer->nextDiv -= timer->eventDiff; timer->nextDiv -= timer->eventDiff;
if (timer->nextDiv <= 0) { if (timer->irqPending) {
++timer->p->memory.io[REG_DIV]; timer->p->memory.io[REG_TIMA] = timer->p->memory.io[REG_TMA];
timer->nextDiv = GB_DMG_DIV_PERIOD; timer->p->memory.io[REG_IF] |= (1 << GB_IRQ_TIMER);
GBUpdateIRQs(timer->p);
timer->irqPending = false;
timer->nextEvent = timer->nextDiv;
} }
timer->nextEvent = timer->nextDiv; if (timer->nextDiv <= 0) {
++timer->internalDiv;
timer->p->memory.io[REG_DIV] = timer->internalDiv >> 4;
timer->nextDiv = GB_DMG_DIV_PERIOD;
timer->nextEvent = timer->nextDiv;
if (timer->nextTima != INT_MAX) { // Make sure to trigger when the correct bit is a falling edge
timer->nextTima -= timer->eventDiff; if (timer->timaPeriod == 1 || (timer->internalDiv & (timer->timaPeriod - 1)) == (timer->timaPeriod >> 1) - 1) {
if (timer->nextTima <= 0) { ++timer->p->memory.io[REG_TIMA];
if (!timer->p->memory.io[REG_TIMA]) { if (!timer->p->memory.io[REG_TIMA]) {
timer->p->memory.io[REG_TIMA] = timer->p->memory.io[REG_TMA]; timer->irqPending = true;
timer->p->memory.io[REG_IF] |= (1 << GB_IRQ_TIMER); timer->nextEvent = 4;
GBUpdateIRQs(timer->p);
timer->nextTima = timer->timaPeriod - 4;
} else {
++timer->p->memory.io[REG_TIMA];
if (!timer->p->memory.io[REG_TIMA]) {
timer->nextTima = 4;
} else {
timer->nextTima = timer->timaPeriod;
}
} }
} }
if (timer->nextTima < timer->nextEvent) {
timer->nextEvent = timer->nextTima;
}
} }
timer->eventDiff = 0; timer->eventDiff = 0;
} }
return timer->nextEvent; return timer->nextEvent;
@ -57,60 +51,49 @@ int32_t GBTimerProcessEvents(struct GBTimer* timer, int32_t cycles) {
void GBTimerDivReset(struct GBTimer* timer) { void GBTimerDivReset(struct GBTimer* timer) {
timer->p->memory.io[REG_DIV] = 0; timer->p->memory.io[REG_DIV] = 0;
timer->nextDiv = timer->eventDiff + timer->p->cpu->cycles + GB_DMG_DIV_PERIOD; timer->internalDiv = 0;
if (timer->nextDiv - timer->eventDiff < timer->nextEvent) {
timer->nextEvent = timer->nextDiv - timer->eventDiff;
if (timer->nextEvent < timer->p->cpu->nextEvent) {
timer->p->cpu->nextEvent = timer->nextEvent;
}
}
} }
uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) { uint8_t GBTimerUpdateTAC(struct GBTimer* timer, GBRegisterTAC tac) {
if (GBRegisterTACIsRun(tac)) { if (GBRegisterTACIsRun(tac)) {
switch (GBRegisterTACGetClock(tac)) { switch (GBRegisterTACGetClock(tac)) {
case 0: case 0:
timer->timaPeriod = 1024; timer->timaPeriod = 1024 >> 4;
break; break;
case 1: case 1:
timer->timaPeriod = 16; timer->timaPeriod = 16 >> 4;
break; break;
case 2: case 2:
timer->timaPeriod = 64; timer->timaPeriod = 64 >> 4;
break; break;
case 3: case 3:
timer->timaPeriod = 256; timer->timaPeriod = 256 >> 4;
break; break;
} }
GBTimerUpdateTIMA(timer);
} else { } else {
timer->nextTima = INT_MAX; timer->timaPeriod = 0;
} }
return tac; return tac;
} }
void GBTimerUpdateTIMA(struct GBTimer* timer) {
timer->nextTima = timer->eventDiff + timer->p->cpu->cycles + timer->timaPeriod;
if (timer->nextTima - timer->eventDiff < timer->nextEvent) {
timer->nextEvent = timer->nextTima - timer->eventDiff;
if (timer->nextEvent < timer->p->cpu->nextEvent) {
timer->p->cpu->nextEvent = timer->nextEvent;
}
}
}
void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* state) { void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* state) {
STORE_32LE(timer->nextEvent, 0, &state->timer.nextEvent); STORE_32LE(timer->nextEvent, 0, &state->timer.nextEvent);
STORE_32LE(timer->eventDiff, 0, &state->timer.eventDiff); STORE_32LE(timer->eventDiff, 0, &state->timer.eventDiff);
STORE_32LE(timer->nextDiv, 0, &state->timer.nextDiv); STORE_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
STORE_32LE(timer->nextTima, 0, &state->timer.nextTima); STORE_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
STORE_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod); STORE_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
GBSerializedTimerFlags flags = 0;
flags = GBSerializedTimerFlagsSetIrqPending(flags, state->timer.flags);
state->timer.flags = flags;
} }
void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* state) { void GBTimerDeserialize(struct GBTimer* timer, const struct GBSerializedState* state) {
LOAD_32LE(timer->nextEvent, 0, &state->timer.nextEvent); LOAD_32LE(timer->nextEvent, 0, &state->timer.nextEvent);
LOAD_32LE(timer->eventDiff, 0, &state->timer.eventDiff); LOAD_32LE(timer->eventDiff, 0, &state->timer.eventDiff);
LOAD_32LE(timer->nextDiv, 0, &state->timer.nextDiv); LOAD_32LE(timer->nextDiv, 0, &state->timer.nextDiv);
LOAD_32LE(timer->nextTima, 0, &state->timer.nextTima); LOAD_32LE(timer->internalDiv, 0, &state->timer.internalDiv);
LOAD_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod); LOAD_32LE(timer->timaPeriod, 0, &state->timer.timaPeriod);
GBSerializedTimerFlags flags = state->timer.flags ;
timer->irqPending = GBSerializedTimerFlagsIsIrqPending(flags);
} }

View File

@ -13,7 +13,7 @@ DECL_BITS(GBRegisterTAC, Clock, 0, 2);
DECL_BIT(GBRegisterTAC, Run, 2); DECL_BIT(GBRegisterTAC, Run, 2);
enum { enum {
GB_DMG_DIV_PERIOD = 256 GB_DMG_DIV_PERIOD = 16
}; };
struct GB; struct GB;
@ -23,16 +23,16 @@ struct GBTimer {
int32_t nextEvent; int32_t nextEvent;
int32_t eventDiff; int32_t eventDiff;
uint32_t internalDiv;
int32_t nextDiv; int32_t nextDiv;
int32_t nextTima; uint32_t timaPeriod;
int32_t timaPeriod; bool irqPending;
}; };
void GBTimerReset(struct GBTimer*); void GBTimerReset(struct GBTimer*);
int32_t GBTimerProcessEvents(struct GBTimer*, int32_t cycles); int32_t GBTimerProcessEvents(struct GBTimer*, int32_t cycles);
void GBTimerDivReset(struct GBTimer*); void GBTimerDivReset(struct GBTimer*);
uint8_t GBTimerUpdateTAC(struct GBTimer*, GBRegisterTAC tac); uint8_t GBTimerUpdateTAC(struct GBTimer*, GBRegisterTAC tac);
void GBTimerUpdateTIMA(struct GBTimer* timer);
struct GBSerializedState; struct GBSerializedState;
void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* state); void GBTimerSerialize(const struct GBTimer* timer, struct GBSerializedState* state);