GBA: Implement display start DMAs
Vicki Pfau vi@endrift.com
Sun, 15 Oct 2017 17:11:24 -0700
7 files changed,
67 insertions(+),
48 deletions(-)
M
CHANGES
→
CHANGES
@@ -27,6 +27,7 @@ - Qt: Redo GameController into multiple classes
- Test: Restructure test suite into multiple executables - Python: Integrate tests from cinema test suite - Util: Don't build crc32 if the function already exists + - GBA: Implement display start DMAs 0.6.1: (2017-10-01) Bugfixes:
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 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; void GBADMAInit(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 GBADMARunHblank(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); CXX_GUARD_END
M
include/mgba/internal/gba/memory.h
→
include/mgba/internal/gba/memory.h
@@ -13,6 +13,7 @@
#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>@@ -76,43 +77,7 @@ 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 { uint32_t* bios;
M
src/gba/audio.c
→
src/gba/audio.c
@@ -111,7 +111,7 @@ default:
mLOG(GBA_AUDIO, GAME_ERROR, "Invalid FIFO destination: 0x%08X", info->dest); return; } - info->reg = GBADMARegisterSetDestControl(info->reg, DMA_FIXED); + info->reg = GBADMARegisterSetDestControl(info->reg, GBA_DMA_FIXED); info->reg = GBADMARegisterSetWidth(info->reg, 1); }@@ -235,7 +235,7 @@ return;
} if (CircleBufferSize(&channel->fifo) <= 4 * sizeof(int32_t) && channel->dmaSource > 0) { 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->nextCount = 4; GBADMASchedule(audio->p, channel->dmaSource, dma);
M
src/gba/dma.c
→
src/gba/dma.c
@@ -93,15 +93,15 @@ };
void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) { 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->nextCount = info->count; break; - case DMA_TIMING_HBLANK: - case DMA_TIMING_VBLANK: + case GBA_DMA_TIMING_HBLANK: + case GBA_DMA_TIMING_VBLANK: // Handled implicitly return; - case DMA_TIMING_CUSTOM: + case GBA_DMA_TIMING_CUSTOM: switch (number) { case 0: mLOG(GBA_MEM, WARN, "Discarding invalid DMA0 scheduling");@@ -111,7 +111,7 @@ case 2:
GBAAudioScheduleFifoDma(&gba->audio, number, info); break; case 3: - // GBAVideoScheduleVCaptureDma(dma, info); + // Handled implicitly break; } }@@ -124,7 +124,7 @@ struct GBADMA* dma;
int i; for (i = 0; i < 4; ++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->nextCount = dma->count; }@@ -138,7 +138,7 @@ struct GBADMA* dma;
int i; for (i = 0; i < 4; ++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->nextCount = dma->count; }@@ -146,6 +146,16 @@ }
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) { UNUSED(timing); UNUSED(cyclesLate);@@ -159,13 +169,16 @@ if (dma->nextCount & 0xFFFFF) {
GBADMAService(gba, memory->activeDMA, dma); } else { 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); // Clear the enable bit in memory 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; } if (GBADMARegisterIsDoIRQ(dma->reg)) {
M
src/gba/io.c
→
src/gba/io.c
@@ -979,7 +979,7 @@ LOAD_32(gba->memory.dma[i].nextSource, 0, &state->dma[i].nextSource);
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].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]); } }
M
src/gba/video.c
→
src/gba/video.c
@@ -187,6 +187,9 @@
if (video->vcount < VIDEO_VERTICAL_PIXELS) { GBADMARunHblank(video->p, -cyclesLate); } + if (video->vcount >= 2 && video->vcount < VIDEO_VERTICAL_PIXELS + 2) { + GBADMARunDisplayStart(video->p, -cyclesLate); + } if (GBARegisterDISPSTATIsHblankIRQ(dispstat)) { GBARaiseIRQ(video->p, IRQ_HBLANK); }