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);
|
value = GBAMemoryWriteDMACNT_HI(&gba->memory, 3, value);
|
||||||
break;
|
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:
|
case REG_WAITCNT:
|
||||||
GBAAdjustWaitstates(&gba->memory, value);
|
GBAAdjustWaitstates(&gba->memory, value);
|
||||||
break;
|
break;
|
||||||
@ -101,6 +131,20 @@ uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
|
|||||||
case REG_DISPSTAT:
|
case REG_DISPSTAT:
|
||||||
return gba->memory.io[REG_DISPSTAT >> 1] | GBAVideoReadDISPSTAT(&gba->video);
|
return gba->memory.io[REG_DISPSTAT >> 1] | GBAVideoReadDISPSTAT(&gba->video);
|
||||||
break;
|
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_DMA0CNT_LO:
|
||||||
case REG_DMA1CNT_LO:
|
case REG_DMA1CNT_LO:
|
||||||
case REG_DMA2CNT_LO:
|
case REG_DMA2CNT_LO:
|
||||||
|
@ -104,7 +104,9 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
|
|||||||
timer = &gba->timers[0];
|
timer = &gba->timers[0];
|
||||||
if (timer->enable) {
|
if (timer->enable) {
|
||||||
timer->nextEvent -= cycles;
|
timer->nextEvent -= cycles;
|
||||||
|
timer->lastEvent -= cycles;
|
||||||
if (timer->nextEvent <= 0) {
|
if (timer->nextEvent <= 0) {
|
||||||
|
timer->lastEvent = timer->nextEvent;
|
||||||
timer->nextEvent += timer->overflowInterval;
|
timer->nextEvent += timer->overflowInterval;
|
||||||
gba->memory.io[REG_TM0CNT_LO >> 1] = timer->reload;
|
gba->memory.io[REG_TM0CNT_LO >> 1] = timer->reload;
|
||||||
timer->oldReload = timer->reload;
|
timer->oldReload = timer->reload;
|
||||||
@ -127,7 +129,9 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
|
|||||||
timer = &gba->timers[1];
|
timer = &gba->timers[1];
|
||||||
if (timer->enable) {
|
if (timer->enable) {
|
||||||
timer->nextEvent -= cycles;
|
timer->nextEvent -= cycles;
|
||||||
|
timer->lastEvent -= cycles;
|
||||||
if (timer->nextEvent <= 0) {
|
if (timer->nextEvent <= 0) {
|
||||||
|
timer->lastEvent = timer->nextEvent;
|
||||||
timer->nextEvent += timer->overflowInterval;
|
timer->nextEvent += timer->overflowInterval;
|
||||||
gba->memory.io[REG_TM1CNT_LO >> 1] = timer->reload;
|
gba->memory.io[REG_TM1CNT_LO >> 1] = timer->reload;
|
||||||
timer->oldReload = timer->reload;
|
timer->oldReload = timer->reload;
|
||||||
@ -156,8 +160,10 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
|
|||||||
timer = &gba->timers[2];
|
timer = &gba->timers[2];
|
||||||
if (timer->enable) {
|
if (timer->enable) {
|
||||||
timer->nextEvent -= cycles;
|
timer->nextEvent -= cycles;
|
||||||
|
timer->lastEvent -= cycles;
|
||||||
nextEvent = timer->nextEvent;
|
nextEvent = timer->nextEvent;
|
||||||
if (timer->nextEvent <= 0) {
|
if (timer->nextEvent <= 0) {
|
||||||
|
timer->lastEvent = timer->nextEvent;
|
||||||
timer->nextEvent += timer->overflowInterval;
|
timer->nextEvent += timer->overflowInterval;
|
||||||
gba->memory.io[REG_TM2CNT_LO >> 1] = timer->reload;
|
gba->memory.io[REG_TM2CNT_LO >> 1] = timer->reload;
|
||||||
timer->oldReload = timer->reload;
|
timer->oldReload = timer->reload;
|
||||||
@ -186,8 +192,10 @@ static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
|
|||||||
timer = &gba->timers[3];
|
timer = &gba->timers[3];
|
||||||
if (timer->enable) {
|
if (timer->enable) {
|
||||||
timer->nextEvent -= cycles;
|
timer->nextEvent -= cycles;
|
||||||
|
timer->lastEvent -= cycles;
|
||||||
nextEvent = timer->nextEvent;
|
nextEvent = timer->nextEvent;
|
||||||
if (timer->nextEvent <= 0) {
|
if (timer->nextEvent <= 0) {
|
||||||
|
timer->lastEvent = timer->nextEvent;
|
||||||
timer->nextEvent += timer->overflowInterval;
|
timer->nextEvent += timer->overflowInterval;
|
||||||
gba->memory.io[REG_TM3CNT_LO >> 1] = timer->reload;
|
gba->memory.io[REG_TM3CNT_LO >> 1] = timer->reload;
|
||||||
timer->oldReload = timer->reload;
|
timer->oldReload = timer->reload;
|
||||||
@ -218,6 +226,65 @@ void GBALoadROM(struct GBA* gba, int fd) {
|
|||||||
// TODO: error check
|
// 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) {
|
void GBAWriteIE(struct GBA* gba, uint16_t value) {
|
||||||
if (value & (1 << IRQ_SIO)) {
|
if (value & (1 << IRQ_SIO)) {
|
||||||
GBALog(GBA_LOG_STUB, "SIO interrupts not implemented");
|
GBALog(GBA_LOG_STUB, "SIO interrupts not implemented");
|
||||||
|
@ -50,6 +50,7 @@ struct GBA {
|
|||||||
struct GBATimer {
|
struct GBATimer {
|
||||||
uint16_t reload;
|
uint16_t reload;
|
||||||
uint16_t oldReload;
|
uint16_t oldReload;
|
||||||
|
int32_t lastEvent;
|
||||||
int32_t nextEvent;
|
int32_t nextEvent;
|
||||||
int32_t overflowInterval;
|
int32_t overflowInterval;
|
||||||
unsigned prescaleBits : 4;
|
unsigned prescaleBits : 4;
|
||||||
@ -73,6 +74,10 @@ void GBAMemoryDeinit(struct GBAMemory* memory);
|
|||||||
void GBABoardInit(struct GBABoard* board);
|
void GBABoardInit(struct GBABoard* board);
|
||||||
void GBABoardReset(struct ARMBoard* 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 GBAWriteIE(struct GBA* gba, uint16_t value);
|
||||||
void GBAWriteIME(struct GBA* gba, uint16_t value);
|
void GBAWriteIME(struct GBA* gba, uint16_t value);
|
||||||
void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq);
|
void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user