DS: Port over DMAs

This commit is contained in:
Jeffrey Pfau 2017-01-03 17:48:07 -08:00
parent 66db559f5b
commit 5a55b53983
8 changed files with 371 additions and 85 deletions

View File

@ -0,0 +1,46 @@
/* Copyright (c) 2013-2017 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_DMA_H
#define DS_DMA_H
#include <mgba-util/common.h>
CXX_GUARD_START
#include <mgba/internal/gba/dma.h>
enum DSDMATiming {
DS_DMA_TIMING_NOW = 0,
DS_DMA_TIMING_VBLANK = 1,
DS_DMA_TIMING_HBLANK = 2,
DS7_DMA_TIMING_SLOT1 = 2,
DS_DMA_TIMING_DISPLAY_START = 3,
DS7_DMA_TIMING_CUSTOM = 3,
DS_DMA_TIMING_MEMORY_DISPLAY = 4,
DS9_DMA_TIMING_SLOT1 = 5,
DS_DMA_TIMING_SLOT2 = 6,
DS_DMA_TIMING_GEOM_FIFO = 7,
};
DECL_BITS(GBADMARegister, Timing9, 11, 3);
struct DS;
struct DSCommon;
void DSDMAInit(struct DS* ds);
void DSDMAReset(struct DSCommon* dscore);
uint32_t DSDMAWriteSAD(struct DSCommon* dscore, int dma, uint32_t address);
uint32_t DSDMAWriteDAD(struct DSCommon* dscore, int dma, uint32_t address);
void DS7DMAWriteCNT(struct DSCommon* dscore, int dma, uint32_t value);
void DS9DMAWriteCNT(struct DSCommon* dscore, int dma, uint32_t value);
struct DSDMA;
void DSDMASchedule(struct DSCommon* dscore, int number, struct GBADMA* info);
void DSDMAUpdate(struct DSCommon* dscore);
CXX_GUARD_END
#endif

View File

@ -10,8 +10,10 @@
CXX_GUARD_START
#include <mgba/internal/arm/arm.h>
#include <mgba/core/log.h>
#include <mgba/core/timing.h>
#include <mgba/internal/arm/arm.h>
#include <mgba/internal/ds/dma.h>
#include <mgba/internal/ds/io.h>
enum DSMemoryRegion {
@ -65,50 +67,8 @@ enum {
DS_BASE_OFFSET = 24
};
enum DSDMAControl {
DS_DMA_INCREMENT = 0,
DS_DMA_DECREMENT = 1,
DS_DMA_FIXED = 2,
DS_DMA_INCREMENT_RELOAD = 3
};
enum DSDMATiming {
DS_DMA_TIMING_NOW = 0,
DS_DMA_TIMING_VBLANK = 1,
DS_DMA_TIMING_HBLANK = 2,
DS7_DMA_TIMING_SLOT1 = 2,
DS_DMA_TIMING_DISPLAY_START = 3,
DS7_DMA_TIMING_CUSTOM = 3,
DS_DMA_TIMING_MEMORY_DISPLAY = 4,
DS9_DMA_TIMING_SLOT1 = 5,
DS_DMA_TIMING_SLOT2 = 6,
DS_DMA_TIMING_GEOM_FIFO = 7,
};
mLOG_DECLARE_CATEGORY(DS_MEM);
DECL_BITFIELD(DSDMARegister, uint16_t);
DECL_BITS(DSDMARegister, DestControl, 5, 2);
DECL_BITS(DSDMARegister, SrcControl, 7, 2);
DECL_BIT(DSDMARegister, Repeat, 9);
DECL_BIT(DSDMARegister, Width, 10);
DECL_BITS(DSDMARegister, Timing7, 12, 2);
DECL_BITS(DSDMARegister, Timing9, 11, 3);
DECL_BIT(DSDMARegister, DoIRQ, 14);
DECL_BIT(DSDMARegister, Enable, 15);
struct DSDMA {
DSDMARegister reg;
uint32_t source;
uint32_t dest;
int32_t count;
uint32_t nextSource;
uint32_t nextDest;
int32_t nextCount;
int32_t nextEvent;
};
struct DSMemory {
uint32_t* bios7;
uint32_t* bios9;
@ -139,7 +99,8 @@ struct DSCoreMemory {
uint16_t* io;
int activeRegion;
struct DSDMA dma[4];
struct GBADMA dma[4];
struct mTimingEvent dmaEvent;
int activeDMA;
};

View File

@ -10,6 +10,42 @@
CXX_GUARD_START
enum DMAControl {
DMA_INCREMENT = 0,
DMA_DECREMENT = 1,
DMA_FIXED = 2,
DMA_INCREMENT_RELOAD = 3
};
enum DMATiming {
DMA_TIMING_NOW = 0,
DMA_TIMING_VBLANK = 1,
DMA_TIMING_HBLANK = 2,
DMA_TIMING_CUSTOM = 3
};
DECL_BITFIELD(GBADMARegister, uint16_t);
DECL_BITS(GBADMARegister, DestControl, 5, 2);
DECL_BITS(GBADMARegister, SrcControl, 7, 2);
DECL_BIT(GBADMARegister, Repeat, 9);
DECL_BIT(GBADMARegister, Width, 10);
DECL_BIT(GBADMARegister, DRQ, 11);
DECL_BITS(GBADMARegister, Timing, 12, 2);
DECL_BIT(GBADMARegister, DoIRQ, 14);
DECL_BIT(GBADMARegister, Enable, 15);
struct GBADMA {
GBADMARegister reg;
uint32_t source;
uint32_t dest;
int32_t count;
uint32_t nextSource;
uint32_t nextDest;
int32_t nextCount;
uint32_t when;
};
struct GBA;
void GBADMAInit(struct GBA* gba);
void GBADMAReset(struct GBA* gba);

View File

@ -13,10 +13,13 @@ CXX_GUARD_START
#include <mgba/core/timing.h>
#include <mgba/internal/arm/arm.h>
#include <mgba/internal/gba/dma.h>
#include <mgba/internal/gba/hardware.h>
#include <mgba/internal/gba/savedata.h>
#include <mgba/internal/gba/vfame.h>
mLOG_DECLARE_CATEGORY(GBA_MEM);
enum GBAMemoryRegion {
REGION_BIOS = 0x0,
REGION_WORKING_RAM = 0x2,
@ -75,44 +78,6 @@ enum {
BASE_OFFSET = 24
};
enum DMAControl {
DMA_INCREMENT = 0,
DMA_DECREMENT = 1,
DMA_FIXED = 2,
DMA_INCREMENT_RELOAD = 3
};
enum DMATiming {
DMA_TIMING_NOW = 0,
DMA_TIMING_VBLANK = 1,
DMA_TIMING_HBLANK = 2,
DMA_TIMING_CUSTOM = 3
};
mLOG_DECLARE_CATEGORY(GBA_MEM);
DECL_BITFIELD(GBADMARegister, uint16_t);
DECL_BITS(GBADMARegister, DestControl, 5, 2);
DECL_BITS(GBADMARegister, SrcControl, 7, 2);
DECL_BIT(GBADMARegister, Repeat, 9);
DECL_BIT(GBADMARegister, Width, 10);
DECL_BIT(GBADMARegister, DRQ, 11);
DECL_BITS(GBADMARegister, Timing, 12, 2);
DECL_BIT(GBADMARegister, DoIRQ, 14);
DECL_BIT(GBADMARegister, Enable, 15);
struct GBADMA {
GBADMARegister reg;
uint32_t source;
uint32_t dest;
int32_t count;
uint32_t nextSource;
uint32_t nextDest;
int32_t nextCount;
uint32_t when;
};
struct GBAMemory {
uint32_t* bios;
uint32_t* wram;

199
src/ds/dma.c Normal file
View File

@ -0,0 +1,199 @@
/* Copyright (c) 2013-2015 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 <mgba/internal/ds/dma.h>
#include <mgba/internal/ds/ds.h>
#include <mgba/internal/ds/io.h>
#include <mgba/internal/ds/memory.h>
static void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate);
static void DSDMAService(struct DSCommon* dscore, int number, struct GBADMA* info);
static const int DMA_OFFSET[] = { 1, -1, 0, 1 };
void DSDMAInit(struct DS* ds) {
ds->ds7.memory.dmaEvent.name = "DS7 DMA";
ds->ds7.memory.dmaEvent.callback = _dmaEvent;
ds->ds7.memory.dmaEvent.context = &ds->ds7;
ds->ds7.memory.dmaEvent.priority = 0x40;
ds->ds9.memory.dmaEvent.name = "DS9 DMA";
ds->ds9.memory.dmaEvent.callback = _dmaEvent;
ds->ds9.memory.dmaEvent.context = &ds->ds9;
ds->ds9.memory.dmaEvent.priority = 0x40;
}
void DSDMAReset(struct DSCommon* dscore) {
memset(dscore->memory.dma, 0, sizeof(dscore->memory.dma));
int i;
for (i = 0; i < 4; ++i) {
// TODO: This is wrong for DS7
dscore->memory.dma[i].count = 0x200000;
}
dscore->memory.activeDMA = -1;
}
uint32_t DSDMAWriteSAD(struct DSCommon* dscore, int dma, uint32_t address) {
address &= 0x0FFFFFFE;
dscore->memory.dma[dma].source = address;
return dscore->memory.dma[dma].source;
}
uint32_t DSDMAWriteDAD(struct DSCommon* dscore, int dma, uint32_t address) {
address &= 0x0FFFFFFE;
dscore->memory.dma[dma].dest = address;
return dscore->memory.dma[dma].dest;
}
void DS7DMAWriteCNT(struct DSCommon* dscore, int dma, uint32_t value) {
struct DSCoreMemory* memory = &dscore->memory;
struct GBADMA* currentDma = &memory->dma[dma];
uint32_t count = value & 0xFFFF;
currentDma->count = count ? count : (dma == 3 ? 0x10000 : 0x4000);
int wasEnabled = GBADMARegisterIsEnable(currentDma->reg);
unsigned control = (value >> 16) & 0xF7E0;
currentDma->reg = control;
if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) {
currentDma->nextSource = currentDma->source;
currentDma->nextDest = currentDma->dest;
DSDMASchedule(dscore, dma, currentDma);
}
}
void DS9DMAWriteCNT(struct DSCommon* dscore, int dma, uint32_t value) {
struct DSCoreMemory* memory = &dscore->memory;
struct GBADMA* currentDma = &memory->dma[dma];
uint32_t count = value & 0x1FFFFF;
currentDma->count = count ? count : 0x200000;
int wasEnabled = GBADMARegisterIsEnable(currentDma->reg);
unsigned control = (value >> 16) & 0xFFE0;
currentDma->reg = control;
if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) {
currentDma->nextSource = currentDma->source;
currentDma->nextDest = currentDma->dest;
DSDMASchedule(dscore, dma, currentDma);
}
}
void DSDMASchedule(struct DSCommon* dscore, int number, struct GBADMA* info) {
switch (GBADMARegisterGetTiming(info->reg)) {
case DS_DMA_TIMING_NOW:
info->when = mTimingCurrentTime(&dscore->timing) + 3; // DMAs take 3 cycles to start
info->nextCount = info->count;
break;
case DS_DMA_TIMING_HBLANK:
case DS_DMA_TIMING_VBLANK:
// Handled implicitly
return;
default:
mLOG(DS_MEM, STUB, "Unimplemented DMA");
}
DSDMAUpdate(dscore);
}
void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(timing);
UNUSED(cyclesLate);
struct DSCommon* dscore = context;
struct DSCoreMemory* memory = &dscore->memory;
struct GBADMA* dma = &memory->dma[memory->activeDMA];
if (dma->nextCount == dma->count) {
dma->when = mTimingCurrentTime(&dscore->timing);
}
if (dma->nextCount & 0xFFFFF) {
dscore->p->cpuBlocked = true; // TODO: Fix for ITCM
DSDMAService(dscore, memory->activeDMA, dma);
} else {
dma->nextCount = 0;
if (!GBADMARegisterIsRepeat(dma->reg) || GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_NOW) {
dma->reg = GBADMARegisterClearEnable(dma->reg);
// Clear the enable bit in memory
memory->io[(DS_REG_DMA0CNT_HI + memory->activeDMA * (DS_REG_DMA1CNT_HI - DS_REG_DMA0CNT_HI)) >> 1] &= 0x7FE0;
}
if (GBADMARegisterGetDestControl(dma->reg) == DMA_INCREMENT_RELOAD) {
dma->nextDest = dma->dest;
}
if (GBADMARegisterIsDoIRQ(dma->reg)) {
DSRaiseIRQ(dscore->cpu, dscore->memory.io, DS_IRQ_DMA0 + memory->activeDMA);
}
DSDMAUpdate(dscore);
}
}
void DSDMAUpdate(struct DSCommon* dscore) {
int i;
struct DSCoreMemory* memory = &dscore->memory;
memory->activeDMA = -1;
uint32_t currentTime = mTimingCurrentTime(&dscore->timing);
for (i = 0; i < 4; ++i) {
struct GBADMA* dma = &memory->dma[i];
if (GBADMARegisterIsEnable(dma->reg) && dma->nextCount) {
memory->activeDMA = i;
break;
}
}
if (memory->activeDMA >= 0) {
mTimingDeschedule(&dscore->timing, &memory->dmaEvent);
mTimingSchedule(&dscore->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].when - currentTime);
} else {
dscore->p->cpuBlocked = false;
}
}
void DSDMAService(struct DSCommon* dscore, int number, struct GBADMA* info) {
struct DSCoreMemory* memory = &dscore->memory;
struct ARMCore* cpu = dscore->cpu;
uint32_t width = 2 << GBADMARegisterGetWidth(info->reg);
int32_t wordsRemaining = info->nextCount;
uint32_t source = info->nextSource;
uint32_t dest = info->nextDest;
uint32_t sourceRegion = source >> DS_BASE_OFFSET;
uint32_t destRegion = dest >> DS_BASE_OFFSET;
int32_t cycles = 2;
if (info->count == info->nextCount) {
if (width == 4) {
cycles += dscore->p->memory.waitstatesNonseq32[sourceRegion] + dscore->p->memory.waitstatesNonseq32[destRegion];
} else {
cycles += dscore->p->memory.waitstatesNonseq16[sourceRegion] + dscore->p->memory.waitstatesNonseq16[destRegion];
}
source &= -width;
dest &= -width;
} else {
if (width == 4) {
cycles += dscore->p->memory.waitstatesSeq32[sourceRegion] + dscore->p->memory.waitstatesSeq32[destRegion];
} else {
cycles += dscore->p->memory.waitstatesSeq16[sourceRegion] + dscore->p->memory.waitstatesSeq16[destRegion];
}
}
info->when += cycles;
uint32_t word;
if (width == 4) {
word = cpu->memory.load32(cpu, source, 0);
cpu->memory.store32(cpu, dest, word, 0);
} else {
word = cpu->memory.load16(cpu, source, 0);
cpu->memory.store16(cpu, dest, word, 0);
}
int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width;
int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width;
source += sourceOffset;
dest += destOffset;
--wordsRemaining;
info->nextCount = wordsRemaining;
info->nextSource = source;
info->nextDest = dest;
if (!wordsRemaining) {
info->nextCount |= 0x80000000;
}
DSDMAUpdate(dscore);
}

View File

@ -111,6 +111,7 @@ static void DSInit(void* cpu, struct mCPUComponent* component) {
DS7InterruptHandlerInit(&ds->ds7.cpu->irqh);
DS9InterruptHandlerInit(&ds->ds9.cpu->irqh);
DSMemoryInit(ds);
DSDMAInit(ds);
ds->video.p = ds;
@ -183,6 +184,7 @@ void DS7Reset(struct ARMCore* cpu) {
mTimingClear(&ds->ds7.timing);
CircleBufferInit(&ds->ds7.fifo, 64);
DSMemoryReset(ds);
DSDMAReset(&ds->ds7);
DS7IOInit(ds);
struct DSCartridge* header = ds->romVf->map(ds->romVf, sizeof(*header), MAP_READ);
@ -215,6 +217,7 @@ void DS9Reset(struct ARMCore* cpu) {
struct DS* ds = (struct DS*) cpu->master;
mTimingClear(&ds->ds9.timing);
CircleBufferInit(&ds->ds9.fifo, 64);
DSDMAReset(&ds->ds9);
DS9IOInit(ds);
ds->activeCpu = cpu;

View File

@ -113,8 +113,47 @@ 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 DS_REG_DMA0SAD_LO:
value = DSDMAWriteSAD(&ds->ds7, 0, value);
break;
case DS_REG_DMA1SAD_LO:
value = DSDMAWriteSAD(&ds->ds7, 1, value);
break;
case DS_REG_DMA2SAD_LO:
value = DSDMAWriteSAD(&ds->ds7, 2, value);
break;
case DS_REG_DMA3SAD_LO:
value = DSDMAWriteSAD(&ds->ds7, 3, value);
break;
case DS_REG_DMA0DAD_LO:
value = DSDMAWriteDAD(&ds->ds7, 0, value);
break;
case DS_REG_DMA1DAD_LO:
value = DSDMAWriteDAD(&ds->ds7, 1, value);
break;
case DS_REG_DMA2DAD_LO:
value = DSDMAWriteDAD(&ds->ds7, 2, value);
break;
case DS_REG_DMA3DAD_LO:
value = DSDMAWriteDAD(&ds->ds7, 3, value);
break;
case DS_REG_DMA0CNT_LO:
DS7DMAWriteCNT(&ds->ds7, 0, value);
break;
case DS_REG_DMA1CNT_LO:
DS7DMAWriteCNT(&ds->ds7, 1, value);
break;
case DS_REG_DMA2CNT_LO:
DS7DMAWriteCNT(&ds->ds7, 2, value);
break;
case DS_REG_DMA3CNT_LO:
DS7DMAWriteCNT(&ds->ds7, 3, value);
break;
case DS_REG_IPCFIFOSEND_LO:
DSIPCWriteFIFO(&ds->ds9, value);
DSIPCWriteFIFO(&ds->ds7, value);
break;
case DS_REG_IE_LO:
DSWriteIE(ds->ds7.cpu, ds->ds7.memory.io, value);
@ -189,6 +228,45 @@ void DS9IOWrite8(struct DS* ds, uint32_t address, uint8_t value) {
void DS9IOWrite32(struct DS* ds, uint32_t address, uint32_t value) {
switch (address) {
case DS_REG_DMA0SAD_LO:
value = DSDMAWriteSAD(&ds->ds9, 0, value);
break;
case DS_REG_DMA1SAD_LO:
value = DSDMAWriteSAD(&ds->ds9, 1, value);
break;
case DS_REG_DMA2SAD_LO:
value = DSDMAWriteSAD(&ds->ds9, 2, value);
break;
case DS_REG_DMA3SAD_LO:
value = DSDMAWriteSAD(&ds->ds9, 3, value);
break;
case DS_REG_DMA0DAD_LO:
value = DSDMAWriteDAD(&ds->ds9, 0, value);
break;
case DS_REG_DMA1DAD_LO:
value = DSDMAWriteDAD(&ds->ds9, 1, value);
break;
case DS_REG_DMA2DAD_LO:
value = DSDMAWriteDAD(&ds->ds9, 2, value);
break;
case DS_REG_DMA3DAD_LO:
value = DSDMAWriteDAD(&ds->ds9, 3, value);
break;
case DS_REG_DMA0CNT_LO:
DS9DMAWriteCNT(&ds->ds9, 0, value);
break;
case DS_REG_DMA1CNT_LO:
DS9DMAWriteCNT(&ds->ds9, 1, value);
break;
case DS_REG_DMA2CNT_LO:
DS9DMAWriteCNT(&ds->ds9, 2, value);
break;
case DS_REG_DMA3CNT_LO:
DS9DMAWriteCNT(&ds->ds9, 3, value);
break;
case DS_REG_IPCFIFOSEND_LO:
DSIPCWriteFIFO(&ds->ds9, value);
break;

View File

@ -77,8 +77,6 @@ static void DS7SetActiveRegion(struct ARMCore* cpu, uint32_t region);
static void DS9SetActiveRegion(struct ARMCore* cpu, uint32_t region);
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->ds7.cpu;
arm7->memory.load32 = DS7Load32;