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

View File

@ -10,29 +10,12 @@
CXX_GUARD_START CXX_GUARD_START
DECL_BITFIELD(DSTimerFlags, uint32_t); #include <mgba/internal/gba/timer.h>
DECL_BITS(DSTimerFlags, PrescaleBits, 0, 4);
DECL_BIT(DSTimerFlags, CountUp, 4);
DECL_BIT(DSTimerFlags, DoIrq, 5);
DECL_BIT(DSTimerFlags, Enable, 6);
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; 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; 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 CXX_GUARD_END

View File

@ -18,7 +18,6 @@ DECL_BIT(GBATimerFlags, CountUp, 4);
DECL_BIT(GBATimerFlags, DoIrq, 5); DECL_BIT(GBATimerFlags, DoIrq, 5);
DECL_BIT(GBATimerFlags, Enable, 6); DECL_BIT(GBATimerFlags, Enable, 6);
struct GBA;
struct GBATimer { struct GBATimer {
uint16_t reload; uint16_t reload;
uint16_t oldReload; uint16_t oldReload;
@ -28,10 +27,16 @@ struct GBATimer {
GBATimerFlags flags; GBATimerFlags flags;
}; };
struct ARMCore;
struct GBA;
void GBATimerInit(struct GBA* 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 GBATimerUpdateRegister(struct GBA* gba, int timer);
void GBATimerWriteTMCNT_LO(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 GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t value); 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 CXX_GUARD_END

View File

@ -80,10 +80,7 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
ds->springIRQ7 = 0; ds->springIRQ7 = 0;
ds->springIRQ9 = 0; ds->springIRQ9 = 0;
ds->timersEnabled7 = 0; DSTimerInit(ds);
ds->timersEnabled9 = 0;
memset(ds->timers7, 0, sizeof(ds->timers7));
memset(ds->timers9, 0, sizeof(ds->timers9));
ds->keySource = NULL; ds->keySource = NULL;
ds->rtcSource = NULL; ds->rtcSource = NULL;
ds->rumble = NULL; ds->rumble = NULL;
@ -91,6 +88,9 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
ds->romVf = NULL; ds->romVf = NULL;
ds->keyCallback = 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) { void DSUnloadROM(struct DS* ds) {
@ -103,6 +103,8 @@ void DSUnloadROM(struct DS* ds) {
void DSDestroy(struct DS* ds) { void DSDestroy(struct DS* ds) {
DSUnloadROM(ds); DSUnloadROM(ds);
DSMemoryDeinit(ds); DSMemoryDeinit(ds);
mTimingDeinit(&ds->timing7);
mTimingDeinit(&ds->timing9);
} }
void DS7InterruptHandlerInit(struct ARMInterruptHandler* irqh) { void DS7InterruptHandlerInit(struct ARMInterruptHandler* irqh) {
@ -140,6 +142,7 @@ void DS7Reset(struct ARMCore* cpu) {
cpu->gprs[ARM_SP] = DS7_SP_BASE; cpu->gprs[ARM_SP] = DS7_SP_BASE;
struct DS* ds = (struct DS*) cpu->master; struct DS* ds = (struct DS*) cpu->master;
mTimingClear(&ds->timing7);
DSMemoryReset(ds); DSMemoryReset(ds);
DS7IOInit(ds); DS7IOInit(ds);
@ -171,6 +174,7 @@ void DS9Reset(struct ARMCore* cpu) {
cpu->gprs[ARM_SP] = DS9_SP_BASE; cpu->gprs[ARM_SP] = DS9_SP_BASE;
struct DS* ds = (struct DS*) cpu->master; struct DS* ds = (struct DS*) cpu->master;
mTimingClear(&ds->timing9);
DS9IOInit(ds); DS9IOInit(ds);
struct DSCartridge* header = ds->romVf->map(ds->romVf, sizeof(*header), MAP_READ); 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; ds->springIRQ7 = 0;
} }
int32_t cycles = cpu->nextEvent; int32_t nextEvent = cpu->nextEvent;
int32_t nextEvent = INT_MAX; while (cpu->cycles >= nextEvent) {
int32_t testEvent; int32_t cycles = cpu->cycles;
cpu->cycles = 0;
cpu->nextEvent = INT_MAX;
#ifndef NDEBUG #ifndef NDEBUG
if (cycles < 0) { if (cycles < 0) {
mLOG(DS, FATAL, "Negative cycles passed: %i", cycles); mLOG(DS, FATAL, "Negative cycles passed: %i", cycles);
} }
#endif #endif
nextEvent = cycles;
do {
nextEvent = mTimingTick(&ds->timing7, nextEvent);
} while (ds->cpuBlocked);
testEvent = DSTimersProcessEvents(ds, cycles);
if (testEvent < nextEvent) {
nextEvent = testEvent;
}
cpu->cycles -= cycles;
cpu->nextEvent = nextEvent; cpu->nextEvent = nextEvent;
if (ds->earlyExit) {
ds->earlyExit = false;
break;
}
if (cpu->halted) { if (cpu->halted) {
cpu->cycles = cpu->nextEvent; 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) { switch (address) {
// Timers // Timers
case DS7_REG_TM0CNT_LO: case DS7_REG_TM0CNT_LO:
DSTimerWriteTMCNT_LO(&ds->timers7[0], value); GBATimerWriteTMCNT_LO(&ds->timers7[0], value);
return; return;
case DS7_REG_TM1CNT_LO: case DS7_REG_TM1CNT_LO:
DSTimerWriteTMCNT_LO(&ds->timers7[1], value); GBATimerWriteTMCNT_LO(&ds->timers7[1], value);
return; return;
case DS7_REG_TM2CNT_LO: case DS7_REG_TM2CNT_LO:
DSTimerWriteTMCNT_LO(&ds->timers7[2], value); GBATimerWriteTMCNT_LO(&ds->timers7[2], value);
return; return;
case DS7_REG_TM3CNT_LO: case DS7_REG_TM3CNT_LO:
DSTimerWriteTMCNT_LO(&ds->timers7[3], value); GBATimerWriteTMCNT_LO(&ds->timers7[3], value);
return; return;
case DS7_REG_TM0CNT_HI: case DS7_REG_TM0CNT_HI:
value &= 0x00C7; value &= 0x00C7;
DSTimerWriteTMCNT_HI(&ds->timers7[0], ds->arm7, &ds->memory.io7[(address - 2) >> 1], value); DSTimerWriteTMCNT_HI(&ds->timers7[0], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM0CNT_LO >> 1], value);
ds->timersEnabled7 &= ~1;
ds->timersEnabled7 |= DSTimerFlagsGetEnable(ds->timers7[0].flags);
break; break;
case DS7_REG_TM1CNT_HI: case DS7_REG_TM1CNT_HI:
value &= 0x00C7; value &= 0x00C7;
DSTimerWriteTMCNT_HI(&ds->timers7[1], ds->arm7, &ds->memory.io7[(address - 2) >> 1], value); DSTimerWriteTMCNT_HI(&ds->timers7[1], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM1CNT_LO >> 1], value);
ds->timersEnabled7 &= ~2;
ds->timersEnabled7 |= DSTimerFlagsGetEnable(ds->timers7[1].flags) << 1;
break; break;
case DS7_REG_TM2CNT_HI: case DS7_REG_TM2CNT_HI:
value &= 0x00C7; value &= 0x00C7;
DSTimerWriteTMCNT_HI(&ds->timers7[2], ds->arm7, &ds->memory.io7[(address - 2) >> 1], value); DSTimerWriteTMCNT_HI(&ds->timers7[2], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM2CNT_LO >> 1], value);
ds->timersEnabled7 &= ~4;
ds->timersEnabled7 |= DSTimerFlagsGetEnable(ds->timers7[2].flags) << 2;
break; break;
case DS7_REG_TM3CNT_HI: case DS7_REG_TM3CNT_HI:
value &= 0x00C7; value &= 0x00C7;
DSTimerWriteTMCNT_HI(&ds->timers7[3], ds->arm7, &ds->memory.io7[(address - 2) >> 1], value); DSTimerWriteTMCNT_HI(&ds->timers7[3], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM3CNT_LO >> 1], value);
ds->timersEnabled7 &= ~8;
ds->timersEnabled7 |= DSTimerFlagsGetEnable(ds->timers7[3].flags) << 3;
break; break;
case DS7_REG_IPCSYNC: 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) { uint16_t DS7IORead(struct DS* ds, uint32_t address) {
switch (address) { switch (address) {
case DS7_REG_TM0CNT_LO: 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; break;
case DS7_REG_TM1CNT_LO: 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; break;
case DS7_REG_TM2CNT_LO: 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; break;
case DS7_REG_TM3CNT_LO: 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; break;
case DS7_REG_TM0CNT_HI: case DS7_REG_TM0CNT_HI:

View File

@ -8,103 +8,84 @@
#include <mgba/internal/arm/arm.h> #include <mgba/internal/arm/arm.h>
#include <mgba/internal/ds/ds.h> #include <mgba/internal/ds/ds.h>
void DSTimerUpdateRegister(struct DSTimer* timer, struct ARMCore* cpu, uint16_t* io) { static void DS7TimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
if (DSTimerFlagsIsEnable(timer->flags) && !DSTimerFlagsIsCountUp(timer->flags)) { struct DS* ds = context;
// Reading this takes two cycles (1N+1I), so let's remove them preemptively struct GBATimer* timer = &ds->timers7[0];
*io = timer->oldReload + ((cpu->cycles - timer->lastEvent - 2) >> DSTimerFlagsGetPrescaleBits(timer->flags)); if (GBATimerFlagsIsDoIrq(timer->flags)) {
}
}
void DSTimerWriteTMCNT_LO(struct DSTimer* timer, uint16_t reload) {
timer->reload = reload;
timer->overflowInterval = (0x10000 - timer->reload) << DSTimerFlagsGetPrescaleBits(timer->flags);
}
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;
}
}
int32_t DSTimersProcessEvents(struct DS* ds, int32_t cycles) {
int32_t nextEvent = INT_MAX;
if (!ds->timersEnabled7) {
return nextEvent;
}
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); DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER0);
} }
GBATimerUpdate(timing, &ds->timers7[0], &ds->memory.io7[DS7_REG_TM0CNT_LO >> 1], cyclesLate);
if (t == 3) { GBATimerUpdateCountUp(timing, &ds->timers7[1], &ds->memory.io7[DS7_REG_TM1CNT_LO >> 1], cyclesLate);
break;
} }
nextTimer = &ds->timers7[t + 1]; static void DS7TimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
if (DSTimerFlagsIsCountUp(nextTimer->flags)) { struct DS* ds = context;
++ds->memory.io7[(DS7_REG_TM1CNT_LO + (t << 2)) >> 1]; struct GBATimer* timer = &ds->timers7[1];
if (!ds->memory.io7[(DS7_REG_TM1CNT_LO + (t << 2)) >> 1]) { if (GBATimerFlagsIsDoIrq(timer->flags)) {
nextTimer->nextEvent = 0; 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);
} }
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);
} }
nextEvent = timer->nextEvent; 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);
} }
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);
} }
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 // Timers
case REG_TM0CNT_LO: case REG_TM0CNT_LO:
GBATimerWriteTMCNT_LO(gba, 0, value); GBATimerWriteTMCNT_LO(&gba->timers[0], value);
return; return;
case REG_TM1CNT_LO: case REG_TM1CNT_LO:
GBATimerWriteTMCNT_LO(gba, 1, value); GBATimerWriteTMCNT_LO(&gba->timers[1], value);
return; return;
case REG_TM2CNT_LO: case REG_TM2CNT_LO:
GBATimerWriteTMCNT_LO(gba, 2, value); GBATimerWriteTMCNT_LO(&gba->timers[2], value);
return; return;
case REG_TM3CNT_LO: case REG_TM3CNT_LO:
GBATimerWriteTMCNT_LO(gba, 3, value); GBATimerWriteTMCNT_LO(&gba->timers[3], value);
return; return;
case REG_TM0CNT_HI: case REG_TM0CNT_HI:
value &= 0x00C7; 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; break;
case REG_TM1CNT_HI: case REG_TM1CNT_HI:
value &= 0x00C7; 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; break;
case REG_TM2CNT_HI: case REG_TM2CNT_HI:
value &= 0x00C7; 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; break;
case REG_TM3CNT_HI: case REG_TM3CNT_HI:
value &= 0x00C7; 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; break;
// SIO // SIO

