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

View File

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