diff --git a/include/mgba/internal/ds/ds.h b/include/mgba/internal/ds/ds.h index eb4c4e8ca..e25618b6d 100644 --- a/include/mgba/internal/ds/ds.h +++ b/include/mgba/internal/ds/ds.h @@ -10,8 +10,8 @@ CXX_GUARD_START -#include #include +#include #include #include @@ -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; diff --git a/include/mgba/internal/ds/timer.h b/include/mgba/internal/ds/timer.h index 2e0faee3d..738221ab5 100644 --- a/include/mgba/internal/ds/timer.h +++ b/include/mgba/internal/ds/timer.h @@ -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 -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 diff --git a/include/mgba/internal/gba/timer.h b/include/mgba/internal/gba/timer.h index d031f3d4f..c20243166 100644 --- a/include/mgba/internal/gba/timer.h +++ b/include/mgba/internal/gba/timer.h @@ -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 diff --git a/src/ds/ds.c b/src/ds/ds.c index 71c0aa102..09b11f75f 100644 --- a/src/ds/ds.c +++ b/src/ds/ds.c @@ -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 } } diff --git a/src/ds/io.c b/src/ds/io.c index 05b39bd91..e8ad2a22d 100644 --- a/src/ds/io.c +++ b/src/ds/io.c @@ -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: diff --git a/src/ds/timer.c b/src/ds/timer.c index 5adee65ab..2e8f10ae8 100644 --- a/src/ds/timer.c +++ b/src/ds/timer.c @@ -8,103 +8,84 @@ #include #include -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); } diff --git a/src/gba/io.c b/src/gba/io.c index c788fb243..9e694b3f0 100644 --- a/src/gba/io.c +++ b/src/gba/io.c @@ -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 diff --git a/src/gba/timer.c b/src/gba/timer.c index 49722ed4d..261cf19d5 100644 --- a/src/gba/timer.c +++ b/src/gba/timer.c @@ -8,36 +8,33 @@ #include #include -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, ¤tTimer->event); - if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { - mTimingSchedule(&gba->timing, ¤tTimer->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, ¤tTimer->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, ¤tTimer->event); - mTimingSchedule(&gba->timing, ¤tTimer->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); } }