GBA: All IRQs have 7 cycle delay (fixes #539, #1208)

This commit is contained in:
Vicki Pfau 2018-10-07 12:51:01 -07:00
parent 01d9c106cb
commit f6a4a13b60
9 changed files with 67 additions and 105 deletions

View File

@ -1,3 +1,7 @@
0.8.0: (Future)
Bugfixes:
- GBA: All IRQs have 7 cycle delay (fixes mgba.io/i/539, mgba.io/i/1208)
0.7.0: (Future) 0.7.0: (Future)
Features: Features:
- ELF support - ELF support

View File

@ -82,6 +82,8 @@ struct GBA {
struct GBATimer timers[4]; struct GBATimer timers[4];
int springIRQ; int springIRQ;
struct mTimingEvent irqEvent;
uint32_t biosChecksum; uint32_t biosChecksum;
int* keySource; int* keySource;
struct mRotationSource* rotationSource; struct mRotationSource* rotationSource;
@ -141,8 +143,6 @@ void GBADestroy(struct GBA* gba);
void GBAReset(struct ARMCore* cpu); void GBAReset(struct ARMCore* cpu);
void GBASkipBIOS(struct GBA* gba); void GBASkipBIOS(struct GBA* gba);
void GBAWriteIE(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);
void GBATestIRQ(struct ARMCore* cpu); void GBATestIRQ(struct ARMCore* cpu);
void GBAHalt(struct GBA* gba); void GBAHalt(struct GBA* gba);

View File

@ -98,28 +98,28 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* | 0x00202 - 0x00203: Old reload value * | 0x00202 - 0x00203: Old reload value
* | 0x00204 - 0x00207: Last event * | 0x00204 - 0x00207: Last event
* | 0x00208 - 0x0020B: Next event * | 0x00208 - 0x0020B: Next event
* | 0x0020C - 0x0020F: Next IRQ * | 0x0020C - 0x0020F: Reserved
* | 0x00210 - 0x00213: Miscellaneous flags * | 0x00210 - 0x00213: Miscellaneous flags
* 0x00214 - 0x00227: Timer 1 * 0x00214 - 0x00227: Timer 1
* | 0x00214 - 0x00215: Reload value * | 0x00214 - 0x00215: Reload value
* | 0x00216 - 0x00217: Old reload value * | 0x00216 - 0x00217: Old reload value
* | 0x00218 - 0x0021B: Last event * | 0x00218 - 0x0021B: Last event
* | 0x0021C - 0x0021F: Next event * | 0x0021C - 0x0021F: Next event
* | 0x00220 - 0x00223: Next IRQ * | 0x00220 - 0x00223: Reserved
* | 0x00224 - 0x00227: Miscellaneous flags * | 0x00224 - 0x00227: Miscellaneous flags
* 0x00228 - 0x0023B: Timer 2 * 0x00228 - 0x0023B: Timer 2
* | 0x00228 - 0x00229: Reload value * | 0x00228 - 0x00229: Reload value
* | 0x0022A - 0x0022B: Old reload value * | 0x0022A - 0x0022B: Old reload value
* | 0x0022C - 0x0022F: Last event * | 0x0022C - 0x0022F: Last event
* | 0x00230 - 0x00233: Next event * | 0x00230 - 0x00233: Next event
* | 0x00234 - 0x00237: Next IRQ * | 0x00234 - 0x00237: Reserved
* | 0x00238 - 0x0023B: Miscellaneous flags * | 0x00238 - 0x0023B: Miscellaneous flags
* 0x0023C - 0x00250: Timer 3 * 0x0023C - 0x00250: Timer 3
* | 0x0023C - 0x0023D: Reload value * | 0x0023C - 0x0023D: Reload value
* | 0x0023E - 0x0023F: Old reload value * | 0x0023E - 0x0023F: Old reload value
* | 0x00240 - 0x00243: Last event * | 0x00240 - 0x00243: Last event
* | 0x00244 - 0x00247: Next event * | 0x00244 - 0x00247: Next event
* | 0x00248 - 0x0024B: Next IRQ * | 0x00248 - 0x0024B: Reserved
* | 0x0024C - 0x0024F: Miscellaneous flags * | 0x0024C - 0x0024F: Miscellaneous flags
* 0x00250 - 0x0025F: DMA 0 * 0x00250 - 0x0025F: DMA 0
* | 0x00250 - 0x00253: DMA next source * | 0x00250 - 0x00253: DMA next source
@ -196,7 +196,9 @@ mLOG_DECLARE_CATEGORY(GBA_STATE);
* 0x0031C - 0x0031F: Miscellaneous flags * 0x0031C - 0x0031F: Miscellaneous flags
* | bit 0: Is CPU halted? * | bit 0: Is CPU halted?
* | bit 1: POSTFLG * | bit 1: POSTFLG
* 0x00320 - 0x003FF: Reserved (leave zero) * | bit 2: Is IRQ pending?
* 0x00320 - 0x00323: Next IRQ event
* 0x00324 - 0x003FF: Reserved (leave zero)
* 0x00400 - 0x007FF: I/O memory * 0x00400 - 0x007FF: I/O memory
* 0x00800 - 0x00BFF: Palette * 0x00800 - 0x00BFF: Palette
* 0x00C00 - 0x00FFF: OAM * 0x00C00 - 0x00FFF: OAM
@ -227,6 +229,7 @@ DECL_BIT(GBASerializedSavedataFlags, DustSettling, 5);
DECL_BITFIELD(GBASerializedMiscFlags, uint32_t); DECL_BITFIELD(GBASerializedMiscFlags, uint32_t);
DECL_BIT(GBASerializedMiscFlags, Halted, 0); DECL_BIT(GBASerializedMiscFlags, Halted, 0);
DECL_BIT(GBASerializedMiscFlags, POSTFLG, 1); DECL_BIT(GBASerializedMiscFlags, POSTFLG, 1);
DECL_BIT(GBASerializedMiscFlags, IrqPending, 2);
struct GBASerializedState { struct GBASerializedState {
uint32_t versionMagic; uint32_t versionMagic;
@ -267,10 +270,10 @@ struct GBASerializedState {
struct { struct {
uint16_t reload; uint16_t reload;
uint16_t reserved; uint16_t reserved0;
uint32_t lastEvent; uint32_t lastEvent;
uint32_t nextEvent; uint32_t nextEvent;
uint32_t nextIrq; uint32_t reserved1;
GBATimerFlags flags; GBATimerFlags flags;
} timers[4]; } timers[4];
@ -320,8 +323,9 @@ struct GBASerializedState {
uint32_t lastPrefetchedPc; uint32_t lastPrefetchedPc;
GBASerializedMiscFlags miscFlags; GBASerializedMiscFlags miscFlags;
uint32_t nextIrq;
uint32_t reserved[56]; uint32_t reserved[55];
uint16_t io[SIZE_IO >> 1]; uint16_t io[SIZE_IO >> 1];
uint16_t pram[SIZE_PALETTE_RAM >> 1]; uint16_t pram[SIZE_PALETTE_RAM >> 1];

View File

@ -17,14 +17,12 @@ DECL_BITS(GBATimerFlags, PrescaleBits, 0, 4);
DECL_BIT(GBATimerFlags, CountUp, 4); DECL_BIT(GBATimerFlags, CountUp, 4);
DECL_BIT(GBATimerFlags, DoIrq, 5); DECL_BIT(GBATimerFlags, DoIrq, 5);
DECL_BIT(GBATimerFlags, Enable, 6); DECL_BIT(GBATimerFlags, Enable, 6);
DECL_BIT(GBATimerFlags, IrqPending, 7);
struct GBA; struct GBA;
struct GBATimer { struct GBATimer {
uint16_t reload; uint16_t reload;
int32_t lastEvent; int32_t lastEvent;
struct mTimingEvent event; struct mTimingEvent event;
struct mTimingEvent irq;
GBATimerFlags flags; GBATimerFlags flags;
}; };

View File

@ -167,6 +167,7 @@ void ARMRaiseIRQ(struct ARMCore* cpu) {
cpu->spsr = cpsr; cpu->spsr = cpsr;
cpu->cpsr.i = 1; cpu->cpsr.i = 1;
cpu->cycles += currentCycles; cpu->cycles += currentCycles;
cpu->halted = 0;
} }
void ARMRaiseSWI(struct ARMCore* cpu) { void ARMRaiseSWI(struct ARMCore* cpu) {

View File

@ -25,6 +25,8 @@
#include <mgba-util/elf-read.h> #include <mgba-util/elf-read.h>
#endif #endif
#define GBA_IRQ_DELAY 7
mLOG_DEFINE_CATEGORY(GBA, "GBA", "gba"); mLOG_DEFINE_CATEGORY(GBA, "GBA", "gba");
mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug", "gba.debug"); mLOG_DEFINE_CATEGORY(GBA_DEBUG, "GBA Debug", "gba.debug");
@ -45,6 +47,8 @@ static void GBAHitStub(struct ARMCore* cpu, uint32_t opcode);
static void GBAIllegal(struct ARMCore* cpu, uint32_t opcode); static void GBAIllegal(struct ARMCore* cpu, uint32_t opcode);
static void GBABreakpoint(struct ARMCore* cpu, int immediate); static void GBABreakpoint(struct ARMCore* cpu, int immediate);
static void _triggerIRQ(struct mTiming*, void* user, uint32_t cyclesLate);
#ifdef USE_DEBUGGERS #ifdef USE_DEBUGGERS
static bool _setSoftwareBreakpoint(struct ARMDebugger*, uint32_t address, enum ExecutionMode mode, uint32_t* opcode); static bool _setSoftwareBreakpoint(struct ARMDebugger*, uint32_t address, enum ExecutionMode mode, uint32_t* opcode);
static bool _clearSoftwareBreakpoint(struct ARMDebugger*, uint32_t address, enum ExecutionMode mode, uint32_t opcode); static bool _clearSoftwareBreakpoint(struct ARMDebugger*, uint32_t address, enum ExecutionMode mode, uint32_t opcode);
@ -86,7 +90,6 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
GBAHardwareInit(&gba->memory.hw, NULL); GBAHardwareInit(&gba->memory.hw, NULL);
gba->springIRQ = 0;
gba->keySource = 0; gba->keySource = 0;
gba->rotationSource = 0; gba->rotationSource = 0;
gba->luminanceSource = 0; gba->luminanceSource = 0;
@ -116,6 +119,11 @@ static void GBAInit(void* cpu, struct mCPUComponent* component) {
gba->yankedRomSize = 0; gba->yankedRomSize = 0;
mTimingInit(&gba->timing, &gba->cpu->cycles, &gba->cpu->nextEvent); mTimingInit(&gba->timing, &gba->cpu->cycles, &gba->cpu->nextEvent);
gba->irqEvent.name = "GBA IRQ Event";
gba->irqEvent.callback = _triggerIRQ;
gba->irqEvent.context = gba;
gba->irqEvent.priority = 0;
} }
void GBAUnloadROM(struct GBA* gba) { void GBAUnloadROM(struct GBA* gba) {
@ -247,11 +255,6 @@ static void GBAProcessEvents(struct ARMCore* cpu) {
gba->bus |= cpu->prefetch[1] << 16; gba->bus |= cpu->prefetch[1] << 16;
} }
if (gba->springIRQ && !cpu->cpsr.i) {
ARMRaiseIRQ(cpu);
gba->springIRQ = 0;
}
int32_t nextEvent = cpu->nextEvent; int32_t nextEvent = cpu->nextEvent;
while (cpu->cycles >= nextEvent) { while (cpu->cycles >= nextEvent) {
cpu->nextEvent = INT_MAX; cpu->nextEvent = INT_MAX;
@ -474,34 +477,17 @@ void GBAApplyPatch(struct GBA* gba, struct Patch* patch) {
gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize); gba->romCrc32 = doCrc32(gba->memory.rom, gba->memory.romSize);
} }
void GBAWriteIE(struct GBA* gba, uint16_t value) {
if (gba->memory.io[REG_IME >> 1] && value & gba->memory.io[REG_IF >> 1]) {
ARMRaiseIRQ(gba->cpu);
}
}
void GBAWriteIME(struct GBA* gba, uint16_t value) {
if (value && gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
ARMRaiseIRQ(gba->cpu);
}
}
void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq) { void GBARaiseIRQ(struct GBA* gba, enum GBAIRQ irq) {
gba->memory.io[REG_IF >> 1] |= 1 << irq; gba->memory.io[REG_IF >> 1] |= 1 << irq;
GBATestIRQ(gba->cpu);
if (gba->memory.io[REG_IE >> 1] & 1 << irq) {
gba->cpu->halted = 0;
if (gba->memory.io[REG_IME >> 1]) {
ARMRaiseIRQ(gba->cpu);
}
}
} }
void GBATestIRQ(struct ARMCore* cpu) { void GBATestIRQ(struct ARMCore* cpu) {
struct GBA* gba = (struct GBA*) cpu->master; struct GBA* gba = (struct GBA*) cpu->master;
if (gba->memory.io[REG_IME >> 1] && gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) { if (gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]) {
gba->springIRQ = gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1]; if (!mTimingIsScheduled(&gba->timing, &gba->irqEvent)) {
gba->cpu->nextEvent = gba->cpu->cycles; mTimingSchedule(&gba->timing, &gba->irqEvent, GBA_IRQ_DELAY);
}
} }
} }
@ -857,6 +843,20 @@ void GBATestKeypadIRQ(struct GBA* gba) {
} }
} }
static void _triggerIRQ(struct mTiming* timing, void* user, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
struct GBA* gba = user;
gba->cpu->halted = 0;
if (!(gba->memory.io[REG_IE >> 1] & gba->memory.io[REG_IF >> 1])) {
return;
}
if (gba->memory.io[REG_IME >> 1] && !gba->cpu->cpsr.i) {
ARMRaiseIRQ(gba->cpu);
}
}
void GBASetBreakpoint(struct GBA* gba, struct mCPUComponent* component, uint32_t address, enum ExecutionMode mode, uint32_t* opcode) { void GBASetBreakpoint(struct GBA* gba, struct mCPUComponent* component, uint32_t address, enum ExecutionMode mode, uint32_t* opcode) {
size_t immediate; size_t immediate;
for (immediate = 0; immediate < gba->cpu->numComponents; ++immediate) { for (immediate = 0; immediate < gba->cpu->numComponents; ++immediate) {

View File

@ -547,15 +547,18 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
GBAAdjustWaitstates(gba, value); GBAAdjustWaitstates(gba, value);
break; break;
case REG_IE: case REG_IE:
GBAWriteIE(gba, value); gba->memory.io[REG_IE >> 1] = value;
break; GBATestIRQ(gba->cpu);
return;
case REG_IF: case REG_IF:
gba->springIRQ &= ~value;
value = gba->memory.io[REG_IF >> 1] & ~value; value = gba->memory.io[REG_IF >> 1] & ~value;
break; gba->memory.io[REG_IF >> 1] = value;
GBATestIRQ(gba->cpu);
return;
case REG_IME: case REG_IME:
GBAWriteIME(gba, value); gba->memory.io[REG_IME >> 1] = value;
break; GBATestIRQ(gba->cpu);
return;
case REG_MAX: case REG_MAX:
// Some bad interrupt libraries will write to this // Some bad interrupt libraries will write to this
break; break;
@ -931,7 +934,6 @@ void GBAIOSerialize(struct GBA* gba, struct GBASerializedState* state) {
STORE_16(gba->timers[i].reload, 0, &state->timers[i].reload); STORE_16(gba->timers[i].reload, 0, &state->timers[i].reload);
STORE_32(gba->timers[i].lastEvent - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].lastEvent); STORE_32(gba->timers[i].lastEvent - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].lastEvent);
STORE_32(gba->timers[i].event.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextEvent); STORE_32(gba->timers[i].event.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextEvent);
STORE_32(gba->timers[i].irq.when - mTimingCurrentTime(&gba->timing), 0, &state->timers[i].nextIrq);
STORE_32(gba->timers[i].flags, 0, &state->timers[i].flags); STORE_32(gba->timers[i].flags, 0, &state->timers[i].flags);
STORE_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource); STORE_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);
STORE_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest); STORE_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);
@ -971,10 +973,6 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
if (GBATimerFlagsIsEnable(gba->timers[i].flags)) { if (GBATimerFlagsIsEnable(gba->timers[i].flags)) {
mTimingSchedule(&gba->timing, &gba->timers[i].event, when); mTimingSchedule(&gba->timing, &gba->timers[i].event, when);
} }
LOAD_32(when, 0, &state->timers[i].nextIrq);
if (GBATimerFlagsIsIrqPending(gba->timers[i].flags)) {
mTimingSchedule(&gba->timing, &gba->timers[i].irq, when);
}
LOAD_16(gba->memory.dma[i].reg, (REG_DMA0CNT_HI + i * 12), state->io); LOAD_16(gba->memory.dma[i].reg, (REG_DMA0CNT_HI + i * 12), state->io);
LOAD_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource); LOAD_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);

