GBA: Implement display start DMAs

This commit is contained in:
Vicki Pfau 2017-10-15 17:11:24 -07:00
parent f69b652e27
commit b05cfe7764
7 changed files with 67 additions and 48 deletions

View File

@ -27,6 +27,7 @@ Misc:
- Test: Restructure test suite into multiple executables - Test: Restructure test suite into multiple executables
- Python: Integrate tests from cinema test suite - Python: Integrate tests from cinema test suite
- Util: Don't build crc32 if the function already exists - Util: Don't build crc32 if the function already exists
- GBA: Implement display start DMAs
0.6.1: (2017-10-01) 0.6.1: (2017-10-01)
Bugfixes: Bugfixes:

View File

@ -10,6 +10,42 @@
CXX_GUARD_START CXX_GUARD_START
enum GBADMAControl {
GBA_DMA_INCREMENT = 0,
GBA_DMA_DECREMENT = 1,
GBA_DMA_FIXED = 2,
GBA_DMA_INCREMENT_RELOAD = 3
};
enum GBADMATiming {
GBA_DMA_TIMING_NOW = 0,
GBA_DMA_TIMING_VBLANK = 1,
GBA_DMA_TIMING_HBLANK = 2,
GBA_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; struct GBA;
void GBADMAInit(struct GBA* gba); void GBADMAInit(struct GBA* gba);
void GBADMAReset(struct GBA* gba); void GBADMAReset(struct GBA* gba);
@ -23,6 +59,7 @@ struct GBADMA;
void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info); void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info);
void GBADMARunHblank(struct GBA* gba, int32_t cycles); void GBADMARunHblank(struct GBA* gba, int32_t cycles);
void GBADMARunVblank(struct GBA* gba, int32_t cycles); void GBADMARunVblank(struct GBA* gba, int32_t cycles);
void GBADMARunDisplayStart(struct GBA* gba, int32_t cycles);
void GBADMAUpdate(struct GBA* gba); void GBADMAUpdate(struct GBA* gba);
CXX_GUARD_END CXX_GUARD_END

View File

