all repos — mgba @ 327c3e78c6f3b4efb60a79c77e36c376754d2085

mGBA Game Boy Advance Emulator

GBA DMA: Refactor DMA out of memory.c
Jeffrey Pfau jeffrey@endrift.com
Tue, 13 Dec 2016 18:17:22 -0800
commit

327c3e78c6f3b4efb60a79c77e36c376754d2085

parent

a1689c80a71813c3506eca2a87a4add5dbf3f53e

M CHANGESCHANGES

@@ -67,6 +67,7 @@ - GB Audio: Improve initial envelope samples

- GB Audio: Initialize wave RAM to GBC values - Debugger: Add functions for read- or write-only watchpoints - GB Memory: Reset ROM bank when loading a ROM + - GBA DMA: Refactor DMA out of memory.c 0.5.1: (2016-10-05) Bugfixes:
M src/gba/audio.csrc/gba/audio.c

@@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "audio.h" #include "core/sync.h" +#include "gba/dma.h" #include "gba/gba.h" #include "gba/io.h" #include "gba/serialize.h"

@@ -234,7 +235,7 @@ dma->nextCount = 4;

dma->nextEvent = 0; dma->reg = GBADMARegisterSetWidth(dma->reg, 1); dma->reg = GBADMARegisterSetDestControl(dma->reg, 2); - GBAMemoryUpdateDMAs(audio->p, -cycles); + GBADMAUpdate(audio->p, -cycles); } else { channel->dmaSource = 0; }
A src/gba/dma.c

@@ -0,0 +1,292 @@