View File

@ -15,7 +15,7 @@
#include <fcntl.h> #include <fcntl.h>
const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000; const uint32_t GBA_SAVESTATE_MAGIC = 0x01000000;
const uint32_t GBA_SAVESTATE_VERSION = 0x00000002; const uint32_t GBA_SAVESTATE_VERSION = 0x00000003;
mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize"); mLOG_DEFINE_CATEGORY(GBA_STATE, "GBA Savestate", "gba.serialize");
@ -62,6 +62,10 @@ void GBASerialize(struct GBA* gba, struct GBASerializedState* state) {
GBASerializedMiscFlags miscFlags = 0; GBASerializedMiscFlags miscFlags = 0;
miscFlags = GBASerializedMiscFlagsSetHalted(miscFlags, gba->cpu->halted); miscFlags = GBASerializedMiscFlagsSetHalted(miscFlags, gba->cpu->halted);
miscFlags = GBASerializedMiscFlagsSetPOSTFLG(miscFlags, gba->memory.io[REG_POSTFLG >> 1] & 1); miscFlags = GBASerializedMiscFlagsSetPOSTFLG(miscFlags, gba->memory.io[REG_POSTFLG >> 1] & 1);
if (mTimingIsScheduled(&gba->timing, &gba->irqEvent)) {
miscFlags = GBASerializedMiscFlagsFillIrqPending(miscFlags);
STORE_32(gba->irqEvent.when - mTimingCurrentTime(&gba->timing), 0, &state->nextIrq);
}
STORE_32(miscFlags, 0, &state->miscFlags); STORE_32(miscFlags, 0, &state->miscFlags);
GBAMemorySerialize(&gba->memory, state); GBAMemorySerialize(&gba->memory, state);
@ -179,6 +183,11 @@ bool GBADeserialize(struct GBA* gba, const struct GBASerializedState* state) {
LOAD_32(miscFlags, 0, &state->miscFlags); LOAD_32(miscFlags, 0, &state->miscFlags);
gba->cpu->halted = GBASerializedMiscFlagsGetHalted(miscFlags); gba->cpu->halted = GBASerializedMiscFlagsGetHalted(miscFlags);
gba->memory.io[REG_POSTFLG >> 1] = GBASerializedMiscFlagsGetPOSTFLG(miscFlags); gba->memory.io[REG_POSTFLG >> 1] = GBASerializedMiscFlagsGetPOSTFLG(miscFlags);
if (GBASerializedMiscFlagsIsIrqPending(miscFlags)) {
int32_t when;
LOAD_32(when, 0, &state->nextIrq);
mTimingSchedule(&gba->timing, &gba->irqEvent, when);
}
GBAVideoDeserialize(&gba->video, state); GBAVideoDeserialize(&gba->video, state);
GBAMemoryDeserialize(&gba->memory, state); GBAMemoryDeserialize(&gba->memory, state);

View File

@ -8,44 +8,11 @@
#include <mgba/internal/gba/gba.h> #include <mgba/internal/gba/gba.h>
#include <mgba/internal/gba/io.h> #include <mgba/internal/gba/io.h>
#define TIMER_IRQ_DELAY 7
#define TIMER_RELOAD_DELAY 0 #define TIMER_RELOAD_DELAY 0
#define TIMER_STARTUP_DELAY 2 #define TIMER_STARTUP_DELAY 2
#define REG_TMCNT_LO(X) (REG_TM0CNT_LO + ((X) << 2)) #define REG_TMCNT_LO(X) (REG_TM0CNT_LO + ((X) << 2))
static void GBATimerIrq(struct GBA* gba, int timerId) {
struct GBATimer* timer = &gba->timers[timerId];
if (GBATimerFlagsIsIrqPending(timer->flags)) {
timer->flags = GBATimerFlagsClearIrqPending(timer->flags);
GBARaiseIRQ(gba, IRQ_TIMER0 + timerId);
}
}
static void GBATimerIrq0(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
GBATimerIrq(context, 0);
}
static void GBATimerIrq1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
GBATimerIrq(context, 1);
}
static void GBATimerIrq2(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
GBATimerIrq(context, 2);
}
static void GBATimerIrq3(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
GBATimerIrq(context, 3);
}
static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) { static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) {
struct GBATimer* timer = &gba->timers[timerId]; struct GBATimer* timer = &gba->timers[timerId];
if (GBATimerFlagsIsCountUp(timer->flags)) { if (GBATimerFlagsIsCountUp(timer->flags)) {
@ -55,10 +22,7 @@ static void GBATimerUpdate(struct GBA* gba, int timerId, uint32_t cyclesLate) {
} }
if (GBATimerFlagsIsDoIrq(timer->flags)) { if (GBATimerFlagsIsDoIrq(timer->flags)) {
timer->flags = GBATimerFlagsFillIrqPending(timer->flags); GBARaiseIRQ(gba, IRQ_TIMER0 + timerId);
if (!mTimingIsScheduled(&gba->timing, &timer->irq)) {
mTimingSchedule(&gba->timing, &timer->irq, TIMER_IRQ_DELAY - cyclesLate);
}
} }
if (gba->audio.enable && timerId < 2) { if (gba->audio.enable && timerId < 2) {
@ -120,22 +84,6 @@ void GBATimerInit(struct GBA* gba) {
gba->timers[3].event.callback = GBATimerUpdate3; gba->timers[3].event.callback = GBATimerUpdate3;
gba->timers[3].event.context = gba; gba->timers[3].event.context = gba;
gba->timers[3].event.priority = 0x23; gba->timers[3].event.priority = 0x23;
gba->timers[0].irq.name = "GBA Timer 0 IRQ";
gba->timers[0].irq.callback = GBATimerIrq0;
gba->timers[0].irq.context = gba;
gba->timers[0].irq.priority = 0x28;
gba->timers[1].irq.name = "GBA Timer 1 IRQ";
gba->timers[1].irq.callback = GBATimerIrq1;
gba->timers[1].irq.context = gba;
gba->timers[1].irq.priority = 0x29;
gba->timers[2].irq.name = "GBA Timer 2 IRQ";
gba->timers[2].irq.callback = GBATimerIrq2;
gba->timers[2].irq.context = gba;
gba->timers[2].irq.priority = 0x2A;
gba->timers[3].irq.name = "GBA Timer 3 IRQ";
gba->timers[3].irq.callback = GBATimerIrq3;
gba->timers[3].irq.context = gba;
gba->timers[3].irq.priority = 0x2B;
} }
void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate) { void GBATimerUpdateRegister(struct GBA* gba, int timer, int32_t cyclesLate) {