DS: Merge GBA and DS timers

This commit is contained in:
Jeffrey Pfau 2017-01-02 19:31:20 -08:00
parent e0ae2e8906
commit d620a8c38c
8 changed files with 236 additions and 227 deletions

View File

@ -10,8 +10,8 @@
CXX_GUARD_START
#include <mgba/internal/arm/arm.h>
#include <mgba/core/log.h>
#include <mgba/core/timing.h>
#include <mgba/internal/ds/memory.h>
#include <mgba/internal/ds/timer.h>
@ -46,6 +46,7 @@ enum DSIRQ {
DS_IRQ_WIFI = 0x18,
};
struct ARMCore;
struct DS;
struct Patch;
struct VFile;
@ -62,15 +63,19 @@ struct DS {
struct DSVideo video;
int timersEnabled7;
int timersEnabled9;
struct DSTimer timers7[4];
struct DSTimer timers9[4];
struct GBATimer timers7[4];
struct GBATimer timers9[4];
struct mCoreSync* sync;
struct mTiming timing7;
struct mTiming timing9;
struct ARMDebugger* debugger;
int springIRQ7;
int springIRQ9;
bool cpuBlocked;
bool earlyExit;
uint32_t bios7Checksum;
uint32_t bios9Checksum;

View File

@ -10,29 +10,12 @@
CXX_GUARD_START
DECL_BITFIELD(DSTimerFlags, uint32_t);
DECL_BITS(DSTimerFlags, PrescaleBits, 0, 4);
DECL_BIT(DSTimerFlags, CountUp, 4);
DECL_BIT(DSTimerFlags, DoIrq, 5);
DECL_BIT(DSTimerFlags, Enable, 6);
#include <mgba/internal/gba/timer.h>
struct DSTimer {
uint16_t reload;
uint16_t oldReload;
int32_t lastEvent;
int32_t nextEvent;
int32_t overflowInterval;
DSTimerFlags flags;
};
// TODO: Merge back into GBATimer
struct ARMCore;
void DSTimerUpdateRegister(struct DSTimer* timer, struct ARMCore* cpu, uint16_t* io);
void DSTimerWriteTMCNT_LO(struct DSTimer* timer, uint16_t reload);
void DSTimerWriteTMCNT_HI(struct DSTimer* timer, struct ARMCore* cpu, uint16_t* io, uint16_t control);
struct DS;
int32_t DSTimersProcessEvents(struct DS* ds, int32_t cycles);
void DSTimerInit(struct DS* ds);
void DSTimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t control);
CXX_GUARD_END

View File

@ -18,7 +18,6 @@ DECL_BIT(GBATimerFlags, CountUp, 4);
DECL_BIT(GBATimerFlags, DoIrq, 5);
DECL_BIT(GBATimerFlags, Enable, 6);
struct GBA;
struct GBATimer {
uint16_t reload;
uint16_t oldReload;
@ -28,10 +27,16 @@ struct GBATimer {
GBATimerFlags flags;
};
struct ARMCore;
struct GBA;
void GBATimerInit(struct GBA* gba);
void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload);
void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t control);
void GBATimerUpdateRegister(struct GBA* gba, int timer);
void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t value);
void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t value);
void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, int32_t skew);
void GBATimerUpdateCountUp(struct mTiming* timing, struct GBATimer* nextTimer, uint16_t* io, uint32_t cyclesLate);
void GBATimerUpdate(struct mTiming* timing, struct GBATimer* timer, uint16_t* io, uint32_t cyclesLate);
CXX_GUARD_END

View File

