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
|
||||
- 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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue