mirror of https://github.com/mgba-emu/mgba.git
Implement timers from GBA.js implementation
This commit is contained in:
parent
6087ad8c5f
commit
57dcbef030
126
src/gba/gba.c
126
src/gba/gba.c
|
@ -9,6 +9,7 @@
|
|||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
enum {
|
||||
|
@ -18,6 +19,7 @@ enum {
|
|||
};
|
||||
|
||||
static void GBAProcessEvents(struct ARMBoard* board);
|
||||
static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles);
|
||||
static void GBAHitStub(struct ARMBoard* board, uint32_t opcode);
|
||||
|
||||
void GBAInit(struct GBA* gba) {
|
||||
|
@ -39,6 +41,8 @@ void GBAInit(struct GBA* gba) {
|
|||
|
||||
GBAIOInit(gba);
|
||||
|
||||
memset(gba->timers, 0, sizeof(gba->timers));
|
||||
|
||||
gba->springIRQ = 0;
|
||||
|
||||
ARMReset(&gba->cpu);
|
||||
|
@ -82,10 +86,128 @@ static void GBAProcessEvents(struct ARMBoard* board) {
|
|||
nextEvent = testEvent;
|
||||
}
|
||||
|
||||
testEvent = GBATimersProcessEvents(gbaBoard->p, cycles);
|
||||
if (testEvent < nextEvent) {
|
||||
nextEvent = testEvent;
|
||||
}
|
||||
|
||||
board->cpu->cycles = 0;
|
||||
board->cpu->nextEvent = nextEvent;
|
||||
}
|
||||
|
||||
static int32_t GBATimersProcessEvents(struct GBA* gba, int32_t cycles) {
|
||||
int32_t nextEvent = INT_MAX;
|
||||
if (gba->timersEnabled) {
|
||||
struct GBATimer* timer;
|
||||
struct GBATimer* nextTimer;
|
||||
|
||||
timer = &gba->timers[0];
|
||||
if (timer->enable) {
|
||||
timer->nextEvent -= cycles;
|
||||
if (timer->nextEvent <= 0) {
|
||||
timer->nextEvent += timer->overflowInterval;
|
||||
gba->memory.io[REG_TM0CNT_LO >> 1] = timer->reload;
|
||||
timer->oldReload = timer->reload;
|
||||
|
||||
if (timer->doIrq) {
|
||||
GBARaiseIRQ(gba, IRQ_TIMER0);
|
||||
}
|
||||
|
||||
nextTimer = &gba->timers[1];
|
||||
if (nextTimer->countUp) {
|
||||
++gba->memory.io[REG_TM1CNT_LO >> 1];
|
||||
if (!gba->memory.io[REG_TM1CNT_LO >> 1]) {
|
||||
nextTimer->nextEvent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
nextEvent = timer->nextEvent;
|
||||
}
|
||||
|
||||
timer = &gba->timers[1];
|
||||
if (timer->enable) {
|
||||
timer->nextEvent -= cycles;
|
||||
if (timer->nextEvent <= 0) {
|
||||
timer->nextEvent += timer->overflowInterval;
|
||||
gba->memory.io[REG_TM1CNT_LO >> 1] = timer->reload;
|
||||
timer->oldReload = timer->reload;
|
||||
|
||||
if (timer->doIrq) {
|
||||
GBARaiseIRQ(gba, IRQ_TIMER1);
|
||||
}
|
||||
|
||||
if (timer->countUp) {
|
||||
timer->nextEvent = INT_MAX;
|
||||
}
|
||||
|
||||
nextTimer = &gba->timers[2];
|
||||
if (nextTimer->countUp) {
|
||||
++gba->memory.io[REG_TM2CNT_LO >> 1];
|
||||
if (!gba->memory.io[REG_TM2CNT_LO >> 1]) {
|
||||
nextTimer->nextEvent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (timer->nextEvent < nextEvent) {
|
||||
nextEvent = timer->nextEvent;
|
||||
}
|
||||
}
|
||||
|
||||
timer = &gba->timers[2];
|
||||
if (timer->enable) {
|
||||
timer->nextEvent -= cycles;
|
||||
nextEvent = timer->nextEvent;
|
||||
if (timer->nextEvent <= 0) {
|
||||
timer->nextEvent += timer->overflowInterval;
|
||||
gba->memory.io[REG_TM2CNT_LO >> 1] = timer->reload;
|
||||
timer->oldReload = timer->reload;
|
||||
|
||||
if (timer->doIrq) {
|
||||
GBARaiseIRQ(gba, IRQ_TIMER2);
|
||||
}
|
||||
|
||||
if (timer->countUp) {
|
||||
timer->nextEvent = INT_MAX;
|
||||
}
|
||||
|
||||
nextTimer = &gba->timers[3];
|
||||
if (nextTimer->countUp) {
|
||||
++gba->memory.io[REG_TM3CNT_LO >> 1];
|
||||
if (!gba->memory.io[REG_TM3CNT_LO >> 1]) {
|
||||
nextTimer->nextEvent = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (timer->nextEvent < nextEvent) {
|
||||
nextEvent = timer->nextEvent;
|
||||
}
|
||||
}
|
||||
|
||||
timer = &gba->timers[3];
|
||||
if (timer->enable) {
|
||||
timer->nextEvent -= cycles;
|
||||
nextEvent = timer->nextEvent;
|
||||
if (timer->nextEvent <= 0) {
|
||||
timer->nextEvent += timer->overflowInterval;
|
||||
gba->memory.io[REG_TM3CNT_LO >> 1] = timer->reload;
|
||||
timer->oldReload = timer->reload;
|
||||
|
||||
if (timer->doIrq) {
|
||||
GBARaiseIRQ(gba, IRQ_TIMER3);
|
||||
}
|
||||
|
||||
if (timer->countUp) {
|
||||
timer->nextEvent = INT_MAX;
|
||||
}
|
||||
}
|
||||
if (timer->nextEvent < nextEvent) {
|
||||
nextEvent = timer->nextEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nextEvent;
|
||||
}
|
||||
|
||||
void GBAAttachDebugger(struct GBA* gba, struct ARMDebugger* debugger) {
|
||||
ARMDebuggerInit(debugger, &gba->cpu);
|
||||
gba->debugger = debugger;
|
||||
|
@ -97,10 +219,6 @@ void GBALoadROM(struct GBA* gba, int fd) {
|
|||
}
|
||||
|
||||
void GBAWriteIE(struct GBA* gba, uint16_t value) {
|
||||
if (value & ((1 << IRQ_TIMER0) | (1 << IRQ_TIMER1) | (1 << IRQ_TIMER2) | (1 << IRQ_TIMER3))) {
|
||||
GBALog(GBA_LOG_STUB, "Timer interrupts not implemented");
|
||||
}
|
||||
|
||||
if (value & (1 << IRQ_SIO)) {
|
||||
GBALog(GBA_LOG_STUB, "SIO interrupts not implemented");
|
||||
}
|
||||
|
|
|
@ -46,6 +46,18 @@ struct GBA {
|
|||
|
||||
struct ARMDebugger* debugger;
|
||||
|
||||
int timersEnabled;
|
||||
struct GBATimer {
|
||||
uint16_t reload;
|
||||
uint16_t oldReload;
|
||||
int32_t nextEvent;
|
||||
int32_t overflowInterval;
|
||||
unsigned prescaleBits : 4;
|
||||
unsigned countUp : 1;
|
||||
unsigned doIrq : 1;
|
||||
unsigned enable : 1;
|
||||
} timers[4];
|
||||
|
||||
int springIRQ;
|
||||
|
||||
enum GBAError errno;
|
||||
|
|
Loading…
Reference in New Issue