@ -80,10 +80,7 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
ds->springIRQ7 = 0;
ds->springIRQ9 = 0;
ds->timersEnabled7 = 0;
ds->timersEnabled9 = 0;
memset(ds->timers7, 0, sizeof(ds->timers7));
memset(ds->timers9, 0, sizeof(ds->timers9));
DSTimerInit(ds);
ds->keySource = NULL;
ds->rtcSource = NULL;
ds->rumble = NULL;
@ -91,6 +88,9 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
ds->romVf = NULL;
ds->keyCallback = NULL;
mTimingInit(&ds->timing7, &ds->arm7->cycles, &ds->arm7->nextEvent);
mTimingInit(&ds->timing9, &ds->arm9->cycles, &ds->arm9->nextEvent);
}
void DSUnloadROM(struct DS* ds) {
@ -103,6 +103,8 @@ void DSUnloadROM(struct DS* ds) {
void DSDestroy(struct DS* ds) {
DSUnloadROM(ds);
DSMemoryDeinit(ds);
mTimingDeinit(&ds->timing7);
mTimingDeinit(&ds->timing9);
}
void DS7InterruptHandlerInit(struct ARMInterruptHandler* irqh) {
@ -140,6 +142,7 @@ void DS7Reset(struct ARMCore* cpu) {
cpu->gprs[ARM_SP] = DS7_SP_BASE;
struct DS* ds = (struct DS*) cpu->master;
mTimingClear(&ds->timing7);
DSMemoryReset(ds);
DS7IOInit(ds);
@ -171,6 +174,7 @@ void DS9Reset(struct ARMCore* cpu) {
cpu->gprs[ARM_SP] = DS9_SP_BASE;
struct DS* ds = (struct DS*) cpu->master;
mTimingClear(&ds->timing9);
DS9IOInit(ds);
struct DSCartridge* header = ds->romVf->map(ds->romVf, sizeof(*header), MAP_READ);
@ -200,25 +204,37 @@ static void DSProcessEvents(struct ARMCore* cpu) {
ds->springIRQ7 = 0;
}
int32_t cycles = cpu->nextEvent;
int32_t nextEvent = INT_MAX;
int32_t testEvent;
int32_t nextEvent = cpu->nextEvent;
while (cpu->cycles >= nextEvent) {
int32_t cycles = cpu->cycles;
cpu->cycles = 0;
cpu->nextEvent = INT_MAX;
#ifndef NDEBUG
if (cycles < 0) {
mLOG(DS, FATAL, "Negative cycles passed: %i", cycles);
}
if (cycles < 0) {
mLOG(DS, FATAL, "Negative cycles passed: %i", cycles);
}
#endif
nextEvent = cycles;
do {
nextEvent = mTimingTick(&ds->timing7, nextEvent);
} while (ds->cpuBlocked);
testEvent = DSTimersProcessEvents(ds, cycles);
if (testEvent < nextEvent) {
nextEvent = testEvent;
}
cpu->nextEvent = nextEvent;
cpu->cycles -= cycles;
cpu->nextEvent = nextEvent;
if (cpu->halted) {
cpu->cycles = cpu->nextEvent;
if (ds->earlyExit) {
ds->earlyExit = false;
break;
}
if (cpu->halted) {
cpu->cycles = nextEvent;
}
#ifndef NDEBUG
else if (nextEvent < 0) {
mLOG(DS, FATAL, "Negative cycles will pass: %i", nextEvent);
}
#endif
}
}

View File

@ -26,41 +26,33 @@ void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
switch (address) {
// Timers
case DS7_REG_TM0CNT_LO:
DSTimerWriteTMCNT_LO(&ds->timers7[0], value);
GBATimerWriteTMCNT_LO(&ds->timers7[0], value);
return;
case DS7_REG_TM1CNT_LO:
DSTimerWriteTMCNT_LO(&ds->timers7[1], value);
GBATimerWriteTMCNT_LO(&ds->timers7[1], value);
return;
case DS7_REG_TM2CNT_LO:
DSTimerWriteTMCNT_LO(&ds->timers7[2], value);
GBATimerWriteTMCNT_LO(&ds->timers7[2], value);
return;
case DS7_REG_TM3CNT_LO:
DSTimerWriteTMCNT_LO(&ds->timers7[3], value);
GBATimerWriteTMCNT_LO(&ds->timers7[3], value);
return;
case DS7_REG_TM0CNT_HI:
value &= 0x00C7;
DSTimerWriteTMCNT_HI(&ds->timers7[0], ds->arm7, &ds->memory.io7[(address - 2) >> 1], value);
ds->timersEnabled7 &= ~1;
ds->timersEnabled7 |= DSTimerFlagsGetEnable(ds->timers7[0].flags);
DSTimerWriteTMCNT_HI(&ds->timers7[0], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM0CNT_LO >> 1], value);
break;
case DS7_REG_TM1CNT_HI:
value &= 0x00C7;
DSTimerWriteTMCNT_HI(&ds->timers7[1], ds->arm7, &ds->memory.io7[(address - 2) >> 1], value);
ds->timersEnabled7 &= ~2;
ds->timersEnabled7 |= DSTimerFlagsGetEnable(ds->timers7[1].flags) << 1;
DSTimerWriteTMCNT_HI(&ds->timers7[1], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM1CNT_LO >> 1], value);
break;
case DS7_REG_TM2CNT_HI:
value &= 0x00C7;
DSTimerWriteTMCNT_HI(&ds->timers7[2], ds->arm7, &ds->memory.io7[(address - 2) >> 1], value);
ds->timersEnabled7 &= ~4;
ds->timersEnabled7 |= DSTimerFlagsGetEnable(ds->timers7[2].flags) << 2;
DSTimerWriteTMCNT_HI(&ds->timers7[2], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM2CNT_LO >> 1], value);
break;
case DS7_REG_TM3CNT_HI:
value &= 0x00C7;
DSTimerWriteTMCNT_HI(&ds->timers7[3], ds->arm7, &ds->memory.io7[(address - 2) >> 1], value);
ds->timersEnabled7 &= ~8;
ds->timersEnabled7 |= DSTimerFlagsGetEnable(ds->timers7[3].flags) << 3;
DSTimerWriteTMCNT_HI(&ds->timers7[3], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM3CNT_LO >> 1], value);
break;
case DS7_REG_IPCSYNC:
@ -113,16 +105,16 @@ void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
uint16_t DS7IORead(struct DS* ds, uint32_t address) {
switch (address) {
case DS7_REG_TM0CNT_LO:
DSTimerUpdateRegister(&ds->timers7[0], ds->arm7, &ds->memory.io7[address >> 1]);
GBATimerUpdateRegisterInternal(&ds->timers7[0], &ds->timing7, ds->arm7, &ds->memory.io7[address >> 1], 0);
break;
case DS7_REG_TM1CNT_LO:
DSTimerUpdateRegister(&ds->timers7[1], ds->arm7, &ds->memory.io7[address >> 1]);
GBATimerUpdateRegisterInternal(&ds->timers7[1], &ds->timing7, ds->arm7, &ds->memory.io7[address >> 1], 0);
break;
case DS7_REG_TM2CNT_LO:
DSTimerUpdateRegister(&ds->timers7[2], ds->arm7, &ds->memory.io7[address >> 1]);
GBATimerUpdateRegisterInternal(&ds->timers7[2], &ds->timing7, ds->arm7, &ds->memory.io7[address >> 1], 0);
break;
case DS7_REG_TM3CNT_LO:
DSTimerUpdateRegister(&ds->timers7[3], ds->arm7, &ds->memory.io7[address >> 1]);
GBATimerUpdateRegisterInternal(&ds->timers7[3], &ds->timing7, ds->arm7, &ds->memory.io7[address >> 1], 0);
break;
case DS7_REG_TM0CNT_HI:

View File

@ -8,103 +8,84 @@
#include <mgba/internal/arm/arm.h>
#include <mgba/internal/ds/ds.h>
void DSTimerUpdateRegister(struct DSTimer* timer, struct ARMCore* cpu, uint16_t* io) {
if (DSTimerFlagsIsEnable(timer->flags) && !DSTimerFlagsIsCountUp(timer->flags)) {
// Reading this takes two cycles (1N+1I), so let's remove them preemptively
*io = timer->oldReload + ((cpu->cycles - timer->lastEvent - 2) >> DSTimerFlagsGetPrescaleBits(timer->flags));
static void DS7TimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DS* ds = context;
struct GBATimer* timer = &ds->timers7[0];
if (GBATimerFlagsIsDoIrq(timer->flags)) {
DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER0);
}
GBATimerUpdate(timing, &ds->timers7[0], &ds->memory.io7[DS7_REG_TM0CNT_LO >> 1], cyclesLate);
GBATimerUpdateCountUp(timing, &ds->timers7[1], &ds->memory.io7[DS7_REG_TM1CNT_LO >> 1], cyclesLate);
}
void DSTimerWriteTMCNT_LO(struct DSTimer* timer, uint16_t reload) {
timer->reload = reload;
timer->overflowInterval = (0x10000 - timer->reload) << DSTimerFlagsGetPrescaleBits(timer->flags);
static void DS7TimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DS* ds = context;
struct GBATimer* timer = &ds->timers7[1];
if (GBATimerFlagsIsDoIrq(timer->flags)) {
DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER1);
}
GBATimerUpdate(timing, &ds->timers7[1], &ds->memory.io7[DS7_REG_TM1CNT_LO >> 1], cyclesLate);
GBATimerUpdateCountUp(timing, &ds->timers7[2], &ds->memory.io7[DS7_REG_TM2CNT_LO >> 1], cyclesLate);
}
void DSTimerWriteTMCNT_HI(struct DSTimer* timer, struct ARMCore* cpu, uint16_t* io, uint16_t control) {
DSTimerUpdateRegister(timer, cpu, io);
unsigned oldPrescale = DSTimerFlagsGetPrescaleBits(timer->flags);
switch (control & 0x0003) {
case 0x0000:
timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 0);
break;
case 0x0001:
timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 6);
break;
case 0x0002:
timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 8);
break;
case 0x0003:
timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 10);
break;
}
timer->flags = DSTimerFlagsTestFillCountUp(timer->flags, control & 0x0004);
timer->flags = DSTimerFlagsTestFillDoIrq(timer->flags, control & 0x0040);
timer->overflowInterval = (0x10000 - timer->reload) << DSTimerFlagsGetPrescaleBits(timer->flags);
bool wasEnabled = DSTimerFlagsIsEnable(timer->flags);
timer->flags = DSTimerFlagsTestFillEnable(timer->flags, control & 0x0080);
if (!wasEnabled && DSTimerFlagsIsEnable(timer->flags)) {
if (!DSTimerFlagsIsCountUp(timer->flags)) {
timer->nextEvent = cpu->cycles + timer->overflowInterval;
} else {
timer->nextEvent = INT_MAX;
}
*io = timer->reload;
timer->oldReload = timer->reload;
timer->lastEvent = cpu->cycles;
} else if (wasEnabled && !DSTimerFlagsIsEnable(timer->flags)) {
if (!DSTimerFlagsIsCountUp(timer->flags)) {
*io = timer->oldReload + ((cpu->cycles - timer->lastEvent) >> oldPrescale);
}
} else if (DSTimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !DSTimerFlagsIsCountUp(timer->flags)) {
// FIXME: this might be before present
timer->nextEvent = timer->lastEvent + timer->overflowInterval;
}
if (timer->nextEvent < cpu->nextEvent) {
cpu->nextEvent = timer->nextEvent;
static void DS7TimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DS* ds = context;
struct GBATimer* timer = &ds->timers7[2];
if (GBATimerFlagsIsDoIrq(timer->flags)) {
DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER2);
}
GBATimerUpdate(timing, &ds->timers7[2], &ds->memory.io7[DS7_REG_TM2CNT_LO >> 1], cyclesLate);
GBATimerUpdateCountUp(timing, &ds->timers7[3], &ds->memory.io7[DS7_REG_TM3CNT_LO >> 1], cyclesLate);
}
int32_t DSTimersProcessEvents(struct DS* ds, int32_t cycles) {
int32_t nextEvent = INT_MAX;
if (!ds->timersEnabled7) {
return nextEvent;
static void DS7TimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DS* ds = context;
struct GBATimer* timer = &ds->timers7[3];
if (GBATimerFlagsIsDoIrq(timer->flags)) {
DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER3);
}
struct DSTimer* timer;
struct DSTimer* nextTimer;
int t;
for (t = 0; t < 4; ++t) {
timer = &ds->timers7[t];
if (DSTimerFlagsIsEnable(timer->flags)) {
timer->nextEvent -= cycles;
timer->lastEvent -= cycles;
while (timer->nextEvent <= 0) {
timer->lastEvent = timer->nextEvent;
timer->nextEvent += timer->overflowInterval;
ds->memory.io7[(DS7_REG_TM0CNT_LO + (t << 2)) >> 1] = timer->reload;
timer->oldReload = timer->reload;
if (DSTimerFlagsIsDoIrq(timer->flags)) {
DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER0);
}
if (t == 3) {
break;
}
nextTimer = &ds->timers7[t + 1];
if (DSTimerFlagsIsCountUp(nextTimer->flags)) {
++ds->memory.io7[(DS7_REG_TM1CNT_LO + (t << 2)) >> 1];
if (!ds->memory.io7[(DS7_REG_TM1CNT_LO + (t << 2)) >> 1]) {
nextTimer->nextEvent = 0;
}
}
}
nextEvent = timer->nextEvent;
}
}
return nextEvent;
GBATimerUpdate(timing, &ds->timers7[3], &ds->memory.io7[DS7_REG_TM3CNT_LO >> 1], cyclesLate);
}
void DSTimerInit(struct DS* ds) {
memset(ds->timers7, 0, sizeof(ds->timers7));
ds->timers7[0].event.name = "DS7 Timer 0";
ds->timers7[0].event.callback = DS7TimerUpdate0;
ds->timers7[0].event.context = ds;
ds->timers7[0].event.priority = 0x20;
ds->timers7[1].event.name = "DS7 Timer 1";
ds->timers7[1].event.callback = DS7TimerUpdate1;
ds->timers7[1].event.context = ds;
ds->timers7[1].event.priority = 0x21;
ds->timers7[2].event.name = "DS7 Timer 2";
ds->timers7[2].event.callback = DS7TimerUpdate2;
ds->timers7[2].event.context = ds;
ds->timers7[2].event.priority = 0x22;
ds->timers7[3].event.name = "DS7 Timer 3";
ds->timers7[3].event.callback = DS7TimerUpdate3;
ds->timers7[3].event.context = ds;
ds->timers7[3].event.priority = 0x23;
memset(ds->timers9, 0, sizeof(ds->timers9));
ds->timers9[0].event.name = "DS9 Timer 0";
ds->timers9[0].event.callback = NULL;
ds->timers9[0].event.context = ds;
ds->timers9[0].event.priority = 0x20;
ds->timers9[1].event.name = "DS9 Timer 1";
ds->timers9[1].event.callback = NULL;
ds->timers9[1].event.context = ds;
ds->timers9[1].event.priority = 0x21;
ds->timers9[2].event.name = "DS9 Timer 2";
ds->timers9[2].event.callback = NULL;
ds->timers9[2].event.context = ds;
ds->timers9[2].event.priority = 0x22;
ds->timers9[3].event.name = "DS9 Timer 3";
ds->timers9[3].event.callback = NULL;
ds->timers9[3].event.context = ds;
ds->timers9[3].event.priority = 0x23;
}
void DSTimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t value) {
GBATimerUpdateRegisterInternal(timer, timing, cpu, io, 0);
GBATimerWriteTMCNT_HI(timer, timing, cpu, io, value);
}

