mirror of https://github.com/mgba-emu/mgba.git
GBA: Implement display start DMAs
This commit is contained in:
parent
f69b652e27
commit
b05cfe7764
1
CHANGES
1
CHANGES
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue