all repos — mgba @ d620a8c38c9f49fb4b40a26c0954f0cff2147ab5

mGBA Game Boy Advance Emulator

DS: Merge GBA and DS timers
Jeffrey Pfau jeffrey@endrift.com
Mon, 02 Jan 2017 19:31:20 -0800
commit

d620a8c38c9f49fb4b40a26c0954f0cff2147ab5

parent

e0ae2e89063ec7139b89358cf476d4d79fdbcc8e

M include/mgba/internal/ds/ds.hinclude/mgba/internal/ds/ds.h

@@ -10,8 +10,8 @@ #include <mgba-util/common.h>

CXX_GUARD_START -#include <mgba/internal/arm/arm.h> #include <mgba/core/log.h> +#include <mgba/core/timing.h> #include <mgba/internal/ds/memory.h> #include <mgba/internal/ds/timer.h>

@@ -46,6 +46,7 @@ DS_IRQ_SPI = 0x17,

DS_IRQ_WIFI = 0x18, }; +struct ARMCore; struct DS; struct Patch; struct VFile;

@@ -62,15 +63,19 @@ struct DSMemory memory;

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;
M include/mgba/internal/ds/timer.hinclude/mgba/internal/ds/timer.h

@@ -10,29 +10,12 @@ #include <mgba-util/common.h>

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

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
M src/ds/ds.csrc/ds/ds.c

@@ -80,10 +80,7 @@ ds->video.p = ds;

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 @@

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 DSDestroy(struct DS* ds) { DSUnloadROM(ds); DSMemoryDeinit(ds); + mTimingDeinit(&ds->timing7); + mTimingDeinit(&ds->timing9); } void DS7InterruptHandlerInit(struct ARMInterruptHandler* irqh) {

@@ -140,6 +142,7 @@ ARMSetPrivilegeMode(cpu, MODE_SYSTEM);

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 @@ ARMSetPrivilegeMode(cpu, MODE_SYSTEM);

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 @@ ARMRaiseIRQ(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 } }
M src/ds/io.csrc/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 @@

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:
M src/ds/timer.csrc/ds/timer.c

@@ -8,103 +8,84 @@

#include <mgba/internal/arm/arm.h> #include <mgba/internal/ds/ds.h> -void DSTimerUpdateRegister(struct DSTimer* timer, struct ARMCore* cpu, uint16_t* io) { - if (DSTimerFlagsIsEnable(timer->flags) && !DSTimerFlagsIsCountUp(timer->flags)) { - // Reading this takes two cycles (1N+1I), so let's remove them preemptively - *io = timer->oldReload + ((cpu->cycles - timer->lastEvent - 2) >> DSTimerFlagsGetPrescaleBits(timer->flags)); +static void DS7TimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) { + struct DS* ds = context; + struct GBATimer* timer = &ds->timers7[0]; + if (GBATimerFlagsIsDoIrq(timer->flags)) { + DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER0); } + GBATimerUpdate(timing, &ds->timers7[0], &ds->memory.io7[DS7_REG_TM0CNT_LO >> 1], cyclesLate); + GBATimerUpdateCountUp(timing, &ds->timers7[1], &ds->memory.io7[DS7_REG_TM1CNT_LO >> 1], cyclesLate); } -void DSTimerWriteTMCNT_LO(struct DSTimer* timer, uint16_t reload) { - timer->reload = reload; - timer->overflowInterval = (0x10000 - timer->reload) << DSTimerFlagsGetPrescaleBits(timer->flags); +static void DS7TimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) { + struct DS* ds = context; + struct GBATimer* timer = &ds->timers7[1]; + if (GBATimerFlagsIsDoIrq(timer->flags)) { + DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER1); + } + GBATimerUpdate(timing, &ds->timers7[1], &ds->memory.io7[DS7_REG_TM1CNT_LO >> 1], cyclesLate); + GBATimerUpdateCountUp(timing, &ds->timers7[2], &ds->memory.io7[DS7_REG_TM2CNT_LO >> 1], cyclesLate); } -void DSTimerWriteTMCNT_HI(struct DSTimer* timer, struct ARMCore* cpu, uint16_t* io, uint16_t control) { - DSTimerUpdateRegister(timer, cpu, io); - - unsigned oldPrescale = DSTimerFlagsGetPrescaleBits(timer->flags); - switch (control & 0x0003) { - case 0x0000: - timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 0); - break; - case 0x0001: - timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 6); - break; - case 0x0002: - timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 8); - break; - case 0x0003: - timer->flags = DSTimerFlagsSetPrescaleBits(timer->flags, 10); - break; +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); } - 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; - } + 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); +} - if (timer->nextEvent < cpu->nextEvent) { - cpu->nextEvent = timer->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); } + GBATimerUpdate(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; - } +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; - 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; - } + 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; +} - 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; +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); }
M src/gba/io.csrc/gba/io.c

@@ -487,33 +487,37 @@ break;

// 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
M src/gba/timer.csrc/gba/timer.c

@@ -8,35 +8,32 @@

#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/io.h> -static void GBATimerUpdate(struct mTiming* timing, struct GBA* gba, int timerId, uint32_t cyclesLate) { - struct GBATimer* timer = &gba->timers[timerId]; - gba->memory.io[(REG_TM0CNT_LO >> 1) + (timerId << 1)] = timer->reload; - timer->oldReload = timer->reload; - timer->lastEvent = timing->masterCycles - cyclesLate; - - if (GBATimerFlagsIsDoIrq(timer->flags)) { - GBARaiseIRQ(gba, IRQ_TIMER0 + timerId); +static void GBATimerUpdateAudio(struct GBA* gba, int timerId, uint32_t cyclesLate) { + if (!gba->audio.enable) { + return; } - - 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.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 ((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); - } +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 (!GBATimerFlagsIsCountUp(timer->flags)) { uint32_t nextEvent = timer->overflowInterval - cyclesLate;

