GB Memory: Add GDMAs
Jeffrey Pfau jeffrey@endrift.com
Tue, 16 Feb 2016 23:00:24 -0800
3 files changed,
76 insertions(+),
8 deletions(-)
M
src/gb/io.c
→
src/gb/io.c
@@ -311,6 +311,16 @@ switch (address) {
case REG_VBK: GBVideoSwitchBank(&gb->video, value); break; + case REG_HDMA1: + case REG_HDMA2: + case REG_HDMA3: + case REG_HDMA4: + // Handled transparently by the registers + break; + case REG_HDMA5: + GBMemoryWriteHDMA5(gb, value); + value &= 0x7F; + break; case REG_BCPS: gb->video.bcpIndex = value & 0x3F; gb->video.bcpIncrement = value & 0x80;@@ -436,8 +446,13 @@ break;
default: if (gb->model >= GB_MODEL_CGB) { switch (address) { + case REG_VBK: + case REG_HDMA1: + case REG_HDMA2: + case REG_HDMA3: + case REG_HDMA4: + case REG_HDMA5: case REG_SVBK: - case REG_VBK: // Handled transparently by the registers goto success; default:
M
src/gb/memory.c
→
src/gb/memory.c
@@ -36,7 +36,7 @@ // TODO
} static void _GBMemoryDMAService(struct GB* gb); - +static void _GBMemoryHDMAService(struct GB* gb); void GBMemoryInit(struct GB* gb) { struct LR35902Core* cpu = gb->cpu;@@ -56,6 +56,8 @@ gb->memory.mbc = 0;
gb->memory.dmaNext = INT_MAX; gb->memory.dmaRemaining = 0; + gb->memory.hdmaNext = INT_MAX; + gb->memory.hdmaRemaining = 0; memset(gb->memory.hram, 0, sizeof(gb->memory.hram));@@ -257,14 +259,24 @@ }
} int32_t GBMemoryProcessEvents(struct GB* gb, int32_t cycles) { - if (!gb->memory.dmaRemaining) { - return INT_MAX; + int nextEvent = INT_MAX; + if (gb->memory.dmaRemaining) { + gb->memory.dmaNext -= cycles; + if (gb->memory.dmaNext <= 0) { + _GBMemoryDMAService(gb); + } + nextEvent = gb->memory.dmaNext; } - gb->memory.dmaNext -= cycles; - if (gb->memory.dmaNext <= 0) { - _GBMemoryDMAService(gb); + if (gb->memory.hdmaRemaining) { + gb->memory.hdmaNext -= cycles; + if (gb->memory.hdmaNext <= 0) { + _GBMemoryHDMAService(gb); + } + if (gb->memory.hdmaNext < nextEvent) { + nextEvent = gb->memory.hdmaNext; + } } - return gb->memory.dmaNext; + return nextEvent; } void GBMemoryDMA(struct GB* gb, uint16_t base) {@@ -282,6 +294,26 @@ gb->memory.dmaDest = 0;
gb->memory.dmaRemaining = 0xA0; } +void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value) { + gb->memory.hdmaSource = gb->memory.io[REG_HDMA1] << 8; + gb->memory.hdmaSource |= gb->memory.io[REG_HDMA2]; + gb->memory.hdmaDest = gb->memory.io[REG_HDMA3] << 8; + gb->memory.hdmaDest |= gb->memory.io[REG_HDMA4]; + gb->memory.hdmaSource &= 0xFFF0; + if (gb->memory.hdmaSource >= 0x8000 && gb->memory.hdmaSource < 0xA000) { + mLOG(GB_MEM, GAME_ERROR, "Invalid HDMA source: %04X", gb->memory.hdmaSource); + return; + } + gb->memory.hdmaDest &= 0x1FF0; + gb->memory.hdmaDest |= 0x8000; + gb->memory.isHdma = value & 0x80; + if (!gb->memory.isHdma) { + gb->memory.hdmaRemaining = ((value & 0x7F) + 1) * 0x10; + gb->memory.hdmaNext = gb->cpu->cycles; + gb->cpu->nextEvent = gb->cpu->cycles; + } +} + void _GBMemoryDMAService(struct GB* gb) { uint8_t b = GBLoad8(gb->cpu, gb->memory.dmaSource); // TODO: Can DMA write OAM during modes 2-3?@@ -295,6 +327,20 @@ } else {
gb->memory.dmaNext = INT_MAX; gb->cpu->memory.store8 = GBStore8; gb->cpu->memory.load8 = GBLoad8; + } +} + +void _GBMemoryHDMAService(struct GB* gb) { + uint8_t b = gb->cpu->memory.load8(gb->cpu, gb->memory.hdmaSource); + gb->cpu->memory.store8(gb->cpu, gb->memory.hdmaDest, b); + ++gb->memory.hdmaSource; + ++gb->memory.hdmaDest; + --gb->memory.hdmaRemaining; + gb->cpu->cycles += 2; + if (gb->memory.hdmaRemaining) { + gb->memory.hdmaNext += 2; + } else { + gb->memory.io[REG_HDMA5] |= 0x80; } }
M
src/gb/memory.h
→
src/gb/memory.h
@@ -97,6 +97,12 @@ uint16_t dmaSource;
uint16_t dmaDest; int dmaRemaining; + int32_t hdmaNext; + uint16_t hdmaSource; + uint16_t hdmaDest; + int hdmaRemaining; + bool isHdma; + size_t romSize; bool rtcAccess;@@ -117,6 +123,7 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value);
int32_t GBMemoryProcessEvents(struct GB* gb, int32_t cycles); void GBMemoryDMA(struct GB* gb, uint16_t base); +void GBMemoryWriteHDMA5(struct GB* gb, uint8_t value); uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address); void GBDMAStore8(struct LR35902Core* cpu, uint16_t address, int8_t value);