View File

@ -487,33 +487,37 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
// Timers
case REG_TM0CNT_LO:
GBATimerWriteTMCNT_LO(gba, 0, value);
GBATimerWriteTMCNT_LO(&gba->timers[0], value);
return;
case REG_TM1CNT_LO:
GBATimerWriteTMCNT_LO(gba, 1, value);
GBATimerWriteTMCNT_LO(&gba->timers[1], value);
return;
case REG_TM2CNT_LO:
GBATimerWriteTMCNT_LO(gba, 2, value);
GBATimerWriteTMCNT_LO(&gba->timers[2], value);
return;
case REG_TM3CNT_LO:
GBATimerWriteTMCNT_LO(gba, 3, value);
GBATimerWriteTMCNT_LO(&gba->timers[3], value);
return;
case REG_TM0CNT_HI:
value &= 0x00C7;
GBATimerWriteTMCNT_HI(gba, 0, value);
GBATimerUpdateRegister(gba, 0);
GBATimerWriteTMCNT_HI(&gba->timers[0], &gba->timing, gba->cpu, &gba->memory.io[REG_TM0CNT_LO >> 1], value);
break;
case REG_TM1CNT_HI:
value &= 0x00C7;
GBATimerWriteTMCNT_HI(gba, 1, value);
GBATimerUpdateRegister(gba, 1);
GBATimerWriteTMCNT_HI(&gba->timers[1], &gba->timing, gba->cpu, &gba->memory.io[REG_TM1CNT_LO >> 1], value);
break;
case REG_TM2CNT_HI:
value &= 0x00C7;
GBATimerWriteTMCNT_HI(gba, 2, value);
GBATimerUpdateRegister(gba, 2);
GBATimerWriteTMCNT_HI(&gba->timers[2], &gba->timing, gba->cpu, &gba->memory.io[REG_TM2CNT_LO >> 1], value);
break;
case REG_TM3CNT_HI:
value &= 0x00C7;
GBATimerWriteTMCNT_HI(gba, 3, value);
GBATimerUpdateRegister(gba, 3);
GBATimerWriteTMCNT_HI(&gba->timers[3], &gba->timing, gba->cpu, &gba->memory.io[REG_TM3CNT_LO >> 1], value);
break;
// SIO

