Copy GBA.js DMA implementation

This commit is contained in:
Jeffrey Pfau 2013-04-16 23:13:52 -07:00
parent 20622b6135
commit e88d177582
6 changed files with 314 additions and 5 deletions

View File

@ -7,6 +7,31 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
case REG_DISPSTAT:
GBAVideoWriteDISPSTAT(&gba->video, value);
break;
case REG_DMA0CNT_LO:
GBAMemoryWriteDMACNT_LO(&gba->memory, 0, value);
break;
case REG_DMA0CNT_HI:
GBAMemoryWriteDMACNT_HI(&gba->memory, 0, value);
break;
case REG_DMA1CNT_LO:
GBAMemoryWriteDMACNT_LO(&gba->memory, 1, value);
break;
case REG_DMA1CNT_HI:
GBAMemoryWriteDMACNT_HI(&gba->memory, 1, value);
break;
case REG_DMA2CNT_LO:
GBAMemoryWriteDMACNT_LO(&gba->memory, 2, value);
break;
case REG_DMA2CNT_HI:
GBAMemoryWriteDMACNT_HI(&gba->memory, 2, value);
break;
case REG_DMA3CNT_LO:
GBAMemoryWriteDMACNT_LO(&gba->memory, 3, value);
break;
case REG_DMA3CNT_HI:
GBAMemoryWriteDMACNT_HI(&gba->memory, 3, value);
break;
case REG_WAITCNT:
GBAAdjustWaitstates(&gba->memory, value);
break;
@ -23,6 +48,39 @@ void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value) {
gba->memory.io[address >> 1] = value;
}
void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value) {
switch (address) {
case REG_DMA0SAD_LO:
GBAMemoryWriteDMASAD(&gba->memory, 0, value);
break;
case REG_DMA0DAD_LO:
GBAMemoryWriteDMADAD(&gba->memory, 0, value);
break;
case REG_DMA1SAD_LO:
GBAMemoryWriteDMASAD(&gba->memory, 1, value);
break;
case REG_DMA1DAD_LO:
GBAMemoryWriteDMADAD(&gba->memory, 1, value);
break;
case REG_DMA2SAD_LO:
GBAMemoryWriteDMASAD(&gba->memory, 2, value);
break;
case REG_DMA2DAD_LO:
GBAMemoryWriteDMADAD(&gba->memory, 2, value);
break;
case REG_DMA3SAD_LO:
GBAMemoryWriteDMASAD(&gba->memory, 3, value);
break;
case REG_DMA3DAD_LO:
GBAMemoryWriteDMADAD(&gba->memory, 3, value);
break;
default:
GBAIOWrite(gba, address, value & 0xFFFF);
GBAIOWrite(gba, address | 2, value >> 16);
break;
}
}
uint16_t GBAIORead(struct GBA* gba, uint32_t address) {
switch (address) {
case REG_DISPSTAT:

View File

@ -142,6 +142,7 @@ enum GBAIORegisters {
};
void GBAIOWrite(struct GBA* gba, uint32_t address, uint16_t value);
void GBAIOWrite32(struct GBA* gba, uint32_t address, uint32_t value);
uint16_t GBAIORead(struct GBA* gba, uint32_t address);
#endif

View File

@ -2,6 +2,7 @@
#include "gba-io.h"
#include <limits.h>
#include <string.h>
#include <sys/mman.h>
@ -13,6 +14,7 @@ static const char GBA_BASE_WAITSTATES[16] = { 0, 0, 2, 0, 0, 0, 0, 0, 4, 4, 4, 4
static const char GBA_BASE_WAITSTATES_SEQ[16] = { 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 4, 4, 8, 8, 4 };
static const char GBA_ROM_WAITSTATES[] = { 4, 3, 2, 8 };
static const char GBA_ROM_WAITSTATES_SEQ[] = { 2, 1, 4, 1, 8, 1 };
static const int DMA_OFFSET[] = { 1, -1, 0, 1 };
void GBAMemoryInit(struct GBAMemory* memory) {
memory->d.load32 = GBALoad32;
@ -29,6 +31,7 @@ void GBAMemoryInit(struct GBAMemory* memory) {
memory->iwram = mmap(0, SIZE_WORKING_IRAM, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
memory->rom = 0;
memset(memory->io, 0, sizeof(memory->io));
memset(memory->dma, 0, sizeof(memory->dma));
if (!memory->wram || !memory->iwram) {
GBAMemoryDeinit(memory);
@ -281,6 +284,7 @@ void GBAStore32(struct ARMMemory* memory, uint32_t address, int32_t value) {
gbaMemory->iwram[(address & (SIZE_WORKING_IRAM - 1)) >> 2] = value;
break;
case BASE_IO:
GBAIOWrite32(gbaMemory->p, address & (SIZE_IO - 1), value);
break;
case BASE_PALETTE_RAM:
break;
@ -392,3 +396,194 @@ void GBAAdjustWaitstates(struct GBAMemory* memory, uint16_t parameters) {
memory->d.activePrefetchCycles32 = memory->waitstates32[memory->activeRegion];
memory->d.activePrefetchCycles16 = memory->waitstates16[memory->activeRegion];
}
int32_t GBAMemoryProcessEvents(struct GBAMemory* memory, int32_t cycles) {
struct GBADMA* dma;
int32_t test = INT_MAX;
dma = &memory->dma[0];
dma->nextIRQ -= cycles;
if (dma->enable && dma->doIrq && dma->nextIRQ) {
if (dma->nextIRQ <= 0) {
dma->nextIRQ = INT_MAX;
GBARaiseIRQ(memory->p, IRQ_DMA0);
} else if (dma->nextIRQ < test) {
test = dma->nextIRQ;
}
}
dma = &memory->dma[1];
dma->nextIRQ -= cycles;
if (dma->enable && dma->doIrq && dma->nextIRQ) {
if (dma->nextIRQ <= 0) {
dma->nextIRQ = INT_MAX;
GBARaiseIRQ(memory->p, IRQ_DMA1);
} else if (dma->nextIRQ < test) {
test = dma->nextIRQ;
}
}
dma = &memory->dma[2];
dma->nextIRQ -= cycles;
if (dma->enable && dma->doIrq && dma->nextIRQ) {
if (dma->nextIRQ <= 0) {
dma->nextIRQ = INT_MAX;
GBARaiseIRQ(memory->p, IRQ_DMA2);
} else if (dma->nextIRQ < test) {
test = dma->nextIRQ;
}
}
dma = &memory->dma[3];
dma->nextIRQ -= cycles;
if (dma->enable && dma->doIrq && dma->nextIRQ) {
if (dma->nextIRQ <= 0) {
dma->nextIRQ = INT_MAX;
GBARaiseIRQ(memory->p, IRQ_DMA3);
} else if (dma->nextIRQ < test) {
test = dma->nextIRQ;
}
}
return test;
}
void GBAMemoryWriteDMASAD(struct GBAMemory* memory, int dma, uint32_t address) {
memory->dma[dma].source = address & 0xFFFFFFFE;
}
void GBAMemoryWriteDMADAD(struct GBAMemory* memory, int dma, uint32_t address) {
memory->dma[dma].dest = address & 0xFFFFFFFE;
}
void GBAMemoryWriteDMACNT_LO(struct GBAMemory* memory, int dma, uint16_t count) {
memory->dma[dma].count = count ? count : (dma == 3 ? 0x10000 : 0x4000);
}
void GBAMemoryWriteDMACNT_HI(struct GBAMemory* memory, int dma, uint16_t control) {
struct GBADMA* currentDma = &memory->dma[dma];
int wasEnabled = currentDma->enable;
currentDma->packed = control;
currentDma->nextIRQ = 0;
if (currentDma->drq) {
GBALog(GBA_LOG_STUB, "DRQ not implemented");
}
if (!wasEnabled && currentDma->enable) {
currentDma->nextSource = currentDma->source;
currentDma->nextDest = currentDma->dest;
currentDma->nextCount = currentDma->count;
GBAMemoryScheduleDMA(memory, dma, currentDma);
}
};
void GBAMemoryScheduleDMA(struct GBAMemory* memory, int number, struct GBADMA* info) {
switch (info->timing) {
case DMA_TIMING_NOW:
GBAMemoryServiceDMA(memory, number, info);
break;
case DMA_TIMING_HBLANK:
// Handled implicitly
break;
case DMA_TIMING_VBLANK:
// Handled implicitly
break;
case DMA_TIMING_CUSTOM:
switch (number) {
case 0:
GBALog(GBA_LOG_WARN, "Discarding invalid DMA0 scheduling");
break;
case 1:
case 2:
//this.cpu.irq.audio.scheduleFIFODma(number, info);
break;
case 3:
//this.cpu.irq.video.scheduleVCaptureDma(dma, info);
break;
}
}
}
void GBAMemoryRunHblankDMAs(struct GBAMemory* memory) {
struct GBADMA* dma;
int i;
for (i = 0; i < 4; ++i) {
dma = &memory->dma[i];
if (dma->enable && dma->timing == DMA_TIMING_HBLANK) {
GBAMemoryServiceDMA(memory, i, dma);
}
}
}
void GBAMemoryRunVblankDMAs(struct GBAMemory* memory) {
struct GBADMA* dma;
int i;
for (i = 0; i < 4; ++i) {
dma = &memory->dma[i];
if (dma->enable && dma->timing == DMA_TIMING_VBLANK) {
GBAMemoryServiceDMA(memory, i, dma);
}
}
}
void GBAMemoryServiceDMA(struct GBAMemory* memory, int number, struct GBADMA* info) {
if (!info->enable) {
// There was a DMA scheduled that got canceled
return;
}
uint32_t width = info->width ? 4 : 2;
int sourceOffset = DMA_OFFSET[info->srcControl] * width;
int destOffset = DMA_OFFSET[info->dstControl] * width;
int32_t wordsRemaining = info->nextCount;
uint32_t source = info->nextSource;
uint32_t dest = info->nextDest;
uint32_t sourceRegion = source >> BASE_OFFSET;
uint32_t destRegion = dest >> BASE_OFFSET;
if (width == 4) {
int32_t word;
source &= 0xFFFFFFFC;
dest &= 0xFFFFFFFC;
while (wordsRemaining--) {
word = GBALoad32(&memory->d, source);
GBAStore32(&memory->d, dest, word);
source += sourceOffset;
dest += destOffset;
}
} else {
uint16_t word;
while (wordsRemaining--) {
word = GBALoadU16(&memory->d, source);
GBAStore16(&memory->d, dest, word);
source += sourceOffset;
dest += destOffset;
}
}
if (info->doIrq) {
info->nextIRQ = memory->p->cpu.cycles + 2;
info->nextIRQ += (width == 4 ? memory->waitstates32[sourceRegion] + memory->waitstates32[destRegion]
: memory->waitstates16[sourceRegion] + memory->waitstates16[destRegion]);
info->nextIRQ += (info->count - 1) * (width == 4 ? memory->waitstatesSeq32[sourceRegion] + memory->waitstatesSeq32[destRegion]
: memory->waitstatesSeq16[sourceRegion] + memory->waitstatesSeq16[destRegion]);
}
info->nextSource = source;
info->nextDest = dest;
info->nextCount = wordsRemaining;
if (!info->repeat) {
info->enable = 0;
// Clear the enable bit in memory
memory->io[(REG_DMA0CNT_HI + number * (REG_DMA1CNT_HI - REG_DMA0CNT_HI)) >> 1] &= 0x7FE0;
} else {
info->nextCount = info->count;
if (info->dstControl == DMA_INCREMENT_RELOAD) {
info->nextDest = info->dest;
}
GBAMemoryScheduleDMA(memory, number, info);
}
}

View File

@ -59,6 +59,45 @@ 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
};
struct GBADMA {
union {
struct {
int : 5;
enum DMAControl dstControl : 2;
enum DMAControl srcControl : 2;
unsigned repeat : 1;
unsigned width : 1;
unsigned drq : 1;
enum DMATiming timing : 2;
unsigned doIrq : 1;
unsigned enable : 1;
};
uint16_t packed;
};
uint32_t source;
uint32_t dest;
int32_t count;
uint32_t nextSource;
uint32_t nextDest;
int32_t nextCount;
int32_t nextIRQ;
};
struct GBAMemory {
struct ARMMemory d;
struct GBA* p;
@ -74,8 +113,12 @@ struct GBAMemory {
char waitstatesSeq32[256];
char waitstatesSeq16[256];
int activeRegion;
struct GBADMA dma[4];
};
int32_t GBAMemoryProcessEvents(struct GBAMemory* memory, int32_t cycles);
int32_t GBALoad32(struct ARMMemory* memory, uint32_t address);
int16_t GBALoad16(struct ARMMemory* memory, uint32_t address);
uint16_t GBALoadU16(struct ARMMemory* memory, uint32_t address);
@ -88,4 +131,14 @@ void GBAStore8(struct ARMMemory* memory, uint32_t address, int8_t value);
void GBAAdjustWaitstates(struct GBAMemory* memory, uint16_t parameters);
void GBAMemoryWriteDMASAD(struct GBAMemory* memory, int dma, uint32_t address);
void GBAMemoryWriteDMADAD(struct GBAMemory* memory, int dma, uint32_t address);
void GBAMemoryWriteDMACNT_LO(struct GBAMemory* memory, int dma, uint16_t count);
void GBAMemoryWriteDMACNT_HI(struct GBAMemory* memory, int dma, uint16_t control);
void GBAMemoryScheduleDMA(struct GBAMemory* memory, int number, struct GBADMA* info);
void GBAMemoryServiceDMA(struct GBAMemory* memory, int number, struct GBADMA* info);
void GBAMemoryRunHblankDMAs(struct GBAMemory* memory);
void GBAMemoryRunVblankDMAs(struct GBAMemory* memory);
#endif

View File

@ -75,6 +75,11 @@ static void GBAProcessEvents(struct ARMBoard* board) {
nextEvent = testEvent;
}
testEvent = GBAMemoryProcessEvents(&gbaBoard->p->memory, cycles);
if (testEvent < nextEvent) {
nextEvent = testEvent;
}
board->cpu->cycles = 0;
board->cpu->nextEvent = nextEvent;
}
@ -98,10 +103,6 @@ void GBAWriteIE(struct GBA* gba, uint16_t value) {
GBALog(GBA_LOG_STUB, "SIO interrupts not implemented");
}
if (value & ((1 << IRQ_DMA0) | (1 << IRQ_DMA1) | (1 << IRQ_DMA2) | (1 << IRQ_DMA3))) {
GBALog(GBA_LOG_STUB, "DMA interrupts not implemented");
}
if (value & (1 << IRQ_KEYPAD)) {
GBALog(GBA_LOG_STUB, "Keypad interrupts not implemented");
}

View File

@ -29,7 +29,8 @@ enum GBAError {
};
enum GBALogLevel {
GBA_LOG_STUB
GBA_LOG_STUB,
GBA_LOG_WARN
};
struct GBABoard {