mirror of https://github.com/mgba-emu/mgba.git
DS: Merge GBA and DS timers
This commit is contained in:
parent
e0ae2e8906
commit
d620a8c38c
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
54
src/ds/ds.c
54
src/ds/ds.c
|
@ -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);
|
cpu->nextEvent = nextEvent;
|
||||||
if (testEvent < nextEvent) {
|
|
||||||
nextEvent = testEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
cpu->cycles -= cycles;
|
if (ds->earlyExit) {
|
||||||
cpu->nextEvent = nextEvent;
|
ds->earlyExit = false;
|
||||||
|
break;
|
||||||
if (cpu->halted) {
|
}
|
||||||
cpu->cycles = cpu->nextEvent;
|
if (cpu->halted) {
|
||||||
|
cpu->cycles = nextEvent;
|
||||||
|
}
|
||||||
|
#ifndef NDEBUG
|
||||||
|
else if (nextEvent < 0) {
|
||||||
|
mLOG(DS, FATAL, "Negative cycles will pass: %i", nextEvent);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
32
src/ds/io.c
32
src/ds/io.c
|
@ -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:
|
||||||
|
|
161
src/ds/timer.c
161
src/ds/timer.c
|
@ -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)) {
|
||||||
|
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) {
|
static void DS7TimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
timer->reload = reload;
|
struct DS* ds = context;
|
||||||
timer->overflowInterval = (0x10000 - timer->reload) << DSTimerFlagsGetPrescaleBits(timer->flags);
|
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) {
|
static void DS7TimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
DSTimerUpdateRegister(timer, cpu, io);
|
struct DS* ds = context;
|
||||||
|
struct GBATimer* timer = &ds->timers7[2];
|
||||||
unsigned oldPrescale = DSTimerFlagsGetPrescaleBits(timer->flags);
|
if (GBATimerFlagsIsDoIrq(timer->flags)) {
|
||||||
switch (control & 0x0003) {
|
DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER2);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
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) {
|
static void DS7TimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
int32_t nextEvent = INT_MAX;
|
struct DS* ds = context;
|
||||||
if (!ds->timersEnabled7) {
|
struct GBATimer* timer = &ds->timers7[3];
|
||||||
return nextEvent;
|
if (GBATimerFlagsIsDoIrq(timer->flags)) {
|
||||||
|
DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER3);
|
||||||
}
|
}
|
||||||
|
GBATimerUpdate(timing, &ds->timers7[3], &ds->memory.io7[DS7_REG_TM3CNT_LO >> 1], cyclesLate);
|
||||||
struct DSTimer* timer;
|
}
|
||||||
struct DSTimer* nextTimer;
|
|
||||||
|
void DSTimerInit(struct DS* ds) {
|
||||||
int t;
|
memset(ds->timers7, 0, sizeof(ds->timers7));
|
||||||
for (t = 0; t < 4; ++t) {
|
ds->timers7[0].event.name = "DS7 Timer 0";
|
||||||
timer = &ds->timers7[t];
|
ds->timers7[0].event.callback = DS7TimerUpdate0;
|
||||||
if (DSTimerFlagsIsEnable(timer->flags)) {
|
ds->timers7[0].event.context = ds;
|
||||||
timer->nextEvent -= cycles;
|
ds->timers7[0].event.priority = 0x20;
|
||||||
timer->lastEvent -= cycles;
|
ds->timers7[1].event.name = "DS7 Timer 1";
|
||||||
while (timer->nextEvent <= 0) {
|
ds->timers7[1].event.callback = DS7TimerUpdate1;
|
||||||
timer->lastEvent = timer->nextEvent;
|
ds->timers7[1].event.context = ds;
|
||||||
timer->nextEvent += timer->overflowInterval;
|
ds->timers7[1].event.priority = 0x21;
|
||||||
ds->memory.io7[(DS7_REG_TM0CNT_LO + (t << 2)) >> 1] = timer->reload;
|
ds->timers7[2].event.name = "DS7 Timer 2";
|
||||||
timer->oldReload = timer->reload;
|
ds->timers7[2].event.callback = DS7TimerUpdate2;
|
||||||
|
ds->timers7[2].event.context = ds;
|
||||||
if (DSTimerFlagsIsDoIrq(timer->flags)) {
|
ds->timers7[2].event.priority = 0x22;
|
||||||
DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER0);
|
ds->timers7[3].event.name = "DS7 Timer 3";
|
||||||
}
|
ds->timers7[3].event.callback = DS7TimerUpdate3;
|
||||||
|
ds->timers7[3].event.context = ds;
|
||||||
if (t == 3) {
|
ds->timers7[3].event.priority = 0x23;
|
||||||
break;
|
|
||||||
}
|
memset(ds->timers9, 0, sizeof(ds->timers9));
|
||||||
|
ds->timers9[0].event.name = "DS9 Timer 0";
|
||||||
nextTimer = &ds->timers7[t + 1];
|
ds->timers9[0].event.callback = NULL;
|
||||||
if (DSTimerFlagsIsCountUp(nextTimer->flags)) {
|
ds->timers9[0].event.context = ds;
|
||||||
++ds->memory.io7[(DS7_REG_TM1CNT_LO + (t << 2)) >> 1];
|
ds->timers9[0].event.priority = 0x20;
|
||||||
if (!ds->memory.io7[(DS7_REG_TM1CNT_LO + (t << 2)) >> 1]) {
|
ds->timers9[1].event.name = "DS9 Timer 1";
|
||||||
nextTimer->nextEvent = 0;
|
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";
|
||||||
nextEvent = timer->nextEvent;
|
ds->timers9[2].event.callback = NULL;
|
||||||
}
|
ds->timers9[2].event.context = ds;
|
||||||
}
|
ds->timers9[2].event.priority = 0x22;
|
||||||
return nextEvent;
|
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);
|
||||||
}
|
}
|
||||||
|
|
20
src/gba/io.c
20
src/gba/io.c
|
@ -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
|
||||||
|
|
151
src/gba/timer.c
151
src/gba/timer.c
|
@ -8,36 +8,33 @@
|
||||||
#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;
|
||||||
|
}
|
||||||
|
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->oldReload = timer->reload;
|
||||||
timer->lastEvent = timing->masterCycles - cyclesLate;
|
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)) {
|
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;
|
||||||
}
|
}
|
||||||
// Reading this takes two cycles (1N+1I), so let's remove them preemptively
|
GBATimerUpdateRegisterInternal(currentTimer, &gba->timing, gba->cpu, &gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1], prefetchSkew);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) {
|
void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, int32_t skew) {
|
||||||
gba->timers[timer].reload = reload;
|
// Reading this takes two cycles (1N+1I), so let's remove them preemptively
|
||||||
gba->timers[timer].overflowInterval = (0x10000 - gba->timers[timer].reload) << GBATimerFlagsGetPrescaleBits(gba->timers[timer].flags);
|
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) {
|
void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) {
|
||||||
struct GBATimer* currentTimer = &gba->timers[timer];
|
timer->reload = reload;
|
||||||
GBATimerUpdateRegister(gba, timer);
|
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) {
|
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, ¤tTimer->event);
|
mTimingDeschedule(timing, &timer->event);
|
||||||
if (!GBATimerFlagsIsCountUp(currentTimer->flags)) {
|
if (!GBATimerFlagsIsCountUp(timer->flags)) {
|
||||||
mTimingSchedule(&gba->timing, ¤tTimer->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, ¤tTimer->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, ¤tTimer->event);
|
mTimingDeschedule(timing, &timer->event);
|
||||||
mTimingSchedule(&gba->timing, ¤tTimer->event, currentTimer->overflowInterval - currentTimer->lastEvent);
|
mTimingSchedule(timing, &timer->event, timer->overflowInterval - timer->lastEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue