Remainder of timer infrastructure
Jeffrey Pfau jeffrey@endrift.com
Fri, 19 Apr 2013 23:01:04 -0700
3 files changed,
116 insertions(+),
0 deletions(-)
M
src/gba/gba-io.c
→
src/gba/gba-io.c
@@ -36,6 +36,36 @@ case REG_DMA3CNT_HI:
value = GBAMemoryWriteDMACNT_HI(&gba->memory, 3, value); break; + case REG_TM0CNT_LO: + GBATimerWriteTMCNT_LO(gba, 0, value); + return; + case REG_TM1CNT_LO: + GBATimerWriteTMCNT_LO(gba, 1, value); + return; + case REG_TM2CNT_LO: + GBATimerWriteTMCNT_LO(gba, 2, value); + return; + case REG_TM3CNT_LO: + GBATimerWriteTMCNT_LO(gba, 3, value); + return; + + case REG_TM0CNT_HI: + value &= 0x00C7; + GBATimerWriteTMCNT_HI(gba, 0, value); + break; + case REG_TM1CNT_HI: + value &= 0x00C7; + GBATimerWriteTMCNT_HI(gba, 1, value); + break; + case REG_TM2CNT_HI: + value &= 0x00C7; + GBATimerWriteTMCNT_HI(gba, 2, value); + break; + case REG_TM3CNT_HI: + value &= 0x00C7; + GBATimerWriteTMCNT_HI(gba, 3, value); + break; + case REG_WAITCNT: GBAAdjustWaitstates(&gba->memory, value); break;@@ -101,6 +131,20 @@ switch (address) {
case REG_DISPSTAT: return gba->memory.io[REG_DISPSTAT >> 1] | GBAVideoReadDISPSTAT(&gba->video); break; + + case REG_TM0CNT_LO: + GBATimerUpdateRegister(gba, 0); + break; + case REG_TM1CNT_LO: + GBATimerUpdateRegister(gba, 1); + break; + case REG_TM2CNT_LO: + GBATimerUpdateRegister(gba, 2); + break; + case REG_TM3CNT_LO: + GBATimerUpdateRegister(gba, 3); + break; + case REG_DMA0CNT_LO: case REG_DMA1CNT_LO: case REG_DMA2CNT_LO:
M
src/gba/gba.c
→
src/gba/gba.c
@@ -104,7 +104,9 @@
timer = &gba->timers[0]; if (timer->enable) { timer->nextEvent -= cycles; + timer->lastEvent -= cycles; if (timer->nextEvent <= 0) { + timer->lastEvent = timer->nextEvent; timer->nextEvent += timer->overflowInterval; gba->memory.io[REG_TM0CNT_LO >> 1] = timer->reload; timer->oldReload = timer->reload;@@ -127,7 +129,9 @@
timer = &gba->timers[1]; if (timer->enable) { timer->nextEvent -= cycles; + timer->lastEvent -= cycles; if (timer->nextEvent <= 0) { + timer->lastEvent = timer->nextEvent; timer->nextEvent += timer->overflowInterval; gba->memory.io[REG_TM1CNT_LO >> 1] = timer->reload; timer->oldReload = timer->reload;@@ -156,8 +160,10 @@
timer = &gba->timers[2]; if (timer->enable) { timer->nextEvent -= cycles; + timer->lastEvent -= cycles; nextEvent = timer->nextEvent; if (timer->nextEvent <= 0) { + timer->lastEvent = timer->nextEvent; timer->nextEvent += timer->overflowInterval; gba->memory.io[REG_TM2CNT_LO >> 1] = timer->reload; timer->oldReload = timer->reload;@@ -186,8 +192,10 @@
timer = &gba->timers[3]; if (timer->enable) { timer->nextEvent -= cycles; + timer->lastEvent -= cycles; nextEvent = timer->nextEvent; if (timer->nextEvent <= 0) { + timer->lastEvent = timer->nextEvent; timer->nextEvent += timer->overflowInterval; gba->memory.io[REG_TM3CNT_LO >> 1] = timer->reload; timer->oldReload = timer->reload;@@ -217,6 +225,65 @@ void GBALoadROM(struct GBA* gba, int fd) {
gba->memory.rom = mmap(0, SIZE_CART0, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_FILE, fd, 0); // TODO: error check } + +void GBATimerUpdateRegister(struct GBA* gba, int timer) { + struct GBATimer* currentTimer = &gba->timers[timer]; + if (currentTimer->enable && !currentTimer->countUp) { + gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu.cycles - currentTimer->lastEvent) >> currentTimer->prescaleBits); + } +} + +void GBATimerWriteTMCNT_LO(struct GBA* gba, int timer, uint16_t reload) { + gba->timers[timer].reload = reload; +} + +void GBATimerWriteTMCNT_HI(struct GBA* gba, int timer, uint16_t control) { + struct GBATimer* currentTimer = &gba->timers[timer]; + GBATimerUpdateRegister(gba, timer); + + int oldPrescale = currentTimer->prescaleBits; + switch (control & 0x0003) { + case 0x0000: + currentTimer->prescaleBits = 0; + break; + case 0x0001: + currentTimer->prescaleBits = 6; + break; + case 0x0002: + currentTimer->prescaleBits = 8; + break; + case 0x0003: + currentTimer->prescaleBits = 10; + break; + } + currentTimer->countUp = !!(control & 0x0004); + currentTimer->doIrq = !!(control & 0x0040); + currentTimer->overflowInterval = (0x10000 - currentTimer->reload) << currentTimer->prescaleBits; + int wasEnabled = currentTimer->enable; + currentTimer->enable = !!(control & 0x0080); + if (!wasEnabled && currentTimer->enable) { + if (!currentTimer->countUp) { + currentTimer->nextEvent = gba->cpu.cycles + currentTimer->overflowInterval; + } else { + currentTimer->nextEvent = INT_MAX; + } + gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->reload; + currentTimer->oldReload = currentTimer->reload; + gba->timersEnabled |= 1 << timer; + } else if (wasEnabled && !currentTimer->enable) { + if (!currentTimer->countUp) { + gba->memory.io[(REG_TM0CNT_LO + (timer << 2)) >> 1] = currentTimer->oldReload + ((gba->cpu.cycles - currentTimer->lastEvent) >> oldPrescale); + } + gba->timersEnabled &= ~(1 << timer); + } else if (currentTimer->prescaleBits != oldPrescale && !currentTimer->countUp) { + // FIXME: this might be before present + currentTimer->nextEvent = currentTimer->lastEvent + currentTimer->overflowInterval; + } + + if (currentTimer->nextEvent < gba->cpu.nextEvent) { + gba->cpu.nextEvent = currentTimer->nextEvent; + } +}; void GBAWriteIE(struct GBA* gba, uint16_t value) { if (value & (1 << IRQ_SIO)) {
M
src/gba/gba.h
→
src/gba/gba.h
@@ -50,6 +50,7 @@ int timersEnabled;
struct GBATimer { uint16_t reload; uint16_t oldReload; + int32_t lastEvent; int32_t nextEvent; int32_t overflowInterval; unsigned prescaleBits : 4;@@ -72,6 +73,10 @@ void GBAMemoryDeinit(struct GBAMemory* memory);
void GBABoardInit(struct GBABoard* board); void GBABoardReset(struct ARMBoard* board); + +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 GBAWriteIE(struct GBA* gba, uint16_t value); void GBAWriteIME(struct GBA* gba, uint16_t value);