@ -13,6 +13,7 @@ CXX_GUARD_START
#include <mgba/core/timing.h> #include <mgba/core/timing.h>
#include <mgba/internal/arm/arm.h> #include <mgba/internal/arm/arm.h>
#include <mgba/internal/gba/dma.h>
#include <mgba/internal/gba/hardware.h> #include <mgba/internal/gba/hardware.h>
#include <mgba/internal/gba/savedata.h> #include <mgba/internal/gba/savedata.h>
#include <mgba/internal/gba/vfame.h> #include <mgba/internal/gba/vfame.h>
@ -76,44 +77,8 @@ enum {
BASE_OFFSET = 24 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); 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 { struct GBAMemory {
uint32_t* bios; uint32_t* bios;
uint32_t* wram; uint32_t* wram;

View File

@ -111,7 +111,7 @@ void GBAAudioScheduleFifoDma(struct GBAAudio* audio, int number, struct GBADMA*
mLOG(GBA_AUDIO, GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest); mLOG(GBA_AUDIO, GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest);
return; return;
} }
info->reg = GBADMARegisterSetDestControl(info->reg, DMA_FIXED); info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED);
info->reg = GBADMARegisterSetWidth(info->reg, 1); info->reg = GBADMARegisterSetWidth(info->reg, 1);
} }
@ -235,7 +235,7 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
} }
if (CircleBufferSize(&channel->fifo) <= 4 * sizeof(int32_t) && channel->dmaSource > 0) { if (CircleBufferSize(&channel->fifo) <= 4 * sizeof(int32_t) && channel->dmaSource > 0) {
struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource]; struct GBADMA* dma = &audio->p->memory.dma[channel->dmaSource];
if (GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_CUSTOM) { if (GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM) {
dma->when = mTimingCurrentTime(&audio->p->timing) - cycles; dma->when = mTimingCurrentTime(&audio->p->timing) - cycles;
dma->nextCount = 4; dma->nextCount = 4;
GBADMASchedule(audio->p, channel->dmaSource, dma); GBADMASchedule(audio->p, channel->dmaSource, dma);

View File

@ -93,15 +93,15 @@ uint16_t GBADMAWriteCNT_HI(struct GBA* gba, int dma, uint16_t control) {
void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) { void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) {
switch (GBADMARegisterGetTiming(info->reg)) { switch (GBADMARegisterGetTiming(info->reg)) {
case DMA_TIMING_NOW: case GBA_DMA_TIMING_NOW:
info->when = mTimingCurrentTime(&gba->timing) + 3; // DMAs take 3 cycles to start info->when = mTimingCurrentTime(&gba->timing) + 3; // DMAs take 3 cycles to start
info->nextCount = info->count; info->nextCount = info->count;
break; break;
case DMA_TIMING_HBLANK: case GBA_DMA_TIMING_HBLANK:
case DMA_TIMING_VBLANK: case GBA_DMA_TIMING_VBLANK:
// Handled implicitly // Handled implicitly
return; return;
case DMA_TIMING_CUSTOM: case GBA_DMA_TIMING_CUSTOM:
switch (number) { switch (number) {
case 0: case 0:
mLOG(GBA_MEM, WARN, "Discarding invalid DMA0 scheduling"); mLOG(GBA_MEM, WARN, "Discarding invalid DMA0 scheduling");
@ -111,7 +111,7 @@ void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) {
GBAAudioScheduleFifoDma(&gba->audio, number, info); GBAAudioScheduleFifoDma(&gba->audio, number, info);
break; break;
case 3: case 3:
// GBAVideoScheduleVCaptureDma(dma, info); // Handled implicitly
break; break;
} }
} }
@ -124,7 +124,7 @@ void GBADMARunHblank(struct GBA* gba, int32_t cycles) {
int i; int i;
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
dma = &memory->dma[i]; dma = &memory->dma[i];
if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_HBLANK && !dma->nextCount) { if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_HBLANK && !dma->nextCount) {
dma->when = mTimingCurrentTime(&gba->timing) + 3 + cycles; dma->when = mTimingCurrentTime(&gba->timing) + 3 + cycles;
dma->nextCount = dma->count; dma->nextCount = dma->count;
} }
@ -138,7 +138,7 @@ void GBADMARunVblank(struct GBA* gba, int32_t cycles) {
int i; int i;
for (i = 0; i < 4; ++i) { for (i = 0; i < 4; ++i) {
dma = &memory->dma[i]; dma = &memory->dma[i];
if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_VBLANK && !dma->nextCount) { if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_VBLANK && !dma->nextCount) {
dma->when = mTimingCurrentTime(&gba->timing) + 3 + cycles; dma->when = mTimingCurrentTime(&gba->timing) + 3 + cycles;
dma->nextCount = dma->count; dma->nextCount = dma->count;
} }
@ -146,6 +146,16 @@ void GBADMARunVblank(struct GBA* gba, int32_t cycles) {
GBADMAUpdate(gba); GBADMAUpdate(gba);
} }
void GBADMARunDisplayStart(struct GBA* gba, int32_t cycles) {
struct GBAMemory* memory = &gba->memory;
struct GBADMA* dma = &memory->dma[3];
if (GBADMARegisterIsEnable(dma->reg) && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM && !dma->nextCount) {
dma->when = mTimingCurrentTime(&gba->timing) + 3 + cycles;
dma->nextCount = dma->count;
GBADMAUpdate(gba);
}
}
void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) { void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
UNUSED(timing); UNUSED(timing);
UNUSED(cyclesLate); UNUSED(cyclesLate);
@ -159,13 +169,16 @@ void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
GBADMAService(gba, memory->activeDMA, dma); GBADMAService(gba, memory->activeDMA, dma);
} else { } else {
dma->nextCount = 0; dma->nextCount = 0;
if (!GBADMARegisterIsRepeat(dma->reg) || GBADMARegisterGetTiming(dma->reg) == DMA_TIMING_NOW) { bool noRepeat = !GBADMARegisterIsRepeat(dma->reg);
noRepeat |= GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_NOW;
noRepeat |= memory->activeDMA == 3 && GBADMARegisterGetTiming(dma->reg) == GBA_DMA_TIMING_CUSTOM;
if (noRepeat) {
dma->reg = GBADMARegisterClearEnable(dma->reg); dma->reg = GBADMARegisterClearEnable(dma->reg);
// Clear the enable bit in memory // Clear the enable bit in memory
memory->io[(REG_DMA0CNT_HI + memory->activeDMA * (REG_DMA1CNT_HI - REG_DMA0CNT_HI)) >> 1] &= 0x7FE0; memory->io[(REG_DMA0CNT_HI + memory->activeDMA * (REG_DMA1CNT_HI - REG_DMA0CNT_HI)) >> 1] &= 0x7FE0;
} }
if (GBADMARegisterGetDestControl(dma->reg) == DMA_INCREMENT_RELOAD) { if (GBADMARegisterGetDestControl(dma->reg) == GBA_DMA_INCREMENT_RELOAD) {
dma->nextDest = dma->dest; dma->nextDest = dma->dest;
} }
if (GBADMARegisterIsDoIRQ(dma->reg)) { if (GBADMARegisterIsDoIRQ(dma->reg)) {

View File

@ -979,7 +979,7 @@ void GBAIODeserialize(struct GBA* gba, const struct GBASerializedState* state) {
LOAD_32(gba->memory.dma[i].nextDest, 0, &state->dma[i].nextDest); 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].nextCount, 0, &state->dma[i].nextCount);
LOAD_32(gba->memory.dma[i].when, 0, &state->dma[i].when); LOAD_32(gba->memory.dma[i].when, 0, &state->dma[i].when);
if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != DMA_TIMING_NOW) { if (GBADMARegisterGetTiming(gba->memory.dma[i].reg) != GBA_DMA_TIMING_NOW) {
GBADMASchedule(gba, i, &gba->memory.dma[i]); GBADMASchedule(gba, i, &gba->memory.dma[i]);
} }
} }

View File

@ -187,6 +187,9 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
if (video->vcount < VIDEO_VERTICAL_PIXELS) { if (video->vcount < VIDEO_VERTICAL_PIXELS) {
GBADMARunHblank(video->p, -cyclesLate); GBADMARunHblank(video->p, -cyclesLate);
} }
if (video->vcount >= 2 && video->vcount < VIDEO_VERTICAL_PIXELS + 2) {
GBADMARunDisplayStart(video->p, -cyclesLate);
}
if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) { if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) {
GBARaiseIRQ(video->p, IRQ_HBLANK); GBARaiseIRQ(video->p, IRQ_HBLANK);
} }