+/* 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 "dma.h" + +#include "gba/gba.h" +#include "gba/io.h" + +static void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate); + +static void GBADMAService(struct GBA* gba, int number, struct GBADMA* info); + +static const int DMA_OFFSET[] = { 1, -1, 0, 1 }; + +void GBADMAInit(struct GBA* gba) { + gba->memory.dmaEvent.name = "GBA DMA"; + gba->memory.dmaEvent.callback = _dmaEvent; + gba->memory.dmaEvent.context = gba; +} + +void GBADMAReset(struct GBA* gba) { + memset(gba->memory.dma, 0, sizeof(gba->memory.dma)); + int i; + for (i = 0; i < 4; ++i) { + gba->memory.dma[i].count = 0x4000; + gba->memory.dma[i].nextEvent = INT_MAX; + } + gba->memory.dma[3].count = 0x10000; + gba->memory.activeDMA = -1; +} +static bool _isValidDMASAD(int dma, uint32_t address) { + if (dma == 0 && address >= BASE_CART0 && address < BASE_CART_SRAM) { + return false; + } + return address >= BASE_WORKING_RAM; +} + +static bool _isValidDMADAD(int dma, uint32_t address) { + return dma == 3 || address < BASE_CART0; +} + +uint32_t GBADMAWriteSAD(struct GBA* gba, int dma, uint32_t address) { + struct GBAMemory* memory = &gba->memory; + address &= 0x0FFFFFFE; + if (_isValidDMASAD(dma, address)) { + memory->dma[dma].source = address; + } + return memory->dma[dma].source; +} + +uint32_t GBADMAWriteDAD(struct GBA* gba, int dma, uint32_t address) { + struct GBAMemory* memory = &gba->memory; + address &= 0x0FFFFFFE; + if (_isValidDMADAD(dma, address)) { + memory->dma[dma].dest = address; + } + return memory->dma[dma].dest; +} + +void GBADMAWriteCNT_LO(struct GBA* gba, int dma, uint16_t count) { + struct GBAMemory* memory = &gba->memory; + memory->dma[dma].count = count ? count : (dma == 3 ? 0x10000 : 0x4000); +} + +uint16_t GBADMAWriteCNT_HI(struct GBA* gba, int dma, uint16_t control) { + struct GBAMemory* memory = &gba->memory; + struct GBADMA* currentDma = &memory->dma[dma]; + int wasEnabled = GBADMARegisterIsEnable(currentDma->reg); + if (dma < 3) { + control &= 0xF7E0; + } else { + control &= 0xFFE0; + } + currentDma->reg = control; + + if (GBADMARegisterIsDRQ(currentDma->reg)) { + mLOG(GBA_MEM, STUB, "DRQ not implemented"); + } + + if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) { + currentDma->nextSource = currentDma->source; + currentDma->nextDest = currentDma->dest; + currentDma->nextCount = currentDma->count; + GBADMASchedule(gba, dma, currentDma); + } + // If the DMA has already occurred, this value might have changed since the function started + return currentDma->reg; +}; + +void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) { + info->hasStarted = 0; + switch (GBADMARegisterGetTiming(info->reg)) { + case DMA_TIMING_NOW: + info->nextEvent = 2 + 1; // XXX: Account for I cycle when writing + info->scheduledAt = mTimingCurrentTime(&gba->timing); + GBADMAUpdate(gba, 0); + break; + case DMA_TIMING_HBLANK: + // Handled implicitly + info->nextEvent = INT_MAX; + break; + case DMA_TIMING_VBLANK: + // Handled implicitly + info->nextEvent = INT_MAX; + break; + case DMA_TIMING_CUSTOM: + info->nextEvent = INT_MAX; + switch (number) { + case 0: + mLOG(GBA_MEM, WARN, "Discarding invalid DMA0 scheduling"); + break; + case 1: + case 2: + GBAAudioScheduleFifoDma(&gba->audio, number, info); + break; + case 3: + // GBAVideoScheduleVCaptureDma(dma, info); + break; + } + } +} + +void GBADMARunHblank(struct GBA* gba, int32_t cycles) { + struct GBAMemory* memory = &gba->memory; + struct GBADMA* dma; + bool dmaSeen = false; + if (memory->activeDMA >= 0) { + GBADMAUpdate(gba, mTimingCurrentTime(&gba->timing) - memory->dma[memory->activeDMA].scheduledAt); + } + int i; + for (i = 0; i < 4; ++i) { + dma = &memory->dma[i]; + if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_HBLANK) { + dma->nextEvent = 2 + cycles; + dma->scheduledAt = mTimingCurrentTime(&gba->timing); + dmaSeen = true; + } + } + if (dmaSeen) { + GBADMAUpdate(gba, 0); + } +} + +void GBADMARunVblank(struct GBA* gba, int32_t cycles) { + struct GBAMemory* memory = &gba->memory; + struct GBADMA* dma; + bool dmaSeen = false; + if (memory->activeDMA >= 0) { + GBADMAUpdate(gba, mTimingCurrentTime(&gba->timing) - memory->dma[memory->activeDMA].scheduledAt); + } + int i; + for (i = 0; i < 4; ++i) { + dma = &memory->dma[i]; + if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_VBLANK) { + dma->nextEvent = 2 + cycles; + dma->scheduledAt = mTimingCurrentTime(&gba->timing); + dmaSeen = true; + } + } + if (dmaSeen) { + GBADMAUpdate(gba, 0); + } +} + +void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) { + UNUSED(timing); + struct GBA* gba = context; + struct GBAMemory* memory = &gba->memory; + struct GBADMA* dma = &memory->dma[memory->activeDMA]; + dma->nextEvent = -cyclesLate; + GBADMAService(gba, memory->activeDMA, dma); +} + +void GBADMAUpdate(struct GBA* gba, int32_t cycles) { + int i; + struct GBAMemory* memory = &gba->memory; + memory->activeDMA = -1; + for (i = 3; i >= 0; --i) { + struct GBADMA* dma = &memory->dma[i]; + if (dma->nextEvent != INT_MAX) { + dma->nextEvent -= cycles; + if (GBADMARegisterIsEnable(dma->reg)) { + memory->activeDMA = i; + } + } + } + + if (memory->activeDMA >= 0) { + mTimingDeschedule(&gba->timing, &memory->dmaEvent); + mTimingSchedule(&gba->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].nextEvent); + } else { + gba->cpuBlocked = false; + } +} + +void GBADMAService(struct GBA* gba, int number, struct GBADMA* info) { + struct GBAMemory* memory = &gba->memory; + struct ARMCore* cpu = gba->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 >> BASE_OFFSET; + uint32_t destRegion = dest >> BASE_OFFSET; + int32_t cycles = 2; + + gba->cpuBlocked = true; + if (info->hasStarted < 2) { + if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) { + cycles += 2; + } + if (width == 4) { + cycles += memory->waitstatesNonseq32[sourceRegion] + memory->waitstatesNonseq32[destRegion]; + } else { + cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion]; + } + if (info->hasStarted < 1) { + info->hasStarted = wordsRemaining; + info->nextEvent = 0; + info->scheduledAt = mTimingCurrentTime(&gba->timing); + GBADMAUpdate(gba, -cycles); + return; + } + info->hasStarted = 2; + source &= -width; + dest &= -width; + } else { + if (width == 4) { + cycles += memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion]; + } else { + cycles += memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion]; + } + } + info->nextEvent += cycles; + + gba->performingDMA = 1 | (number << 1); + uint32_t word; + if (width == 4) { + word = cpu->memory.load32(cpu, source, 0); + gba->bus = word; + cpu->memory.store32(cpu, dest, word, 0); + } else { + if (sourceRegion == REGION_CART2_EX && memory->savedata.type == SAVEDATA_EEPROM) { + word = GBASavedataReadEEPROM(&memory->savedata); + cpu->memory.store16(cpu, dest, word, 0); + } else if (destRegion == REGION_CART2_EX) { + if (memory->savedata.type == SAVEDATA_AUTODETECT) { + mLOG(GBA_MEM, INFO, "Detected EEPROM savegame"); + GBASavedataInitEEPROM(&memory->savedata, gba->realisticTiming); + } + word = cpu->memory.load16(cpu, source, 0); + GBASavedataWriteEEPROM(&memory->savedata, word, wordsRemaining); + } else { + word = cpu->memory.load16(cpu, source, 0); + cpu->memory.store16(cpu, dest, word, 0); + } + gba->bus = word | (word << 16); + } + int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width; + int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width; + source += sourceOffset; + dest += destOffset; + --wordsRemaining; + gba->performingDMA = 0; + + if (!wordsRemaining) { + if (!GBADMARegisterIsRepeat(info->reg) || GBADMARegisterGetTiming(info->reg) == DMA_TIMING_NOW) { + info->reg = GBADMARegisterClearEnable(info->reg); + info->nextEvent = INT_MAX; + + // Clear the enable bit in memory + memory->io[(REG_DMA0CNT_HI + number * (REG_DMA1CNT_HI - REG_DMA0CNT_HI)) >> 1] &= 0x7FE0; + } else { + info->nextCount = info->count; + if (GBADMARegisterGetDestControl(info->reg) == DMA_INCREMENT_RELOAD) { + info->nextDest = info->dest; + } + GBADMASchedule(gba, number, info); + } + if (GBADMARegisterIsDoIRQ(info->reg)) { + GBARaiseIRQ(gba, IRQ_DMA0 + number); + } + } else { + info->nextDest = dest; + info->nextCount = wordsRemaining; + info->scheduledAt = mTimingCurrentTime(&gba->timing); + } + info->nextSource = source; + GBADMAUpdate(gba, 0); +}
A src/gba/dma.h

@@ -0,0 +1,26 @@

+/* 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 GBA_DMA_H +#define GBA_DMA_H + +#include "util/common.h" + +struct GBA; +void GBADMAInit(struct GBA* gba); +void GBADMAReset(struct GBA* gba); + +uint32_t GBADMAWriteSAD(struct GBA* gba, int dma, uint32_t address); +uint32_t GBADMAWriteDAD(struct GBA* gba, int dma, uint32_t address); +void GBADMAWriteCNT_LO(struct GBA* gba, int dma, uint16_t count); +uint16_t GBADMAWriteCNT_HI(struct GBA* gba, int dma, uint16_t control); + +struct GBADMA; +void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info); +void GBADMARunHblank(struct GBA* gba, int32_t cycles); +void GBADMARunVblank(struct GBA* gba, int32_t cycles); +void GBADMAUpdate(struct GBA* gba, int32_t cycles); + +#endif
M src/gba/io.csrc/gba/io.c

@@ -5,6 +5,7 @@ * 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 "io.h" +#include "gba/dma.h" #include "gba/rr/rr.h" #include "gba/serialize.h" #include "gba/sio.h"

@@ -460,28 +461,28 @@ GBAIOWrite32(gba, address - 2, gba->memory.io[(address >> 1) - 1] | (value << 16));

break; case REG_DMA0CNT_LO: - GBAMemoryWriteDMACNT_LO(gba, 0, value); + GBADMAWriteCNT_LO(gba, 0, value); break; case REG_DMA0CNT_HI: - value = GBAMemoryWriteDMACNT_HI(gba, 0, value); + value = GBADMAWriteCNT_HI(gba, 0, value); break; case REG_DMA1CNT_LO: - GBAMemoryWriteDMACNT_LO(gba, 1, value); + GBADMAWriteCNT_LO(gba, 1, value); break; case REG_DMA1CNT_HI: - value = GBAMemoryWriteDMACNT_HI(gba, 1, value); + value = GBADMAWriteCNT_HI(gba, 1, value); break; case REG_DMA2CNT_LO: - GBAMemoryWriteDMACNT_LO(gba, 2, value); + GBADMAWriteCNT_LO(gba, 2, value); break; case REG_DMA2CNT_HI: - value = GBAMemoryWriteDMACNT_HI(gba, 2, value); + value = GBADMAWriteCNT_HI(gba, 2, value); break; case REG_DMA3CNT_LO: - GBAMemoryWriteDMACNT_LO(gba, 3, value); + GBADMAWriteCNT_LO(gba, 3, value); break; case REG_DMA3CNT_HI: - value = GBAMemoryWriteDMACNT_HI(gba, 3, value); + value = GBADMAWriteCNT_HI(gba, 3, value); break; // Timers

@@ -621,28 +622,28 @@ case REG_FIFO_B_LO:

GBAAudioWriteFIFO(&gba->audio, address, value); break; case REG_DMA0SAD_LO: - value = GBAMemoryWriteDMASAD(gba, 0, value); + value = GBADMAWriteSAD(gba, 0, value); break; case REG_DMA0DAD_LO: - value = GBAMemoryWriteDMADAD(gba, 0, value); + value = GBADMAWriteDAD(gba, 0, value); break; case REG_DMA1SAD_LO: - value = GBAMemoryWriteDMASAD(gba, 1, value); + value = GBADMAWriteSAD(gba, 1, value); break; case REG_DMA1DAD_LO: - value = GBAMemoryWriteDMADAD(gba, 1, value); + value = GBADMAWriteDAD(gba, 1, value); break; case REG_DMA2SAD_LO: - value = GBAMemoryWriteDMASAD(gba, 2, value); + value = GBADMAWriteSAD(gba, 2, value); break; case REG_DMA2DAD_LO: - value = GBAMemoryWriteDMADAD(gba, 2, value); + value = GBADMAWriteDAD(gba, 2, value); break; case REG_DMA3SAD_LO: - value = GBAMemoryWriteDMASAD(gba, 3, value); + value = GBADMAWriteSAD(gba, 3, value); break; case REG_DMA3DAD_LO: - value = GBAMemoryWriteDMADAD(gba, 3, value); + value = GBADMAWriteDAD(gba, 3, value); break; default: if (address >= REG_DEBUG_STRING && address - REG_DEBUG_STRING < sizeof(gba->debugString)) {

@@ -952,10 +953,10 @@ LOAD_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest);

LOAD_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount); LOAD_32(gba->memory.dma[i].nextEvent, 0, &state->dma[i].nextEvent); if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) { - GBAMemoryScheduleDMA(gba, i, &gba->memory.dma[i]); + GBADMASchedule(gba, i, &gba->memory.dma[i]); } } GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]); - GBAMemoryUpdateDMAs(gba, 0); + GBADMAUpdate(gba, 0); GBAHardwareDeserialize(&gba->memory.hw, state); }
M src/gba/memory.csrc/gba/memory.c

@@ -1,4 +1,4 @@

-/* Copyright (c) 2013-2015 Jeffrey Pfau +/* 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

@@ -6,6 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "memory.h" #include "arm/decoder.h" +#include "gba/dma.h" #include "gba/hardware.h" #include "gba/io.h" #include "gba/serialize.h"

@@ -19,11 +20,9 @@

mLOG_DEFINE_CATEGORY(GBA_MEM, "GBA Memory"); static void _pristineCow(struct GBA* gba); -static void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate); static uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region); -static void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info); static int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait); static const char GBA_BASE_WAITSTATES[16] = { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4 };

@@ -32,7 +31,6 @@ static const char GBA_BASE_WAITSTATES_SEQ[16] = { 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 4, 4, 8, 8, 4 };

static const char GBA_BASE_WAITSTATES_SEQ_32[16] = { 0, 0, 5, 0, 0, 1, 1, 0, 5, 5, 9, 9, 17, 17, 9 }; static const char GBA_ROM_WAITSTATES[] = { 4, 3, 2, 8 }; static const char GBA_ROM_WAITSTATES_SEQ[] = { 2, 1, 4, 1, 8, 1 }; -static const int DMA_OFFSET[] = { 1, -1, 0, 1 }; void GBAMemoryInit(struct GBA* gba) { struct ARMCore* cpu = gba->cpu;

@@ -84,10 +82,7 @@ cpu->memory.activeNonseqCycles16 = 0;

gba->memory.biosPrefetch = 0; gba->memory.mirroring = false; - gba->memory.dmaEvent.name = "GBA DMA"; - gba->memory.dmaEvent.callback = _dmaEvent; - gba->memory.dmaEvent.context = gba; - + GBADMAInit(gba); GBAVFameInit(&gba->memory.vfame); }

@@ -120,14 +115,6 @@ }

gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM); memset(gba->memory.io, 0, sizeof(gba->memory.io)); - memset(gba->memory.dma, 0, sizeof(gba->memory.dma)); - int i; - for (i = 0; i < 4; ++i) { - gba->memory.dma[i].count = 0x4000; - gba->memory.dma[i].nextEvent = INT_MAX; - } - gba->memory.dma[3].count = 0x10000; - gba->memory.activeDMA = -1; gba->memory.prefetch = false; gba->memory.lastPrefetchedPc = 0;

@@ -136,6 +123,8 @@ if (!gba->memory.wram || !gba->memory.iwram) {

GBAMemoryDeinit(gba); mLOG(GBA_MEM, FATAL, "Could not map memory"); } + + GBADMAReset(gba); } static void _analyzeForIdleLoop(struct GBA* gba, struct ARMCore* cpu, uint32_t address) {

@@ -1490,267 +1479,6 @@ cpu->memory.activeSeqCycles16 = memory->waitstatesSeq16[memory->activeRegion];

cpu->memory.activeNonseqCycles32 = memory->waitstatesNonseq32[memory->activeRegion]; cpu->memory.activeNonseqCycles16 = memory->waitstatesNonseq16[memory->activeRegion]; -} - -static bool _isValidDMASAD(int dma, uint32_t address) { - if (dma == 0 && address >= BASE_CART0 && address < BASE_CART_SRAM) { - return false; - } - return address >= BASE_WORKING_RAM; -} - -static bool _isValidDMADAD(int dma, uint32_t address) { - return dma == 3 || address < BASE_CART0; -} - -uint32_t GBAMemoryWriteDMASAD(struct GBA* gba, int dma, uint32_t address) { - struct GBAMemory* memory = &gba->memory; - address &= 0x0FFFFFFE; - if (_isValidDMASAD(dma, address)) { - memory->dma[dma].source = address; - } - return memory->dma[dma].source; -} - -uint32_t GBAMemoryWriteDMADAD(struct GBA* gba, int dma, uint32_t address) { - struct GBAMemory* memory = &gba->memory; - address &= 0x0FFFFFFE; - if (_isValidDMADAD(dma, address)) { - memory->dma[dma].dest = address; - } - return memory->dma[dma].dest; -} - -void GBAMemoryWriteDMACNT_LO(struct GBA* gba, int dma, uint16_t count) { - struct GBAMemory* memory = &gba->memory; - memory->dma[dma].count = count ? count : (dma == 3 ? 0x10000 : 0x4000); -} - -uint16_t GBAMemoryWriteDMACNT_HI(struct GBA* gba, int dma, uint16_t control) { - struct GBAMemory* memory = &gba->memory; - struct GBADMA* currentDma = &memory->dma[dma]; - int wasEnabled = GBADMARegisterIsEnable(currentDma->reg); - if (dma < 3) { - control &= 0xF7E0; - } else { - control &= 0xFFE0; - } - currentDma->reg = control; - - if (GBADMARegisterIsDRQ(currentDma->reg)) { - mLOG(GBA_MEM, STUB, "DRQ not implemented"); - } - - if (!wasEnabled && GBADMARegisterIsEnable(currentDma->reg)) { - currentDma->nextSource = currentDma->source; - currentDma->nextDest = currentDma->dest; - currentDma->nextCount = currentDma->count; - GBAMemoryScheduleDMA(gba, dma, currentDma); - } - // If the DMA has already occurred, this value might have changed since the function started - return currentDma->reg; -}; - -void GBAMemoryScheduleDMA(struct GBA* gba, int number, struct GBADMA* info) { - info->hasStarted = 0; - switch (GBADMARegisterGetTiming(info->reg)) { - case DMA_TIMING_NOW: - info->nextEvent = 2 + 1; // XXX: Account for I cycle when writing - info->scheduledAt = mTimingCurrentTime(&gba->timing); - GBAMemoryUpdateDMAs(gba, 0); - break; - case DMA_TIMING_HBLANK: - // Handled implicitly - info->nextEvent = INT_MAX; - break; - case DMA_TIMING_VBLANK: - // Handled implicitly - info->nextEvent = INT_MAX; - break; - case DMA_TIMING_CUSTOM: - info->nextEvent = INT_MAX; - switch (number) { - case 0: - mLOG(GBA_MEM, WARN, "Discarding invalid DMA0 scheduling"); - break; - case 1: - case 2: - GBAAudioScheduleFifoDma(&gba->audio, number, info); - break; - case 3: - // GBAVideoScheduleVCaptureDma(dma, info); - break; - } - } -} - -void GBAMemoryRunHblankDMAs(struct GBA* gba, int32_t cycles) { - struct GBAMemory* memory = &gba->memory; - struct GBADMA* dma; - bool dmaSeen = false; - if (memory->activeDMA >= 0) { - GBAMemoryUpdateDMAs(gba, mTimingCurrentTime(&gba->timing) - memory->dma[memory->activeDMA].scheduledAt); - } - int i; - for (i = 0; i < 4; ++i) { - dma = &memory->dma[i]; - if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_HBLANK) { - dma->nextEvent = 2 + cycles; - dma->scheduledAt = mTimingCurrentTime(&gba->timing); - dmaSeen = true; - } - } - if (dmaSeen) { - GBAMemoryUpdateDMAs(gba, 0); - } -} - -void GBAMemoryRunVblankDMAs(struct GBA* gba, int32_t cycles) { - struct GBAMemory* memory = &gba->memory; - struct GBADMA* dma; - bool dmaSeen = false; - if (memory->activeDMA >= 0) { - GBAMemoryUpdateDMAs(gba, mTimingCurrentTime(&gba->timing) - memory->dma[memory->activeDMA].scheduledAt); - } - int i; - for (i = 0; i < 4; ++i) { - dma = &memory->dma[i]; - if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_VBLANK) { - dma->nextEvent = 2 + cycles; - dma->scheduledAt = mTimingCurrentTime(&gba->timing); - dmaSeen = true; - } - } - if (dmaSeen) { - GBAMemoryUpdateDMAs(gba, 0); - } -} - -void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) { - UNUSED(timing); - struct GBA* gba = context; - struct GBAMemory* memory = &gba->memory; - struct GBADMA* dma = &memory->dma[memory->activeDMA]; - dma->nextEvent = -cyclesLate; - GBAMemoryServiceDMA(gba, memory->activeDMA, dma); -} - -void GBAMemoryUpdateDMAs(struct GBA* gba, int32_t cycles) { - int i; - struct GBAMemory* memory = &gba->memory; - memory->activeDMA = -1; - for (i = 3; i >= 0; --i) { - struct GBADMA* dma = &memory->dma[i]; - if (dma->nextEvent != INT_MAX) { - dma->nextEvent -= cycles; - if (GBADMARegisterIsEnable(dma->reg)) { - memory->activeDMA = i; - } - } - } - - if (memory->activeDMA >= 0) { - mTimingDeschedule(&gba->timing, &memory->dmaEvent); - mTimingSchedule(&gba->timing, &memory->dmaEvent, memory->dma[memory->activeDMA].nextEvent); - } else { - gba->cpuBlocked = false; - } -} - -void GBAMemoryServiceDMA(struct GBA* gba, int number, struct GBADMA* info) { - struct GBAMemory* memory = &gba->memory; - struct ARMCore* cpu = gba->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 >> BASE_OFFSET; - uint32_t destRegion = dest >> BASE_OFFSET; - int32_t cycles = 2; - - gba->cpuBlocked = true; - if (info->hasStarted < 2) { - if (sourceRegion < REGION_CART0 || destRegion < REGION_CART0) { - cycles += 2; - } - if (width == 4) { - cycles += memory->waitstatesNonseq32[sourceRegion] + memory->waitstatesNonseq32[destRegion]; - } else { - cycles += memory->waitstatesNonseq16[sourceRegion] + memory->waitstatesNonseq16[destRegion]; - } - if (info->hasStarted < 1) { - info->hasStarted = wordsRemaining; - info->nextEvent = 0; - info->scheduledAt = mTimingCurrentTime(&gba->timing); - GBAMemoryUpdateDMAs(gba, -cycles); - return; - } - info->hasStarted = 2; - source &= -width; - dest &= -width; - } else { - if (width == 4) { - cycles += memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion]; - } else { - cycles += memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion]; - } - } - info->nextEvent += cycles; - - gba->performingDMA = 1 | (number << 1); - uint32_t word; - if (width == 4) { - word = cpu->memory.load32(cpu, source, 0); - gba->bus = word; - cpu->memory.store32(cpu, dest, word, 0); - } else { - if (sourceRegion == REGION_CART2_EX && memory->savedata.type == SAVEDATA_EEPROM) { - word = GBASavedataReadEEPROM(&memory->savedata); - cpu->memory.store16(cpu, dest, word, 0); - } else if (destRegion == REGION_CART2_EX) { - if (memory->savedata.type == SAVEDATA_AUTODETECT) { - mLOG(GBA_MEM, INFO, "Detected EEPROM savegame"); - GBASavedataInitEEPROM(&memory->savedata, gba->realisticTiming); - } - word = cpu->memory.load16(cpu, source, 0); - GBASavedataWriteEEPROM(&memory->savedata, word, wordsRemaining); - } else { - word = cpu->memory.load16(cpu, source, 0); - cpu->memory.store16(cpu, dest, word, 0); - } - gba->bus = word | (word << 16); - } - int sourceOffset = DMA_OFFSET[GBADMARegisterGetSrcControl(info->reg)] * width; - int destOffset = DMA_OFFSET[GBADMARegisterGetDestControl(info->reg)] * width; - source += sourceOffset; - dest += destOffset; - --wordsRemaining; - gba->performingDMA = 0; - - if (!wordsRemaining) { - if (!GBADMARegisterIsRepeat(info->reg) || GBADMARegisterGetTiming(info->reg) == DMA_TIMING_NOW) { - info->reg = GBADMARegisterClearEnable(info->reg); - info->nextEvent = INT_MAX; - - // Clear the enable bit in memory - memory->io[(REG_DMA0CNT_HI + number * (REG_DMA1CNT_HI - REG_DMA0CNT_HI)) >> 1] &= 0x7FE0; - } else { - info->nextCount = info->count; - if (GBADMARegisterGetDestControl(info->reg) == DMA_INCREMENT_RELOAD) { - info->nextDest = info->dest; - } - GBAMemoryScheduleDMA(gba, number, info); - } - if (GBADMARegisterIsDoIRQ(info->reg)) { - GBARaiseIRQ(gba, IRQ_DMA0 + number); - } - } else { - info->nextDest = dest; - info->nextCount = wordsRemaining; - info->scheduledAt = mTimingCurrentTime(&gba->timing); - } - info->nextSource = source; - GBAMemoryUpdateDMAs(gba, 0); } int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
M src/gba/memory.hsrc/gba/memory.h

@@ -178,16 +178,6 @@ int* cycleCounter);

void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters); -uint32_t GBAMemoryWriteDMASAD(struct GBA* gba, int dma, uint32_t address); -uint32_t GBAMemoryWriteDMADAD(struct GBA* gba, int dma, uint32_t address); -void GBAMemoryWriteDMACNT_LO(struct GBA* gba, int dma, uint16_t count); -uint16_t GBAMemoryWriteDMACNT_HI(struct GBA* gba, int dma, uint16_t control); - -void GBAMemoryScheduleDMA(struct GBA* gba, int number, struct GBADMA* info); -void GBAMemoryRunHblankDMAs(struct GBA* gba, int32_t cycles); -void GBAMemoryRunVblankDMAs(struct GBA* gba, int32_t cycles); -void GBAMemoryUpdateDMAs(struct GBA* gba, int32_t cycles); - struct GBASerializedState; void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state); void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state);
M src/gba/video.csrc/gba/video.c

@@ -7,6 +7,7 @@ #include "video.h"

#include "core/sync.h" #include "core/tile-cache.h" +#include "gba/dma.h" #include "gba/gba.h" #include "gba/io.h" #include "gba/renderers/tile-cache.h"

@@ -151,7 +152,7 @@ video->p->memory.io[REG_DISPSTAT >> 1] = GBARegisterDISPSTATFillInVblank(dispstat);

if (video->frameskipCounter <= 0) { video->renderer->finishFrame(video->renderer); } - GBAMemoryRunVblankDMAs(video->p, -cyclesLate); + GBADMARunVblank(video->p, -cyclesLate); if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) { GBARaiseIRQ(video->p, IRQ_VBLANK); }

@@ -183,7 +184,7 @@ video->renderer->drawScanline(video->renderer, video->vcount);

} if (video->vcount < VIDEO_VERTICAL_PIXELS) { - GBAMemoryRunHblankDMAs(video->p, -cyclesLate); + GBADMARunHblank(video->p, -cyclesLate); } if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) { GBARaiseIRQ(video->p, IRQ_HBLANK);