DS: Add timers and start interrupts

This commit is contained in:
Jeffrey Pfau 2016-06-08 09:43:56 -07:00
parent 9c91235a34
commit e649be94f5
6 changed files with 290 additions and 16 deletions

View File

@ -80,6 +80,10 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
ds->springIRQ7 = 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->rtcSource = NULL;
ds->rumble = NULL;
@ -196,22 +200,26 @@ static void DSProcessEvents(struct ARMCore* cpu) {
ds->springIRQ7 = 0;
}
do {
int32_t cycles = cpu->nextEvent;
int32_t nextEvent = INT_MAX;
int32_t cycles = cpu->nextEvent;
int32_t nextEvent = INT_MAX;
int32_t testEvent;
#ifndef NDEBUG
if (cycles < 0) {
mLOG(DS, FATAL, "Negative cycles passed: %i", cycles);
}
if (cycles < 0) {
mLOG(DS, FATAL, "Negative cycles passed: %i", cycles);
}
#endif
cpu->cycles -= cycles;
cpu->nextEvent = nextEvent;
testEvent = DSTimersProcessEvents(ds, cycles);
if (testEvent < nextEvent) {
nextEvent = testEvent;
}
if (cpu->halted) {
cpu->cycles = cpu->nextEvent;
}
} while (cpu->cycles >= cpu->nextEvent);
cpu->cycles -= cycles;
cpu->nextEvent = nextEvent;
if (cpu->halted) {
cpu->cycles = cpu->nextEvent;
}
}
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:
_writeTCMControl(cpu, crm, opcode2, value);
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);
}
}

View File

@ -12,6 +12,7 @@
#include "core/log.h"
#include "ds/memory.h"
#include "ds/timer.h"
#include "ds/video.h"
extern const uint32_t DS_ARM946ES_FREQUENCY;
@ -57,6 +58,10 @@ struct DS {
struct ARMCore* arm9;
struct DSMemory memory;
struct DSVideo video;
int timersEnabled7;
int timersEnabled9;
struct DSTimer timers7[4];
struct DSTimer timers9[4];
struct mCoreSync* sync;
@ -131,4 +136,8 @@ bool DSIsROM(struct VFile* vf);
void DSGetGameCode(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

View File

@ -24,11 +24,53 @@ void DS7IOInit(struct DS* ds) {
void DS7IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
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 |= 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;
default:
mLOG(DS_IO, STUB, "Stub DS7 I/O register write: %06X:%04X", address, value);
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) {
switch (address) {
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
break;
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) {
switch (address) {

View File

@ -248,6 +248,9 @@ uint32_t DS7Load32(struct ARMCore* cpu, uint32_t address, int* cycleCounter) {
}
mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
break;
case DS_REGION_IO:
value = DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
break;
default:
mLOG(DS_MEM, STUB, "Unimplemented DS7 Load32: %08X", address);
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);
break;
case DS_REGION_IO:
DS7IOWrite32(ds, address & 0x00FFFFFF, value);
break;
default:
mLOG(DS_MEM, STUB, "Unimplemented DS7 Store32: %08X:%08X", address, value);
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);
});
break;
case DS_REGION_IO:
LDM_LOOP(value = DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16));
break;
default:
mLOG(DS_MEM, STUB, "Unimplemented DS7 LDM: %08X", address);
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);
break;
case DS_REGION_IO:
value = DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
break;
default:
mLOG(DS_MEM, STUB, "Unimplemented DS9 Load32: %08X", address);
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);
break;
case DS_REGION_IO:
DS9IOWrite32(ds, address & 0x00FFFFFF, value);
break;
default:
mLOG(DS_MEM, STUB, "Unimplemented DS9 Store32: %08X:%08X", address, value);
break;

110
src/ds/timer.c Normal file
View File

@ -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;
}

34
src/ds/timer.h Normal file
View File

@ -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