mirror of
https://github.com/libretro/mgba.git
synced 2024-11-30 19:50:34 +00:00
GBA Timer: More timer improvements
This commit is contained in:
parent
db08a75d9b
commit
7c59350e9d
@ -79,7 +79,6 @@ struct GBA {
|
||||
uint32_t bus;
|
||||
int performingDMA;
|
||||
|
||||
struct mTimingEvent timerMaster;
|
||||
struct GBATimer timers[4];
|
||||
|
||||
int springIRQ;
|
||||
|
@ -264,10 +264,10 @@ struct GBASerializedState {
|
||||
|
||||
struct {
|
||||
uint16_t reload;
|
||||
uint16_t oldReload;
|
||||
uint16_t reserved;
|
||||
uint32_t lastEvent;
|
||||
uint32_t nextEvent;
|
||||
int32_t overflowInterval;
|
||||
uint32_t nextIrq;
|
||||
GBATimerFlags flags;
|
||||
} timers[4];
|
||||
|
||||
|
@ -17,12 +17,14 @@ DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4);
|
||||
DECL_BIT(GBATimerFlags, CountUp, 4);
|
||||
DECL_BIT(GBATimerFlags, DoIrq, 5);
|
||||
DECL_BIT(GBATimerFlags, Enable, 6);
|
||||
DECL_BIT(GBATimerFlags, IrqPending, 7);
|
||||
|
||||
struct GBA;
|
||||
struct GBATimer {
|
||||
uint16_t reload;
|
||||
int32_t lastEvent;
|
||||
struct mTimingEvent event;
|
||||
struct mTimingEvent irq;
|
||||
GBATimerFlags flags;
|
||||
};
|
||||
|
||||
|
@ -928,6 +928,7 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
|
||||
STORE_16(gba->timers[i].reload, 0, &state->timers[i].reload);
|
||||
STORE_32(gba->timers[i].lastEvent - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].lastEvent);
|
||||
STORE_32(gba->timers[i].event.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextEvent);
|
||||
STORE_32(gba->timers[i].irq.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextIrq);
|
||||
STORE_32(gba->timers[i].flags, 0, &state->timers[i].flags);
|
||||
STORE_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);
|
||||
STORE_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);
|
||||
@ -951,10 +952,6 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
||||
}
|
||||
|
||||
uint32_t when;
|
||||
LOAD_32(when, 0, &state->masterCycles);
|
||||
when += state->cpu.cycles;
|
||||
when = 0x400 - (when & 0x3FF);
|
||||
mTimingSchedule(&gba->timing, &gba->timerMaster, when);
|
||||
for (i = 0; i < 4; ++i) {
|
||||
LOAD_16(gba->timers[i].reload, 0, &state->timers[i].reload);
|
||||
LOAD_32(gba->timers[i].flags, 0, &state->timers[i].flags);
|
||||
@ -969,6 +966,10 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
||||
if (GBATimerFlagsIsEnable(gba->timers[i].flags)) {
|
||||
mTimingSchedule(&gba->timing, &gba->timers[i].event, when);
|
||||
}
|
||||
LOAD_32(when, 0, &state->timers[i].nextIrq);
|
||||
if (GBATimerFlagsIsIrqPending(gba->timers[i].flags)) {
|
||||
mTimingSchedule(&gba->timing, &gba->timers[i].irq, when);
|
||||
}
|
||||
|
||||
LOAD_16(gba->memory.dma[i].reg, (REG_DMA0CNT_HI + i * 12), state->io);
|
||||
LOAD_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);
|
||||
|
@ -8,7 +8,39 @@
|
||||
#include <mgba/internal/gba/gba.h>
|
||||
#include <mgba/internal/gba/io.h>
|
||||
|
||||
#define TIMER_MASTER 1024
|
||||
#define TIMER_IRQ_DELAY 7
|
||||
|
||||
static void GBATimerIrq(struct GBA* gba, int timerId) {
|
||||
struct GBATimer* timer = &gba->timers[timerId];
|
||||
if (GBATimerFlagsIsIrqPending(timer->flags)) {
|
||||
timer->flags = GBATimerFlagsClearIrqPending(timer->flags);
|
||||
GBARaiseIRQ(gba, IRQ_TIMER0 + timerId);
|
||||
}
|
||||
}
|
||||
|
||||
static void GBATimerIrq0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
GBATimerIrq(context, 0);
|
||||
}
|
||||
|
||||
static void GBATimerIrq1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
GBATimerIrq(context, 1);
|
||||
}
|
||||
|
||||
static void GBATimerIrq2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
GBATimerIrq(context, 2);
|
||||
}
|
||||
|
||||
static void GBATimerIrq3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
UNUSED(cyclesLate);
|
||||
GBATimerIrq(context, 3);
|
||||
}
|
||||
|
||||
static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) {
|
||||
struct GBATimer* timer = &gba->timers[timerId];
|
||||
@ -20,7 +52,10 @@ static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) {
|
||||
GBATimerUpdateRegister(gba, timerId, 0);
|
||||
|
||||
if (GBATimerFlagsIsDoIrq(timer->flags)) {
|
||||
GBARaiseIRQ(gba, IRQ_TIMER0 + timerId);
|
||||
timer->flags = GBATimerFlagsFillIrqPending(timer->flags);
|
||||
if (!mTimingIsScheduled(&gba->timing, &timer->irq)) {
|
||||
mTimingSchedule(&gba->timing, &timer->irq, TIMER_IRQ_DELAY - cyclesLate);
|
||||
}
|
||||
}
|
||||
|
||||
if (gba->audio.enable && timerId < 2) {
|
||||
@ -44,15 +79,6 @@ static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) {
|
||||
}
|
||||
}
|
||||
|
||||
static void GBATimerMasterUpdate(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
struct GBA* gba = context;
|
||||
mTimingSchedule(timing, &gba->timerMaster, TIMER_MASTER - cyclesLate);
|
||||
GBATimerUpdateRegister(gba, 0, cyclesLate);
|
||||
GBATimerUpdateRegister(gba, 1, cyclesLate);
|
||||
GBATimerUpdateRegister(gba, 2, cyclesLate);
|
||||
GBATimerUpdateRegister(gba, 3, cyclesLate);
|
||||
}
|
||||
|
||||
static void GBATimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||
UNUSED(timing);
|
||||
GBATimerUpdate(context, 0, cyclesLate);
|
||||
@ -74,28 +100,39 @@ static void GBATimerUpdate3(struct mTiming* timing, void* context, uint32_t cycl
|
||||
}
|
||||
|
||||
void GBATimerInit(struct GBA* gba) {
|
||||
gba->timerMaster.name = "GBA Timer Master";
|
||||
gba->timerMaster.callback = GBATimerMasterUpdate;
|
||||
gba->timerMaster.context = gba;
|
||||
gba->timerMaster.priority = 0x20;
|
||||
mTimingSchedule(&gba->timing, &gba->timerMaster, TIMER_MASTER);
|
||||
memset(gba->timers, 0, sizeof(gba->timers));
|
||||
gba->timers[0].event.name = "GBA Timer 0";
|
||||
gba->timers[0].event.callback = GBATimerUpdate0;
|
||||
gba->timers[0].event.context = gba;
|
||||
gba->timers[0].event.priority = 0x21;
|
||||
gba->timers[0].event.priority = 0x20;
|
||||
gba->timers[1].event.name = "GBA Timer 1";
|
||||
gba->timers[1].event.callback = GBATimerUpdate1;
|
||||
gba->timers[1].event.context = gba;
|
||||
gba->timers[1].event.priority = 0x22;
|
||||
gba->timers[1].event.priority = 0x21;
|
||||
gba->timers[2].event.name = "GBA Timer 2";
|
||||
gba->timers[2].event.callback = GBATimerUpdate2;
|
||||
gba->timers[2].event.context = gba;
|
||||
gba->timers[2].event.priority = 0x23;
|
||||
gba->timers[2].event.priority = 0x22;
|
||||
gba->timers[3].event.name = "GBA Timer 3";
|
||||
gba->timers[3].event.callback = GBATimerUpdate3;
|
||||
gba->timers[3].event.context = gba;
|
||||
gba->timers[3].event.priority = 0x24;
|
||||
gba->timers[3].event.priority = 0x23;
|
||||
gba->timers[0].irq.name = "GBA Timer 0 IRQ";
|
||||
gba->timers[0].irq.callback = GBATimerIrq0;
|
||||
gba->timers[0].irq.context = gba;
|
||||
gba->timers[0].irq.priority = 0x28;
|
||||
gba->timers[1].irq.name = "GBA Timer 1 IRQ";
|
||||
gba->timers[1].irq.callback = GBATimerIrq1;
|
||||
gba->timers[1].irq.context = gba;
|
||||
gba->timers[1].irq.priority = 0x29;
|
||||
gba->timers[2].irq.name = "GBA Timer 2 IRQ";
|
||||
gba->timers[2].irq.callback = GBATimerIrq2;
|
||||
gba->timers[2].irq.context = gba;
|
||||
gba->timers[2].irq.priority = 0x2A;
|
||||
gba->timers[3].irq.name = "GBA Timer 3 IRQ";
|
||||
gba->timers[3].irq.callback = GBATimerIrq3;
|
||||
gba->timers[3].irq.context = gba;
|
||||
gba->timers[3].irq.priority = 0x2B;
|
||||
}
|
||||
|
||||
void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate) {
|
||||
@ -117,17 +154,10 @@ void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate) {
|
||||
tickIncrement >>= prescaleBits;
|
||||
tickIncrement += gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1];
|
||||
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = tickIncrement;
|
||||
tickIncrement -= 0x10000;
|
||||
mTimingDeschedule(&gba->timing, ¤tTimer->event);
|
||||
if (tickIncrement >= 0) {
|
||||
mTimingSchedule(&gba->timing, ¤tTimer->event, 7 - (tickIncrement << prescaleBits));
|
||||
} else {
|
||||
int32_t nextIncrement = mTimingUntil(&gba->timing, &gba->timerMaster);
|
||||
tickIncrement = -tickIncrement;
|
||||
tickIncrement <<= prescaleBits;
|
||||
if (nextIncrement - tickIncrement > 0) {
|
||||
mTimingSchedule(&gba->timing, ¤tTimer->event, 7 + tickIncrement);
|
||||
}
|
||||
if (!mTimingIsScheduled(&gba->timing, ¤tTimer->event)) {
|
||||
tickIncrement = (0x10000 - tickIncrement) << prescaleBits;
|
||||
currentTime -= mTimingCurrentTime(&gba->timing) - cyclesLate;
|
||||
mTimingSchedule(&gba->timing, ¤tTimer->event, tickIncrement + currentTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user