DS: Share code between cores

This commit is contained in:
Jeffrey Pfau 2017-01-03 13:58:54 -08:00
parent 2359a4e886
commit c037dada3e
9 changed files with 333 additions and 352 deletions

View File

@ -54,21 +54,27 @@ struct mDebugger;
mLOG_DECLARE_CATEGORY(DS);
struct DSCommon {
struct DS* p;
struct ARMCore* cpu;
struct GBATimer timers[4];
struct mTiming timing;
int springIRQ;
struct DSCoreMemory memory;
struct DSCommon* ipc;
};
struct DS {
struct mCPUComponent d;
struct ARMCore* arm7;
struct ARMCore* arm9;
struct DSCommon ds7;
struct DSCommon ds9;
struct DSMemory memory;
struct DSVideo video;
int timersEnabled7;
int timersEnabled9;
struct GBATimer timers7[4];
struct GBATimer timers9[4];
struct mCoreSync* sync;
struct mTiming timing7;
struct mTiming timing9;
struct mTimingEvent slice;
struct ARMCore* activeCpu;
uint32_t sliceStart;
@ -76,8 +82,6 @@ struct DS {
struct ARMDebugger* debugger;
int springIRQ7;
int springIRQ9;
bool cpuBlocked;
bool earlyExit;

View File

@ -12,61 +12,70 @@ CXX_GUARD_START
#include <mgba/core/log.h>
enum DSIORegisters {
// DMA
DS_REG_DMA0SAD_LO = 0x0B0,
DS_REG_DMA0SAD_HI = 0x0B2,
DS_REG_DMA0DAD_LO = 0x0B4,
DS_REG_DMA0DAD_HI = 0x0B6,
DS_REG_DMA0CNT_LO = 0x0B8,
DS_REG_DMA0CNT_HI = 0x0BA,
DS_REG_DMA1SAD_LO = 0x0BC,
DS_REG_DMA1SAD_HI = 0x0BE,
DS_REG_DMA1DAD_LO = 0x0C0,
DS_REG_DMA1DAD_HI = 0x0C2,
DS_REG_DMA1CNT_LO = 0x0C4,
DS_REG_DMA1CNT_HI = 0x0C6,
DS_REG_DMA2SAD_LO = 0x0C8,
DS_REG_DMA2SAD_HI = 0x0CA,
DS_REG_DMA2DAD_LO = 0x0CC,
DS_REG_DMA2DAD_HI = 0x0CE,
DS_REG_DMA2CNT_LO = 0x0D0,
DS_REG_DMA2CNT_HI = 0x0D2,
DS_REG_DMA3SAD_LO = 0x0D4,
DS_REG_DMA3SAD_HI = 0x0D6,
DS_REG_DMA3DAD_LO = 0x0D8,
DS_REG_DMA3DAD_HI = 0x0DA,
DS_REG_DMA3CNT_LO = 0x0DC,
DS_REG_DMA3CNT_HI = 0x0DE,
// Timers
DS_REG_TM0CNT_LO = 0x100,
DS_REG_TM0CNT_HI = 0x102,
DS_REG_TM1CNT_LO = 0x104,
DS_REG_TM1CNT_HI = 0x106,
DS_REG_TM2CNT_LO = 0x108,
DS_REG_TM2CNT_HI = 0x10A,
DS_REG_TM3CNT_LO = 0x10C,
DS_REG_TM3CNT_HI = 0x10E,
// IPC
DS_REG_IPCSYNC = 0x180,
DS_REG_IPCFIFOCNT = 0x182,
DS_REG_IPCFIFOSEND_LO = 0x184,
DS_REG_IPCFIFOSEND_HI = 0x186,
DS_REG_IPCFIFORECV_LO = 0x100000,
DS_REG_IPCFIFORECV_HI = 0x100002,
// Interrupts
DS_REG_IME = 0x208,
DS_REG_IE_LO = 0x210,
DS_REG_IE_HI = 0x212,
DS_REG_IF_LO = 0x214,
DS_REG_IF_HI = 0x216,
};
enum DS7IORegisters {
// Video
DS7_REG_DISPSTAT = 0x004,
DS7_REG_VCOUNT = 0x006,
// DMA
DS7_REG_DISPSTAT_REG_DMA0SAD_LO = 0x0B0,
DS7_REG_DISPSTAT_REG_DMA0SAD_HI = 0x0B2,
DS7_REG_DISPSTAT_REG_DMA0DAD_LO = 0x0B4,
DS7_REG_DISPSTAT_REG_DMA0DAD_HI = 0x0B6,
DS7_REG_DISPSTAT_REG_DMA0CNT_LO = 0x0B8,
DS7_REG_DISPSTAT_REG_DMA0CNT_HI = 0x0BA,
DS7_REG_DISPSTAT_REG_DMA1SAD_LO = 0x0BC,
DS7_REG_DISPSTAT_REG_DMA1SAD_HI = 0x0BE,
DS7_REG_DISPSTAT_REG_DMA1DAD_LO = 0x0C0,
DS7_REG_DISPSTAT_REG_DMA1DAD_HI = 0x0C2,
DS7_REG_DISPSTAT_REG_DMA1CNT_LO = 0x0C4,
DS7_REG_DISPSTAT_REG_DMA1CNT_HI = 0x0C6,
DS7_REG_DISPSTAT_REG_DMA2SAD_LO = 0x0C8,
DS7_REG_DISPSTAT_REG_DMA2SAD_HI = 0x0CA,
DS7_REG_DISPSTAT_REG_DMA2DAD_LO = 0x0CC,
DS7_REG_DISPSTAT_REG_DMA2DAD_HI = 0x0CE,
DS7_REG_DISPSTAT_REG_DMA2CNT_LO = 0x0D0,
DS7_REG_DISPSTAT_REG_DMA2CNT_HI = 0x0D2,
DS7_REG_DISPSTAT_REG_DMA3SAD_LO = 0x0D4,
DS7_REG_DISPSTAT_REG_DMA3SAD_HI = 0x0D6,
DS7_REG_DISPSTAT_REG_DMA3DAD_LO = 0x0D8,
DS7_REG_DISPSTAT_REG_DMA3DAD_HI = 0x0DA,
DS7_REG_DISPSTAT_REG_DMA3CNT_LO = 0x0DC,
DS7_REG_DISPSTAT_REG_DMA3CNT_HI = 0x0DE,
// Timers
DS7_REG_TM0CNT_LO = 0x100,
DS7_REG_TM0CNT_HI = 0x102,
DS7_REG_TM1CNT_LO = 0x104,
DS7_REG_TM1CNT_HI = 0x106,
DS7_REG_TM2CNT_LO = 0x108,
DS7_REG_TM2CNT_HI = 0x10A,
DS7_REG_TM3CNT_LO = 0x10C,
DS7_REG_TM3CNT_HI = 0x10E,
// Keypad
DS7_REG_KEYINPUT = 0x130,
DS7_REG_KEYCNT = 0x132,
DS7_REG_EXTKEYIN = 0x136,
DS7_REG_RTC = 0x138,
// IPC
DS7_REG_IPCSYNC = 0x180,
DS7_REG_IPCFIFOCNT = 0x182,
DS7_REG_IPCFIFOSEND_LO = 0x184,
DS7_REG_IPCFIFOSEND_HI = 0x186,
DS7_REG_IPCFIFORECV_LO = 0x100000,
DS7_REG_IPCFIFORECV_HI = 0x100002,
// Game card
DS7_REG_AUXSPICNT = 0x1A0,
DS7_REG_AUXSPIDATA = 0x1A2,
@ -85,13 +94,8 @@ enum DS7IORegisters {
DS7_REG_SLOT1DATA_2 = 0x100012,
DS7_REG_SLOT1DATA_3 = 0x100013,
// Interrupts, etc
// Etc
DS7_REG_EXMEMSTAT = 0x204,
DS7_REG_IME = 0x208,
DS7_REG_IE_LO = 0x210,
DS7_REG_IE_HI = 0x212,
DS7_REG_IF_LO = 0x214,
DS7_REG_IF_HI = 0x216,
// Memory control
DS7_REG_VRAMSTAT = 0x240,
@ -198,62 +202,10 @@ enum DS9IORegisters {
DS9_REG_B_BLDY = 0x1054,
DS9_REG_B_MASTER_BRIGHT = 0x106C,
// DMA
DS9_REG_DMA0SAD_LO = 0x0B0,
DS9_REG_DMA0SAD_HI = 0x0B2,
DS9_REG_DMA0DAD_LO = 0x0B4,
DS9_REG_DMA0DAD_HI = 0x0B6,
DS9_REG_DMA0CNT_LO = 0x0B8,
DS9_REG_DMA0CNT_HI = 0x0BA,
DS9_REG_DMA1SAD_LO = 0x0BC,
DS9_REG_DMA1SAD_HI = 0x0BE,
DS9_REG_DMA1DAD_LO = 0x0C0,
DS9_REG_DMA1DAD_HI = 0x0C2,
DS9_REG_DMA1CNT_LO = 0x0C4,
DS9_REG_DMA1CNT_HI = 0x0C6,
DS9_REG_DMA2SAD_LO = 0x0C8,
DS9_REG_DMA2SAD_HI = 0x0CA,
DS9_REG_DMA2DAD_LO = 0x0CC,
DS9_REG_DMA2DAD_HI = 0x0CE,
DS9_REG_DMA2CNT_LO = 0x0D0,
DS9_REG_DMA2CNT_HI = 0x0D2,
DS9_REG_DMA3SAD_LO = 0x0D4,
DS9_REG_DMA3SAD_HI = 0x0D6,
DS9_REG_DMA3DAD_LO = 0x0D8,
DS9_REG_DMA3DAD_HI = 0x0DA,
DS9_REG_DMA3CNT_LO = 0x0DC,
DS9_REG_DMA3CNT_HI = 0x0DE,
DS9_REG_DMA0FILL_LO = 0x0E0,
DS9_REG_DMA0FILL_HI = 0x0E2,
DS9_REG_DMA1FILL_LO = 0x0E4,
DS9_REG_DMA1FILL_HI = 0x0E6,
DS9_REG_DMA2FILL_LO = 0x0E8,
DS9_REG_DMA2FILL_HI = 0x0EA,
DS9_REG_DMA3FILL_LO = 0x0EC,
DS9_REG_DMA3FILL_HI = 0x0EE,
// Timers
DS9_REG_TM0CNT_LO = 0x100,
DS9_REG_TM0CNT_HI = 0x102,
DS9_REG_TM1CNT_LO = 0x104,
DS9_REG_TM1CNT_HI = 0x106,
DS9_REG_TM2CNT_LO = 0x108,
DS9_REG_TM2CNT_HI = 0x10A,
DS9_REG_TM3CNT_LO = 0x10C,
DS9_REG_TM3CNT_HI = 0x10E,
// Keypad
DS9_REG_KEYINPUT = 0x130,
DS9_REG_KEYCNT = 0x132,
// IPC
DS9_REG_IPCSYNC = 0x180,
DS9_REG_IPCFIFOCNT = 0x182,
DS9_REG_IPCFIFOSEND_LO = 0x184,
DS9_REG_IPCFIFOSEND_HI = 0x186,
DS9_REG_IPCFIFORECV_LO = 0x100000,
DS9_REG_IPCFIFORECV_HI = 0x100002,
// Game card
DS9_REG_AUXSPICNT = 0x1A0,
DS9_REG_AUXSPIDATA = 0x1A2,
@ -272,13 +224,8 @@ enum DS9IORegisters {
DS9_REG_SLOT1DATA_2 = 0x100012,
DS9_REG_SLOT1DATA_3 = 0x100013,
// Interrupts, etc
// Etc
DS9_REG_EXMEMCNT = 0x204,
DS9_REG_IME = 0x208,
DS9_REG_IE_LO = 0x210,
DS9_REG_IE_HI = 0x212,
DS9_REG_IF_LO = 0x214,
DS9_REG_IF_HI = 0x216,
// Memory control
DS9_REG_VRAMCNT_A = 0x240,

View File

@ -133,15 +133,14 @@ struct DSMemory {
char waitstatesPrefetchSeq16[16];
char waitstatesPrefetchNonseq32[16];
char waitstatesPrefetchNonseq16[16];
int activeRegion7;
int activeRegion9;
};
struct DSDMA dma7[4];
struct DSDMA dma9[4];
int activeDMA7;
int activeDMA9;
int32_t nextDMA;
int32_t eventDiff;
struct DSCoreMemory {
uint16_t* io;
int activeRegion;
struct DSDMA dma[4];
int activeDMA;
};
struct DS;

View File

@ -159,8 +159,8 @@ static void _DSCoreUnloadROM(struct mCore* core) {
static void _DSCoreReset(struct mCore* core) {
struct DSCore* dscore = (struct DSCore*) core;
struct DS* ds = (struct DS*) core->board;
ARMReset(ds->arm7);
ARMReset(ds->arm9);
ARMReset(ds->ds7.cpu);
ARMReset(ds->ds9.cpu);
}
static void _DSCoreRunFrame(struct mCore* core) {

View File

@ -49,7 +49,7 @@ static void DS9WriteCP15(struct ARMCore* cpu, int crn, int crm, int opcode1, int
static void DS9InterruptHandlerInit(struct ARMInterruptHandler* irqh);
static void DS9ProcessEvents(struct ARMCore* cpu);
static void DSProcessEvents(struct DS* ds, struct mTiming* timing);
static void DSProcessEvents(struct DSCommon* dscore);
static void DSHitStub(struct ARMCore* cpu, uint32_t opcode);
static void DSIllegal(struct ARMCore* cpu, uint32_t opcode);
static void DSBreakpoint(struct ARMCore* cpu, int immediate);
@ -58,16 +58,16 @@ static void _slice(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(cyclesLate);
struct DS* ds = context;
uint32_t cycles = mTimingCurrentTime(timing) - ds->sliceStart;
if (ds->activeCpu == ds->arm9) {
ds->activeCpu = ds->arm7;
if (ds->activeCpu == ds->ds9.cpu) {
ds->activeCpu = ds->ds7.cpu;
ds->cycleDrift += cycles;
cycles = ds->cycleDrift >> 1;
timing = &ds->timing7;
timing = &ds->ds7.timing;
} else {
ds->activeCpu = ds->arm9;
ds->activeCpu = ds->ds9.cpu;
ds->cycleDrift -= cycles << 1;
cycles = ds->cycleDrift + SLICE_CYCLES;
timing = &ds->timing9;
timing = &ds->ds9.timing;
}
mTimingSchedule(timing, &ds->slice, cycles);
ds->sliceStart = mTimingCurrentTime(timing);
@ -77,38 +77,42 @@ void DSCreate(struct DS* ds) {
ds->d.id = DS_COMPONENT_MAGIC;
ds->d.init = DSInit;
ds->d.deinit = NULL;
ds->arm7 = NULL;
ds->arm9 = NULL;
ds->ds7.p = ds;
ds->ds9.p = ds;
ds->ds7.cpu = NULL;
ds->ds9.cpu = NULL;
ds->ds7.ipc = &ds->ds9;
ds->ds9.ipc = &ds->ds7;
}
static void DSInit(void* cpu, struct mCPUComponent* component) {
struct DS* ds = (struct DS*) component;
struct ARMCore* core = cpu;
if (!ds->arm7) {
if (!ds->ds7.cpu) {
// The ARM7 must get initialized first
ds->arm7 = core;
ds->ds7.cpu = core;
ds->debugger = 0;
ds->sync = 0;
return;
}
ds->arm9 = cpu;
ds->ds9.cpu = cpu;
ds->activeCpu = NULL;
ds->arm9->cp15.r1.c0 = ARMControlRegFillVE(0);
ds->ds9.cpu->cp15.r1.c0 = ARMControlRegFillVE(0);
ds->slice.name = "DS CPU Time Slicing";
ds->slice.callback = _slice;
ds->slice.context = ds;
ds->slice.priority = UINT_MAX;
DS7InterruptHandlerInit(&ds->arm7->irqh);
DS9InterruptHandlerInit(&ds->arm9->irqh);
DS7InterruptHandlerInit(&ds->ds7.cpu->irqh);
DS9InterruptHandlerInit(&ds->ds9.cpu->irqh);
DSMemoryInit(ds);
ds->video.p = ds;
ds->springIRQ7 = 0;
ds->springIRQ9 = 0;
ds->ds7.springIRQ = 0;
ds->ds9.springIRQ = 0;
DSTimerInit(ds);
ds->keySource = NULL;
ds->rtcSource = NULL;
@ -118,8 +122,8 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
ds->keyCallback = NULL;
mTimingInit(&ds->timing7, &ds->arm7->cycles, &ds->arm7->nextEvent);
mTimingInit(&ds->timing9, &ds->arm9->cycles, &ds->arm9->nextEvent);
mTimingInit(&ds->ds7.timing, &ds->ds7.cpu->cycles, &ds->ds7.cpu->nextEvent);
mTimingInit(&ds->ds9.timing, &ds->ds9.cpu->cycles, &ds->ds9.cpu->nextEvent);
}
void DSUnloadROM(struct DS* ds) {
@ -132,8 +136,8 @@ void DSUnloadROM(struct DS* ds) {
void DSDestroy(struct DS* ds) {
DSUnloadROM(ds);
DSMemoryDeinit(ds);
mTimingDeinit(&ds->timing7);
mTimingDeinit(&ds->timing9);
mTimingDeinit(&ds->ds7.timing);
mTimingDeinit(&ds->ds9.timing);
}
void DS7InterruptHandlerInit(struct ARMInterruptHandler* irqh) {
@ -171,7 +175,7 @@ void DS7Reset(struct ARMCore* cpu) {
cpu->gprs[ARM_SP] = DS7_SP_BASE;
struct DS* ds = (struct DS*) cpu->master;
mTimingClear(&ds->timing7);
mTimingClear(&ds->ds7.timing);
DSMemoryReset(ds);
DS7IOInit(ds);
@ -203,13 +207,13 @@ void DS9Reset(struct ARMCore* cpu) {
cpu->gprs[ARM_SP] = DS9_SP_BASE;
struct DS* ds = (struct DS*) cpu->master;
mTimingClear(&ds->timing9);
mTimingClear(&ds->ds9.timing);
DS9IOInit(ds);
ds->activeCpu = cpu;
mTimingSchedule(&ds->timing9, &ds->slice, SLICE_CYCLES);
mTimingSchedule(&ds->ds9.timing, &ds->slice, SLICE_CYCLES);
ds->cycleDrift = 0;
ds->sliceStart = mTimingCurrentTime(&ds->timing9);
ds->sliceStart = mTimingCurrentTime(&ds->ds9.timing);
struct DSCartridge* header = ds->romVf->map(ds->romVf, sizeof(*header), MAP_READ);
if (header) {
@ -232,26 +236,22 @@ void DS9Reset(struct ARMCore* cpu) {
static void DS7ProcessEvents(struct ARMCore* cpu) {
struct DS* ds = (struct DS*) cpu->master;
if (ds->springIRQ7 && !cpu->cpsr.i) {
ARMRaiseIRQ(cpu);
ds->springIRQ7 = 0;
}
DSProcessEvents(ds, &ds->timing7);
DSProcessEvents(&ds->ds7);
}
static void DS9ProcessEvents(struct ARMCore* cpu) {
struct DS* ds = (struct DS*) cpu->master;
if (ds->springIRQ9 && !cpu->cpsr.i) {
ARMRaiseIRQ(cpu);
ds->springIRQ9 = 0;
}
DSProcessEvents(ds, &ds->timing9);
DSProcessEvents(&ds->ds9);
}
static void DSProcessEvents(struct DS* ds, struct mTiming* timing) {
struct ARMCore* cpu = ds->activeCpu;
static void DSProcessEvents(struct DSCommon* dscore) {
struct ARMCore* cpu = dscore->cpu;
struct DS* ds = dscore->p;
if (dscore->springIRQ && !cpu->cpsr.i) {
ARMRaiseIRQ(cpu);
dscore->springIRQ = 0;
}
int32_t nextEvent = cpu->nextEvent;
while (cpu->cycles >= nextEvent) {
int32_t cycles = cpu->cycles;
@ -266,7 +266,7 @@ static void DSProcessEvents(struct DS* ds, struct mTiming* timing) {
#endif
nextEvent = cycles;
do {
nextEvent = mTimingTick(timing, nextEvent);
nextEvent = mTimingTick(&dscore->timing, nextEvent);
} while (ds->cpuBlocked);
cpu->nextEvent = nextEvent;
@ -287,41 +287,41 @@ static void DSProcessEvents(struct DS* ds, struct mTiming* timing) {
}
void DSRunLoop(struct DS* ds) {
if (ds->activeCpu == ds->arm9) {
ARMv5RunLoop(ds->arm9);
if (ds->activeCpu == ds->ds9.cpu) {
ARMv5RunLoop(ds->ds9.cpu);
} else {
ARMv4RunLoop(ds->arm7);
ARMv4RunLoop(ds->ds7.cpu);
}
}
void DS7Step(struct DS* ds) {
while (ds->activeCpu == ds->arm9) {
ARMv5RunLoop(ds->arm9);
while (ds->activeCpu == ds->ds9.cpu) {
ARMv5RunLoop(ds->ds9.cpu);
}
ARMv4Run(ds->arm7);
ARMv4Run(ds->ds7.cpu);
}
void DS9Step(struct DS* ds) {
while (ds->activeCpu == ds->arm7) {
ARMv4RunLoop(ds->arm7);
while (ds->activeCpu == ds->ds7.cpu) {
ARMv4RunLoop(ds->ds7.cpu);
}
ARMv5Run(ds->arm9);
ARMv5Run(ds->ds9.cpu);
}
void DSAttachDebugger(struct DS* ds, struct mDebugger* debugger) {
ds->debugger = (struct ARMDebugger*) debugger->platform;
ds->arm7->components[CPU_COMPONENT_DEBUGGER] = &debugger->d;
ds->arm9->components[CPU_COMPONENT_DEBUGGER] = &debugger->d;
ARMHotplugAttach(ds->arm7, CPU_COMPONENT_DEBUGGER);
ARMHotplugAttach(ds->arm9, CPU_COMPONENT_DEBUGGER);
ds->ds7.cpu->components[CPU_COMPONENT_DEBUGGER] = &debugger->d;
ds->ds9.cpu->components[CPU_COMPONENT_DEBUGGER] = &debugger->d;
ARMHotplugAttach(ds->ds7.cpu, CPU_COMPONENT_DEBUGGER);
ARMHotplugAttach(ds->ds9.cpu, CPU_COMPONENT_DEBUGGER);
}
void DSDetachDebugger(struct DS* ds) {
ds->debugger = NULL;
ARMHotplugDetach(ds->arm7, CPU_COMPONENT_DEBUGGER);
ARMHotplugDetach(ds->arm9, CPU_COMPONENT_DEBUGGER);
ds->arm7->components[CPU_COMPONENT_DEBUGGER] = NULL;
ds->arm9->components[CPU_COMPONENT_DEBUGGER] = NULL;
ARMHotplugDetach(ds->ds7.cpu, CPU_COMPONENT_DEBUGGER);
ARMHotplugDetach(ds->ds9.cpu, CPU_COMPONENT_DEBUGGER);
ds->ds7.cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
ds->ds9.cpu->components[CPU_COMPONENT_DEBUGGER] = NULL;
}
bool DSLoadROM(struct DS* ds, struct VFile* vf) {
@ -442,7 +442,7 @@ void DSBreakpoint(struct ARMCore* cpu, int immediate) {
void DS7TestIRQ(struct ARMCore* cpu) {
struct DS* ds = (struct DS*) cpu->master;
if (0) {
ds->springIRQ7 = 1;
ds->ds7.springIRQ = 1;
cpu->nextEvent = cpu->cycles;
}
}
@ -450,7 +450,7 @@ void DS7TestIRQ(struct ARMCore* cpu) {
void DS9TestIRQ(struct ARMCore* cpu) {
struct DS* ds = (struct DS*) cpu->master;
if (0) {
ds->springIRQ9 = 1;
ds->ds9.springIRQ = 1;
cpu->nextEvent = cpu->cycles;
}
}
@ -552,30 +552,27 @@ void DS9WriteCP15(struct ARMCore* cpu, int crn, int crm, int opcode1, int opcode
}
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])) {
if (io[DS_REG_IME >> 1] && (value & io[DS_REG_IF_LO >> 1] || (value >> 16) & io[DS_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])) {
if (value && (io[DS_REG_IE_LO >> 1] & io[DS_REG_IF_LO >> 1] || io[DS_REG_IE_HI >> 1] & io[DS_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;
io[DS_REG_IF_LO >> 1] |= 1 << irq;
} else {
io[DS7_REG_IF_HI >> 1] |= 1 << (irq - 16);
io[DS_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);
if ((irq < 16 && (io[DS_REG_IE_LO >> 1] & 1 << irq)) || (io[DS_REG_IE_HI >> 1] & 1 << (irq - 16))) {
cpu->halted = 0;
if (io[DS_REG_IME >> 1]) {
ARMRaiseIRQ(cpu);
}
}
}

View File

@ -69,10 +69,10 @@ static void _switchCpu(struct CLIDebugger* debugger, struct CLIDebugVector* dv)
struct mCore* core = dsDebugger->core;
struct DS* ds = core->board;
debugger->d.platform->deinit(debugger->d.platform);
if (core->cpu == ds->arm9) {
core->cpu = ds->arm7;
if (core->cpu == ds->ds9.cpu) {
core->cpu = ds->ds7.cpu;
} else {
core->cpu = ds->arm9;
core->cpu = ds->ds9.cpu;
}
debugger->d.platform->init(core->cpu, debugger->d.platform);
debugger->system->printStatus(debugger->system);

View File

@ -10,64 +10,92 @@
mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O");
static void _writeIPCSync(struct ARMCore* remoteCpu, uint16_t* remoteIo, int16_t value) {
remoteIo[DS7_REG_IPCSYNC >> 1] &= 0xFFF0;
remoteIo[DS7_REG_IPCSYNC >> 1] |= (value >> 8) & 0x0F;
if (value & 0x2000 && remoteIo[DS7_REG_IPCSYNC >> 1] & 0x4000) {
remoteIo[DS_REG_IPCSYNC >> 1] &= 0xFFF0;
remoteIo[DS_REG_IPCSYNC >> 1] |= (value >> 8) & 0x0F;
if (value & 0x2000 && remoteIo[DS_REG_IPCSYNC >> 1] & 0x4000) {
mLOG(DS_IO, STUB, "Unimplemented IPC IRQ");
UNUSED(remoteCpu);
}
}
static bool DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t value) {
switch (address) {
// Timers
case DS_REG_TM0CNT_LO:
GBATimerWriteTMCNT_LO(&dscore->timers[0], value);
return true;
case DS_REG_TM1CNT_LO:
GBATimerWriteTMCNT_LO(&dscore->timers[1], value);
return true;
case DS_REG_TM2CNT_LO:
GBATimerWriteTMCNT_LO(&dscore->timers[2], value);
return true;
case DS_REG_TM3CNT_LO:
GBATimerWriteTMCNT_LO(&dscore->timers[3], value);
return true;
case DS_REG_TM0CNT_HI:
value &= 0x00C7;
DSTimerWriteTMCNT_HI(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], value);
break;
case DS_REG_TM1CNT_HI:
value &= 0x00C7;
DSTimerWriteTMCNT_HI(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], value);
break;
case DS_REG_TM2CNT_HI:
value &= 0x00C7;
DSTimerWriteTMCNT_HI(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], value);
break;
case DS_REG_TM3CNT_HI:
value &= 0x00C7;
DSTimerWriteTMCNT_HI(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], value);
break;
case DS_REG_IPCSYNC:
value &= 0x6F00;
value |= dscore->memory.io[address >> 1] & 0x000F;
_writeIPCSync(dscore->ipc->cpu, dscore->ipc->memory.io, value);
break;
case DS_REG_IME:
DSWriteIME(dscore->cpu, dscore->memory.io, value);
break;
case DS_REG_IF_LO:
case DS_REG_IF_HI:
value = dscore->memory.io[address >> 1] & ~value;
break;
default:
return false;
}
return true;
}
static void DSIOUpdateTimer(struct DSCommon* dscore, uint32_t address) {
switch (address) {
case DS_REG_TM0CNT_LO:
GBATimerUpdateRegisterInternal(&dscore->timers[0], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
break;
case DS_REG_TM1CNT_LO:
GBATimerUpdateRegisterInternal(&dscore->timers[1], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
break;
case DS_REG_TM2CNT_LO:
GBATimerUpdateRegisterInternal(&dscore->timers[2], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
break;
case DS_REG_TM3CNT_LO:
GBATimerUpdateRegisterInternal(&dscore->timers[3], &dscore->timing, dscore->cpu, &dscore->memory.io[address >> 1], 0);
break;
}
}
void DS7IOInit(struct DS* ds) {
memset(ds->memory.io7, 0, sizeof(ds->memory.io7));
}
void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
switch (address) {
// Timers
case DS7_REG_TM0CNT_LO:
GBATimerWriteTMCNT_LO(&ds->timers7[0], value);
return;
case DS7_REG_TM1CNT_LO:
GBATimerWriteTMCNT_LO(&ds->timers7[1], value);
return;
case DS7_REG_TM2CNT_LO:
GBATimerWriteTMCNT_LO(&ds->timers7[2], value);
return;
case DS7_REG_TM3CNT_LO:
GBATimerWriteTMCNT_LO(&ds->timers7[3], value);
return;
case DS7_REG_TM0CNT_HI:
value &= 0x00C7;
DSTimerWriteTMCNT_HI(&ds->timers7[0], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM0CNT_LO >> 1], value);
break;
case DS7_REG_TM1CNT_HI:
value &= 0x00C7;
DSTimerWriteTMCNT_HI(&ds->timers7[1], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM1CNT_LO >> 1], value);
break;
case DS7_REG_TM2CNT_HI:
value &= 0x00C7;
DSTimerWriteTMCNT_HI(&ds->timers7[2], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM2CNT_LO >> 1], value);
break;
case DS7_REG_TM3CNT_HI:
value &= 0x00C7;
DSTimerWriteTMCNT_HI(&ds->timers7[3], &ds->timing7, ds->arm7, &ds->memory.io7[DS7_REG_TM3CNT_LO >> 1], value);
break;
case DS7_REG_IPCSYNC:
value &= 0x6F00;
value |= ds->memory.io7[address >> 1] & 0x000F;
_writeIPCSync(ds->arm9, ds->memory.io9, value);
break;
case DS7_REG_IME:
DSWriteIME(ds->arm7, ds->memory.io7, value);
break;
case DS7_REG_IF_LO:
case DS7_REG_IF_HI:
value = ds->memory.io7[address >> 1] & ~value;
break;
default:
if (DSIOWrite(&ds->ds7, address, value)) {
break;
}
mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
if (address >= DS7_REG_MAX) {
mLOG(DS_IO, GAME_ERROR, "Write to unused DS7 I/O register: %06X:%04X", address, value);
@ -81,7 +109,7 @@ void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
if (address < DS7_REG_MAX) {
uint16_t value16 = value << (8 * (address & 1));
value16 |= (ds->memory.io7[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
value16 |= (ds->ds7.memory.io[(address & 0xFFF) >> 1]) & ~(0xFF << (8 * (address & 1)));
DS7IOWrite(ds, address & 0xFFFFFFFE, value16);
} else {
mLOG(DS, STUB, "Writing to unknown DS7 register: %08X:%02X", address, value);
@ -90,43 +118,36 @@ void DS7IOWrite8(struct DS* ds, uint32_t address, uint8_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);
case DS_REG_IE_LO:
DSWriteIE(ds->ds7.cpu, ds->ds7.memory.io, 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;
ds->ds7.memory.io[address >> 1] = value;
ds->ds7.memory.io[(address >> 1) + 1] = value >> 16;
}
uint16_t DS7IORead(struct DS* ds, uint32_t address) {
switch (address) {
case DS7_REG_TM0CNT_LO:
GBATimerUpdateRegisterInternal(&ds->timers7[0], &ds->timing7, ds->arm7, &ds->memory.io7[address >> 1], 0);
case DS_REG_TM0CNT_LO:
case DS_REG_TM1CNT_LO:
case DS_REG_TM2CNT_LO:
case DS_REG_TM3CNT_LO:
DSIOUpdateTimer(&ds->ds7, address);
break;
case DS7_REG_TM1CNT_LO:
GBATimerUpdateRegisterInternal(&ds->timers7[1], &ds->timing7, ds->arm7, &ds->memory.io7[address >> 1], 0);
break;
case DS7_REG_TM2CNT_LO:
GBATimerUpdateRegisterInternal(&ds->timers7[2], &ds->timing7, ds->arm7, &ds->memory.io7[address >> 1], 0);
break;
case DS7_REG_TM3CNT_LO:
GBATimerUpdateRegisterInternal(&ds->timers7[3], &ds->timing7, ds->arm7, &ds->memory.io7[address >> 1], 0);
break;
case DS7_REG_TM0CNT_HI:
case DS7_REG_TM1CNT_HI:
case DS7_REG_TM2CNT_HI:
case DS7_REG_TM3CNT_HI:
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:
case DS_REG_TM0CNT_HI:
case DS_REG_TM1CNT_HI:
case DS_REG_TM2CNT_HI:
case DS_REG_TM3CNT_HI:
case DS_REG_IPCSYNC:
case DS_REG_IME:
case DS_REG_IE_LO:
case DS_REG_IE_HI:
case DS_REG_IF_LO:
case DS_REG_IF_HI:
// Handled transparently by the registers
break;
default:
@ -144,12 +165,10 @@ void DS9IOInit(struct DS* ds) {
void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
switch (address) {
case DS9_REG_IPCSYNC:
value &= 0x6F00;
value |= ds->memory.io9[address >> 1] & 0x000F;
_writeIPCSync(ds->arm7, ds->memory.io7, value);
break;
default:
if (DSIOWrite(&ds->ds9, address, value)) {
break;
}
mLOG(DS_IO, STUB, "Stub DS9 I/O register write: %06X:%04X", address, value);
if (address >= DS7_REG_MAX) {
mLOG(DS_IO, GAME_ERROR, "Write to unused DS9 I/O register: %06X:%04X", address, value);
@ -177,20 +196,35 @@ void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
DS9IOWrite(ds, address | 2, value >> 16);
return;
}
ds->memory.io9[address >> 1] = value;
ds->memory.io9[(address >> 1) + 1] = value >> 16;
ds->ds9.memory.io[address >> 1] = value;
ds->ds9.memory.io[(address >> 1) + 1] = value >> 16;
}
uint16_t DS9IORead(struct DS* ds, uint32_t address) {
switch (address) {
case DS9_REG_IPCSYNC:
case DS_REG_TM0CNT_LO:
case DS_REG_TM1CNT_LO:
case DS_REG_TM2CNT_LO:
case DS_REG_TM3CNT_LO:
DSIOUpdateTimer(&ds->ds9, address);
break;
case DS_REG_TM0CNT_HI:
case DS_REG_TM1CNT_HI:
case DS_REG_TM2CNT_HI:
case DS_REG_TM3CNT_HI:
case DS_REG_IPCSYNC:
case DS_REG_IME:
case DS_REG_IE_LO:
case DS_REG_IE_HI:
case DS_REG_IF_LO:
case DS_REG_IF_HI:
// Handled transparently by the registers
break;
default:
mLOG(DS_IO, STUB, "Stub DS9 I/O register read: %06X", address);
}
if (address < DS9_REG_MAX) {
return ds->memory.io9[address >> 1];
return ds->ds9.memory.io[address >> 1];
}
return 0;
}

View File

@ -80,7 +80,7 @@ static int32_t DSMemoryStall(struct ARMCore* cpu, int32_t wait);
static const int DMA_OFFSET[] = { 1, -1, 0, 1 };
void DSMemoryInit(struct DS* ds) {
struct ARMCore* arm7 = ds->arm7;
struct ARMCore* arm7 = ds->ds7.cpu;
arm7->memory.load32 = DS7Load32;
arm7->memory.load16 = DS7Load16;
arm7->memory.load8 = DS7Load8;
@ -91,7 +91,7 @@ void DSMemoryInit(struct DS* ds) {
arm7->memory.storeMultiple = DS7StoreMultiple;
arm7->memory.stall = DSMemoryStall;
struct ARMCore* arm9 = ds->arm9;
struct ARMCore* arm9 = ds->ds9.cpu;
arm9->memory.load32 = DS9Load32;
arm9->memory.load16 = DS9Load16;
arm9->memory.load8 = DS9Load8;
@ -111,8 +111,10 @@ void DSMemoryInit(struct DS* ds) {
ds->memory.dtcm = NULL;
ds->memory.rom = NULL;
ds->memory.activeRegion7 = -1;
ds->memory.activeRegion9 = -1;
ds->ds7.memory.activeRegion = -1;
ds->ds9.memory.activeRegion = -1;
ds->ds7.memory.io = ds->memory.io7;
ds->ds9.memory.io = ds->memory.io9;
arm7->memory.activeRegion = 0;
arm7->memory.activeMask = 0;
@ -165,12 +167,10 @@ void DSMemoryReset(struct DS* ds) {
}
ds->memory.dtcm = anonymousMemoryMap(DS9_SIZE_DTCM);
memset(ds->memory.dma7, 0, sizeof(ds->memory.dma7));
memset(ds->memory.dma9, 0, sizeof(ds->memory.dma9));
ds->memory.activeDMA7 = -1;
ds->memory.activeDMA9 = -1;
ds->memory.nextDMA = INT_MAX;
ds->memory.eventDiff = 0;
memset(ds->ds7.memory.dma, 0, sizeof(ds->ds7.memory.dma));
memset(ds->ds9.memory.dma, 0, sizeof(ds->ds9.memory.dma));
ds->ds7.memory.activeDMA = -1;
ds->ds9.memory.activeDMA = -1;
// TODO: Correct size
ds->memory.wramSize7 = 0x8000;
@ -188,7 +188,7 @@ static void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
int newRegion = address >> DS_BASE_OFFSET;
memory->activeRegion7 = newRegion;
ds->ds7.memory.activeRegion = newRegion;
switch (newRegion) {
case DS_REGION_WORKING_RAM:
if (address >= DS7_BASE_WORKING_RAM || !memory->wramSize7) {
@ -550,7 +550,7 @@ static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t address) {
int newRegion = address >> DS_BASE_OFFSET;
memory->activeRegion9 = newRegion;
ds->ds9.memory.activeRegion = newRegion;
switch (newRegion) {
case DS9_REGION_ITCM:
case DS9_REGION_ITCM_MIRROR:

View File

@ -8,81 +8,81 @@
#include <mgba/internal/arm/arm.h>
#include <mgba/internal/ds/ds.h>
static void DS7TimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DS* ds = context;
struct GBATimer* timer = &ds->timers7[0];
static void DSTimerUpdate0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DSCommon* dscore = context;
struct GBATimer* timer = &dscore->timers[0];
if (GBATimerFlagsIsDoIrq(timer->flags)) {
DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER0);
DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_TIMER0);
}
GBATimerUpdate(timing, &ds->timers7[0], &ds->memory.io7[DS7_REG_TM0CNT_LO >> 1], cyclesLate);
GBATimerUpdateCountUp(timing, &ds->timers7[1], &ds->memory.io7[DS7_REG_TM1CNT_LO >> 1], cyclesLate);
GBATimerUpdate(timing, &dscore->timers[0], &dscore->memory.io[DS_REG_TM0CNT_LO >> 1], cyclesLate);
GBATimerUpdateCountUp(timing, &dscore->timers[1], &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], cyclesLate);
}
static void DS7TimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DS* ds = context;
struct GBATimer* timer = &ds->timers7[1];
static void DSTimerUpdate1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DSCommon* dscore = context;
struct GBATimer* timer = &dscore->timers[1];
if (GBATimerFlagsIsDoIrq(timer->flags)) {
DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER1);
DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_TIMER1);
}
GBATimerUpdate(timing, &ds->timers7[1], &ds->memory.io7[DS7_REG_TM1CNT_LO >> 1], cyclesLate);
GBATimerUpdateCountUp(timing, &ds->timers7[2], &ds->memory.io7[DS7_REG_TM2CNT_LO >> 1], cyclesLate);
GBATimerUpdate(timing, &dscore->timers[1], &dscore->memory.io[DS_REG_TM1CNT_LO >> 1], cyclesLate);
GBATimerUpdateCountUp(timing, &dscore->timers[2], &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], cyclesLate);
}
static void DS7TimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DS* ds = context;
struct GBATimer* timer = &ds->timers7[2];
static void DSTimerUpdate2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DSCommon* dscore = context;
struct GBATimer* timer = &dscore->timers[2];
if (GBATimerFlagsIsDoIrq(timer->flags)) {
DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER2);
DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_TIMER2);
}
GBATimerUpdate(timing, &ds->timers7[2], &ds->memory.io7[DS7_REG_TM2CNT_LO >> 1], cyclesLate);
GBATimerUpdateCountUp(timing, &ds->timers7[3], &ds->memory.io7[DS7_REG_TM3CNT_LO >> 1], cyclesLate);
GBATimerUpdate(timing, &dscore->timers[2], &dscore->memory.io[DS_REG_TM2CNT_LO >> 1], cyclesLate);
GBATimerUpdateCountUp(timing, &dscore->timers[3], &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], cyclesLate);
}
static void DS7TimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DS* ds = context;
struct GBATimer* timer = &ds->timers7[3];
static void DSTimerUpdate3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
struct DSCommon* dscore = context;
struct GBATimer* timer = &dscore->timers[3];
if (GBATimerFlagsIsDoIrq(timer->flags)) {
DSRaiseIRQ(ds->arm7, ds->memory.io7, DS_IRQ_TIMER3);
DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_TIMER3);
}
GBATimerUpdate(timing, &ds->timers7[3], &ds->memory.io7[DS7_REG_TM3CNT_LO >> 1], cyclesLate);
GBATimerUpdate(timing, &dscore->timers[3], &dscore->memory.io[DS_REG_TM3CNT_LO >> 1], cyclesLate);
}
void DSTimerInit(struct DS* ds) {
memset(ds->timers7, 0, sizeof(ds->timers7));
ds->timers7[0].event.name = "DS7 Timer 0";
ds->timers7[0].event.callback = DS7TimerUpdate0;
ds->timers7[0].event.context = ds;
ds->timers7[0].event.priority = 0x20;
ds->timers7[1].event.name = "DS7 Timer 1";
ds->timers7[1].event.callback = DS7TimerUpdate1;
ds->timers7[1].event.context = ds;
ds->timers7[1].event.priority = 0x21;
ds->timers7[2].event.name = "DS7 Timer 2";
ds->timers7[2].event.callback = DS7TimerUpdate2;
ds->timers7[2].event.context = ds;
ds->timers7[2].event.priority = 0x22;
ds->timers7[3].event.name = "DS7 Timer 3";
ds->timers7[3].event.callback = DS7TimerUpdate3;
ds->timers7[3].event.context = ds;
ds->timers7[3].event.priority = 0x23;
memset(ds->ds7.timers, 0, sizeof(ds->ds7.timers));
ds->ds7.timers[0].event.name = "DS7 Timer 0";
ds->ds7.timers[0].event.callback = DSTimerUpdate0;
ds->ds7.timers[0].event.context = &ds->ds7;
ds->ds7.timers[0].event.priority = 0x20;
ds->ds7.timers[1].event.name = "DS7 Timer 1";
ds->ds7.timers[0].event.callback = DSTimerUpdate1;
ds->ds7.timers[0].event.context = &ds->ds7;
ds->ds7.timers[1].event.priority = 0x21;
ds->ds7.timers[2].event.name = "DS7 Timer 2";
ds->ds7.timers[0].event.callback = DSTimerUpdate2;
ds->ds7.timers[0].event.context = &ds->ds7;
ds->ds7.timers[2].event.priority = 0x22;
ds->ds7.timers[3].event.name = "DS7 Timer 3";
ds->ds7.timers[0].event.callback = DSTimerUpdate3;
ds->ds7.timers[0].event.context = &ds->ds7;
ds->ds7.timers[3].event.priority = 0x23;
memset(ds->timers9, 0, sizeof(ds->timers9));
ds->timers9[0].event.name = "DS9 Timer 0";
ds->timers9[0].event.callback = NULL;
ds->timers9[0].event.context = ds;
ds->timers9[0].event.priority = 0x20;
ds->timers9[1].event.name = "DS9 Timer 1";
ds->timers9[1].event.callback = NULL;
ds->timers9[1].event.context = ds;
ds->timers9[1].event.priority = 0x21;
ds->timers9[2].event.name = "DS9 Timer 2";
ds->timers9[2].event.callback = NULL;
ds->timers9[2].event.context = ds;
ds->timers9[2].event.priority = 0x22;
ds->timers9[3].event.name = "DS9 Timer 3";
ds->timers9[3].event.callback = NULL;
ds->timers9[3].event.context = ds;
ds->timers9[3].event.priority = 0x23;
memset(ds->ds9.timers, 0, sizeof(ds->ds9.timers));
ds->ds9.timers[0].event.name = "DS9 Timer 0";
ds->ds9.timers[0].event.callback = DSTimerUpdate0;
ds->ds9.timers[0].event.context = ds;
ds->ds9.timers[0].event.priority = 0x20;
ds->ds9.timers[1].event.name = "DS9 Timer 1";
ds->ds9.timers[1].event.callback = DSTimerUpdate1;
ds->ds9.timers[1].event.context = ds;
ds->ds9.timers[1].event.priority = 0x21;
ds->ds9.timers[2].event.name = "DS9 Timer 2";
ds->ds9.timers[2].event.callback = DSTimerUpdate2;
ds->ds9.timers[2].event.context = ds;
ds->ds9.timers[2].event.priority = 0x22;
ds->ds9.timers[3].event.name = "DS9 Timer 3";
ds->ds9.timers[3].event.callback = DSTimerUpdate3;
ds->ds9.timers[3].event.context = ds;
ds->ds9.timers[3].event.priority = 0x23;
}
void DSTimerWriteTMCNT_HI(struct GBATimer* timer, struct mTiming* timing, struct ARMCore* cpu, uint16_t* io, uint16_t value) {