@@ -45,19 +42,44 @@ }

} 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 @@ int32_t prefetchSkew = 0;

if (gba->memory.lastPrefetchedPc >= (uint32_t) gba->cpu->gprs[ARM_PC]) { prefetchSkew = (gba->memory.lastPrefetchedPc - gba->cpu->gprs[ARM_PC]) * (gba->cpu->memory.activeSeqCycles16 + 1) / WORD_SIZE_THUMB; } - // Reading this takes two cycles (1N+1I), so let's remove them preemptively - int32_t diff = gba->cpu->cycles - (currentTimer->lastEvent - gba->timing.masterCycles); - gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((diff - 2 + prefetchSkew) >> GBATimerFlagsGetPrescaleBits(currentTimer->flags)); + GBATimerUpdateRegisterInternal(currentTimer, &gba->timing, gba->cpu, &gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1], prefetchSkew); } } -void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) { - gba->timers[timer].reload = reload; - gba->timers[timer].overflowInterval = (0x10000 - gba->timers[timer].reload) << GBATimerFlagsGetPrescaleBits(gba->timers[timer].flags); +void GBATimerUpdateRegisterInternal(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, int32_t skew) { + // Reading this takes two cycles (1N+1I), so let's remove them preemptively + int32_t diff = cpu->cycles - (timer->lastEvent - timing->masterCycles); + *io = timer->oldReload + ((diff - 2 + skew) >> GBATimerFlagsGetPrescaleBits(timer->flags)); } -void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) { - struct GBATimer* currentTimer = &gba->timers[timer]; - GBATimerUpdateRegister(gba, timer); +void GBATimerWriteTMCNT_LO(struct GBATimer* timer, uint16_t reload) { + timer->reload = reload; + timer->overflowInterval = (0x10000 - timer->reload) << GBATimerFlagsGetPrescaleBits(timer->flags); +} - unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(currentTimer->flags); +void GBATimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t control) { + unsigned oldPrescale = GBATimerFlagsGetPrescaleBits(timer->flags); switch (control & 0x0003) { case 0x0000: - currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 0); + timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 0); break; case 0x0001: - currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 6); + timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 6); break; case 0x0002: - currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 8); + timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 8); break; case 0x0003: - currentTimer->flags = GBATimerFlagsSetPrescaleBits(currentTimer->flags, 10); + timer->flags = GBATimerFlagsSetPrescaleBits(timer->flags, 10); break; } - currentTimer->flags = GBATimerFlagsTestFillCountUp(currentTimer->flags, timer > 0 && (control & 0x0004)); - currentTimer->flags = GBATimerFlagsTestFillDoIrq(currentTimer->flags, control & 0x0040); - currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << GBATimerFlagsGetPrescaleBits(currentTimer->flags); - bool wasEnabled = GBATimerFlagsIsEnable(currentTimer->flags); - currentTimer->flags = GBATimerFlagsTestFillEnable(currentTimer->flags, control & 0x0080); - if (!wasEnabled && GBATimerFlagsIsEnable(currentTimer->flags)) { - mTimingDeschedule(&gba->timing, &currentTimer->event); - if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { - mTimingSchedule(&gba->timing, &currentTimer->event, currentTimer->overflowInterval); + timer->flags = GBATimerFlagsTestFillCountUp(timer->flags, timer > 0 && (control & 0x0004)); + timer->flags = GBATimerFlagsTestFillDoIrq(timer->flags, control & 0x0040); + timer->overflowInterval = (0x10000 - timer->reload) << GBATimerFlagsGetPrescaleBits(timer->flags); + bool wasEnabled = GBATimerFlagsIsEnable(timer->flags); + timer->flags = GBATimerFlagsTestFillEnable(timer->flags, control & 0x0080); + if (!wasEnabled && GBATimerFlagsIsEnable(timer->flags)) { + mTimingDeschedule(timing, &timer->event); + if (!GBATimerFlagsIsCountUp(timer->flags)) { + mTimingSchedule(timing, &timer->event, timer->overflowInterval); } - gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload; - currentTimer->oldReload = currentTimer->reload; - currentTimer->lastEvent = gba->timing.masterCycles + gba->cpu->cycles; - } else if (wasEnabled && !GBATimerFlagsIsEnable(currentTimer->flags)) { - mTimingDeschedule(&gba->timing, &currentTimer->event); - if (!GBATimerFlagsIsCountUp(currentTimer->flags)) { - gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu->cycles - currentTimer->lastEvent) >> oldPrescale); + *io = timer->reload; + timer->oldReload = timer->reload; + timer->lastEvent = timing->masterCycles + cpu->cycles; + } else if (wasEnabled && !GBATimerFlagsIsEnable(timer->flags)) { + mTimingDeschedule(timing, &timer->event); + if (!GBATimerFlagsIsCountUp(timer->flags)) { + *io = timer->oldReload + ((cpu->cycles - timer->lastEvent) >> oldPrescale); } - } else if (GBATimerFlagsIsEnable(currentTimer->flags) && GBATimerFlagsGetPrescaleBits(currentTimer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(currentTimer->flags)) { - mTimingDeschedule(&gba->timing, &currentTimer->event); - mTimingSchedule(&gba->timing, &currentTimer->event, currentTimer->overflowInterval - currentTimer->lastEvent); + } else if (GBATimerFlagsIsEnable(timer->flags) && GBATimerFlagsGetPrescaleBits(timer->flags) != oldPrescale && !GBATimerFlagsIsCountUp(timer->flags)) { + mTimingDeschedule(timing, &timer->event); + mTimingSchedule(timing, &timer->event, timer->overflowInterval - timer->lastEvent); } }