View File

@ -8,36 +8,33 @@
#include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h>
static void GBATimerUpdate(struct mTiming* timing, struct GBA* gba, int timerId, uint32_t cyclesLate) {
struct GBATimer* timer = &gba->timers[timerId];
gba->memory.io[(REG_TM0CNT_LO >> 1) + (timerId << 1)] = timer->reload;
static void GBATimerUpdateAudio(struct GBA* gba, int timerId, uint32_t cyclesLate) {
if (!gba->audio.enable) {
return;
}
if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == timerId) {
GBAAudioSampleFIFO(&gba->audio, 0, cyclesLate);
}
if ((gba->audio.chBLeft || gba->audio.chBRight) && gba->audio.chBTimer == timerId) {
GBAAudioSampleFIFO(&gba->audio, 1, cyclesLate);
}
}
void GBATimerUpdateCountUp(struct mTiming* timing, struct GBATimer* nextTimer, uint16_t* io, uint32_t cyclesLate) {
if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled?
++*io;
if (!*io && GBATimerFlagsIsEnable(nextTimer->flags)) {
mTimingSchedule(timing, &nextTimer->event, -cyclesLate);
}
}
}
void GBATimerUpdate(struct mTiming* timing, struct GBATimer* timer, uint16_t* io, uint32_t cyclesLate) {
*io = timer->reload;
timer->oldReload = timer->reload;
timer->lastEvent = timing->masterCycles - cyclesLate;
if (GBATimerFlagsIsDoIrq(timer->flags)) {
GBARaiseIRQ(gba, IRQ_TIMER0 + timerId);
}
if (gba->audio.enable && timerId < 2) {
if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == timerId) {
GBAAudioSampleFIFO(&gba->audio, 0, cyclesLate);
}
if ((gba->audio.chBLeft || gba->audio.chBRight) && gba->audio.chBTimer == timerId) {
GBAAudioSampleFIFO(&gba->audio, 1, cyclesLate);
}
}
if (timerId < 4) {
struct GBATimer* nextTimer = &gba->timers[timerId + 1];
if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled?
++gba->memory.io[(REG_TM1CNT_LO >> 1) + (timerId << 1)];
if (!gba->memory.io[(REG_TM1CNT_LO >> 1) + (timerId << 1)] && GBATimerFlagsIsEnable(nextTimer->flags)) {
mTimingSchedule(timing, &nextTimer->event, -cyclesLate);
}
}
}
if (!GBATimerFlagsIsCountUp(timer->flags)) {
uint32_t nextEvent = timer->overflowInterval - cyclesLate;
mTimingSchedule(timing, &timer->event, nextEvent);
@ -45,19 +42,44 @@ static void GBATimerUpdate(struct mTiming* timing, struct GBA* gba, int timerId,
}
static void GBATimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
GBATimerUpdate(timing, context, 0, cyclesLate);
struct GBA* gba = context;
struct GBATimer* timer = &gba->timers[0];
if (GBATimerFlagsIsDoIrq(timer->flags)) {
GBARaiseIRQ(gba, IRQ_TIMER0);
}
GBATimerUpdateAudio(gba, 0, cyclesLate);
GBATimerUpdate(timing, &gba->timers[0], &gba->memory.io[REG_TM0CNT_LO >> 1], cyclesLate);
GBATimerUpdateCountUp(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate);
}
static void GBATimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
GBATimerUpdate(timing, context, 1, cyclesLate);
struct GBA* gba = context;
struct GBATimer* timer = &gba->timers[1];
if (GBATimerFlagsIsDoIrq(timer->flags)) {
GBARaiseIRQ(gba, IRQ_TIMER1);
}
GBATimerUpdateAudio(gba, 1, cyclesLate);
GBATimerUpdate(timing, &gba->timers[1], &gba->memory.io[REG_TM1CNT_LO >> 1], cyclesLate);
GBATimerUpdateCountUp(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate);
}
static void GBATimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
GBATimerUpdate(timing, context, 2, cyclesLate);
struct GBA* gba = context;
struct GBATimer* timer = &gba->timers[2];
if (GBATimerFlagsIsDoIrq(timer->flags)) {
GBARaiseIRQ(gba, IRQ_TIMER2);
}
GBATimerUpdate(timing, &gba->timers[2], &gba->memory.io[REG_TM2CNT_LO >> 1], cyclesLate);
GBATimerUpdateCountUp(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], cyclesLate);
}
static void GBATimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
GBATimerUpdate(timing, context, 3, cyclesLate);
struct GBA* gba = context;
struct GBATimer* timer = &gba->timers[3];
if (GBATimerFlagsIsDoIrq(timer->flags)) {
GBARaiseIRQ(gba, IRQ_TIMER3);
}
GBATimerUpdate(timing, &gba->timers[3], &gba->memory.io[REG_TM3CNT_LO >> 1], cyclesLate);
}
void GBATimerInit(struct GBA* gba) {
@ -87,56 +109,57 @@ void GBATimerUpdateRegister(struct GBA* gba, int timer) {
if (gba->memory.lastPrefetchedPc >= (uint32_t) gba->cpu->gprs[ARM_PC]) {
prefetchSkew = (gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * (gba->cpu->memory.activeSeqCycles16 + 1) / WORD_SIZE_THUMB;
}
// Reading this takes two cycles (1N+1I), so let's remove them preemptively
int32_t diff = gba->cpu->cycles - (currentTimer->lastEvent - gba->timing.masterCycles);
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((diff - 2 + prefetchSkew) >> GBATimerFlagsGetPrescaleBits(currentTimer->flags));
GBATimerUpdateRegisterInternal(currentTimer, &gba->timing, gba->cpu, &gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1], prefetchSkew);
}
}
void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) {
gba->timers[timer].reload = reload;
gba->timers[timer].overflowInterval = (0x10000 - gba->timers[timer].reload) << GBATimerFlagsGetPrescaleBits(gba->timers[timer].flags);
void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, int32_t skew) {
// Reading this takes two cycles (1N+1I), so let's remove them preemptively
int32_t diff = cpu->cycles - (timer->lastEvent - timing->masterCycles);
*io = timer->oldReload + ((diff - 2 + skew) >> GBATimerFlagsGetPrescaleBits(timer->flags));
}
void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) {
struct GBATimer* currentTimer = &gba->timers[timer];
GBATimerUpdateRegister(gba, timer);
void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) {
timer->reload = reload;
timer->overflowInterval = (0x10000 - timer->reload) << GBATimerFlagsGetPrescaleBits(timer->flags);
}
unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(currentTimer->flags);
void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t control) {
unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(timer->flags);
switch (control & 0x0003) {
case 0x0000:
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 0);
timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 0);
break;
case 0x0001:
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 6);
timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 6);
break;
case 0x0002:
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 8);
timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 8);
break;
case 0x0003:
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 10);
timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 10);
break;
}
currentTimer->flags = GBATimerFlagsTestFillCountUp(currentTimer->flags, timer > 0 && (control & 0x0004));
currentTimer->flags = GBATimerFlagsTestFillDoIrq(currentTimer->flags, control & 0x0040);
currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << GBATimerFlagsGetPrescaleBits(currentTimer->flags);
bool wasEnabled = GBATimerFlagsIsEnable(currentTimer->flags);
currentTimer->flags = GBATimerFlagsTestFillEnable(currentTimer->flags, control & 0x0080);
if (!wasEnabled && GBATimerFlagsIsEnable(currentTimer->flags)) {
mTimingDeschedule(&gba->timing, &currentTimer->event);
if (!GBATimerFlagsIsCountUp(currentTimer->flags)) {
mTimingSchedule(&gba->timing, &currentTimer->event, currentTimer->overflowInterval);
timer->flags = GBATimerFlagsTestFillCountUp(timer->flags, timer > 0 && (control & 0x0004));
timer->flags = GBATimerFlagsTestFillDoIrq(timer->flags, control & 0x0040);
timer->overflowInterval = (0x10000 - timer->reload) << GBATimerFlagsGetPrescaleBits(timer->flags);
bool wasEnabled = GBATimerFlagsIsEnable(timer->flags);
timer->flags = GBATimerFlagsTestFillEnable(timer->flags, control & 0x0080);
if (!wasEnabled && GBATimerFlagsIsEnable(timer->flags)) {
mTimingDeschedule(timing, &timer->event);
if (!GBATimerFlagsIsCountUp(timer->flags)) {
mTimingSchedule(timing, &timer->event, timer->overflowInterval);
}
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload;
currentTimer->oldReload = currentTimer->reload;
currentTimer->lastEvent = gba->timing.masterCycles + gba->cpu->cycles;
} else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) {
mTimingDeschedule(&gba->timing, &currentTimer->event);
if (!GBATimerFlagsIsCountUp(currentTimer->flags)) {
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> oldPrescale);
*io = timer->reload;
timer->oldReload = timer->reload;
timer->lastEvent = timing->masterCycles + cpu->cycles;
} else if (wasEnabled && !GBATimerFlagsIsEnable(timer->flags)) {
mTimingDeschedule(timing, &timer->event);
if (!GBATimerFlagsIsCountUp(timer->flags)) {
*io = timer->oldReload + ((cpu->cycles - timer->lastEvent) >> oldPrescale);
}
} else if (GBATimerFlagsIsEnable(currentTimer->flags) && GBATimerFlagsGetPrescaleBits(currentTimer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(currentTimer->flags)) {
mTimingDeschedule(&gba->timing, &currentTimer->event);
mTimingSchedule(&gba->timing, &currentTimer->event, currentTimer->overflowInterval - currentTimer->lastEvent);
} else if (GBATimerFlagsIsEnable(timer->flags) && GBATimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(timer->flags)) {
mTimingDeschedule(timing, &timer->event);
mTimingSchedule(timing, &timer->event, timer->overflowInterval - timer->lastEvent);
}
}