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
- 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:

View File

@ -10,6 +10,42 @@
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

View File

@ -13,6 +13,7 @@ CXX_GUARD_START
#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,44 +77,8 @@ enum {
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;
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);
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 @@ void GBAAudioSampleFIFO(struct GBAAudio* audio, int fifoId, int32_t cycles) {
}
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);

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) {
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 @@ void GBADMASchedule(struct GBA* gba, int number, struct GBADMA* info) {
GBAAudioScheduleFifoDma(&gba->audio, number, info);
break;
case 3:
// GBAVideoScheduleVCaptureDma(dma, info);
// Handled implicitly
break;
}
}
@ -124,7 +124,7 @@ void GBADMARunHblank(struct GBA* gba, int32_t cycles) {
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 @@ void GBADMARunVblank(struct GBA* gba, int32_t cycles) {
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 @@ void GBADMARunVblank(struct GBA* gba, int32_t cycles) {
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 @@ void _dmaEvent(struct mTiming* timing, void* context, uint32_t cyclesLate) {
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)) {

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].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]);
}
}

View File

@ -187,6 +187,9 @@ void _startHblank(struct mTiming* timing, void* context, uint32_t cyclesLate) {
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);
}