View File

@ -8,17 +8,10 @@
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h> #include <mgba/internal/gba/io.h>
static void GBATimerUpdate(struct mTiming* timing, struct GBA* gba, int timerId, uint32_t cyclesLate) { static void GBATimerUpdateAudio(struct GBA* gba, int timerId, uint32_t cyclesLate) {
struct GBATimer* timer = &gba->timers[timerId]; if (!gba->audio.enable) {
gba->memory.io[(REG_TM0CNT_LO >> 1) + (timerId << 1)] = timer->reload; return;
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) { if ((gba->audio.chALeft || gba->audio.chARight) && gba->audio.chATimer == timerId) {
GBAAudioSampleFIFO(&gba->audio, 0, cyclesLate); GBAAudioSampleFIFO(&gba->audio, 0, cyclesLate);
} }
@ -28,16 +21,20 @@ static void GBATimerUpdate(struct mTiming* timing, struct GBA* gba, int timerId,
} }
} }
if (timerId < 4) { void GBATimerUpdateCountUp(struct mTiming* timing, struct GBATimer* nextTimer, uint16_t* io, uint32_t cyclesLate) {
struct GBATimer* nextTimer = &gba->timers[timerId + 1];
if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled? if (GBATimerFlagsIsCountUp(nextTimer->flags)) { // TODO: Does this increment while disabled?
++gba->memory.io[(REG_TM1CNT_LO >> 1) + (timerId << 1)]; ++*io;
if (!gba->memory.io[(REG_TM1CNT_LO >> 1) + (timerId << 1)] && GBATimerFlagsIsEnable(nextTimer->flags)) { if (!*io && GBATimerFlagsIsEnable(nextTimer->flags)) {
mTimingSchedule(timing, &nextTimer->event, -cyclesLate); 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 (!GBATimerFlagsIsCountUp(timer->flags)) { if (!GBATimerFlagsIsCountUp(timer->flags)) {
uint32_t nextEvent = timer->overflowInterval - cyclesLate; uint32_t nextEvent = timer->overflowInterval - cyclesLate;
mTimingSchedule(timing, &timer->event, nextEvent); 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) { 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) { 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) { 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) { 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) { 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]) { 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; prefetchSkew = (gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * (gba->cpu->memory.activeSeqCycles16 + 1) / WORD_SIZE_THUMB;
} }
GBATimerUpdateRegisterInternal(currentTimer, &gba->timing, gba->cpu, &gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1], prefetchSkew);
}
}
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 // Reading this takes two cycles (1N+1I), so let's remove them preemptively
int32_t diff = gba->cpu->cycles - (currentTimer->lastEvent - gba->timing.masterCycles); int32_t diff = cpu->cycles - (timer->lastEvent - timing->masterCycles);
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((diff - 2 + prefetchSkew) >> GBATimerFlagsGetPrescaleBits(currentTimer->flags)); *io = timer->oldReload + ((diff - 2 + skew) >> GBATimerFlagsGetPrescaleBits(timer->flags));
}
} }
void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) { void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) {
gba->timers[timer].reload = reload; timer->reload = reload;
gba->timers[timer].overflowInterval = (0x10000 - gba->timers[timer].reload) << GBATimerFlagsGetPrescaleBits(gba->timers[timer].flags); timer->overflowInterval = (0x10000 - timer->reload) << GBATimerFlagsGetPrescaleBits(timer->flags);
} }
void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) { void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t control) {
struct GBATimer* currentTimer = &gba->timers[timer]; unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(timer->flags);
GBATimerUpdateRegister(gba, timer);
unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(currentTimer->flags);
switch (control & 0x0003) { switch (control & 0x0003) {
case 0x0000: case 0x0000:
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 0); timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 0);
break; break;
case 0x0001: case 0x0001:
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 6); timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 6);
break; break;
case 0x0002: case 0x0002:
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 8); timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 8);
break; break;
case 0x0003: case 0x0003:
currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 10); timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 10);
break; break;
} }
currentTimer->flags = GBATimerFlagsTestFillCountUp(currentTimer->flags, timer > 0 && (control & 0x0004)); timer->flags = GBATimerFlagsTestFillCountUp(timer->flags, timer > 0 && (control & 0x0004));
currentTimer->flags = GBATimerFlagsTestFillDoIrq(currentTimer->flags, control & 0x0040); timer->flags = GBATimerFlagsTestFillDoIrq(timer->flags, control & 0x0040);
currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << GBATimerFlagsGetPrescaleBits(currentTimer->flags); timer->overflowInterval = (0x10000 - timer->reload) << GBATimerFlagsGetPrescaleBits(timer->flags);
bool wasEnabled = GBATimerFlagsIsEnable(currentTimer->flags); bool wasEnabled = GBATimerFlagsIsEnable(timer->flags);
currentTimer->flags = GBATimerFlagsTestFillEnable(currentTimer->flags, control & 0x0080); timer->flags = GBATimerFlagsTestFillEnable(timer->flags, control & 0x0080);
if (!wasEnabled && GBATimerFlagsIsEnable(currentTimer->flags)) { if (!wasEnabled && GBATimerFlagsIsEnable(timer->flags)) {
mTimingDeschedule(&gba->timing, &currentTimer->event); mTimingDeschedule(timing, &timer->event);
if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { if (!GBATimerFlagsIsCountUp(timer->flags)) {
mTimingSchedule(&gba->timing, &currentTimer->event, currentTimer->overflowInterval); mTimingSchedule(timing, &timer->event, timer->overflowInterval);
} }
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload; *io = timer->reload;
currentTimer->oldReload = currentTimer->reload; timer->oldReload = timer->reload;
currentTimer->lastEvent = gba->timing.masterCycles + gba->cpu->cycles; timer->lastEvent = timing->masterCycles + cpu->cycles;
} else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) { } else if (wasEnabled && !GBATimerFlagsIsEnable(timer->flags)) {
mTimingDeschedule(&gba->timing, &currentTimer->event); mTimingDeschedule(timing, &timer->event);
if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { if (!GBATimerFlagsIsCountUp(timer->flags)) {
gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> oldPrescale); *io = timer->oldReload + ((cpu->cycles - timer->lastEvent) >> oldPrescale);
} }
} else if (GBATimerFlagsIsEnable(currentTimer->flags) && GBATimerFlagsGetPrescaleBits(currentTimer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(currentTimer->flags)) { } else if (GBATimerFlagsIsEnable(timer->flags) && GBATimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(timer->flags)) {
mTimingDeschedule(&gba->timing, &currentTimer->event); mTimingDeschedule(timing, &timer->event);
mTimingSchedule(&gba->timing, &currentTimer->event, currentTimer->overflowInterval - currentTimer->lastEvent); mTimingSchedule(timing, &timer->event, timer->overflowInterval - timer->lastEvent);
} }
} }