mirror of https://github.com/mgba-emu/mgba.git
DS: Add timers and start interrupts
This commit is contained in:
parent
9c91235a34
commit
e649be94f5
64
src/ds/ds.c
64
src/ds/ds.c
|
@ -80,6 +80,10 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
|
||||||
|
|
||||||
ds->springIRQ7 = 0;
|
ds->springIRQ7 = 0;
|
||||||
ds->springIRQ9 = 0;
|
ds->springIRQ9 = 0;
|
||||||
|
ds->timersEnabled7 = 0;
|
||||||
|
ds->timersEnabled9 = 0;
|
||||||
|
memset(ds->timers7, 0, sizeof(ds->timers7));
|
||||||
|
memset(ds->timers9, 0, sizeof(ds->timers9));
|
||||||
ds->keySource = NULL;
|
ds->keySource = NULL;
|
||||||
ds->rtcSource = NULL;
|
ds->rtcSource = NULL;
|
||||||
ds->rumble = NULL;
|
ds->rumble = NULL;
|
||||||
|
@ -196,22 +200,26 @@ static void DSProcessEvents(struct ARMCore* cpu) {
|
||||||
ds->springIRQ7 = 0;
|
ds->springIRQ7 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
int32_t cycles = cpu->nextEvent;
|
||||||
int32_t cycles = cpu->nextEvent;
|
int32_t nextEvent = INT_MAX;
|
||||||
int32_t nextEvent = INT_MAX;
|
int32_t testEvent;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (cycles < 0) {
|
if (cycles < 0) {
|
||||||
mLOG(DS, FATAL, "Negative cycles passed: %i", cycles);
|
mLOG(DS, FATAL, "Negative cycles passed: %i", cycles);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cpu->cycles -= cycles;
|
testEvent = DSTimersProcessEvents(ds, cycles);
|
||||||
cpu->nextEvent = nextEvent;
|
if (testEvent < nextEvent) {
|
||||||
|
nextEvent = testEvent;
|
||||||
|
}
|
||||||
|
|
||||||
if (cpu->halted) {
|
cpu->cycles -= cycles;
|
||||||
cpu->cycles = cpu->nextEvent;
|
cpu->nextEvent = nextEvent;
|
||||||
}
|
|
||||||
} while (cpu->cycles >= cpu->nextEvent);
|
if (cpu->halted) {
|
||||||
|
cpu->cycles = cpu->nextEvent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DSAttachDebugger(struct DS* ds, struct mDebugger* debugger) {
|
void DSAttachDebugger(struct DS* ds, struct mDebugger* debugger) {
|
||||||
|
@ -454,4 +462,34 @@ void DS9WriteCP15(struct ARMCore* cpu, int crn, int crm, int opcode1, int opcode
|
||||||
case 9:
|
case 9:
|
||||||
_writeTCMControl(cpu, crm, opcode2, value);
|
_writeTCMControl(cpu, crm, opcode2, value);
|
||||||
break;
|
break;
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSWriteIE(struct ARMCore* cpu, uint16_t* io, uint32_t value) {
|
||||||
|
if (io[DS7_REG_IME >> 1] && (value & io[DS7_REG_IF_LO >> 1] || (value >> 16) & io[DS7_REG_IF_HI >> 1])) {
|
||||||
|
ARMRaiseIRQ(cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void DSWriteIME(struct ARMCore* cpu, uint16_t* io, uint16_t value) {
|
||||||
|
if (value && (io[DS7_REG_IE_LO >> 1] & io[DS7_REG_IF_LO >> 1] || io[DS7_REG_IE_HI >> 1] & io[DS7_REG_IF_HI >> 1])) {
|
||||||
|
ARMRaiseIRQ(cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSRaiseIRQ(struct ARMCore* cpu, uint16_t* io, enum DSIRQ irq) {
|
||||||
|
if (irq < 16) {
|
||||||
|
io[DS7_REG_IF_LO >> 1] |= 1 << irq;
|
||||||
|
} else {
|
||||||
|
io[DS7_REG_IF_HI >> 1] |= 1 << (irq - 16);
|
||||||
|
}
|
||||||
|
cpu->halted = 0;
|
||||||
|
|
||||||
|
if (!io[DS7_REG_IME >> 1]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (irq < 16 && (io[DS7_REG_IE_LO >> 1] & 1 << irq)) {
|
||||||
|
ARMRaiseIRQ(cpu);
|
||||||
|
} else if (io[DS7_REG_IE_HI >> 1] & 1 << (irq - 16)) {
|
||||||
|
ARMRaiseIRQ(cpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "core/log.h"
|
#include "core/log.h"
|
||||||
|
|
||||||
#include "ds/memory.h"
|
#include "ds/memory.h"
|
||||||
|
#include "ds/timer.h"
|
||||||
#include "ds/video.h"
|
#include "ds/video.h"
|
||||||
|
|
||||||
extern const uint32_t DS_ARM946ES_FREQUENCY;
|
extern const uint32_t DS_ARM946ES_FREQUENCY;
|
||||||
|
@ -57,6 +58,10 @@ struct DS {
|
||||||
struct ARMCore* arm9;
|
struct ARMCore* arm9;
|
||||||
struct DSMemory memory;
|
struct DSMemory memory;
|
||||||
struct DSVideo video;
|
struct DSVideo video;
|
||||||
|
int timersEnabled7;
|
||||||
|
int timersEnabled9;
|
||||||
|
struct DSTimer timers7[4];
|
||||||
|
struct DSTimer timers9[4];
|
||||||
|
|
||||||
struct mCoreSync* sync;
|
struct mCoreSync* sync;
|
||||||
|
|
||||||
|
@ -131,4 +136,8 @@ bool DSIsROM(struct VFile* vf);
|
||||||
void DSGetGameCode(struct DS* ds, char* out);
|
void DSGetGameCode(struct DS* ds, char* out);
|
||||||
void DSGetGameTitle(struct DS* ds, char* out);
|
void DSGetGameTitle(struct DS* ds, char* out);
|
||||||
|
|
||||||
|
void DSWriteIME(struct ARMCore* cpu, uint16_t* io, uint16_t value);
|
||||||
|
void DSWriteIE(struct ARMCore* cpu, uint16_t* io, uint32_t value);
|
||||||
|
void DSRaiseIRQ(struct ARMCore* cpu, uint16_t* io, enum DSIRQ irq);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
74
src/ds/io.c
74
src/ds/io.c
|
@ -24,11 +24,53 @@ void DS7IOInit(struct DS* ds) {
|
||||||
|
|
||||||
void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
|
void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
|
||||||
switch (address) {
|
switch (address) {
|
||||||
case DS9_REG_IPCSYNC:
|
// Timers
|
||||||
|
case DS7_REG_TM0CNT_LO:
|
||||||
|
DSTimerWriteTMCNT_LO(&ds->timers7[0], value);
|
||||||
|
return;
|
||||||
|
case DS7_REG_TM1CNT_LO:
|
||||||
|
DSTimerWriteTMCNT_LO(&ds->timers7[1], value);
|
||||||
|
return;
|
||||||
|
case DS7_REG_TM2CNT_LO:
|
||||||
|
DSTimerWriteTMCNT_LO(&ds->timers7[2], value);
|
||||||
|
return;
|
||||||
|
case DS7_REG_TM3CNT_LO:
|
||||||
|
DSTimerWriteTMCNT_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);
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DS7_REG_IPCSYNC:
|
||||||
value &= 0x6F00;
|
value &= 0x6F00;
|
||||||
value |= ds->memory.io7[address >> 1] & 0x000F;
|
value |= ds->memory.io7[address >> 1] & 0x000F;
|
||||||
_writeIPCSync(ds->arm9, ds->memory.io9, value);
|
_writeIPCSync(ds->arm9, ds->memory.io9, value);
|
||||||
break;
|
break;
|
||||||
|
case DS7_REG_IME:
|
||||||
|
DSWriteIME(ds->arm7, ds->memory.io7, value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
|
mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
|
||||||
if (address >= DS7_REG_MAX) {
|
if (address >= DS7_REG_MAX) {
|
||||||
|
@ -50,11 +92,28 @@ void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value);
|
void DS7IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
|
||||||
|
switch (address) {
|
||||||
|
case DS7_REG_IE_LO:
|
||||||
|
DSWriteIE(ds->arm7, ds->memory.io7, value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
DS7IOWrite(ds, address, value & 0xFFFF);
|
||||||
|
DS7IOWrite(ds, address | 2, value >> 16);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ds->memory.io7[address >> 1] = value;
|
||||||
|
ds->memory.io7[(address >> 1) + 1] = value >> 16;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t DS7IORead(struct DS* ds, uint32_t address) {
|
uint16_t DS7IORead(struct DS* ds, uint32_t address) {
|
||||||
switch (address) {
|
switch (address) {
|
||||||
case DS7_REG_IPCSYNC:
|
case DS7_REG_IPCSYNC:
|
||||||
|
case DS7_REG_IME:
|
||||||
|
case DS7_REG_IE_LO:
|
||||||
|
case DS7_REG_IE_HI:
|
||||||
|
case DS7_REG_IF_LO:
|
||||||
|
case DS7_REG_IF_HI:
|
||||||
// Handled transparently by the registers
|
// Handled transparently by the registers
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -98,7 +157,16 @@ void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value);
|
void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
|
||||||
|
switch (address) {
|
||||||
|
default:
|
||||||
|
DS9IOWrite(ds, address, value & 0xFFFF);
|
||||||
|
DS9IOWrite(ds, address | 2, value >> 16);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ds->memory.io9[address >> 1] = value;
|
||||||
|
ds->memory.io9[(address >> 1) + 1] = value >> 16;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t DS9IORead(struct DS* ds, uint32_t address) {
|
uint16_t DS9IORead(struct DS* ds, uint32_t address) {
|
||||||
switch (address) {
|
switch (address) {
|
||||||
|
|
|
@ -248,6 +248,9 @@ uint32_t DS7Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
}
|
}
|
||||||
mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
|
mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
|
||||||
break;
|
break;
|
||||||
|
case DS_REGION_IO:
|
||||||
|
value = DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
|
mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
|
||||||
break;
|
break;
|
||||||
|
@ -347,6 +350,9 @@ void DS7Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle
|
||||||
}
|
}
|
||||||
mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
|
mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
|
||||||
break;
|
break;
|
||||||
|
case DS_REGION_IO:
|
||||||
|
DS7IOWrite32(ds, address & 0x00FFFFFF, value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
|
mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
|
||||||
break;
|
break;
|
||||||
|
@ -455,6 +461,9 @@ uint32_t DS7LoadMultiple(struct ARMCore* cpu, uint32_t address, int mask, enum L
|
||||||
mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
|
mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case DS_REGION_IO:
|
||||||
|
LDM_LOOP(value = DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
|
mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
|
||||||
LDM_LOOP(value = 0);
|
LDM_LOOP(value = 0);
|
||||||
|
@ -592,6 +601,9 @@ uint32_t DS9Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
|
||||||
}
|
}
|
||||||
mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
|
mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
|
||||||
break;
|
break;
|
||||||
|
case DS_REGION_IO:
|
||||||
|
value = DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
|
mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
|
||||||
break;
|
break;
|
||||||
|
@ -682,6 +694,9 @@ void DS9Store32(struct ARMCore* cpu, uint32_t address, int32_t value, int* cycle
|
||||||
}
|
}
|
||||||
mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
|
mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
|
||||||
break;
|
break;
|
||||||
|
case DS_REGION_IO:
|
||||||
|
DS9IOWrite32(ds, address & 0x00FFFFFF, value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
|
mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "timer.h"
|
||||||
|
|
||||||
|
#include "arm/arm.h"
|
||||||
|
#include "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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DSTimerWriteTMCNT_LO(struct DSTimer* timer, uint16_t reload) {
|
||||||
|
timer->reload = reload;
|
||||||
|
timer->overflowInterval = (0x10000 - timer->reload) << DSTimerFlagsGetPrescaleBits(timer->flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timer->nextEvent < cpu->nextEvent) {
|
||||||
|
cpu->nextEvent = timer->nextEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t DSTimersProcessEvents(struct DS* ds, int32_t cycles) {
|
||||||
|
int32_t nextEvent = INT_MAX;
|
||||||
|
if (!ds->timersEnabled7) {
|
||||||
|
return nextEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/* Copyright (c) 2013-2016 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef DS_TIMER_H
|
||||||
|
#define DS_TIMER_H
|
||||||
|
|
||||||
|
#include "util/common.h"
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
#endif
|
Loading…
Reference in New Issue