DS: Port over DMAs
Jeffrey Pfau jeffrey@endrift.com
Tue, 03 Jan 2017 17:48:07 -0800
8 files changed,
371 insertions(+),
85 deletions(-)
A
include/mgba/internal/ds/dma.h
@@ -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
M
include/mgba/internal/ds/memory.h
→
include/mgba/internal/ds/memory.h
@@ -10,8 +10,10 @@ #include <mgba-util/common.h>
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 @@ DS_OFFSET_MASK = 0x00FFFFFF,
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; };
M
include/mgba/internal/gba/dma.h
→
include/mgba/internal/gba/dma.h
@@ -10,6 +10,42 @@ #include <mgba-util/common.h>
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);
M
include/mgba/internal/gba/memory.h
→
include/mgba/internal/gba/memory.h
@@ -13,9 +13,12 @@
#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,@@ -73,44 +76,6 @@
enum { OFFSET_MASK = 0x00FFFFFF, 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 {
A
src/ds/dma.c
@@ -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); +}
M
src/ds/ds.c
→
src/ds/ds.c
@@ -111,6 +111,7 @@
DS7InterruptHandlerInit(&ds->ds7.cpu->irqh); DS9InterruptHandlerInit(&ds->ds9.cpu->irqh); DSMemoryInit(ds); + DSDMAInit(ds); ds->video.p = ds;@@ -183,6 +184,7 @@ struct DS* ds = (struct DS*) cpu->master;
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 @@
struct DS* ds = (struct DS*) cpu->master; mTimingClear(&ds->ds9.timing); CircleBufferInit(&ds->ds9.fifo, 64); + DSDMAReset(&ds->ds9); DS9IOInit(ds); ds->activeCpu = cpu;
M
src/ds/io.c
→
src/ds/io.c
@@ -113,8 +113,47 @@ }
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 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;
M
src/ds/memory.c
→
src/ds/memory.c
@@ -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;