mirror of https://github.com/mgba-emu/mgba.git
Remainder of timer infrastructure
This commit is contained in:
parent
57dcbef030
commit
5d81a4eb18
|
@ -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:
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue