Remainder of timer infrastructure

This commit is contained in:
Jeffrey Pfau 2013-04-19 23:01:04 -07:00
parent 57dcbef030
commit 5d81a4eb18
3 changed files with 116 additions and 0 deletions

View File

@ -36,6 +36,36 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
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 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t 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:

View File

@ -104,7 +104,9 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
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 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
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 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
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 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
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;
@ -218,6 +226,65 @@ void GBALoadROM(struct GBA* gba, int fd) {
// 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)) {
GBALog(GBA_LOG_STUB, "SIO interrupts not implemented");

View File

@ -50,6 +50,7 @@ struct GBA {
struct GBATimer {
uint16_t reload;
uint16_t oldReload;
int32_t lastEvent;
int32_t nextEvent;
int32_t overflowInterval;
unsigned prescaleBits : 4;
@ -73,6 +74,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);
void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq);