mirror of https://github.com/mgba-emu/mgba.git
GBA DMA: Refactor DMA out of memory.c
This commit is contained in:
parent
a1689c80a7
commit
327c3e78c6
1
CHANGES
1
CHANGES
|
@ -67,6 +67,7 @@ Misc:
|
||||||
- GB Audio: Initialize wave RAM to GBC values
|
- GB Audio: Initialize wave RAM to GBC values
|
||||||
- Debugger: Add functions for read- or write-only watchpoints
|
- Debugger: Add functions for read- or write-only watchpoints
|
||||||
- GB Memory: Reset ROM bank when loading a ROM
|
- GB Memory: Reset ROM bank when loading a ROM
|
||||||
|
- GBA DMA: Refactor DMA out of memory.c
|
||||||
|
|
||||||
0.5.1: (2016-10-05)
|
0.5.1: (2016-10-05)
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
|
|
||||||
#include "core/sync.h"
|
#include "core/sync.h"
|
||||||
|
#include "gba/dma.h"
|
||||||
#include "gba/gba.h"
|
#include "gba/gba.h"
|
||||||
#include "gba/io.h"
|
#include "gba/io.h"
|
||||||
#include "gba/serialize.h"
|
#include "gba/serialize.h"
|
||||||
|
@ -234,7 +235,7 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
|
||||||
dma->nextEvent = 0;
|
dma->nextEvent = 0;
|
||||||
dma->reg = GBADMARegisterSetWidth(dma->reg, 1);
|
dma->reg = GBADMARegisterSetWidth(dma->reg, 1);
|
||||||
dma->reg = GBADMARegisterSetDestControl(dma->reg, 2);
|
dma->reg = GBADMARegisterSetDestControl(dma->reg, 2);
|
||||||
GBAMemoryUpdateDMAs(audio->p, -cycles);
|
GBADMAUpdate(audio->p, -cycles);
|
||||||
} else {
|
} else {
|
||||||
channel->dmaSource = 0;
|
channel->dmaSource = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
37
src/gba/io.c
37
src/gba/io.c
|
@ -5,6 +5,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "io.h"
|
#include "io.h"
|
||||||
|
|
||||||
|
#include "gba/dma.h"
|
||||||
#include "gba/rr/rr.h"
|
#include "gba/rr/rr.h"
|
||||||
#include "gba/serialize.h"
|
#include "gba/serialize.h"
|
||||||
#include "gba/sio.h"
|
#include "gba/sio.h"
|
||||||
|
@ -460,28 +461,28 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case REG_DMA0CNT_LO:
|
case REG_DMA0CNT_LO:
|
||||||
GBAMemoryWriteDMACNT_LO(gba, 0, value);
|
GBADMAWriteCNT_LO(gba, 0, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA0CNT_HI:
|
case REG_DMA0CNT_HI:
|
||||||
value = GBAMemoryWriteDMACNT_HI(gba, 0, value);
|
value = GBADMAWriteCNT_HI(gba, 0, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA1CNT_LO:
|
case REG_DMA1CNT_LO:
|
||||||
GBAMemoryWriteDMACNT_LO(gba, 1, value);
|
GBADMAWriteCNT_LO(gba, 1, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA1CNT_HI:
|
case REG_DMA1CNT_HI:
|
||||||
value = GBAMemoryWriteDMACNT_HI(gba, 1, value);
|
value = GBADMAWriteCNT_HI(gba, 1, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA2CNT_LO:
|
case REG_DMA2CNT_LO:
|
||||||
GBAMemoryWriteDMACNT_LO(gba, 2, value);
|
GBADMAWriteCNT_LO(gba, 2, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA2CNT_HI:
|
case REG_DMA2CNT_HI:
|
||||||
value = GBAMemoryWriteDMACNT_HI(gba, 2, value);
|
value = GBADMAWriteCNT_HI(gba, 2, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA3CNT_LO:
|
case REG_DMA3CNT_LO:
|
||||||
GBAMemoryWriteDMACNT_LO(gba, 3, value);
|
GBADMAWriteCNT_LO(gba, 3, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA3CNT_HI:
|
case REG_DMA3CNT_HI:
|
||||||
value = GBAMemoryWriteDMACNT_HI(gba, 3, value);
|
value = GBADMAWriteCNT_HI(gba, 3, value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Timers
|
// Timers
|
||||||
|
@ -621,28 +622,28 @@ void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) {
|
||||||
GBAAudioWriteFIFO(&gba->audio, address, value);
|
GBAAudioWriteFIFO(&gba->audio, address, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA0SAD_LO:
|
case REG_DMA0SAD_LO:
|
||||||
value = GBAMemoryWriteDMASAD(gba, 0, value);
|
value = GBADMAWriteSAD(gba, 0, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA0DAD_LO:
|
case REG_DMA0DAD_LO:
|
||||||
value = GBAMemoryWriteDMADAD(gba, 0, value);
|
value = GBADMAWriteDAD(gba, 0, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA1SAD_LO:
|
case REG_DMA1SAD_LO:
|
||||||
value = GBAMemoryWriteDMASAD(gba, 1, value);
|
value = GBADMAWriteSAD(gba, 1, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA1DAD_LO:
|
case REG_DMA1DAD_LO:
|
||||||
value = GBAMemoryWriteDMADAD(gba, 1, value);
|
value = GBADMAWriteDAD(gba, 1, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA2SAD_LO:
|
case REG_DMA2SAD_LO:
|
||||||
value = GBAMemoryWriteDMASAD(gba, 2, value);
|
value = GBADMAWriteSAD(gba, 2, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA2DAD_LO:
|
case REG_DMA2DAD_LO:
|
||||||
value = GBAMemoryWriteDMADAD(gba, 2, value);
|
value = GBADMAWriteDAD(gba, 2, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA3SAD_LO:
|
case REG_DMA3SAD_LO:
|
||||||
value = GBAMemoryWriteDMASAD(gba, 3, value);
|
value = GBADMAWriteSAD(gba, 3, value);
|
||||||
break;
|
break;
|
||||||
case REG_DMA3DAD_LO:
|
case REG_DMA3DAD_LO:
|
||||||
value = GBAMemoryWriteDMADAD(gba, 3, value);
|
value = GBADMAWriteDAD(gba, 3, value);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (address >= REG_DEBUG_STRING && address - REG_DEBUG_STRING < sizeof(gba->debugString)) {
|
if (address >= REG_DEBUG_STRING && address - REG_DEBUG_STRING < sizeof(gba->debugString)) {
|
||||||
|
@ -952,10 +953,10 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
|
||||||
LOAD_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount);
|
LOAD_32(gba->memory.dma[i].nextCount, 0, &state->dma[i].nextCount);
|
||||||
LOAD_32(gba->memory.dma[i].nextEvent, 0, &state->dma[i].nextEvent);
|
LOAD_32(gba->memory.dma[i].nextEvent, 0, &state->dma[i].nextEvent);
|
||||||
if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) {
|
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]);
|
GBAAudioWriteSOUNDCNT_X(&gba->audio, gba->memory.io[REG_SOUNDCNT_X >> 1]);
|
||||||
GBAMemoryUpdateDMAs(gba, 0);
|
GBADMAUpdate(gba, 0);
|
||||||
GBAHardwareDeserialize(&gba->memory.hw, state);
|
GBAHardwareDeserialize(&gba->memory.hw, state);
|
||||||
}
|
}
|
||||||
|
|
282
src/gba/memory.c
282
src/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
|
* 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
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
@ -6,6 +6,7 @@
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
#include "arm/decoder.h"
|
#include "arm/decoder.h"
|
||||||
|
#include "gba/dma.h"
|
||||||
#include "gba/hardware.h"
|
#include "gba/hardware.h"
|
||||||
#include "gba/io.h"
|
#include "gba/io.h"
|
||||||
#include "gba/serialize.h"
|
#include "gba/serialize.h"
|
||||||
|
@ -19,11 +20,9 @@
|
||||||
mLOG_DEFINE_CATEGORY(GBA_MEM, "GBA Memory");
|
mLOG_DEFINE_CATEGORY(GBA_MEM, "GBA Memory");
|
||||||
|
|
||||||
static void _pristineCow(struct GBA* gba);
|
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 uint32_t _deadbeef[1] = { 0xE710B710 }; // Illegal instruction on both ARM and Thumb
|
||||||
|
|
||||||
static void GBASetActiveRegion(struct ARMCore* cpu, uint32_t region);
|
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 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 };
|
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,
|
||||||
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_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[] = { 4, 3, 2, 8 };
|
||||||
static const char GBA_ROM_WAITSTATES_SEQ[] = { 2, 1, 4, 1, 8, 1 };
|
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) {
|
void GBAMemoryInit(struct GBA* gba) {
|
||||||
struct ARMCore* cpu = gba->cpu;
|
struct ARMCore* cpu = gba->cpu;
|
||||||
|
@ -84,10 +82,7 @@ void GBAMemoryInit(struct GBA* gba) {
|
||||||
gba->memory.biosPrefetch = 0;
|
gba->memory.biosPrefetch = 0;
|
||||||
gba->memory.mirroring = false;
|
gba->memory.mirroring = false;
|
||||||
|
|
||||||
gba->memory.dmaEvent.name = "GBA DMA";
|
GBADMAInit(gba);
|
||||||
gba->memory.dmaEvent.callback = _dmaEvent;
|
|
||||||
gba->memory.dmaEvent.context = gba;
|
|
||||||
|
|
||||||
GBAVFameInit(&gba->memory.vfame);
|
GBAVFameInit(&gba->memory.vfame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,14 +115,6 @@ void GBAMemoryReset(struct GBA* gba) {
|
||||||
gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM);
|
gba->memory.iwram = anonymousMemoryMap(SIZE_WORKING_IRAM);
|
||||||
|
|
||||||
memset(gba->memory.io, 0, sizeof(gba->memory.io));
|
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.prefetch = false;
|
||||||
gba->memory.lastPrefetchedPc = 0;
|
gba->memory.lastPrefetchedPc = 0;
|
||||||
|
@ -136,6 +123,8 @@ void GBAMemoryReset(struct GBA* gba) {
|
||||||
GBAMemoryDeinit(gba);
|
GBAMemoryDeinit(gba);
|
||||||
mLOG(GBA_MEM, FATAL, "Could not map memory");
|
mLOG(GBA_MEM, FATAL, "Could not map memory");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GBADMAReset(gba);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _analyzeForIdleLoop(struct GBA* gba, struct ARMCore* cpu, uint32_t address) {
|
static void _analyzeForIdleLoop(struct GBA* gba, struct ARMCore* cpu, uint32_t address) {
|
||||||
|
@ -1492,267 +1481,6 @@ void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters) {
|
||||||
cpu->memory.activeNonseqCycles16 = memory->waitstatesNonseq16[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) {
|
int32_t GBAMemoryStall(struct ARMCore* cpu, int32_t wait) {
|
||||||
struct GBA* gba = (struct GBA*) cpu->master;
|
struct GBA* gba = (struct GBA*) cpu->master;
|
||||||
struct GBAMemory* memory = &gba->memory;
|
struct GBAMemory* memory = &gba->memory;
|
||||||
|
|
|
@ -178,16 +178,6 @@ uint32_t GBAStoreMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum
|
||||||
|
|
||||||
void GBAAdjustWaitstates(struct GBA* gba, uint16_t parameters);
|
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;
|
struct GBASerializedState;
|
||||||
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state);
|
void GBAMemorySerialize(const struct GBAMemory* memory, struct GBASerializedState* state);
|
||||||
void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state);
|
void GBAMemoryDeserialize(struct GBAMemory* memory, const struct GBASerializedState* state);
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "core/sync.h"
|
#include "core/sync.h"
|
||||||
#include "core/tile-cache.h"
|
#include "core/tile-cache.h"
|
||||||
|
#include "gba/dma.h"
|
||||||
#include "gba/gba.h"
|
#include "gba/gba.h"
|
||||||
#include "gba/io.h"
|
#include "gba/io.h"
|
||||||
#include "gba/renderers/tile-cache.h"
|
#include "gba/renderers/tile-cache.h"
|
||||||
|
@ -151,7 +152,7 @@ void _startHdraw(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
if (video->frameskipCounter <= 0) {
|
if (video->frameskipCounter <= 0) {
|
||||||
video->renderer->finishFrame(video->renderer);
|
video->renderer->finishFrame(video->renderer);
|
||||||
}
|
}
|
||||||
GBAMemoryRunVblankDMAs(video->p, -cyclesLate);
|
GBADMARunVblank(video->p, -cyclesLate);
|
||||||
if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
|
if (GBARegisterDISPSTATIsVblankIRQ(dispstat)) {
|
||||||
GBARaiseIRQ(video->p, IRQ_VBLANK);
|
GBARaiseIRQ(video->p, IRQ_VBLANK);
|
||||||
}
|
}
|
||||||
|
@ -183,7 +184,7 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (video->vcount < VIDEO_VERTICAL_PIXELS) {
|
if (video->vcount < VIDEO_VERTICAL_PIXELS) {
|
||||||
GBAMemoryRunHblankDMAs(video->p, -cyclesLate);
|
GBADMARunHblank(video->p, -cyclesLate);
|
||||||
}
|
}
|
||||||
if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
|
if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
|
||||||
GBARaiseIRQ(video->p, IRQ_HBLANK);
|
GBARaiseIRQ(video->p, IRQ_HBLANK);
|
||||||
|
|
Loading…
Reference in New Issue