From 9d209aa9bb48c2f6431e59f2c8395205d9b3d3a1 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Tue, 16 Feb 2016 23:00:24 -0800 Subject: [PATCH] GB Memory: Add GDMAs --- src/gb/io.c | 17 +++++++++++++- src/gb/memory.c | 60 +++++++++++++++++++++++++++++++++++++++++++------ src/gb/memory.h | 7 ++++++ 3 files changed, 76 insertions(+), 8 deletions(-) diff --git a/src/gb/io.c b/src/gb/io.c index c2a3541b6..542593376 100644 --- a/src/gb/io.c +++ b/src/gb/io.c @@ -311,6 +311,16 @@ void GBIOWrite(struct GB* gb, unsigned address, uint8_t value) { 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 @@ uint8_t GBIORead(struct GB* gb, unsigned address) { default: if (gb->model >= GB_MODEL_CGB) { switch (address) { - case REG_SVBK: case REG_VBK: + case REG_HDMA1: + case REG_HDMA2: + case REG_HDMA3: + case REG_HDMA4: + case REG_HDMA5: + case REG_SVBK: // Handled transparently by the registers goto success; default: diff --git a/src/gb/memory.c b/src/gb/memory.c index 86d9f512d..0f6365ef3 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -36,7 +36,7 @@ static void GBSetActiveRegion(struct LR35902Core* cpu, uint16_t address) { } 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 @@ void GBMemoryInit(struct GB* gb) { 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 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) { } 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 @@ void GBMemoryDMA(struct GB* gb, uint16_t base) { 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? @@ -298,6 +330,20 @@ void _GBMemoryDMAService(struct GB* gb) { } } +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; + } +} + uint8_t GBDMALoad8(struct LR35902Core* cpu, uint16_t address) { struct GB* gb = (struct GB*) cpu->master; struct GBMemory* memory = &gb->memory; diff --git a/src/gb/memory.h b/src/gb/memory.h index 9bf06e9c2..9dc525da3 100644 --- a/src/gb/memory.h +++ b/src/gb/memory.h @@ -97,6 +97,12 @@ struct GBMemory { 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);